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_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
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 for(std::vector<QueuedMeshUpdate*>::iterator
87 i != m_queue.end(); i++)
89 QueuedMeshUpdate *q = *i;
95 peer_id=0 adds with nobody to send to
97 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
99 DSTACK(__FUNCTION_NAME);
103 JMutexAutoLock lock(m_mutex);
109 Find if block is already in queue.
110 If it is, update the data and quit.
112 for(std::vector<QueuedMeshUpdate*>::iterator
114 i != m_queue.end(); i++)
116 QueuedMeshUpdate *q = *i;
122 if(ack_block_to_server)
123 q->ack_block_to_server = true;
131 QueuedMeshUpdate *q = new QueuedMeshUpdate;
134 q->ack_block_to_server = ack_block_to_server;
135 m_queue.push_back(q);
138 // Returned pointer must be deleted
139 // Returns NULL if queue is empty
140 QueuedMeshUpdate * MeshUpdateQueue::pop()
142 JMutexAutoLock lock(m_mutex);
144 bool must_be_urgent = !m_urgents.empty();
145 for(std::vector<QueuedMeshUpdate*>::iterator
147 i != m_queue.end(); i++)
149 QueuedMeshUpdate *q = *i;
150 if(must_be_urgent && m_urgents.count(q->p) == 0)
153 m_urgents.erase(q->p);
163 void * MeshUpdateThread::Thread()
167 log_register_thread("MeshUpdateThread");
169 DSTACK(__FUNCTION_NAME);
171 BEGIN_DEBUG_EXCEPTION_HANDLER
175 /*// Wait for output queue to flush.
176 // Allow 2 in queue, this makes less frametime jitter.
177 // Umm actually, there is no much difference
178 if(m_queue_out.size() >= 2)
184 QueuedMeshUpdate *q = m_queue_in.pop();
191 ScopeProfiler sp(g_profiler, "Client: Mesh making");
193 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
194 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
203 r.ack_block_to_server = q->ack_block_to_server;
205 /*infostream<<"MeshUpdateThread: Processed "
206 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
209 m_queue_out.push_back(r);
214 END_DEBUG_EXCEPTION_HANDLER(errorstream)
220 IrrlichtDevice *device,
221 const char *playername,
222 std::string password,
223 MapDrawControl &control,
224 IWritableTextureSource *tsrc,
225 IWritableItemDefManager *itemdef,
226 IWritableNodeDefManager *nodedef
231 m_mesh_update_thread(this),
233 new ClientMap(this, this, control,
234 device->getSceneManager()->getRootSceneNode(),
235 device->getSceneManager(), 666),
236 device->getSceneManager(),
239 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
241 m_server_ser_ver(SER_FMT_VER_INVALID),
243 m_inventory_updated(false),
244 m_inventory_from_server(NULL),
245 m_inventory_from_server_age(0.0),
251 m_password(password),
252 m_access_denied(false),
253 m_texture_receive_progress(0),
254 m_textures_received(false),
255 m_itemdef_received(false),
256 m_nodedef_received(false)
258 m_packetcounter_timer = 0.0;
259 //m_delete_unused_sectors_timer = 0.0;
260 m_connection_reinit_timer = 0.0;
261 m_avg_rtt_timer = 0.0;
262 m_playerpos_send_timer = 0.0;
263 m_ignore_damage_timer = 0.0;
265 // Build main texture atlas, now that the GameDef exists (that is, us)
266 if(g_settings->getBool("enable_texture_atlas"))
267 m_tsrc->buildMainAtlas(this);
269 infostream<<"Not building texture atlas."<<std::endl;
275 Player *player = new LocalPlayer(this);
277 player->updateName(playername);
279 m_env.addPlayer(player);
286 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
290 m_mesh_update_thread.setRun(false);
291 while(m_mesh_update_thread.IsRunning())
294 delete m_inventory_from_server;
297 void Client::connect(Address address)
299 DSTACK(__FUNCTION_NAME);
300 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
301 m_con.SetTimeoutMs(0);
302 m_con.Connect(address);
305 bool Client::connectedAndInitialized()
307 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
309 if(m_con.Connected() == false)
312 if(m_server_ser_ver == SER_FMT_VER_INVALID)
318 void Client::step(float dtime)
320 DSTACK(__FUNCTION_NAME);
326 if(m_ignore_damage_timer > dtime)
327 m_ignore_damage_timer -= dtime;
329 m_ignore_damage_timer = 0.0;
331 m_animation_time += dtime;
332 if(m_animation_time > 60.0)
333 m_animation_time -= 60.0;
335 //infostream<<"Client steps "<<dtime<<std::endl;
338 //TimeTaker timer("ReceiveAll()", m_device);
344 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
346 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
347 m_con.RunTimeouts(dtime);
354 float &counter = m_packetcounter_timer;
360 infostream<<"Client packetcounter (20s):"<<std::endl;
361 m_packetcounter.print(infostream);
362 m_packetcounter.clear();
366 // Get connection status
367 bool connected = connectedAndInitialized();
372 Delete unused sectors
374 NOTE: This jams the game for a while because deleting sectors
378 float &counter = m_delete_unused_sectors_timer;
386 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
388 core::list<v3s16> deleted_blocks;
390 float delete_unused_sectors_timeout =
391 g_settings->getFloat("client_delete_unused_sectors_timeout");
393 // Delete sector blocks
394 /*u32 num = m_env.getMap().unloadUnusedData
395 (delete_unused_sectors_timeout,
396 true, &deleted_blocks);*/
398 // Delete whole sectors
399 m_env.getMap().unloadUnusedData
400 (delete_unused_sectors_timeout,
403 if(deleted_blocks.size() > 0)
405 /*infostream<<"Client: Deleted blocks of "<<num
406 <<" unused sectors"<<std::endl;*/
407 /*infostream<<"Client: Deleted "<<num
408 <<" unused sectors"<<std::endl;*/
414 // Env is locked so con can be locked.
415 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
417 core::list<v3s16>::Iterator i = deleted_blocks.begin();
418 core::list<v3s16> sendlist;
421 if(sendlist.size() == 255 || i == deleted_blocks.end())
423 if(sendlist.size() == 0)
432 u32 replysize = 2+1+6*sendlist.size();
433 SharedBuffer<u8> reply(replysize);
434 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
435 reply[2] = sendlist.size();
437 for(core::list<v3s16>::Iterator
438 j = sendlist.begin();
439 j != sendlist.end(); j++)
441 writeV3S16(&reply[2+1+6*k], *j);
444 m_con.Send(PEER_ID_SERVER, 1, reply, true);
446 if(i == deleted_blocks.end())
452 sendlist.push_back(*i);
460 if(connected == false)
462 float &counter = m_connection_reinit_timer;
468 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
470 Player *myplayer = m_env.getLocalPlayer();
471 assert(myplayer != NULL);
473 // Send TOSERVER_INIT
474 // [0] u16 TOSERVER_INIT
475 // [2] u8 SER_FMT_VER_HIGHEST
476 // [3] u8[20] player_name
477 // [23] u8[28] password (new in some version)
478 // [51] u16 client network protocol version (new in some version)
479 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
480 writeU16(&data[0], TOSERVER_INIT);
481 writeU8(&data[2], SER_FMT_VER_HIGHEST);
483 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
484 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
486 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
489 memset((char*)&data[23], 0, PASSWORD_SIZE);
490 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
492 // This should be incremented in each version
493 writeU16(&data[51], PROTOCOL_VERSION);
495 // Send as unreliable
496 Send(0, data, false);
499 // Not connected, return
504 Do stuff if connected
508 Run Map's timers and unload unused data
510 const float map_timer_and_unload_dtime = 5.25;
511 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
513 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
514 core::list<v3s16> deleted_blocks;
515 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
516 g_settings->getFloat("client_unload_unused_data_timeout"),
519 /*if(deleted_blocks.size() > 0)
520 infostream<<"Client: Unloaded "<<deleted_blocks.size()
521 <<" unused blocks"<<std::endl;*/
525 NOTE: This loop is intentionally iterated the way it is.
528 core::list<v3s16>::Iterator i = deleted_blocks.begin();
529 core::list<v3s16> sendlist;
532 if(sendlist.size() == 255 || i == deleted_blocks.end())
534 if(sendlist.size() == 0)
543 u32 replysize = 2+1+6*sendlist.size();
544 SharedBuffer<u8> reply(replysize);
545 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
546 reply[2] = sendlist.size();
548 for(core::list<v3s16>::Iterator
549 j = sendlist.begin();
550 j != sendlist.end(); j++)
552 writeV3S16(&reply[2+1+6*k], *j);
555 m_con.Send(PEER_ID_SERVER, 1, reply, true);
557 if(i == deleted_blocks.end())
563 sendlist.push_back(*i);
573 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
575 // Control local player (0ms)
576 LocalPlayer *player = m_env.getLocalPlayer();
577 assert(player != NULL);
578 player->applyControl(dtime);
580 //TimeTaker envtimer("env step", m_device);
589 ClientEnvEvent event = m_env.getClientEvent();
590 if(event.type == CEE_NONE)
594 else if(event.type == CEE_PLAYER_DAMAGE)
596 if(m_ignore_damage_timer <= 0)
598 u8 damage = event.player_damage.amount;
600 if(event.player_damage.send_to_server)
603 // Add to ClientEvent queue
605 event.type = CE_PLAYER_DAMAGE;
606 event.player_damage.amount = damage;
607 m_client_event_queue.push_back(event);
617 float &counter = m_avg_rtt_timer;
622 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
623 // connectedAndInitialized() is true, peer exists.
624 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
625 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
630 Send player position to server
633 float &counter = m_playerpos_send_timer;
643 Replace updated meshes
646 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
648 //TimeTaker timer("** Processing mesh update result queue");
651 /*infostream<<"Mesh update result queue size is "
652 <<m_mesh_update_thread.m_queue_out.size()
655 while(m_mesh_update_thread.m_queue_out.size() > 0)
657 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
658 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
661 //JMutexAutoLock lock(block->mesh_mutex);
663 // Delete the old mesh
664 if(block->mesh != NULL)
666 // TODO: Remove hardware buffers of meshbuffers of block->mesh
671 // Replace with the new mesh
672 block->mesh = r.mesh;
674 if(r.ack_block_to_server)
676 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
677 <<","<<r.p.Z<<")"<<std::endl;*/
688 u32 replysize = 2+1+6;
689 SharedBuffer<u8> reply(replysize);
690 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
692 writeV3S16(&reply[3], r.p);
694 m_con.Send(PEER_ID_SERVER, 1, reply, true);
700 If the server didn't update the inventory in a while, revert
701 the local inventory (so the player notices the lag problem
702 and knows something is wrong).
704 if(m_inventory_from_server)
706 float interval = 10.0;
707 float count_before = floor(m_inventory_from_server_age / interval);
709 m_inventory_from_server_age += dtime;
711 float count_after = floor(m_inventory_from_server_age / interval);
713 if(count_after != count_before)
715 // Do this every <interval> seconds after TOCLIENT_INVENTORY
716 // Reset the locally changed inventory to the authoritative inventory
717 Player *player = m_env.getLocalPlayer();
718 player->inventory = *m_inventory_from_server;
719 m_inventory_updated = true;
724 // Virtual methods from con::PeerHandler
725 void Client::peerAdded(con::Peer *peer)
727 infostream<<"Client::peerAdded(): peer->id="
728 <<peer->id<<std::endl;
730 void Client::deletingPeer(con::Peer *peer, bool timeout)
732 infostream<<"Client::deletingPeer(): "
733 "Server Peer is getting deleted "
734 <<"(timeout="<<timeout<<")"<<std::endl;
737 void Client::ReceiveAll()
739 DSTACK(__FUNCTION_NAME);
740 u32 start_ms = porting::getTimeMs();
743 // Limit time even if there would be huge amounts of data to
745 if(porting::getTimeMs() > start_ms + 100)
751 catch(con::NoIncomingDataException &e)
755 catch(con::InvalidIncomingDataException &e)
757 infostream<<"Client::ReceiveAll(): "
758 "InvalidIncomingDataException: what()="
759 <<e.what()<<std::endl;
764 void Client::Receive()
766 DSTACK(__FUNCTION_NAME);
767 SharedBuffer<u8> data;
771 //TimeTaker t1("con mutex and receive", m_device);
772 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
773 datasize = m_con.Receive(sender_peer_id, data);
775 //TimeTaker t1("ProcessData", m_device);
776 ProcessData(*data, datasize, sender_peer_id);
780 sender_peer_id given to this shall be quaranteed to be a valid peer
782 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
784 DSTACK(__FUNCTION_NAME);
786 // Ignore packets that don't even fit a command
789 m_packetcounter.add(60000);
793 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
795 //infostream<<"Client: received command="<<command<<std::endl;
796 m_packetcounter.add((u16)command);
799 If this check is removed, be sure to change the queue
800 system to know the ids
802 if(sender_peer_id != PEER_ID_SERVER)
804 infostream<<"Client::ProcessData(): Discarding data not "
805 "coming from server: peer_id="<<sender_peer_id
810 u8 ser_version = m_server_ser_ver;
812 //infostream<<"Client received command="<<(int)command<<std::endl;
814 if(command == TOCLIENT_INIT)
819 u8 deployed = data[2];
821 infostream<<"Client: TOCLIENT_INIT received with "
822 "deployed="<<((int)deployed&0xff)<<std::endl;
824 if(deployed < SER_FMT_VER_LOWEST
825 || deployed > SER_FMT_VER_HIGHEST)
827 infostream<<"Client: TOCLIENT_INIT: Server sent "
828 <<"unsupported ser_fmt_ver"<<std::endl;
832 m_server_ser_ver = deployed;
834 // Get player position
835 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
836 if(datasize >= 2+1+6)
837 playerpos_s16 = readV3S16(&data[2+1]);
838 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
841 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
843 // Set player position
844 Player *player = m_env.getLocalPlayer();
845 assert(player != NULL);
846 player->setPosition(playerpos_f);
849 if(datasize >= 2+1+6+8)
852 m_map_seed = readU64(&data[2+1+6]);
853 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
858 SharedBuffer<u8> reply(replysize);
859 writeU16(&reply[0], TOSERVER_INIT2);
861 m_con.Send(PEER_ID_SERVER, 1, reply, true);
866 if(command == TOCLIENT_ACCESS_DENIED)
868 // The server didn't like our password. Note, this needs
869 // to be processed even if the serialisation format has
870 // not been agreed yet, the same as TOCLIENT_INIT.
871 m_access_denied = true;
872 m_access_denied_reason = L"Unknown";
875 std::string datastring((char*)&data[2], datasize-2);
876 std::istringstream is(datastring, std::ios_base::binary);
877 m_access_denied_reason = deSerializeWideString(is);
882 if(ser_version == SER_FMT_VER_INVALID)
884 infostream<<"Client: Server serialization"
885 " format invalid or not initialized."
886 " Skipping incoming command="<<command<<std::endl;
890 // Just here to avoid putting the two if's together when
891 // making some copypasta
894 if(command == TOCLIENT_REMOVENODE)
899 p.X = readS16(&data[2]);
900 p.Y = readS16(&data[4]);
901 p.Z = readS16(&data[6]);
903 //TimeTaker t1("TOCLIENT_REMOVENODE");
907 else if(command == TOCLIENT_ADDNODE)
909 if(datasize < 8 + MapNode::serializedLength(ser_version))
913 p.X = readS16(&data[2]);
914 p.Y = readS16(&data[4]);
915 p.Z = readS16(&data[6]);
917 //TimeTaker t1("TOCLIENT_ADDNODE");
920 n.deSerialize(&data[8], ser_version);
924 else if(command == TOCLIENT_BLOCKDATA)
926 // Ignore too small packet
931 p.X = readS16(&data[2]);
932 p.Y = readS16(&data[4]);
933 p.Z = readS16(&data[6]);
935 /*infostream<<"Client: Thread: BLOCKDATA for ("
936 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
937 /*infostream<<"Client: Thread: BLOCKDATA for ("
938 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
940 std::string datastring((char*)&data[8], datasize-8);
941 std::istringstream istr(datastring, std::ios_base::binary);
947 sector = m_env.getMap().emergeSector(p2d);
949 assert(sector->getPos() == p2d);
951 //TimeTaker timer("MapBlock deSerialize");
954 block = sector->getBlockNoCreateNoEx(p.Y);
958 Update an existing block
960 //infostream<<"Updating"<<std::endl;
961 block->deSerialize(istr, ser_version, false);
968 //infostream<<"Creating new"<<std::endl;
969 block = new MapBlock(&m_env.getMap(), p, this);
970 block->deSerialize(istr, ser_version, false);
971 sector->insertBlock(block);
985 u32 replysize = 2+1+6;
986 SharedBuffer<u8> reply(replysize);
987 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
989 writeV3S16(&reply[3], p);
991 m_con.Send(PEER_ID_SERVER, 1, reply, true);
995 Add it to mesh update queue and set it to be acknowledged after update.
997 //infostream<<"Adding mesh update task for received block"<<std::endl;
998 addUpdateMeshTaskWithEdge(p, true);
1000 else if(command == TOCLIENT_INVENTORY)
1005 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1008 //TimeTaker t2("mutex locking", m_device);
1009 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1012 //TimeTaker t3("istringstream init", m_device);
1013 std::string datastring((char*)&data[2], datasize-2);
1014 std::istringstream is(datastring, std::ios_base::binary);
1017 //m_env.printPlayers(infostream);
1019 //TimeTaker t4("player get", m_device);
1020 Player *player = m_env.getLocalPlayer();
1021 assert(player != NULL);
1024 //TimeTaker t1("inventory.deSerialize()", m_device);
1025 player->inventory.deSerialize(is);
1028 m_inventory_updated = true;
1030 delete m_inventory_from_server;
1031 m_inventory_from_server = new Inventory(player->inventory);
1032 m_inventory_from_server_age = 0.0;
1034 //infostream<<"Client got player inventory:"<<std::endl;
1035 //player->inventory.print(infostream);
1038 else if(command == TOCLIENT_TIME_OF_DAY)
1043 u16 time_of_day = readU16(&data[2]);
1044 time_of_day = time_of_day % 24000;
1045 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1053 m_env.setTimeOfDay(time_of_day);
1055 u32 dr = m_env.getDayNightRatio();
1057 infostream<<"Client: time_of_day="<<time_of_day
1063 else if(command == TOCLIENT_CHAT_MESSAGE)
1071 std::string datastring((char*)&data[2], datasize-2);
1072 std::istringstream is(datastring, std::ios_base::binary);
1075 is.read((char*)buf, 2);
1076 u16 len = readU16(buf);
1078 std::wstring message;
1079 for(u16 i=0; i<len; i++)
1081 is.read((char*)buf, 2);
1082 message += (wchar_t)readU16(buf);
1085 /*infostream<<"Client received chat message: "
1086 <<wide_to_narrow(message)<<std::endl;*/
1088 m_chat_queue.push_back(message);
1090 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1092 //if(g_settings->getBool("enable_experimental"))
1096 u16 count of removed objects
1097 for all removed objects {
1100 u16 count of added objects
1101 for all added objects {
1104 u32 initialization data length
1105 string initialization data
1110 // Get all data except the command number
1111 std::string datastring((char*)&data[2], datasize-2);
1112 // Throw them in an istringstream
1113 std::istringstream is(datastring, std::ios_base::binary);
1117 // Read removed objects
1119 u16 removed_count = readU16((u8*)buf);
1120 for(u16 i=0; i<removed_count; i++)
1123 u16 id = readU16((u8*)buf);
1126 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1127 m_env.removeActiveObject(id);
1131 // Read added objects
1133 u16 added_count = readU16((u8*)buf);
1134 for(u16 i=0; i<added_count; i++)
1137 u16 id = readU16((u8*)buf);
1139 u8 type = readU8((u8*)buf);
1140 std::string data = deSerializeLongString(is);
1143 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1144 m_env.addActiveObject(id, type, data);
1149 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1151 //if(g_settings->getBool("enable_experimental"))
1163 // Get all data except the command number
1164 std::string datastring((char*)&data[2], datasize-2);
1165 // Throw them in an istringstream
1166 std::istringstream is(datastring, std::ios_base::binary);
1168 while(is.eof() == false)
1172 u16 id = readU16((u8*)buf);
1176 u16 message_size = readU16((u8*)buf);
1177 std::string message;
1178 message.reserve(message_size);
1179 for(u16 i=0; i<message_size; i++)
1182 message.append(buf, 1);
1184 // Pass on to the environment
1186 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1187 m_env.processActiveObjectMessage(id, message);
1192 else if(command == TOCLIENT_HP)
1194 std::string datastring((char*)&data[2], datasize-2);
1195 std::istringstream is(datastring, std::ios_base::binary);
1196 Player *player = m_env.getLocalPlayer();
1197 assert(player != NULL);
1198 u8 oldhp = player->hp;
1204 // Add to ClientEvent queue
1206 event.type = CE_PLAYER_DAMAGE;
1207 event.player_damage.amount = oldhp - hp;
1208 m_client_event_queue.push_back(event);
1211 else if(command == TOCLIENT_MOVE_PLAYER)
1213 std::string datastring((char*)&data[2], datasize-2);
1214 std::istringstream is(datastring, std::ios_base::binary);
1215 Player *player = m_env.getLocalPlayer();
1216 assert(player != NULL);
1217 v3f pos = readV3F1000(is);
1218 f32 pitch = readF1000(is);
1219 f32 yaw = readF1000(is);
1220 player->setPosition(pos);
1221 /*player->setPitch(pitch);
1222 player->setYaw(yaw);*/
1224 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1225 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1231 Add to ClientEvent queue.
1232 This has to be sent to the main program because otherwise
1233 it would just force the pitch and yaw values to whatever
1234 the camera points to.
1237 event.type = CE_PLAYER_FORCE_MOVE;
1238 event.player_force_move.pitch = pitch;
1239 event.player_force_move.yaw = yaw;
1240 m_client_event_queue.push_back(event);
1242 // Ignore damage for a few seconds, so that the player doesn't
1243 // get damage from falling on ground
1244 m_ignore_damage_timer = 3.0;
1246 else if(command == TOCLIENT_PLAYERITEM)
1248 std::string datastring((char*)&data[2], datasize-2);
1249 std::istringstream is(datastring, std::ios_base::binary);
1251 u16 count = readU16(is);
1253 for (u16 i = 0; i < count; ++i) {
1254 u16 peer_id = readU16(is);
1255 Player *player = m_env.getPlayer(peer_id);
1259 infostream<<"Client: ignoring player item "
1260 << deSerializeString(is)
1261 << " for non-existing peer id " << peer_id
1264 } else if (player->isLocal()) {
1265 infostream<<"Client: ignoring player item "
1266 << deSerializeString(is)
1267 << " for local player" << std::endl;
1270 InventoryList *inv = player->inventory.getList("main");
1271 std::string itemstring(deSerializeString(is));
1273 item.deSerialize(itemstring, m_itemdef);
1274 inv->changeItem(0, item);
1275 if(itemstring.empty())
1277 infostream<<"Client: empty player item for peer "
1278 <<peer_id<<std::endl;
1282 infostream<<"Client: player item for peer "
1283 <<peer_id<<": "<<itemstring<<std::endl;
1288 else if(command == TOCLIENT_DEATHSCREEN)
1290 std::string datastring((char*)&data[2], datasize-2);
1291 std::istringstream is(datastring, std::ios_base::binary);
1293 bool set_camera_point_target = readU8(is);
1294 v3f camera_point_target = readV3F1000(is);
1297 event.type = CE_DEATHSCREEN;
1298 event.deathscreen.set_camera_point_target = set_camera_point_target;
1299 event.deathscreen.camera_point_target_x = camera_point_target.X;
1300 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1301 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1302 m_client_event_queue.push_back(event);
1304 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1306 io::IFileSystem *irrfs = m_device->getFileSystem();
1307 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1309 std::string datastring((char*)&data[2], datasize-2);
1310 std::istringstream is(datastring, std::ios_base::binary);
1312 // Mesh update thread must be stopped while
1313 // updating content definitions
1314 assert(!m_mesh_update_thread.IsRunning());
1316 int num_textures = readU16(is);
1318 core::list<TextureRequest> texture_requests;
1320 for(int i=0; i<num_textures; i++){
1322 bool texture_found = false;
1324 //read texture from cache
1325 std::string name = deSerializeString(is);
1326 std::string sha1_texture = deSerializeString(is);
1328 // if name contains illegal characters, ignore the texture
1329 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1330 errorstream<<"Client: ignoring illegal texture name "
1331 <<"sent by server: \""<<name<<"\""<<std::endl;
1335 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1337 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1340 if(fis.good() == false){
1341 infostream<<"Client::Texture not found in cache: "
1342 <<name << " expected it at: "<<tpath<<std::endl;
1346 std::ostringstream tmp_os(std::ios_base::binary);
1350 fis.read(buf, 1024);
1351 std::streamsize len = fis.gcount();
1352 tmp_os.write(buf, len);
1361 infostream<<"Client: Failed to read texture from cache\""
1362 <<name<<"\""<<std::endl;
1367 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1369 unsigned char *digest = sha1.getDigest();
1371 std::string digest_string = base64_encode(digest, 20);
1373 if (digest_string == sha1_texture) {
1374 // Silly irrlicht's const-incorrectness
1375 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1377 // Create an irrlicht memory file
1378 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1379 *data_rw, tmp_os.str().size(), "_tempreadfile");
1382 video::IImage *img = vdrv->createImageFromFile(rfile);
1384 infostream<<"Client: Cannot create image from data of "
1385 <<"received texture \""<<name<<"\""<<std::endl;
1389 m_tsrc->insertSourceImage(name, img);
1393 texture_found = true;
1397 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1398 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1405 //add texture request
1406 if (!texture_found) {
1407 infostream<<"Client: Adding texture to request list: \""
1408 <<name<<"\""<<std::endl;
1409 texture_requests.push_back(TextureRequest(name));
1415 event.type = CE_TEXTURES_UPDATED;
1416 m_client_event_queue.push_back(event);
1419 //send Texture request
1422 u16 number of textures requested
1428 std::ostringstream os(std::ios_base::binary);
1433 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1434 os.write((char*)buf, 2);
1436 writeU16(buf,texture_requests.size());
1437 os.write((char*)buf, 2);
1440 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1441 i != texture_requests.end(); i++) {
1442 os<<serializeString(i->name);
1446 std::string s = os.str();
1447 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1449 Send(0, data, true);
1450 infostream<<"Client: Sending request list to server " <<std::endl;
1452 else if(command == TOCLIENT_TEXTURES)
1454 io::IFileSystem *irrfs = m_device->getFileSystem();
1455 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1457 std::string datastring((char*)&data[2], datasize-2);
1458 std::istringstream is(datastring, std::ios_base::binary);
1460 // Mesh update thread must be stopped while
1461 // updating content definitions
1462 assert(!m_mesh_update_thread.IsRunning());
1466 u16 total number of texture bunches
1467 u16 index of this bunch
1468 u32 number of textures in this bunch
1476 int num_bunches = readU16(is);
1477 int bunch_i = readU16(is);
1478 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1479 if(bunch_i == num_bunches - 1)
1480 m_textures_received = true;
1481 int num_textures = readU32(is);
1482 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1483 <<num_bunches<<" textures="<<num_textures
1484 <<" size="<<datasize<<std::endl;
1485 for(int i=0; i<num_textures; i++){
1486 std::string name = deSerializeString(is);
1487 std::string data = deSerializeLongString(is);
1489 // if name contains illegal characters, ignore the texture
1490 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1491 errorstream<<"Client: ignoring illegal texture name "
1492 <<"sent by server: \""<<name<<"\""<<std::endl;
1496 // Silly irrlicht's const-incorrectness
1497 Buffer<char> data_rw(data.c_str(), data.size());
1498 // Create an irrlicht memory file
1499 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1500 *data_rw, data.size(), "_tempreadfile");
1503 video::IImage *img = vdrv->createImageFromFile(rfile);
1505 errorstream<<"Client: Cannot create image from data of "
1506 <<"received texture \""<<name<<"\""<<std::endl;
1511 fs::CreateAllDirs(getTextureCacheDir());
1513 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1514 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1516 if (outfile.good()) {
1517 outfile.write(data.c_str(),data.length());
1521 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1524 m_tsrc->insertSourceImage(name, img);
1530 event.type = CE_TEXTURES_UPDATED;
1531 m_client_event_queue.push_back(event);
1533 else if(command == TOCLIENT_TOOLDEF)
1535 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1537 else if(command == TOCLIENT_NODEDEF)
1539 infostream<<"Client: Received node definitions: packet size: "
1540 <<datasize<<std::endl;
1542 // Mesh update thread must be stopped while
1543 // updating content definitions
1544 assert(!m_mesh_update_thread.IsRunning());
1546 // Decompress node definitions
1547 std::string datastring((char*)&data[2], datasize-2);
1548 std::istringstream is(datastring, std::ios_base::binary);
1549 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1550 std::ostringstream tmp_os;
1551 decompressZlib(tmp_is, tmp_os);
1553 // Deserialize node definitions
1554 std::istringstream tmp_is2(tmp_os.str());
1555 m_nodedef->deSerialize(tmp_is2);
1556 m_nodedef_received = true;
1558 else if(command == TOCLIENT_CRAFTITEMDEF)
1560 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1562 else if(command == TOCLIENT_ITEMDEF)
1564 infostream<<"Client: Received item definitions: packet size: "
1565 <<datasize<<std::endl;
1567 // Mesh update thread must be stopped while
1568 // updating content definitions
1569 assert(!m_mesh_update_thread.IsRunning());
1571 // Decompress item definitions
1572 std::string datastring((char*)&data[2], datasize-2);
1573 std::istringstream is(datastring, std::ios_base::binary);
1574 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1575 std::ostringstream tmp_os;
1576 decompressZlib(tmp_is, tmp_os);
1578 // Deserialize node definitions
1579 std::istringstream tmp_is2(tmp_os.str());
1580 m_itemdef->deSerialize(tmp_is2);
1581 m_itemdef_received = true;
1585 infostream<<"Client: Ignoring unknown command "
1586 <<command<<std::endl;
1590 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1592 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1593 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1596 void Client::interact(u8 action, const PointedThing& pointed)
1598 if(connectedAndInitialized() == false){
1599 infostream<<"Client::interact() "
1600 "cancelled (not connected)"
1605 std::ostringstream os(std::ios_base::binary);
1611 [5] u32 length of the next item
1612 [9] serialized PointedThing
1614 0: start digging (from undersurface) or use
1615 1: stop digging (all parameters ignored)
1616 2: digging completed
1617 3: place block or item (to abovesurface)
1620 writeU16(os, TOSERVER_INTERACT);
1621 writeU8(os, action);
1622 writeU16(os, getPlayerItem());
1623 std::ostringstream tmp_os(std::ios::binary);
1624 pointed.serialize(tmp_os);
1625 os<<serializeLongString(tmp_os.str());
1627 std::string s = os.str();
1628 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1631 Send(0, data, true);
1634 void Client::sendSignNodeText(v3s16 p, std::string text)
1642 std::ostringstream os(std::ios_base::binary);
1646 writeU16(buf, TOSERVER_SIGNNODETEXT);
1647 os.write((char*)buf, 2);
1651 os.write((char*)buf, 6);
1653 u16 textlen = text.size();
1654 // Write text length
1655 writeS16(buf, textlen);
1656 os.write((char*)buf, 2);
1659 os.write((char*)text.c_str(), textlen);
1662 std::string s = os.str();
1663 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1665 Send(0, data, true);
1668 void Client::sendInventoryAction(InventoryAction *a)
1670 std::ostringstream os(std::ios_base::binary);
1674 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1675 os.write((char*)buf, 2);
1680 std::string s = os.str();
1681 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1683 Send(0, data, true);
1686 void Client::sendChatMessage(const std::wstring &message)
1688 std::ostringstream os(std::ios_base::binary);
1692 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1693 os.write((char*)buf, 2);
1696 writeU16(buf, message.size());
1697 os.write((char*)buf, 2);
1700 for(u32 i=0; i<message.size(); i++)
1704 os.write((char*)buf, 2);
1708 std::string s = os.str();
1709 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1711 Send(0, data, true);
1714 void Client::sendChangePassword(const std::wstring oldpassword,
1715 const std::wstring newpassword)
1717 Player *player = m_env.getLocalPlayer();
1721 std::string playername = player->getName();
1722 std::string oldpwd = translatePassword(playername, oldpassword);
1723 std::string newpwd = translatePassword(playername, newpassword);
1725 std::ostringstream os(std::ios_base::binary);
1726 u8 buf[2+PASSWORD_SIZE*2];
1728 [0] u16 TOSERVER_PASSWORD
1729 [2] u8[28] old password
1730 [30] u8[28] new password
1733 writeU16(buf, TOSERVER_PASSWORD);
1734 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1736 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1737 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1739 buf[2+PASSWORD_SIZE-1] = 0;
1740 buf[30+PASSWORD_SIZE-1] = 0;
1741 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1744 std::string s = os.str();
1745 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1747 Send(0, data, true);
1751 void Client::sendDamage(u8 damage)
1753 DSTACK(__FUNCTION_NAME);
1754 std::ostringstream os(std::ios_base::binary);
1756 writeU16(os, TOSERVER_DAMAGE);
1757 writeU8(os, damage);
1760 std::string s = os.str();
1761 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1763 Send(0, data, true);
1766 void Client::sendRespawn()
1768 DSTACK(__FUNCTION_NAME);
1769 std::ostringstream os(std::ios_base::binary);
1771 writeU16(os, TOSERVER_RESPAWN);
1774 std::string s = os.str();
1775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1777 Send(0, data, true);
1780 void Client::sendPlayerPos()
1782 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1784 Player *myplayer = m_env.getLocalPlayer();
1785 if(myplayer == NULL)
1790 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1791 our_peer_id = m_con.GetPeerID();
1794 // Set peer id if not set already
1795 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1796 myplayer->peer_id = our_peer_id;
1797 // Check that an existing peer_id is the same as the connection's
1798 assert(myplayer->peer_id == our_peer_id);
1800 v3f pf = myplayer->getPosition();
1801 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1802 v3f sf = myplayer->getSpeed();
1803 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1804 s32 pitch = myplayer->getPitch() * 100;
1805 s32 yaw = myplayer->getYaw() * 100;
1810 [2] v3s32 position*100
1811 [2+12] v3s32 speed*100
1812 [2+12+12] s32 pitch*100
1813 [2+12+12+4] s32 yaw*100
1816 SharedBuffer<u8> data(2+12+12+4+4);
1817 writeU16(&data[0], TOSERVER_PLAYERPOS);
1818 writeV3S32(&data[2], position);
1819 writeV3S32(&data[2+12], speed);
1820 writeS32(&data[2+12+12], pitch);
1821 writeS32(&data[2+12+12+4], yaw);
1823 // Send as unreliable
1824 Send(0, data, false);
1827 void Client::sendPlayerItem(u16 item)
1829 Player *myplayer = m_env.getLocalPlayer();
1830 if(myplayer == NULL)
1833 u16 our_peer_id = m_con.GetPeerID();
1835 // Set peer id if not set already
1836 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1837 myplayer->peer_id = our_peer_id;
1838 // Check that an existing peer_id is the same as the connection's
1839 assert(myplayer->peer_id == our_peer_id);
1841 SharedBuffer<u8> data(2+2);
1842 writeU16(&data[0], TOSERVER_PLAYERITEM);
1843 writeU16(&data[2], item);
1846 Send(0, data, true);
1849 void Client::removeNode(v3s16 p)
1851 core::map<v3s16, MapBlock*> modified_blocks;
1855 //TimeTaker t("removeNodeAndUpdate", m_device);
1856 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1858 catch(InvalidPositionException &e)
1862 // add urgent task to update the modified node
1863 addUpdateMeshTaskForNode(p, false, true);
1865 for(core::map<v3s16, MapBlock * >::Iterator
1866 i = modified_blocks.getIterator();
1867 i.atEnd() == false; i++)
1869 v3s16 p = i.getNode()->getKey();
1870 addUpdateMeshTaskWithEdge(p);
1874 void Client::addNode(v3s16 p, MapNode n)
1876 TimeTaker timer1("Client::addNode()");
1878 core::map<v3s16, MapBlock*> modified_blocks;
1882 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1883 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1885 catch(InvalidPositionException &e)
1888 for(core::map<v3s16, MapBlock * >::Iterator
1889 i = modified_blocks.getIterator();
1890 i.atEnd() == false; i++)
1892 v3s16 p = i.getNode()->getKey();
1893 addUpdateMeshTaskWithEdge(p);
1897 void Client::setPlayerControl(PlayerControl &control)
1899 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1900 LocalPlayer *player = m_env.getLocalPlayer();
1901 assert(player != NULL);
1902 player->control = control;
1905 void Client::selectPlayerItem(u16 item)
1907 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1908 m_playeritem = item;
1909 m_inventory_updated = true;
1910 sendPlayerItem(item);
1913 // Returns true if the inventory of the local player has been
1914 // updated from the server. If it is true, it is set to false.
1915 bool Client::getLocalInventoryUpdated()
1917 // m_inventory_updated is behind envlock
1918 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1919 bool updated = m_inventory_updated;
1920 m_inventory_updated = false;
1924 // Copies the inventory of the local player to parameter
1925 void Client::getLocalInventory(Inventory &dst)
1927 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1928 Player *player = m_env.getLocalPlayer();
1929 assert(player != NULL);
1930 dst = player->inventory;
1933 Inventory* Client::getInventory(const InventoryLocation &loc)
1936 case InventoryLocation::UNDEFINED:
1939 case InventoryLocation::CURRENT_PLAYER:
1941 Player *player = m_env.getLocalPlayer();
1942 assert(player != NULL);
1943 return &player->inventory;
1946 case InventoryLocation::PLAYER:
1948 Player *player = m_env.getPlayer(loc.name.c_str());
1951 return &player->inventory;
1954 case InventoryLocation::NODEMETA:
1956 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1959 return meta->getInventory();
1967 void Client::inventoryAction(InventoryAction *a)
1970 Send it to the server
1972 sendInventoryAction(a);
1975 Predict some local inventory changes
1977 a->clientApply(this, this);
1980 ClientActiveObject * Client::getSelectedActiveObject(
1982 v3f from_pos_f_on_map,
1983 core::line3d<f32> shootline_on_map
1986 core::array<DistanceSortedActiveObject> objects;
1988 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1990 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1993 // After this, the closest object is the first in the array.
1996 for(u32 i=0; i<objects.size(); i++)
1998 ClientActiveObject *obj = objects[i].obj;
2000 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2001 if(selection_box == NULL)
2004 v3f pos = obj->getPosition();
2006 core::aabbox3d<f32> offsetted_box(
2007 selection_box->MinEdge + pos,
2008 selection_box->MaxEdge + pos
2011 if(offsetted_box.intersectsWithLine(shootline_on_map))
2013 //infostream<<"Returning selected object"<<std::endl;
2018 //infostream<<"No object selected; returning NULL."<<std::endl;
2022 void Client::printDebugInfo(std::ostream &os)
2024 //JMutexAutoLock lock1(m_fetchblock_mutex);
2025 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2027 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2028 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2029 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2033 core::list<std::wstring> Client::getConnectedPlayerNames()
2035 core::list<Player*> players = m_env.getPlayers(true);
2036 core::list<std::wstring> playerNames;
2037 for(core::list<Player*>::Iterator
2038 i = players.begin();
2039 i != players.end(); i++)
2041 Player *player = *i;
2042 playerNames.push_back(narrow_to_wide(player->getName()));
2047 float Client::getAnimationTime()
2049 return m_animation_time;
2052 int Client::getCrackLevel()
2054 return m_crack_level;
2057 void Client::setCrack(int level, v3s16 pos)
2059 int old_crack_level = m_crack_level;
2060 v3s16 old_crack_pos = m_crack_pos;
2062 m_crack_level = level;
2065 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2068 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2070 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2073 addUpdateMeshTaskForNode(pos, false, true);
2077 u32 Client::getDayNightRatio()
2079 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2080 return m_env.getDayNightRatio();
2085 Player *player = m_env.getLocalPlayer();
2086 assert(player != NULL);
2090 bool Client::getChatMessage(std::wstring &message)
2092 if(m_chat_queue.size() == 0)
2094 message = m_chat_queue.pop_front();
2098 void Client::typeChatMessage(const std::wstring &message)
2100 // Discard empty line
2105 sendChatMessage(message);
2108 if (message[0] == L'/')
2110 m_chat_queue.push_back(
2111 (std::wstring)L"issued command: "+message);
2115 LocalPlayer *player = m_env.getLocalPlayer();
2116 assert(player != NULL);
2117 std::wstring name = narrow_to_wide(player->getName());
2118 m_chat_queue.push_back(
2119 (std::wstring)L"<"+name+L"> "+message);
2123 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2125 /*infostream<<"Client::addUpdateMeshTask(): "
2126 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2127 <<" ack_to_server="<<ack_to_server
2128 <<" urgent="<<urgent
2131 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2136 Create a task to update the mesh of the block
2139 MeshMakeData *data = new MeshMakeData(this);
2142 //TimeTaker timer("data fill");
2144 // Debug: 1-6ms, avg=2ms
2146 data->setCrack(m_crack_level, m_crack_pos);
2147 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2151 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2153 // Add task to queue
2154 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2156 /*infostream<<"Mesh update input queue size is "
2157 <<m_mesh_update_thread.m_queue_in.size()
2161 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2165 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2166 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2171 v3s16 p = blockpos + v3s16(0,0,0);
2172 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2173 addUpdateMeshTask(p, ack_to_server, urgent);
2175 catch(InvalidPositionException &e){}
2178 v3s16 p = blockpos + v3s16(-1,0,0);
2179 addUpdateMeshTask(p, false, urgent);
2181 catch(InvalidPositionException &e){}
2183 v3s16 p = blockpos + v3s16(0,-1,0);
2184 addUpdateMeshTask(p, false, urgent);
2186 catch(InvalidPositionException &e){}
2188 v3s16 p = blockpos + v3s16(0,0,-1);
2189 addUpdateMeshTask(p, false, urgent);
2191 catch(InvalidPositionException &e){}
2194 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2198 infostream<<"Client::addUpdateMeshTaskForNode(): "
2199 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2203 v3s16 blockpos = getNodeBlockPos(nodepos);
2204 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2207 v3s16 p = blockpos + v3s16(0,0,0);
2208 addUpdateMeshTask(p, ack_to_server, urgent);
2210 catch(InvalidPositionException &e){}
2212 if(nodepos.X == blockpos_relative.X){
2214 v3s16 p = blockpos + v3s16(-1,0,0);
2215 addUpdateMeshTask(p, false, urgent);
2217 catch(InvalidPositionException &e){}
2219 if(nodepos.Y == blockpos_relative.Y){
2221 v3s16 p = blockpos + v3s16(0,-1,0);
2222 addUpdateMeshTask(p, false, urgent);
2224 catch(InvalidPositionException &e){}
2226 if(nodepos.Z == blockpos_relative.Z){
2228 v3s16 p = blockpos + v3s16(0,0,-1);
2229 addUpdateMeshTask(p, false, urgent);
2231 catch(InvalidPositionException &e){}
2235 ClientEvent Client::getClientEvent()
2237 if(m_client_event_queue.size() == 0)
2240 event.type = CE_NONE;
2243 return m_client_event_queue.pop_front();
2246 void Client::afterContentReceived()
2248 assert(m_itemdef_received);
2249 assert(m_nodedef_received);
2250 assert(m_textures_received);
2252 // Rebuild inherited images and recreate textures
2253 m_tsrc->rebuildImagesAndTextures();
2255 // Update texture atlas
2256 if(g_settings->getBool("enable_texture_atlas"))
2257 m_tsrc->buildMainAtlas(this);
2259 // Update node aliases
2260 m_nodedef->updateAliases(m_itemdef);
2262 // Update node textures
2263 m_nodedef->updateTextures(m_tsrc);
2265 // Update item textures and meshes
2266 m_itemdef->updateTexturesAndMeshes(this);
2268 // Start mesh update thread after setting up content definitions
2269 m_mesh_update_thread.Start();
2272 float Client::getRTT(void)
2275 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2276 } catch(con::PeerNotFoundException &e){
2281 // IGameDef interface
2283 IItemDefManager* Client::getItemDefManager()
2287 INodeDefManager* Client::getNodeDefManager()
2291 ICraftDefManager* Client::getCraftDefManager()
2294 //return m_craftdef;
2296 ITextureSource* Client::getTextureSource()
2300 u16 Client::allocateUnknownNodeId(const std::string &name)
2302 errorstream<<"Client::allocateUnknownNodeId(): "
2303 <<"Client cannot allocate node IDs"<<std::endl;
2305 return CONTENT_IGNORE;