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);
1177 else if(command == TOCLIENT_MOVE_PLAYER)
1179 std::string datastring((char*)&data[2], datasize-2);
1180 std::istringstream is(datastring, std::ios_base::binary);
1181 Player *player = m_env.getLocalPlayer();
1182 assert(player != NULL);
1183 v3f pos = readV3F1000(is);
1184 f32 pitch = readF1000(is);
1185 f32 yaw = readF1000(is);
1186 player->setPosition(pos);
1187 /*player->setPitch(pitch);
1188 player->setYaw(yaw);*/
1190 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1191 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1197 Add to ClientEvent queue.
1198 This has to be sent to the main program because otherwise
1199 it would just force the pitch and yaw values to whatever
1200 the camera points to.
1203 event.type = CE_PLAYER_FORCE_MOVE;
1204 event.player_force_move.pitch = pitch;
1205 event.player_force_move.yaw = yaw;
1206 m_client_event_queue.push_back(event);
1208 // Ignore damage for a few seconds, so that the player doesn't
1209 // get damage from falling on ground
1210 m_ignore_damage_timer = 3.0;
1212 else if(command == TOCLIENT_PLAYERITEM)
1214 std::string datastring((char*)&data[2], datasize-2);
1215 std::istringstream is(datastring, std::ios_base::binary);
1217 u16 count = readU16(is);
1219 for (u16 i = 0; i < count; ++i) {
1220 u16 peer_id = readU16(is);
1221 Player *player = m_env.getPlayer(peer_id);
1225 infostream<<"Client: ignoring player item "
1226 << deSerializeString(is)
1227 << " for non-existing peer id " << peer_id
1230 } else if (player->isLocal()) {
1231 infostream<<"Client: ignoring player item "
1232 << deSerializeString(is)
1233 << " for local player" << std::endl;
1236 InventoryList *inv = player->inventory.getList("main");
1237 std::string itemstring(deSerializeString(is));
1239 item.deSerialize(itemstring, m_itemdef);
1240 inv->changeItem(0, item);
1241 if(itemstring.empty())
1243 infostream<<"Client: empty player item for peer "
1244 <<peer_id<<std::endl;
1248 infostream<<"Client: player item for peer "
1249 <<peer_id<<": "<<itemstring<<std::endl;
1254 else if(command == TOCLIENT_DEATHSCREEN)
1256 std::string datastring((char*)&data[2], datasize-2);
1257 std::istringstream is(datastring, std::ios_base::binary);
1259 bool set_camera_point_target = readU8(is);
1260 v3f camera_point_target = readV3F1000(is);
1263 event.type = CE_DEATHSCREEN;
1264 event.deathscreen.set_camera_point_target = set_camera_point_target;
1265 event.deathscreen.camera_point_target_x = camera_point_target.X;
1266 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1267 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1268 m_client_event_queue.push_back(event);
1270 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1272 io::IFileSystem *irrfs = m_device->getFileSystem();
1273 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1275 std::string datastring((char*)&data[2], datasize-2);
1276 std::istringstream is(datastring, std::ios_base::binary);
1278 // Mesh update thread must be stopped while
1279 // updating content definitions
1280 assert(!m_mesh_update_thread.IsRunning());
1282 int num_textures = readU16(is);
1284 core::list<TextureRequest> texture_requests;
1286 for(int i=0; i<num_textures; i++){
1288 bool texture_found = false;
1290 //read texture from cache
1291 std::string name = deSerializeString(is);
1292 std::string sha1_texture = deSerializeString(is);
1294 // if name contains illegal characters, ignore the texture
1295 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1296 errorstream<<"Client: ignoring illegal texture name "
1297 <<"sent by server: \""<<name<<"\""<<std::endl;
1301 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1303 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1306 if(fis.good() == false){
1307 infostream<<"Client::Texture not found in cache: "
1308 <<name << " expected it at: "<<tpath<<std::endl;
1312 std::ostringstream tmp_os(std::ios_base::binary);
1316 fis.read(buf, 1024);
1317 std::streamsize len = fis.gcount();
1318 tmp_os.write(buf, len);
1327 infostream<<"Client: Failed to read texture from cache\""
1328 <<name<<"\""<<std::endl;
1333 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1335 unsigned char *digest = sha1.getDigest();
1337 std::string digest_string = base64_encode(digest, 20);
1339 if (digest_string == sha1_texture) {
1340 // Silly irrlicht's const-incorrectness
1341 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1343 // Create an irrlicht memory file
1344 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1345 *data_rw, tmp_os.str().size(), "_tempreadfile");
1348 video::IImage *img = vdrv->createImageFromFile(rfile);
1350 infostream<<"Client: Cannot create image from data of "
1351 <<"received texture \""<<name<<"\""<<std::endl;
1355 m_tsrc->insertSourceImage(name, img);
1359 texture_found = true;
1363 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1364 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1371 //add texture request
1372 if (!texture_found) {
1373 infostream<<"Client: Adding texture to request list: \""
1374 <<name<<"\""<<std::endl;
1375 texture_requests.push_back(TextureRequest(name));
1381 event.type = CE_TEXTURES_UPDATED;
1382 m_client_event_queue.push_back(event);
1385 //send Texture request
1388 u16 number of textures requested
1394 std::ostringstream os(std::ios_base::binary);
1399 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1400 os.write((char*)buf, 2);
1402 writeU16(buf,texture_requests.size());
1403 os.write((char*)buf, 2);
1406 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1407 i != texture_requests.end(); i++) {
1408 os<<serializeString(i->name);
1412 std::string s = os.str();
1413 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1415 Send(0, data, true);
1416 infostream<<"Client: Sending request list to server " <<std::endl;
1418 else if(command == TOCLIENT_TEXTURES)
1420 io::IFileSystem *irrfs = m_device->getFileSystem();
1421 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1423 std::string datastring((char*)&data[2], datasize-2);
1424 std::istringstream is(datastring, std::ios_base::binary);
1426 // Mesh update thread must be stopped while
1427 // updating content definitions
1428 assert(!m_mesh_update_thread.IsRunning());
1432 u16 total number of texture bunches
1433 u16 index of this bunch
1434 u32 number of textures in this bunch
1442 int num_bunches = readU16(is);
1443 int bunch_i = readU16(is);
1444 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1445 if(bunch_i == num_bunches - 1)
1446 m_textures_received = true;
1447 int num_textures = readU32(is);
1448 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1449 <<num_bunches<<" textures="<<num_textures
1450 <<" size="<<datasize<<std::endl;
1451 for(int i=0; i<num_textures; i++){
1452 std::string name = deSerializeString(is);
1453 std::string data = deSerializeLongString(is);
1455 // if name contains illegal characters, ignore the texture
1456 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1457 errorstream<<"Client: ignoring illegal texture name "
1458 <<"sent by server: \""<<name<<"\""<<std::endl;
1462 // Silly irrlicht's const-incorrectness
1463 Buffer<char> data_rw(data.c_str(), data.size());
1464 // Create an irrlicht memory file
1465 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1466 *data_rw, data.size(), "_tempreadfile");
1469 video::IImage *img = vdrv->createImageFromFile(rfile);
1471 errorstream<<"Client: Cannot create image from data of "
1472 <<"received texture \""<<name<<"\""<<std::endl;
1477 fs::CreateAllDirs(getTextureCacheDir());
1479 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1480 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1482 if (outfile.good()) {
1483 outfile.write(data.c_str(),data.length());
1487 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1490 m_tsrc->insertSourceImage(name, img);
1496 event.type = CE_TEXTURES_UPDATED;
1497 m_client_event_queue.push_back(event);
1499 else if(command == TOCLIENT_TOOLDEF)
1501 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1503 else if(command == TOCLIENT_NODEDEF)
1505 infostream<<"Client: Received node definitions: packet size: "
1506 <<datasize<<std::endl;
1508 // Mesh update thread must be stopped while
1509 // updating content definitions
1510 assert(!m_mesh_update_thread.IsRunning());
1512 // Decompress node definitions
1513 std::string datastring((char*)&data[2], datasize-2);
1514 std::istringstream is(datastring, std::ios_base::binary);
1515 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1516 std::ostringstream tmp_os;
1517 decompressZlib(tmp_is, tmp_os);
1519 // Deserialize node definitions
1520 std::istringstream tmp_is2(tmp_os.str());
1521 m_nodedef->deSerialize(tmp_is2);
1522 m_nodedef_received = true;
1524 else if(command == TOCLIENT_CRAFTITEMDEF)
1526 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1528 else if(command == TOCLIENT_ITEMDEF)
1530 infostream<<"Client: Received item definitions: packet size: "
1531 <<datasize<<std::endl;
1533 // Mesh update thread must be stopped while
1534 // updating content definitions
1535 assert(!m_mesh_update_thread.IsRunning());
1537 // Decompress item definitions
1538 std::string datastring((char*)&data[2], datasize-2);
1539 std::istringstream is(datastring, std::ios_base::binary);
1540 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1541 std::ostringstream tmp_os;
1542 decompressZlib(tmp_is, tmp_os);
1544 // Deserialize node definitions
1545 std::istringstream tmp_is2(tmp_os.str());
1546 m_itemdef->deSerialize(tmp_is2);
1547 m_itemdef_received = true;
1551 infostream<<"Client: Ignoring unknown command "
1552 <<command<<std::endl;
1556 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1558 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1559 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1562 void Client::interact(u8 action, const PointedThing& pointed)
1564 if(connectedAndInitialized() == false){
1565 infostream<<"Client::interact() "
1566 "cancelled (not connected)"
1571 std::ostringstream os(std::ios_base::binary);
1577 [5] u32 length of the next item
1578 [9] serialized PointedThing
1580 0: start digging (from undersurface) or use
1581 1: stop digging (all parameters ignored)
1582 2: digging completed
1583 3: place block or item (to abovesurface)
1586 writeU16(os, TOSERVER_INTERACT);
1587 writeU8(os, action);
1588 writeU16(os, getPlayerItem());
1589 std::ostringstream tmp_os(std::ios::binary);
1590 pointed.serialize(tmp_os);
1591 os<<serializeLongString(tmp_os.str());
1593 std::string s = os.str();
1594 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1597 Send(0, data, true);
1600 void Client::sendSignNodeText(v3s16 p, std::string text)
1608 std::ostringstream os(std::ios_base::binary);
1612 writeU16(buf, TOSERVER_SIGNNODETEXT);
1613 os.write((char*)buf, 2);
1617 os.write((char*)buf, 6);
1619 u16 textlen = text.size();
1620 // Write text length
1621 writeS16(buf, textlen);
1622 os.write((char*)buf, 2);
1625 os.write((char*)text.c_str(), textlen);
1628 std::string s = os.str();
1629 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1631 Send(0, data, true);
1634 void Client::sendInventoryAction(InventoryAction *a)
1636 std::ostringstream os(std::ios_base::binary);
1640 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1641 os.write((char*)buf, 2);
1646 std::string s = os.str();
1647 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1649 Send(0, data, true);
1652 void Client::sendChatMessage(const std::wstring &message)
1654 std::ostringstream os(std::ios_base::binary);
1658 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1659 os.write((char*)buf, 2);
1662 writeU16(buf, message.size());
1663 os.write((char*)buf, 2);
1666 for(u32 i=0; i<message.size(); i++)
1670 os.write((char*)buf, 2);
1674 std::string s = os.str();
1675 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1677 Send(0, data, true);
1680 void Client::sendChangePassword(const std::wstring oldpassword,
1681 const std::wstring newpassword)
1683 Player *player = m_env.getLocalPlayer();
1687 std::string playername = player->getName();
1688 std::string oldpwd = translatePassword(playername, oldpassword);
1689 std::string newpwd = translatePassword(playername, newpassword);
1691 std::ostringstream os(std::ios_base::binary);
1692 u8 buf[2+PASSWORD_SIZE*2];
1694 [0] u16 TOSERVER_PASSWORD
1695 [2] u8[28] old password
1696 [30] u8[28] new password
1699 writeU16(buf, TOSERVER_PASSWORD);
1700 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1702 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1703 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1705 buf[2+PASSWORD_SIZE-1] = 0;
1706 buf[30+PASSWORD_SIZE-1] = 0;
1707 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1710 std::string s = os.str();
1711 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1713 Send(0, data, true);
1717 void Client::sendDamage(u8 damage)
1719 DSTACK(__FUNCTION_NAME);
1720 std::ostringstream os(std::ios_base::binary);
1722 writeU16(os, TOSERVER_DAMAGE);
1723 writeU8(os, damage);
1726 std::string s = os.str();
1727 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1729 Send(0, data, true);
1732 void Client::sendRespawn()
1734 DSTACK(__FUNCTION_NAME);
1735 std::ostringstream os(std::ios_base::binary);
1737 writeU16(os, TOSERVER_RESPAWN);
1740 std::string s = os.str();
1741 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1743 Send(0, data, true);
1746 void Client::sendPlayerPos()
1748 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1750 Player *myplayer = m_env.getLocalPlayer();
1751 if(myplayer == NULL)
1756 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1757 our_peer_id = m_con.GetPeerID();
1760 // Set peer id if not set already
1761 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1762 myplayer->peer_id = our_peer_id;
1763 // Check that an existing peer_id is the same as the connection's
1764 assert(myplayer->peer_id == our_peer_id);
1766 v3f pf = myplayer->getPosition();
1767 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1768 v3f sf = myplayer->getSpeed();
1769 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1770 s32 pitch = myplayer->getPitch() * 100;
1771 s32 yaw = myplayer->getYaw() * 100;
1776 [2] v3s32 position*100
1777 [2+12] v3s32 speed*100
1778 [2+12+12] s32 pitch*100
1779 [2+12+12+4] s32 yaw*100
1782 SharedBuffer<u8> data(2+12+12+4+4);
1783 writeU16(&data[0], TOSERVER_PLAYERPOS);
1784 writeV3S32(&data[2], position);
1785 writeV3S32(&data[2+12], speed);
1786 writeS32(&data[2+12+12], pitch);
1787 writeS32(&data[2+12+12+4], yaw);
1789 // Send as unreliable
1790 Send(0, data, false);
1793 void Client::sendPlayerItem(u16 item)
1795 Player *myplayer = m_env.getLocalPlayer();
1796 if(myplayer == NULL)
1799 u16 our_peer_id = m_con.GetPeerID();
1801 // Set peer id if not set already
1802 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1803 myplayer->peer_id = our_peer_id;
1804 // Check that an existing peer_id is the same as the connection's
1805 assert(myplayer->peer_id == our_peer_id);
1807 SharedBuffer<u8> data(2+2);
1808 writeU16(&data[0], TOSERVER_PLAYERITEM);
1809 writeU16(&data[2], item);
1812 Send(0, data, true);
1815 void Client::removeNode(v3s16 p)
1817 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1819 core::map<v3s16, MapBlock*> modified_blocks;
1823 //TimeTaker t("removeNodeAndUpdate", m_device);
1824 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1826 catch(InvalidPositionException &e)
1830 for(core::map<v3s16, MapBlock * >::Iterator
1831 i = modified_blocks.getIterator();
1832 i.atEnd() == false; i++)
1834 v3s16 p = i.getNode()->getKey();
1835 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1836 addUpdateMeshTaskWithEdge(p);
1840 void Client::addNode(v3s16 p, MapNode n)
1842 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1844 TimeTaker timer1("Client::addNode()");
1846 core::map<v3s16, MapBlock*> modified_blocks;
1850 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1851 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1853 catch(InvalidPositionException &e)
1856 //TimeTaker timer2("Client::addNode(): updateMeshes");
1858 for(core::map<v3s16, MapBlock * >::Iterator
1859 i = modified_blocks.getIterator();
1860 i.atEnd() == false; i++)
1862 v3s16 p = i.getNode()->getKey();
1863 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1864 addUpdateMeshTaskWithEdge(p);
1868 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1870 m_env.getClientMap().updateCamera(pos, dir, fov);
1873 void Client::renderPostFx()
1875 m_env.getClientMap().renderPostFx();
1878 MapNode Client::getNode(v3s16 p)
1880 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1881 return m_env.getMap().getNode(p);
1884 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1886 return m_env.getMap().getNodeMetadata(p);
1889 LocalPlayer* Client::getLocalPlayer()
1891 return m_env.getLocalPlayer();
1894 void Client::setPlayerControl(PlayerControl &control)
1896 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1897 LocalPlayer *player = m_env.getLocalPlayer();
1898 assert(player != NULL);
1899 player->control = control;
1902 void Client::selectPlayerItem(u16 item)
1904 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1905 m_playeritem = item;
1906 m_inventory_updated = true;
1907 sendPlayerItem(item);
1910 // Returns true if the inventory of the local player has been
1911 // updated from the server. If it is true, it is set to false.
1912 bool Client::getLocalInventoryUpdated()
1914 // m_inventory_updated is behind envlock
1915 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1916 bool updated = m_inventory_updated;
1917 m_inventory_updated = false;
1921 // Copies the inventory of the local player to parameter
1922 void Client::getLocalInventory(Inventory &dst)
1924 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1925 Player *player = m_env.getLocalPlayer();
1926 assert(player != NULL);
1927 dst = player->inventory;
1930 Inventory* Client::getInventory(const InventoryLocation &loc)
1933 case InventoryLocation::UNDEFINED:
1936 case InventoryLocation::CURRENT_PLAYER:
1938 Player *player = m_env.getLocalPlayer();
1939 assert(player != NULL);
1940 return &player->inventory;
1943 case InventoryLocation::PLAYER:
1945 Player *player = m_env.getPlayer(loc.name.c_str());
1948 return &player->inventory;
1951 case InventoryLocation::NODEMETA:
1953 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1956 return meta->getInventory();
1964 void Client::inventoryAction(InventoryAction *a)
1967 Send it to the server
1969 sendInventoryAction(a);
1972 Predict some local inventory changes
1974 a->clientApply(this, this);
1977 ClientActiveObject * Client::getSelectedActiveObject(
1979 v3f from_pos_f_on_map,
1980 core::line3d<f32> shootline_on_map
1983 core::array<DistanceSortedActiveObject> objects;
1985 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1987 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1990 // After this, the closest object is the first in the array.
1993 for(u32 i=0; i<objects.size(); i++)
1995 ClientActiveObject *obj = objects[i].obj;
1997 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1998 if(selection_box == NULL)
2001 v3f pos = obj->getPosition();
2003 core::aabbox3d<f32> offsetted_box(
2004 selection_box->MinEdge + pos,
2005 selection_box->MaxEdge + pos
2008 if(offsetted_box.intersectsWithLine(shootline_on_map))
2010 //infostream<<"Returning selected object"<<std::endl;
2015 //infostream<<"No object selected; returning NULL."<<std::endl;
2019 void Client::printDebugInfo(std::ostream &os)
2021 //JMutexAutoLock lock1(m_fetchblock_mutex);
2022 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2024 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2025 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2026 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2030 u32 Client::getDayNightRatio()
2032 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2033 return m_env.getDayNightRatio();
2038 Player *player = m_env.getLocalPlayer();
2039 assert(player != NULL);
2043 void Client::setTempMod(v3s16 p, NodeMod mod)
2045 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2046 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2048 core::map<v3s16, MapBlock*> affected_blocks;
2049 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2052 for(core::map<v3s16, MapBlock*>::Iterator
2053 i = affected_blocks.getIterator();
2054 i.atEnd() == false; i++)
2056 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2060 void Client::clearTempMod(v3s16 p)
2062 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2063 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2065 core::map<v3s16, MapBlock*> affected_blocks;
2066 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2069 for(core::map<v3s16, MapBlock*>::Iterator
2070 i = affected_blocks.getIterator();
2071 i.atEnd() == false; i++)
2073 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2077 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2079 /*infostream<<"Client::addUpdateMeshTask(): "
2080 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2083 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2088 Create a task to update the mesh of the block
2091 MeshMakeData *data = new MeshMakeData;
2094 //TimeTaker timer("data fill");
2096 // Debug: 1-6ms, avg=2ms
2097 data->fill(getDayNightRatio(), b);
2101 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2103 // Add task to queue
2104 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2106 /*infostream<<"Mesh update input queue size is "
2107 <<m_mesh_update_thread.m_queue_in.size()
2111 // Temporary test: make mesh directly in here
2113 //TimeTaker timer("make mesh");
2115 scene::SMesh *mesh_new = NULL;
2116 mesh_new = makeMapBlockMesh(data);
2117 b->replaceMesh(mesh_new);
2123 Mark mesh as non-expired at this point so that it can already
2124 be marked as expired again if the data changes
2126 b->setMeshExpired(false);
2129 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2133 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2134 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2139 v3s16 p = blockpos + v3s16(0,0,0);
2140 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2141 addUpdateMeshTask(p, ack_to_server);
2143 catch(InvalidPositionException &e){}
2146 v3s16 p = blockpos + v3s16(-1,0,0);
2147 addUpdateMeshTask(p);
2149 catch(InvalidPositionException &e){}
2151 v3s16 p = blockpos + v3s16(0,-1,0);
2152 addUpdateMeshTask(p);
2154 catch(InvalidPositionException &e){}
2156 v3s16 p = blockpos + v3s16(0,0,-1);
2157 addUpdateMeshTask(p);
2159 catch(InvalidPositionException &e){}
2162 ClientEvent Client::getClientEvent()
2164 if(m_client_event_queue.size() == 0)
2167 event.type = CE_NONE;
2170 return m_client_event_queue.pop_front();
2173 void Client::afterContentReceived()
2175 assert(m_itemdef_received);
2176 assert(m_nodedef_received);
2177 assert(m_textures_received);
2179 // Rebuild inherited images and recreate textures
2180 m_tsrc->rebuildImagesAndTextures();
2182 // Update texture atlas
2183 if(g_settings->getBool("enable_texture_atlas"))
2184 m_tsrc->buildMainAtlas(this);
2186 // Update node aliases
2187 m_nodedef->updateAliases(m_itemdef);
2189 // Update node textures
2190 m_nodedef->updateTextures(m_tsrc);
2192 // Update item textures and meshes
2193 m_itemdef->updateTexturesAndMeshes(this);
2195 // Start mesh update thread after setting up content definitions
2196 m_mesh_update_thread.Start();
2199 float Client::getRTT(void)
2202 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2203 } catch(con::PeerNotFoundException &e){
2208 // IGameDef interface
2210 IItemDefManager* Client::getItemDefManager()
2214 INodeDefManager* Client::getNodeDefManager()
2218 ICraftDefManager* Client::getCraftDefManager()
2221 //return m_craftdef;
2223 ITextureSource* Client::getTextureSource()
2227 u16 Client::allocateUnknownNodeId(const std::string &name)
2229 errorstream<<"Client::allocateUnknownNodeId(): "
2230 <<"Client cannot allocate node IDs"<<std::endl;
2232 return CONTENT_IGNORE;