3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
28 #include "mapsector.h"
29 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
42 static std::string getTextureCacheDir()
44 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
51 TextureRequest(const std::string &name_=""):
60 QueuedMeshUpdate::QueuedMeshUpdate():
63 ack_block_to_server(false)
67 QueuedMeshUpdate::~QueuedMeshUpdate()
77 MeshUpdateQueue::MeshUpdateQueue()
82 MeshUpdateQueue::~MeshUpdateQueue()
84 JMutexAutoLock lock(m_mutex);
86 for(std::vector<QueuedMeshUpdate*>::iterator
88 i != m_queue.end(); i++)
90 QueuedMeshUpdate *q = *i;
96 peer_id=0 adds with nobody to send to
98 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
100 DSTACK(__FUNCTION_NAME);
104 JMutexAutoLock lock(m_mutex);
110 Find if block is already in queue.
111 If it is, update the data and quit.
113 for(std::vector<QueuedMeshUpdate*>::iterator
115 i != m_queue.end(); i++)
117 QueuedMeshUpdate *q = *i;
123 if(ack_block_to_server)
124 q->ack_block_to_server = true;
132 QueuedMeshUpdate *q = new QueuedMeshUpdate;
135 q->ack_block_to_server = ack_block_to_server;
136 m_queue.push_back(q);
139 // Returned pointer must be deleted
140 // Returns NULL if queue is empty
141 QueuedMeshUpdate * MeshUpdateQueue::pop()
143 JMutexAutoLock lock(m_mutex);
145 bool must_be_urgent = !m_urgents.empty();
146 for(std::vector<QueuedMeshUpdate*>::iterator
148 i != m_queue.end(); i++)
150 QueuedMeshUpdate *q = *i;
151 if(must_be_urgent && m_urgents.count(q->p) == 0)
154 m_urgents.erase(q->p);
164 void * MeshUpdateThread::Thread()
168 log_register_thread("MeshUpdateThread");
170 DSTACK(__FUNCTION_NAME);
172 BEGIN_DEBUG_EXCEPTION_HANDLER
176 /*// Wait for output queue to flush.
177 // Allow 2 in queue, this makes less frametime jitter.
178 // Umm actually, there is no much difference
179 if(m_queue_out.size() >= 2)
185 QueuedMeshUpdate *q = m_queue_in.pop();
192 ScopeProfiler sp(g_profiler, "Client: Mesh making");
194 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
195 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
204 r.ack_block_to_server = q->ack_block_to_server;
206 /*infostream<<"MeshUpdateThread: Processed "
207 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
210 m_queue_out.push_back(r);
215 END_DEBUG_EXCEPTION_HANDLER(errorstream)
221 IrrlichtDevice *device,
222 const char *playername,
223 std::string password,
224 MapDrawControl &control,
225 IWritableTextureSource *tsrc,
226 IWritableItemDefManager *itemdef,
227 IWritableNodeDefManager *nodedef
232 m_mesh_update_thread(this),
234 new ClientMap(this, this, control,
235 device->getSceneManager()->getRootSceneNode(),
236 device->getSceneManager(), 666),
237 device->getSceneManager(),
240 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
242 m_server_ser_ver(SER_FMT_VER_INVALID),
244 m_inventory_updated(false),
245 m_inventory_from_server(NULL),
246 m_inventory_from_server_age(0.0),
252 m_password(password),
253 m_access_denied(false),
254 m_texture_receive_progress(0),
255 m_textures_received(false),
256 m_itemdef_received(false),
257 m_nodedef_received(false)
259 m_packetcounter_timer = 0.0;
260 //m_delete_unused_sectors_timer = 0.0;
261 m_connection_reinit_timer = 0.0;
262 m_avg_rtt_timer = 0.0;
263 m_playerpos_send_timer = 0.0;
264 m_ignore_damage_timer = 0.0;
266 // Build main texture atlas, now that the GameDef exists (that is, us)
267 if(g_settings->getBool("enable_texture_atlas"))
268 m_tsrc->buildMainAtlas(this);
270 infostream<<"Not building texture atlas."<<std::endl;
276 Player *player = new LocalPlayer(this);
278 player->updateName(playername);
280 m_env.addPlayer(player);
287 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
291 m_mesh_update_thread.setRun(false);
292 while(m_mesh_update_thread.IsRunning())
295 delete m_inventory_from_server;
298 void Client::connect(Address address)
300 DSTACK(__FUNCTION_NAME);
301 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
302 m_con.SetTimeoutMs(0);
303 m_con.Connect(address);
306 bool Client::connectedAndInitialized()
308 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
310 if(m_con.Connected() == false)
313 if(m_server_ser_ver == SER_FMT_VER_INVALID)
319 void Client::step(float dtime)
321 DSTACK(__FUNCTION_NAME);
327 if(m_ignore_damage_timer > dtime)
328 m_ignore_damage_timer -= dtime;
330 m_ignore_damage_timer = 0.0;
332 m_animation_time += dtime;
333 if(m_animation_time > 60.0)
334 m_animation_time -= 60.0;
336 //infostream<<"Client steps "<<dtime<<std::endl;
339 //TimeTaker timer("ReceiveAll()", m_device);
345 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
347 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
348 m_con.RunTimeouts(dtime);
355 float &counter = m_packetcounter_timer;
361 infostream<<"Client packetcounter (20s):"<<std::endl;
362 m_packetcounter.print(infostream);
363 m_packetcounter.clear();
367 // Get connection status
368 bool connected = connectedAndInitialized();
373 Delete unused sectors
375 NOTE: This jams the game for a while because deleting sectors
379 float &counter = m_delete_unused_sectors_timer;
387 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
389 core::list<v3s16> deleted_blocks;
391 float delete_unused_sectors_timeout =
392 g_settings->getFloat("client_delete_unused_sectors_timeout");
394 // Delete sector blocks
395 /*u32 num = m_env.getMap().unloadUnusedData
396 (delete_unused_sectors_timeout,
397 true, &deleted_blocks);*/
399 // Delete whole sectors
400 m_env.getMap().unloadUnusedData
401 (delete_unused_sectors_timeout,
404 if(deleted_blocks.size() > 0)
406 /*infostream<<"Client: Deleted blocks of "<<num
407 <<" unused sectors"<<std::endl;*/
408 /*infostream<<"Client: Deleted "<<num
409 <<" unused sectors"<<std::endl;*/
415 // Env is locked so con can be locked.
416 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
418 core::list<v3s16>::Iterator i = deleted_blocks.begin();
419 core::list<v3s16> sendlist;
422 if(sendlist.size() == 255 || i == deleted_blocks.end())
424 if(sendlist.size() == 0)
433 u32 replysize = 2+1+6*sendlist.size();
434 SharedBuffer<u8> reply(replysize);
435 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
436 reply[2] = sendlist.size();
438 for(core::list<v3s16>::Iterator
439 j = sendlist.begin();
440 j != sendlist.end(); j++)
442 writeV3S16(&reply[2+1+6*k], *j);
445 m_con.Send(PEER_ID_SERVER, 1, reply, true);
447 if(i == deleted_blocks.end())
453 sendlist.push_back(*i);
461 if(connected == false)
463 float &counter = m_connection_reinit_timer;
469 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
471 Player *myplayer = m_env.getLocalPlayer();
472 assert(myplayer != NULL);
474 // Send TOSERVER_INIT
475 // [0] u16 TOSERVER_INIT
476 // [2] u8 SER_FMT_VER_HIGHEST
477 // [3] u8[20] player_name
478 // [23] u8[28] password (new in some version)
479 // [51] u16 client network protocol version (new in some version)
480 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
481 writeU16(&data[0], TOSERVER_INIT);
482 writeU8(&data[2], SER_FMT_VER_HIGHEST);
484 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
485 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
487 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
490 memset((char*)&data[23], 0, PASSWORD_SIZE);
491 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
493 // This should be incremented in each version
494 writeU16(&data[51], PROTOCOL_VERSION);
496 // Send as unreliable
497 Send(0, data, false);
500 // Not connected, return
505 Do stuff if connected
509 Run Map's timers and unload unused data
511 const float map_timer_and_unload_dtime = 5.25;
512 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
514 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
515 core::list<v3s16> deleted_blocks;
516 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
517 g_settings->getFloat("client_unload_unused_data_timeout"),
520 /*if(deleted_blocks.size() > 0)
521 infostream<<"Client: Unloaded "<<deleted_blocks.size()
522 <<" unused blocks"<<std::endl;*/
526 NOTE: This loop is intentionally iterated the way it is.
529 core::list<v3s16>::Iterator i = deleted_blocks.begin();
530 core::list<v3s16> sendlist;
533 if(sendlist.size() == 255 || i == deleted_blocks.end())
535 if(sendlist.size() == 0)
544 u32 replysize = 2+1+6*sendlist.size();
545 SharedBuffer<u8> reply(replysize);
546 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
547 reply[2] = sendlist.size();
549 for(core::list<v3s16>::Iterator
550 j = sendlist.begin();
551 j != sendlist.end(); j++)
553 writeV3S16(&reply[2+1+6*k], *j);
556 m_con.Send(PEER_ID_SERVER, 1, reply, true);
558 if(i == deleted_blocks.end())
564 sendlist.push_back(*i);
574 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
576 // Control local player (0ms)
577 LocalPlayer *player = m_env.getLocalPlayer();
578 assert(player != NULL);
579 player->applyControl(dtime);
581 //TimeTaker envtimer("env step", m_device);
590 ClientEnvEvent event = m_env.getClientEvent();
591 if(event.type == CEE_NONE)
595 else if(event.type == CEE_PLAYER_DAMAGE)
597 if(m_ignore_damage_timer <= 0)
599 u8 damage = event.player_damage.amount;
601 if(event.player_damage.send_to_server)
604 // Add to ClientEvent queue
606 event.type = CE_PLAYER_DAMAGE;
607 event.player_damage.amount = damage;
608 m_client_event_queue.push_back(event);
618 float &counter = m_avg_rtt_timer;
623 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
624 // connectedAndInitialized() is true, peer exists.
625 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
626 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
631 Send player position to server
634 float &counter = m_playerpos_send_timer;
644 Replace updated meshes
647 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
649 //TimeTaker timer("** Processing mesh update result queue");
652 /*infostream<<"Mesh update result queue size is "
653 <<m_mesh_update_thread.m_queue_out.size()
656 while(m_mesh_update_thread.m_queue_out.size() > 0)
658 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
659 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
662 //JMutexAutoLock lock(block->mesh_mutex);
664 // Delete the old mesh
665 if(block->mesh != NULL)
667 // TODO: Remove hardware buffers of meshbuffers of block->mesh
672 // Replace with the new mesh
673 block->mesh = r.mesh;
675 if(r.ack_block_to_server)
677 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
678 <<","<<r.p.Z<<")"<<std::endl;*/
689 u32 replysize = 2+1+6;
690 SharedBuffer<u8> reply(replysize);
691 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
693 writeV3S16(&reply[3], r.p);
695 m_con.Send(PEER_ID_SERVER, 1, reply, true);
701 If the server didn't update the inventory in a while, revert
702 the local inventory (so the player notices the lag problem
703 and knows something is wrong).
705 if(m_inventory_from_server)
707 float interval = 10.0;
708 float count_before = floor(m_inventory_from_server_age / interval);
710 m_inventory_from_server_age += dtime;
712 float count_after = floor(m_inventory_from_server_age / interval);
714 if(count_after != count_before)
716 // Do this every <interval> seconds after TOCLIENT_INVENTORY
717 // Reset the locally changed inventory to the authoritative inventory
718 Player *player = m_env.getLocalPlayer();
719 player->inventory = *m_inventory_from_server;
720 m_inventory_updated = true;
725 // Virtual methods from con::PeerHandler
726 void Client::peerAdded(con::Peer *peer)
728 infostream<<"Client::peerAdded(): peer->id="
729 <<peer->id<<std::endl;
731 void Client::deletingPeer(con::Peer *peer, bool timeout)
733 infostream<<"Client::deletingPeer(): "
734 "Server Peer is getting deleted "
735 <<"(timeout="<<timeout<<")"<<std::endl;
738 void Client::ReceiveAll()
740 DSTACK(__FUNCTION_NAME);
741 u32 start_ms = porting::getTimeMs();
744 // Limit time even if there would be huge amounts of data to
746 if(porting::getTimeMs() > start_ms + 100)
752 catch(con::NoIncomingDataException &e)
756 catch(con::InvalidIncomingDataException &e)
758 infostream<<"Client::ReceiveAll(): "
759 "InvalidIncomingDataException: what()="
760 <<e.what()<<std::endl;
765 void Client::Receive()
767 DSTACK(__FUNCTION_NAME);
768 SharedBuffer<u8> data;
772 //TimeTaker t1("con mutex and receive", m_device);
773 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
774 datasize = m_con.Receive(sender_peer_id, data);
776 //TimeTaker t1("ProcessData", m_device);
777 ProcessData(*data, datasize, sender_peer_id);
781 sender_peer_id given to this shall be quaranteed to be a valid peer
783 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
785 DSTACK(__FUNCTION_NAME);
787 // Ignore packets that don't even fit a command
790 m_packetcounter.add(60000);
794 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
796 //infostream<<"Client: received command="<<command<<std::endl;
797 m_packetcounter.add((u16)command);
800 If this check is removed, be sure to change the queue
801 system to know the ids
803 if(sender_peer_id != PEER_ID_SERVER)
805 infostream<<"Client::ProcessData(): Discarding data not "
806 "coming from server: peer_id="<<sender_peer_id
811 u8 ser_version = m_server_ser_ver;
813 //infostream<<"Client received command="<<(int)command<<std::endl;
815 if(command == TOCLIENT_INIT)
820 u8 deployed = data[2];
822 infostream<<"Client: TOCLIENT_INIT received with "
823 "deployed="<<((int)deployed&0xff)<<std::endl;
825 if(deployed < SER_FMT_VER_LOWEST
826 || deployed > SER_FMT_VER_HIGHEST)
828 infostream<<"Client: TOCLIENT_INIT: Server sent "
829 <<"unsupported ser_fmt_ver"<<std::endl;
833 m_server_ser_ver = deployed;
835 // Get player position
836 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
837 if(datasize >= 2+1+6)
838 playerpos_s16 = readV3S16(&data[2+1]);
839 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
842 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
844 // Set player position
845 Player *player = m_env.getLocalPlayer();
846 assert(player != NULL);
847 player->setPosition(playerpos_f);
850 if(datasize >= 2+1+6+8)
853 m_map_seed = readU64(&data[2+1+6]);
854 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
859 SharedBuffer<u8> reply(replysize);
860 writeU16(&reply[0], TOSERVER_INIT2);
862 m_con.Send(PEER_ID_SERVER, 1, reply, true);
867 if(command == TOCLIENT_ACCESS_DENIED)
869 // The server didn't like our password. Note, this needs
870 // to be processed even if the serialisation format has
871 // not been agreed yet, the same as TOCLIENT_INIT.
872 m_access_denied = true;
873 m_access_denied_reason = L"Unknown";
876 std::string datastring((char*)&data[2], datasize-2);
877 std::istringstream is(datastring, std::ios_base::binary);
878 m_access_denied_reason = deSerializeWideString(is);
883 if(ser_version == SER_FMT_VER_INVALID)
885 infostream<<"Client: Server serialization"
886 " format invalid or not initialized."
887 " Skipping incoming command="<<command<<std::endl;
891 // Just here to avoid putting the two if's together when
892 // making some copypasta
895 if(command == TOCLIENT_REMOVENODE)
900 p.X = readS16(&data[2]);
901 p.Y = readS16(&data[4]);
902 p.Z = readS16(&data[6]);
904 //TimeTaker t1("TOCLIENT_REMOVENODE");
908 else if(command == TOCLIENT_ADDNODE)
910 if(datasize < 8 + MapNode::serializedLength(ser_version))
914 p.X = readS16(&data[2]);
915 p.Y = readS16(&data[4]);
916 p.Z = readS16(&data[6]);
918 //TimeTaker t1("TOCLIENT_ADDNODE");
921 n.deSerialize(&data[8], ser_version);
925 else if(command == TOCLIENT_BLOCKDATA)
927 // Ignore too small packet
932 p.X = readS16(&data[2]);
933 p.Y = readS16(&data[4]);
934 p.Z = readS16(&data[6]);
936 /*infostream<<"Client: Thread: BLOCKDATA for ("
937 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
938 /*infostream<<"Client: Thread: BLOCKDATA for ("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
941 std::string datastring((char*)&data[8], datasize-8);
942 std::istringstream istr(datastring, std::ios_base::binary);
948 sector = m_env.getMap().emergeSector(p2d);
950 assert(sector->getPos() == p2d);
952 //TimeTaker timer("MapBlock deSerialize");
955 block = sector->getBlockNoCreateNoEx(p.Y);
959 Update an existing block
961 //infostream<<"Updating"<<std::endl;
962 block->deSerialize(istr, ser_version, false);
969 //infostream<<"Creating new"<<std::endl;
970 block = new MapBlock(&m_env.getMap(), p, this);
971 block->deSerialize(istr, ser_version, false);
972 sector->insertBlock(block);
986 u32 replysize = 2+1+6;
987 SharedBuffer<u8> reply(replysize);
988 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
990 writeV3S16(&reply[3], p);
992 m_con.Send(PEER_ID_SERVER, 1, reply, true);
996 Add it to mesh update queue and set it to be acknowledged after update.
998 //infostream<<"Adding mesh update task for received block"<<std::endl;
999 addUpdateMeshTaskWithEdge(p, true);
1001 else if(command == TOCLIENT_INVENTORY)
1006 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1009 //TimeTaker t2("mutex locking", m_device);
1010 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1013 //TimeTaker t3("istringstream init", m_device);
1014 std::string datastring((char*)&data[2], datasize-2);
1015 std::istringstream is(datastring, std::ios_base::binary);
1018 //m_env.printPlayers(infostream);
1020 //TimeTaker t4("player get", m_device);
1021 Player *player = m_env.getLocalPlayer();
1022 assert(player != NULL);
1025 //TimeTaker t1("inventory.deSerialize()", m_device);
1026 player->inventory.deSerialize(is);
1029 m_inventory_updated = true;
1031 delete m_inventory_from_server;
1032 m_inventory_from_server = new Inventory(player->inventory);
1033 m_inventory_from_server_age = 0.0;
1035 //infostream<<"Client got player inventory:"<<std::endl;
1036 //player->inventory.print(infostream);
1039 else if(command == TOCLIENT_TIME_OF_DAY)
1044 u16 time_of_day = readU16(&data[2]);
1045 time_of_day = time_of_day % 24000;
1046 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1054 m_env.setTimeOfDay(time_of_day);
1056 u32 dr = m_env.getDayNightRatio();
1058 infostream<<"Client: time_of_day="<<time_of_day
1064 else if(command == TOCLIENT_CHAT_MESSAGE)
1072 std::string datastring((char*)&data[2], datasize-2);
1073 std::istringstream is(datastring, std::ios_base::binary);
1076 is.read((char*)buf, 2);
1077 u16 len = readU16(buf);
1079 std::wstring message;
1080 for(u16 i=0; i<len; i++)
1082 is.read((char*)buf, 2);
1083 message += (wchar_t)readU16(buf);
1086 /*infostream<<"Client received chat message: "
1087 <<wide_to_narrow(message)<<std::endl;*/
1089 m_chat_queue.push_back(message);
1091 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1093 //if(g_settings->getBool("enable_experimental"))
1097 u16 count of removed objects
1098 for all removed objects {
1101 u16 count of added objects
1102 for all added objects {
1105 u32 initialization data length
1106 string initialization data
1111 // Get all data except the command number
1112 std::string datastring((char*)&data[2], datasize-2);
1113 // Throw them in an istringstream
1114 std::istringstream is(datastring, std::ios_base::binary);
1118 // Read removed objects
1120 u16 removed_count = readU16((u8*)buf);
1121 for(u16 i=0; i<removed_count; i++)
1124 u16 id = readU16((u8*)buf);
1127 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1128 m_env.removeActiveObject(id);
1132 // Read added objects
1134 u16 added_count = readU16((u8*)buf);
1135 for(u16 i=0; i<added_count; i++)
1138 u16 id = readU16((u8*)buf);
1140 u8 type = readU8((u8*)buf);
1141 std::string data = deSerializeLongString(is);
1144 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1145 m_env.addActiveObject(id, type, data);
1150 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1152 //if(g_settings->getBool("enable_experimental"))
1164 // Get all data except the command number
1165 std::string datastring((char*)&data[2], datasize-2);
1166 // Throw them in an istringstream
1167 std::istringstream is(datastring, std::ios_base::binary);
1169 while(is.eof() == false)
1173 u16 id = readU16((u8*)buf);
1177 u16 message_size = readU16((u8*)buf);
1178 std::string message;
1179 message.reserve(message_size);
1180 for(u16 i=0; i<message_size; i++)
1183 message.append(buf, 1);
1185 // Pass on to the environment
1187 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1188 m_env.processActiveObjectMessage(id, message);
1193 else if(command == TOCLIENT_HP)
1195 std::string datastring((char*)&data[2], datasize-2);
1196 std::istringstream is(datastring, std::ios_base::binary);
1197 Player *player = m_env.getLocalPlayer();
1198 assert(player != NULL);
1199 u8 oldhp = player->hp;
1205 // Add to ClientEvent queue
1207 event.type = CE_PLAYER_DAMAGE;
1208 event.player_damage.amount = oldhp - hp;
1209 m_client_event_queue.push_back(event);
1212 else if(command == TOCLIENT_MOVE_PLAYER)
1214 std::string datastring((char*)&data[2], datasize-2);
1215 std::istringstream is(datastring, std::ios_base::binary);
1216 Player *player = m_env.getLocalPlayer();
1217 assert(player != NULL);
1218 v3f pos = readV3F1000(is);
1219 f32 pitch = readF1000(is);
1220 f32 yaw = readF1000(is);
1221 player->setPosition(pos);
1222 /*player->setPitch(pitch);
1223 player->setYaw(yaw);*/
1225 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1226 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1232 Add to ClientEvent queue.
1233 This has to be sent to the main program because otherwise
1234 it would just force the pitch and yaw values to whatever
1235 the camera points to.
1238 event.type = CE_PLAYER_FORCE_MOVE;
1239 event.player_force_move.pitch = pitch;
1240 event.player_force_move.yaw = yaw;
1241 m_client_event_queue.push_back(event);
1243 // Ignore damage for a few seconds, so that the player doesn't
1244 // get damage from falling on ground
1245 m_ignore_damage_timer = 3.0;
1247 else if(command == TOCLIENT_PLAYERITEM)
1249 std::string datastring((char*)&data[2], datasize-2);
1250 std::istringstream is(datastring, std::ios_base::binary);
1252 u16 count = readU16(is);
1254 for (u16 i = 0; i < count; ++i) {
1255 u16 peer_id = readU16(is);
1256 Player *player = m_env.getPlayer(peer_id);
1260 infostream<<"Client: ignoring player item "
1261 << deSerializeString(is)
1262 << " for non-existing peer id " << peer_id
1265 } else if (player->isLocal()) {
1266 infostream<<"Client: ignoring player item "
1267 << deSerializeString(is)
1268 << " for local player" << std::endl;
1271 InventoryList *inv = player->inventory.getList("main");
1272 std::string itemstring(deSerializeString(is));
1274 item.deSerialize(itemstring, m_itemdef);
1275 inv->changeItem(0, item);
1276 if(itemstring.empty())
1278 infostream<<"Client: empty player item for peer "
1279 <<peer_id<<std::endl;
1283 infostream<<"Client: player item for peer "
1284 <<peer_id<<": "<<itemstring<<std::endl;
1289 else if(command == TOCLIENT_DEATHSCREEN)
1291 std::string datastring((char*)&data[2], datasize-2);
1292 std::istringstream is(datastring, std::ios_base::binary);
1294 bool set_camera_point_target = readU8(is);
1295 v3f camera_point_target = readV3F1000(is);
1298 event.type = CE_DEATHSCREEN;
1299 event.deathscreen.set_camera_point_target = set_camera_point_target;
1300 event.deathscreen.camera_point_target_x = camera_point_target.X;
1301 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1302 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1303 m_client_event_queue.push_back(event);
1305 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1307 io::IFileSystem *irrfs = m_device->getFileSystem();
1308 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1310 std::string datastring((char*)&data[2], datasize-2);
1311 std::istringstream is(datastring, std::ios_base::binary);
1313 // Mesh update thread must be stopped while
1314 // updating content definitions
1315 assert(!m_mesh_update_thread.IsRunning());
1317 int num_textures = readU16(is);
1319 core::list<TextureRequest> texture_requests;
1321 for(int i=0; i<num_textures; i++){
1323 bool texture_found = false;
1325 //read texture from cache
1326 std::string name = deSerializeString(is);
1327 std::string sha1_texture = deSerializeString(is);
1329 // if name contains illegal characters, ignore the texture
1330 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1331 errorstream<<"Client: ignoring illegal texture name "
1332 <<"sent by server: \""<<name<<"\""<<std::endl;
1336 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1338 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1341 if(fis.good() == false){
1342 infostream<<"Client::Texture not found in cache: "
1343 <<name << " expected it at: "<<tpath<<std::endl;
1347 std::ostringstream tmp_os(std::ios_base::binary);
1351 fis.read(buf, 1024);
1352 std::streamsize len = fis.gcount();
1353 tmp_os.write(buf, len);
1362 infostream<<"Client: Failed to read texture from cache\""
1363 <<name<<"\""<<std::endl;
1368 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1370 unsigned char *digest = sha1.getDigest();
1372 std::string digest_string = base64_encode(digest, 20);
1374 if (digest_string == sha1_texture) {
1375 // Silly irrlicht's const-incorrectness
1376 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1378 // Create an irrlicht memory file
1379 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1380 *data_rw, tmp_os.str().size(), "_tempreadfile");
1383 video::IImage *img = vdrv->createImageFromFile(rfile);
1385 infostream<<"Client: Cannot create image from data of "
1386 <<"received texture \""<<name<<"\""<<std::endl;
1390 m_tsrc->insertSourceImage(name, img);
1394 texture_found = true;
1398 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1399 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1406 //add texture request
1407 if (!texture_found) {
1408 infostream<<"Client: Adding texture to request list: \""
1409 <<name<<"\""<<std::endl;
1410 texture_requests.push_back(TextureRequest(name));
1416 event.type = CE_TEXTURES_UPDATED;
1417 m_client_event_queue.push_back(event);
1420 //send Texture request
1423 u16 number of textures requested
1429 std::ostringstream os(std::ios_base::binary);
1434 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1435 os.write((char*)buf, 2);
1437 writeU16(buf,texture_requests.size());
1438 os.write((char*)buf, 2);
1441 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1442 i != texture_requests.end(); i++) {
1443 os<<serializeString(i->name);
1447 std::string s = os.str();
1448 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1450 Send(0, data, true);
1451 infostream<<"Client: Sending request list to server " <<std::endl;
1453 else if(command == TOCLIENT_TEXTURES)
1455 io::IFileSystem *irrfs = m_device->getFileSystem();
1456 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1458 std::string datastring((char*)&data[2], datasize-2);
1459 std::istringstream is(datastring, std::ios_base::binary);
1461 // Mesh update thread must be stopped while
1462 // updating content definitions
1463 assert(!m_mesh_update_thread.IsRunning());
1467 u16 total number of texture bunches
1468 u16 index of this bunch
1469 u32 number of textures in this bunch
1477 int num_bunches = readU16(is);
1478 int bunch_i = readU16(is);
1479 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1480 if(bunch_i == num_bunches - 1)
1481 m_textures_received = true;
1482 int num_textures = readU32(is);
1483 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1484 <<num_bunches<<" textures="<<num_textures
1485 <<" size="<<datasize<<std::endl;
1486 for(int i=0; i<num_textures; i++){
1487 std::string name = deSerializeString(is);
1488 std::string data = deSerializeLongString(is);
1490 // if name contains illegal characters, ignore the texture
1491 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1492 errorstream<<"Client: ignoring illegal texture name "
1493 <<"sent by server: \""<<name<<"\""<<std::endl;
1497 // Silly irrlicht's const-incorrectness
1498 Buffer<char> data_rw(data.c_str(), data.size());
1499 // Create an irrlicht memory file
1500 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1501 *data_rw, data.size(), "_tempreadfile");
1504 video::IImage *img = vdrv->createImageFromFile(rfile);
1506 errorstream<<"Client: Cannot create image from data of "
1507 <<"received texture \""<<name<<"\""<<std::endl;
1512 fs::CreateAllDirs(getTextureCacheDir());
1514 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1515 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1517 if (outfile.good()) {
1518 outfile.write(data.c_str(),data.length());
1522 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1525 m_tsrc->insertSourceImage(name, img);
1531 event.type = CE_TEXTURES_UPDATED;
1532 m_client_event_queue.push_back(event);
1534 else if(command == TOCLIENT_TOOLDEF)
1536 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1538 else if(command == TOCLIENT_NODEDEF)
1540 infostream<<"Client: Received node 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 node 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_nodedef->deSerialize(tmp_is2);
1557 m_nodedef_received = true;
1559 else if(command == TOCLIENT_CRAFTITEMDEF)
1561 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1563 else if(command == TOCLIENT_ITEMDEF)
1565 infostream<<"Client: Received item definitions: packet size: "
1566 <<datasize<<std::endl;
1568 // Mesh update thread must be stopped while
1569 // updating content definitions
1570 assert(!m_mesh_update_thread.IsRunning());
1572 // Decompress item definitions
1573 std::string datastring((char*)&data[2], datasize-2);
1574 std::istringstream is(datastring, std::ios_base::binary);
1575 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1576 std::ostringstream tmp_os;
1577 decompressZlib(tmp_is, tmp_os);
1579 // Deserialize node definitions
1580 std::istringstream tmp_is2(tmp_os.str());
1581 m_itemdef->deSerialize(tmp_is2);
1582 m_itemdef_received = true;
1586 infostream<<"Client: Ignoring unknown command "
1587 <<command<<std::endl;
1591 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1593 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1594 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1597 void Client::interact(u8 action, const PointedThing& pointed)
1599 if(connectedAndInitialized() == false){
1600 infostream<<"Client::interact() "
1601 "cancelled (not connected)"
1606 std::ostringstream os(std::ios_base::binary);
1612 [5] u32 length of the next item
1613 [9] serialized PointedThing
1615 0: start digging (from undersurface) or use
1616 1: stop digging (all parameters ignored)
1617 2: digging completed
1618 3: place block or item (to abovesurface)
1621 writeU16(os, TOSERVER_INTERACT);
1622 writeU8(os, action);
1623 writeU16(os, getPlayerItem());
1624 std::ostringstream tmp_os(std::ios::binary);
1625 pointed.serialize(tmp_os);
1626 os<<serializeLongString(tmp_os.str());
1628 std::string s = os.str();
1629 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1632 Send(0, data, true);
1635 void Client::sendSignNodeText(v3s16 p, std::string text)
1643 std::ostringstream os(std::ios_base::binary);
1647 writeU16(buf, TOSERVER_SIGNNODETEXT);
1648 os.write((char*)buf, 2);
1652 os.write((char*)buf, 6);
1654 u16 textlen = text.size();
1655 // Write text length
1656 writeS16(buf, textlen);
1657 os.write((char*)buf, 2);
1660 os.write((char*)text.c_str(), textlen);
1663 std::string s = os.str();
1664 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1666 Send(0, data, true);
1669 void Client::sendInventoryAction(InventoryAction *a)
1671 std::ostringstream os(std::ios_base::binary);
1675 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1676 os.write((char*)buf, 2);
1681 std::string s = os.str();
1682 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1684 Send(0, data, true);
1687 void Client::sendChatMessage(const std::wstring &message)
1689 std::ostringstream os(std::ios_base::binary);
1693 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1694 os.write((char*)buf, 2);
1697 writeU16(buf, message.size());
1698 os.write((char*)buf, 2);
1701 for(u32 i=0; i<message.size(); i++)
1705 os.write((char*)buf, 2);
1709 std::string s = os.str();
1710 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1712 Send(0, data, true);
1715 void Client::sendChangePassword(const std::wstring oldpassword,
1716 const std::wstring newpassword)
1718 Player *player = m_env.getLocalPlayer();
1722 std::string playername = player->getName();
1723 std::string oldpwd = translatePassword(playername, oldpassword);
1724 std::string newpwd = translatePassword(playername, newpassword);
1726 std::ostringstream os(std::ios_base::binary);
1727 u8 buf[2+PASSWORD_SIZE*2];
1729 [0] u16 TOSERVER_PASSWORD
1730 [2] u8[28] old password
1731 [30] u8[28] new password
1734 writeU16(buf, TOSERVER_PASSWORD);
1735 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1737 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1738 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1740 buf[2+PASSWORD_SIZE-1] = 0;
1741 buf[30+PASSWORD_SIZE-1] = 0;
1742 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1745 std::string s = os.str();
1746 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1748 Send(0, data, true);
1752 void Client::sendDamage(u8 damage)
1754 DSTACK(__FUNCTION_NAME);
1755 std::ostringstream os(std::ios_base::binary);
1757 writeU16(os, TOSERVER_DAMAGE);
1758 writeU8(os, damage);
1761 std::string s = os.str();
1762 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1764 Send(0, data, true);
1767 void Client::sendRespawn()
1769 DSTACK(__FUNCTION_NAME);
1770 std::ostringstream os(std::ios_base::binary);
1772 writeU16(os, TOSERVER_RESPAWN);
1775 std::string s = os.str();
1776 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1778 Send(0, data, true);
1781 void Client::sendPlayerPos()
1783 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1785 Player *myplayer = m_env.getLocalPlayer();
1786 if(myplayer == NULL)
1791 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1792 our_peer_id = m_con.GetPeerID();
1795 // Set peer id if not set already
1796 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1797 myplayer->peer_id = our_peer_id;
1798 // Check that an existing peer_id is the same as the connection's
1799 assert(myplayer->peer_id == our_peer_id);
1801 v3f pf = myplayer->getPosition();
1802 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1803 v3f sf = myplayer->getSpeed();
1804 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1805 s32 pitch = myplayer->getPitch() * 100;
1806 s32 yaw = myplayer->getYaw() * 100;
1811 [2] v3s32 position*100
1812 [2+12] v3s32 speed*100
1813 [2+12+12] s32 pitch*100
1814 [2+12+12+4] s32 yaw*100
1817 SharedBuffer<u8> data(2+12+12+4+4);
1818 writeU16(&data[0], TOSERVER_PLAYERPOS);
1819 writeV3S32(&data[2], position);
1820 writeV3S32(&data[2+12], speed);
1821 writeS32(&data[2+12+12], pitch);
1822 writeS32(&data[2+12+12+4], yaw);
1824 // Send as unreliable
1825 Send(0, data, false);
1828 void Client::sendPlayerItem(u16 item)
1830 Player *myplayer = m_env.getLocalPlayer();
1831 if(myplayer == NULL)
1834 u16 our_peer_id = m_con.GetPeerID();
1836 // Set peer id if not set already
1837 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1838 myplayer->peer_id = our_peer_id;
1839 // Check that an existing peer_id is the same as the connection's
1840 assert(myplayer->peer_id == our_peer_id);
1842 SharedBuffer<u8> data(2+2);
1843 writeU16(&data[0], TOSERVER_PLAYERITEM);
1844 writeU16(&data[2], item);
1847 Send(0, data, true);
1850 void Client::removeNode(v3s16 p)
1852 core::map<v3s16, MapBlock*> modified_blocks;
1856 //TimeTaker t("removeNodeAndUpdate", m_device);
1857 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1859 catch(InvalidPositionException &e)
1863 // add urgent task to update the modified node
1864 addUpdateMeshTaskForNode(p, false, true);
1866 for(core::map<v3s16, MapBlock * >::Iterator
1867 i = modified_blocks.getIterator();
1868 i.atEnd() == false; i++)
1870 v3s16 p = i.getNode()->getKey();
1871 addUpdateMeshTaskWithEdge(p);
1875 void Client::addNode(v3s16 p, MapNode n)
1877 TimeTaker timer1("Client::addNode()");
1879 core::map<v3s16, MapBlock*> modified_blocks;
1883 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1884 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1886 catch(InvalidPositionException &e)
1889 for(core::map<v3s16, MapBlock * >::Iterator
1890 i = modified_blocks.getIterator();
1891 i.atEnd() == false; i++)
1893 v3s16 p = i.getNode()->getKey();
1894 addUpdateMeshTaskWithEdge(p);
1898 void Client::setPlayerControl(PlayerControl &control)
1900 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1901 LocalPlayer *player = m_env.getLocalPlayer();
1902 assert(player != NULL);
1903 player->control = control;
1906 void Client::selectPlayerItem(u16 item)
1908 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1909 m_playeritem = item;
1910 m_inventory_updated = true;
1911 sendPlayerItem(item);
1914 // Returns true if the inventory of the local player has been
1915 // updated from the server. If it is true, it is set to false.
1916 bool Client::getLocalInventoryUpdated()
1918 // m_inventory_updated is behind envlock
1919 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1920 bool updated = m_inventory_updated;
1921 m_inventory_updated = false;
1925 // Copies the inventory of the local player to parameter
1926 void Client::getLocalInventory(Inventory &dst)
1928 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1929 Player *player = m_env.getLocalPlayer();
1930 assert(player != NULL);
1931 dst = player->inventory;
1934 Inventory* Client::getInventory(const InventoryLocation &loc)
1937 case InventoryLocation::UNDEFINED:
1940 case InventoryLocation::CURRENT_PLAYER:
1942 Player *player = m_env.getLocalPlayer();
1943 assert(player != NULL);
1944 return &player->inventory;
1947 case InventoryLocation::PLAYER:
1949 Player *player = m_env.getPlayer(loc.name.c_str());
1952 return &player->inventory;
1955 case InventoryLocation::NODEMETA:
1957 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1960 return meta->getInventory();
1968 void Client::inventoryAction(InventoryAction *a)
1971 Send it to the server
1973 sendInventoryAction(a);
1976 Predict some local inventory changes
1978 a->clientApply(this, this);
1981 ClientActiveObject * Client::getSelectedActiveObject(
1983 v3f from_pos_f_on_map,
1984 core::line3d<f32> shootline_on_map
1987 core::array<DistanceSortedActiveObject> objects;
1989 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1991 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1994 // After this, the closest object is the first in the array.
1997 for(u32 i=0; i<objects.size(); i++)
1999 ClientActiveObject *obj = objects[i].obj;
2001 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2002 if(selection_box == NULL)
2005 v3f pos = obj->getPosition();
2007 core::aabbox3d<f32> offsetted_box(
2008 selection_box->MinEdge + pos,
2009 selection_box->MaxEdge + pos
2012 if(offsetted_box.intersectsWithLine(shootline_on_map))
2014 //infostream<<"Returning selected object"<<std::endl;
2019 //infostream<<"No object selected; returning NULL."<<std::endl;
2023 void Client::printDebugInfo(std::ostream &os)
2025 //JMutexAutoLock lock1(m_fetchblock_mutex);
2026 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2028 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2029 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2030 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2034 core::list<std::wstring> Client::getConnectedPlayerNames()
2036 core::list<Player*> players = m_env.getPlayers(true);
2037 core::list<std::wstring> playerNames;
2038 for(core::list<Player*>::Iterator
2039 i = players.begin();
2040 i != players.end(); i++)
2042 Player *player = *i;
2043 playerNames.push_back(narrow_to_wide(player->getName()));
2048 float Client::getAnimationTime()
2050 return m_animation_time;
2053 int Client::getCrackLevel()
2055 return m_crack_level;
2058 void Client::setCrack(int level, v3s16 pos)
2060 int old_crack_level = m_crack_level;
2061 v3s16 old_crack_pos = m_crack_pos;
2063 m_crack_level = level;
2066 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2069 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2071 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2074 addUpdateMeshTaskForNode(pos, false, true);
2078 u32 Client::getDayNightRatio()
2080 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2081 return m_env.getDayNightRatio();
2086 Player *player = m_env.getLocalPlayer();
2087 assert(player != NULL);
2091 bool Client::getChatMessage(std::wstring &message)
2093 if(m_chat_queue.size() == 0)
2095 message = m_chat_queue.pop_front();
2099 void Client::typeChatMessage(const std::wstring &message)
2101 // Discard empty line
2106 sendChatMessage(message);
2109 if (message[0] == L'/')
2111 m_chat_queue.push_back(
2112 (std::wstring)L"issued command: "+message);
2116 LocalPlayer *player = m_env.getLocalPlayer();
2117 assert(player != NULL);
2118 std::wstring name = narrow_to_wide(player->getName());
2119 m_chat_queue.push_back(
2120 (std::wstring)L"<"+name+L"> "+message);
2124 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2126 /*infostream<<"Client::addUpdateMeshTask(): "
2127 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2128 <<" ack_to_server="<<ack_to_server
2129 <<" urgent="<<urgent
2132 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2137 Create a task to update the mesh of the block
2140 MeshMakeData *data = new MeshMakeData(this);
2143 //TimeTaker timer("data fill");
2145 // Debug: 1-6ms, avg=2ms
2147 data->setCrack(m_crack_level, m_crack_pos);
2148 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2152 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2154 // Add task to queue
2155 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2157 /*infostream<<"Mesh update input queue size is "
2158 <<m_mesh_update_thread.m_queue_in.size()
2162 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2166 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2167 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2172 v3s16 p = blockpos + v3s16(0,0,0);
2173 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2174 addUpdateMeshTask(p, ack_to_server, urgent);
2176 catch(InvalidPositionException &e){}
2179 v3s16 p = blockpos + v3s16(-1,0,0);
2180 addUpdateMeshTask(p, false, urgent);
2182 catch(InvalidPositionException &e){}
2184 v3s16 p = blockpos + v3s16(0,-1,0);
2185 addUpdateMeshTask(p, false, urgent);
2187 catch(InvalidPositionException &e){}
2189 v3s16 p = blockpos + v3s16(0,0,-1);
2190 addUpdateMeshTask(p, false, urgent);
2192 catch(InvalidPositionException &e){}
2195 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2199 infostream<<"Client::addUpdateMeshTaskForNode(): "
2200 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2204 v3s16 blockpos = getNodeBlockPos(nodepos);
2205 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2208 v3s16 p = blockpos + v3s16(0,0,0);
2209 addUpdateMeshTask(p, ack_to_server, urgent);
2211 catch(InvalidPositionException &e){}
2213 if(nodepos.X == blockpos_relative.X){
2215 v3s16 p = blockpos + v3s16(-1,0,0);
2216 addUpdateMeshTask(p, false, urgent);
2218 catch(InvalidPositionException &e){}
2220 if(nodepos.Y == blockpos_relative.Y){
2222 v3s16 p = blockpos + v3s16(0,-1,0);
2223 addUpdateMeshTask(p, false, urgent);
2225 catch(InvalidPositionException &e){}
2227 if(nodepos.Z == blockpos_relative.Z){
2229 v3s16 p = blockpos + v3s16(0,0,-1);
2230 addUpdateMeshTask(p, false, urgent);
2232 catch(InvalidPositionException &e){}
2236 ClientEvent Client::getClientEvent()
2238 if(m_client_event_queue.size() == 0)
2241 event.type = CE_NONE;
2244 return m_client_event_queue.pop_front();
2247 void Client::afterContentReceived()
2249 assert(m_itemdef_received);
2250 assert(m_nodedef_received);
2251 assert(m_textures_received);
2253 // Rebuild inherited images and recreate textures
2254 m_tsrc->rebuildImagesAndTextures();
2256 // Update texture atlas
2257 if(g_settings->getBool("enable_texture_atlas"))
2258 m_tsrc->buildMainAtlas(this);
2260 // Update node aliases
2261 m_nodedef->updateAliases(m_itemdef);
2263 // Update node textures
2264 m_nodedef->updateTextures(m_tsrc);
2266 // Update item textures and meshes
2267 m_itemdef->updateTexturesAndMeshes(this);
2269 // Start mesh update thread after setting up content definitions
2270 m_mesh_update_thread.Start();
2273 float Client::getRTT(void)
2276 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2277 } catch(con::PeerNotFoundException &e){
2282 // IGameDef interface
2284 IItemDefManager* Client::getItemDefManager()
2288 INodeDefManager* Client::getNodeDefManager()
2292 ICraftDefManager* Client::getCraftDefManager()
2295 //return m_craftdef;
2297 ITextureSource* Client::getTextureSource()
2301 u16 Client::allocateUnknownNodeId(const std::string &name)
2303 errorstream<<"Client::allocateUnknownNodeId(): "
2304 <<"Client cannot allocate node IDs"<<std::endl;
2306 return CONTENT_IGNORE;