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 "craftitemdef.h"
38 #include <IFileSystem.h>
44 QueuedMeshUpdate::QueuedMeshUpdate():
47 ack_block_to_server(false)
51 QueuedMeshUpdate::~QueuedMeshUpdate()
61 MeshUpdateQueue::MeshUpdateQueue()
66 MeshUpdateQueue::~MeshUpdateQueue()
68 JMutexAutoLock lock(m_mutex);
70 core::list<QueuedMeshUpdate*>::Iterator i;
71 for(i=m_queue.begin(); i!=m_queue.end(); i++)
73 QueuedMeshUpdate *q = *i;
79 peer_id=0 adds with nobody to send to
81 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
83 DSTACK(__FUNCTION_NAME);
87 JMutexAutoLock lock(m_mutex);
90 Find if block is already in queue.
91 If it is, update the data and quit.
93 core::list<QueuedMeshUpdate*>::Iterator i;
94 for(i=m_queue.begin(); i!=m_queue.end(); i++)
96 QueuedMeshUpdate *q = *i;
102 if(ack_block_to_server)
103 q->ack_block_to_server = true;
111 QueuedMeshUpdate *q = new QueuedMeshUpdate;
114 q->ack_block_to_server = ack_block_to_server;
115 m_queue.push_back(q);
118 // Returned pointer must be deleted
119 // Returns NULL if queue is empty
120 QueuedMeshUpdate * MeshUpdateQueue::pop()
122 JMutexAutoLock lock(m_mutex);
124 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
125 if(i == m_queue.end())
127 QueuedMeshUpdate *q = *i;
136 void * MeshUpdateThread::Thread()
140 log_register_thread("MeshUpdateThread");
142 DSTACK(__FUNCTION_NAME);
144 BEGIN_DEBUG_EXCEPTION_HANDLER
148 /*// Wait for output queue to flush.
149 // Allow 2 in queue, this makes less frametime jitter.
150 // Umm actually, there is no much difference
151 if(m_queue_out.size() >= 2)
157 QueuedMeshUpdate *q = m_queue_in.pop();
164 ScopeProfiler sp(g_profiler, "Client: Mesh making");
166 scene::SMesh *mesh_new = NULL;
167 mesh_new = makeMapBlockMesh(q->data, m_gamedef);
172 r.ack_block_to_server = q->ack_block_to_server;
174 /*infostream<<"MeshUpdateThread: Processed "
175 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
178 m_queue_out.push_back(r);
183 END_DEBUG_EXCEPTION_HANDLER(errorstream)
189 IrrlichtDevice *device,
190 const char *playername,
191 std::string password,
192 MapDrawControl &control,
193 IWritableTextureSource *tsrc,
194 IWritableToolDefManager *tooldef,
195 IWritableNodeDefManager *nodedef,
196 IWritableCraftItemDefManager *craftitemdef
201 m_craftitemdef(craftitemdef),
202 m_mesh_update_thread(this),
204 new ClientMap(this, this, control,
205 device->getSceneManager()->getRootSceneNode(),
206 device->getSceneManager(), 666),
207 device->getSceneManager(),
210 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
212 m_server_ser_ver(SER_FMT_VER_INVALID),
214 m_inventory_updated(false),
217 m_password(password),
218 m_access_denied(false),
219 m_texture_receive_progress(0),
220 m_textures_received(false),
221 m_tooldef_received(false),
222 m_nodedef_received(false),
223 m_craftitemdef_received(false)
225 m_packetcounter_timer = 0.0;
226 //m_delete_unused_sectors_timer = 0.0;
227 m_connection_reinit_timer = 0.0;
228 m_avg_rtt_timer = 0.0;
229 m_playerpos_send_timer = 0.0;
230 m_ignore_damage_timer = 0.0;
232 // Build main texture atlas, now that the GameDef exists (that is, us)
233 if(g_settings->getBool("enable_texture_atlas"))
234 m_tsrc->buildMainAtlas(this);
236 infostream<<"Not building texture atlas."<<std::endl;
238 // Update node textures
239 m_nodedef->updateTextures(m_tsrc);
241 // Start threads after setting up content definitions
242 m_mesh_update_thread.Start();
248 Player *player = new LocalPlayer(this);
250 player->updateName(playername);
252 m_env.addPlayer(player);
254 // Initialize player in the inventory context
255 m_inventory_context.current_player = player;
262 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
266 m_mesh_update_thread.setRun(false);
267 while(m_mesh_update_thread.IsRunning())
271 void Client::connect(Address address)
273 DSTACK(__FUNCTION_NAME);
274 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
275 m_con.SetTimeoutMs(0);
276 m_con.Connect(address);
279 bool Client::connectedAndInitialized()
281 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
283 if(m_con.Connected() == false)
286 if(m_server_ser_ver == SER_FMT_VER_INVALID)
292 void Client::step(float dtime)
294 DSTACK(__FUNCTION_NAME);
300 if(m_ignore_damage_timer > dtime)
301 m_ignore_damage_timer -= dtime;
303 m_ignore_damage_timer = 0.0;
305 //infostream<<"Client steps "<<dtime<<std::endl;
308 //TimeTaker timer("ReceiveAll()", m_device);
314 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
316 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
317 m_con.RunTimeouts(dtime);
324 float &counter = m_packetcounter_timer;
330 infostream<<"Client packetcounter (20s):"<<std::endl;
331 m_packetcounter.print(infostream);
332 m_packetcounter.clear();
336 // Get connection status
337 bool connected = connectedAndInitialized();
342 Delete unused sectors
344 NOTE: This jams the game for a while because deleting sectors
348 float &counter = m_delete_unused_sectors_timer;
356 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
358 core::list<v3s16> deleted_blocks;
360 float delete_unused_sectors_timeout =
361 g_settings->getFloat("client_delete_unused_sectors_timeout");
363 // Delete sector blocks
364 /*u32 num = m_env.getMap().unloadUnusedData
365 (delete_unused_sectors_timeout,
366 true, &deleted_blocks);*/
368 // Delete whole sectors
369 m_env.getMap().unloadUnusedData
370 (delete_unused_sectors_timeout,
373 if(deleted_blocks.size() > 0)
375 /*infostream<<"Client: Deleted blocks of "<<num
376 <<" unused sectors"<<std::endl;*/
377 /*infostream<<"Client: Deleted "<<num
378 <<" unused sectors"<<std::endl;*/
384 // Env is locked so con can be locked.
385 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
387 core::list<v3s16>::Iterator i = deleted_blocks.begin();
388 core::list<v3s16> sendlist;
391 if(sendlist.size() == 255 || i == deleted_blocks.end())
393 if(sendlist.size() == 0)
402 u32 replysize = 2+1+6*sendlist.size();
403 SharedBuffer<u8> reply(replysize);
404 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
405 reply[2] = sendlist.size();
407 for(core::list<v3s16>::Iterator
408 j = sendlist.begin();
409 j != sendlist.end(); j++)
411 writeV3S16(&reply[2+1+6*k], *j);
414 m_con.Send(PEER_ID_SERVER, 1, reply, true);
416 if(i == deleted_blocks.end())
422 sendlist.push_back(*i);
430 if(connected == false)
432 float &counter = m_connection_reinit_timer;
438 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
440 Player *myplayer = m_env.getLocalPlayer();
441 assert(myplayer != NULL);
443 // Send TOSERVER_INIT
444 // [0] u16 TOSERVER_INIT
445 // [2] u8 SER_FMT_VER_HIGHEST
446 // [3] u8[20] player_name
447 // [23] u8[28] password (new in some version)
448 // [51] u16 client network protocol version (new in some version)
449 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
450 writeU16(&data[0], TOSERVER_INIT);
451 writeU8(&data[2], SER_FMT_VER_HIGHEST);
453 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
454 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
456 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
459 memset((char*)&data[23], 0, PASSWORD_SIZE);
460 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
462 // This should be incremented in each version
463 writeU16(&data[51], PROTOCOL_VERSION);
465 // Send as unreliable
466 Send(0, data, false);
469 // Not connected, return
474 Do stuff if connected
478 Run Map's timers and unload unused data
480 const float map_timer_and_unload_dtime = 5.25;
481 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
483 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
484 core::list<v3s16> deleted_blocks;
485 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
486 g_settings->getFloat("client_unload_unused_data_timeout"),
489 /*if(deleted_blocks.size() > 0)
490 infostream<<"Client: Unloaded "<<deleted_blocks.size()
491 <<" unused blocks"<<std::endl;*/
495 NOTE: This loop is intentionally iterated the way it is.
498 core::list<v3s16>::Iterator i = deleted_blocks.begin();
499 core::list<v3s16> sendlist;
502 if(sendlist.size() == 255 || i == deleted_blocks.end())
504 if(sendlist.size() == 0)
513 u32 replysize = 2+1+6*sendlist.size();
514 SharedBuffer<u8> reply(replysize);
515 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
516 reply[2] = sendlist.size();
518 for(core::list<v3s16>::Iterator
519 j = sendlist.begin();
520 j != sendlist.end(); j++)
522 writeV3S16(&reply[2+1+6*k], *j);
525 m_con.Send(PEER_ID_SERVER, 1, reply, true);
527 if(i == deleted_blocks.end())
533 sendlist.push_back(*i);
543 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
545 // Control local player (0ms)
546 LocalPlayer *player = m_env.getLocalPlayer();
547 assert(player != NULL);
548 player->applyControl(dtime);
550 //TimeTaker envtimer("env step", m_device);
559 ClientEnvEvent event = m_env.getClientEvent();
560 if(event.type == CEE_NONE)
564 else if(event.type == CEE_PLAYER_DAMAGE)
566 if(m_ignore_damage_timer <= 0)
568 u8 damage = event.player_damage.amount;
570 if(event.player_damage.send_to_server)
573 // Add to ClientEvent queue
575 event.type = CE_PLAYER_DAMAGE;
576 event.player_damage.amount = damage;
577 m_client_event_queue.push_back(event);
587 float &counter = m_avg_rtt_timer;
592 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
593 // connectedAndInitialized() is true, peer exists.
594 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
595 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
600 Send player position to server
603 float &counter = m_playerpos_send_timer;
613 Replace updated meshes
616 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
618 //TimeTaker timer("** Processing mesh update result queue");
621 /*infostream<<"Mesh update result queue size is "
622 <<m_mesh_update_thread.m_queue_out.size()
625 while(m_mesh_update_thread.m_queue_out.size() > 0)
627 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
628 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
631 block->replaceMesh(r.mesh);
633 if(r.ack_block_to_server)
635 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
636 <<","<<r.p.Z<<")"<<std::endl;*/
647 u32 replysize = 2+1+6;
648 SharedBuffer<u8> reply(replysize);
649 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
651 writeV3S16(&reply[3], r.p);
653 m_con.Send(PEER_ID_SERVER, 1, reply, true);
659 // Virtual methods from con::PeerHandler
660 void Client::peerAdded(con::Peer *peer)
662 infostream<<"Client::peerAdded(): peer->id="
663 <<peer->id<<std::endl;
665 void Client::deletingPeer(con::Peer *peer, bool timeout)
667 infostream<<"Client::deletingPeer(): "
668 "Server Peer is getting deleted "
669 <<"(timeout="<<timeout<<")"<<std::endl;
672 void Client::ReceiveAll()
674 DSTACK(__FUNCTION_NAME);
675 u32 start_ms = porting::getTimeMs();
678 // Limit time even if there would be huge amounts of data to
680 if(porting::getTimeMs() > start_ms + 100)
686 catch(con::NoIncomingDataException &e)
690 catch(con::InvalidIncomingDataException &e)
692 infostream<<"Client::ReceiveAll(): "
693 "InvalidIncomingDataException: what()="
694 <<e.what()<<std::endl;
699 void Client::Receive()
701 DSTACK(__FUNCTION_NAME);
702 SharedBuffer<u8> data;
706 //TimeTaker t1("con mutex and receive", m_device);
707 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
708 datasize = m_con.Receive(sender_peer_id, data);
710 //TimeTaker t1("ProcessData", m_device);
711 ProcessData(*data, datasize, sender_peer_id);
715 sender_peer_id given to this shall be quaranteed to be a valid peer
717 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
719 DSTACK(__FUNCTION_NAME);
721 // Ignore packets that don't even fit a command
724 m_packetcounter.add(60000);
728 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
730 //infostream<<"Client: received command="<<command<<std::endl;
731 m_packetcounter.add((u16)command);
734 If this check is removed, be sure to change the queue
735 system to know the ids
737 if(sender_peer_id != PEER_ID_SERVER)
739 infostream<<"Client::ProcessData(): Discarding data not "
740 "coming from server: peer_id="<<sender_peer_id
745 u8 ser_version = m_server_ser_ver;
747 //infostream<<"Client received command="<<(int)command<<std::endl;
749 if(command == TOCLIENT_INIT)
754 u8 deployed = data[2];
756 infostream<<"Client: TOCLIENT_INIT received with "
757 "deployed="<<((int)deployed&0xff)<<std::endl;
759 if(deployed < SER_FMT_VER_LOWEST
760 || deployed > SER_FMT_VER_HIGHEST)
762 infostream<<"Client: TOCLIENT_INIT: Server sent "
763 <<"unsupported ser_fmt_ver"<<std::endl;
767 m_server_ser_ver = deployed;
769 // Get player position
770 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
771 if(datasize >= 2+1+6)
772 playerpos_s16 = readV3S16(&data[2+1]);
773 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
776 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
778 // Set player position
779 Player *player = m_env.getLocalPlayer();
780 assert(player != NULL);
781 player->setPosition(playerpos_f);
784 if(datasize >= 2+1+6+8)
787 m_map_seed = readU64(&data[2+1+6]);
788 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
793 SharedBuffer<u8> reply(replysize);
794 writeU16(&reply[0], TOSERVER_INIT2);
796 m_con.Send(PEER_ID_SERVER, 1, reply, true);
801 if(command == TOCLIENT_ACCESS_DENIED)
803 // The server didn't like our password. Note, this needs
804 // to be processed even if the serialisation format has
805 // not been agreed yet, the same as TOCLIENT_INIT.
806 m_access_denied = true;
807 m_access_denied_reason = L"Unknown";
810 std::string datastring((char*)&data[2], datasize-2);
811 std::istringstream is(datastring, std::ios_base::binary);
812 m_access_denied_reason = deSerializeWideString(is);
817 if(ser_version == SER_FMT_VER_INVALID)
819 infostream<<"Client: Server serialization"
820 " format invalid or not initialized."
821 " Skipping incoming command="<<command<<std::endl;
825 // Just here to avoid putting the two if's together when
826 // making some copypasta
829 if(command == TOCLIENT_REMOVENODE)
834 p.X = readS16(&data[2]);
835 p.Y = readS16(&data[4]);
836 p.Z = readS16(&data[6]);
838 //TimeTaker t1("TOCLIENT_REMOVENODE");
840 // This will clear the cracking animation after digging
841 ((ClientMap&)m_env.getMap()).clearTempMod(p);
845 else if(command == TOCLIENT_ADDNODE)
847 if(datasize < 8 + MapNode::serializedLength(ser_version))
851 p.X = readS16(&data[2]);
852 p.Y = readS16(&data[4]);
853 p.Z = readS16(&data[6]);
855 //TimeTaker t1("TOCLIENT_ADDNODE");
858 n.deSerialize(&data[8], ser_version);
862 else if(command == TOCLIENT_BLOCKDATA)
864 // Ignore too small packet
869 p.X = readS16(&data[2]);
870 p.Y = readS16(&data[4]);
871 p.Z = readS16(&data[6]);
873 /*infostream<<"Client: Thread: BLOCKDATA for ("
874 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
875 /*infostream<<"Client: Thread: BLOCKDATA for ("
876 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
878 std::string datastring((char*)&data[8], datasize-8);
879 std::istringstream istr(datastring, std::ios_base::binary);
885 sector = m_env.getMap().emergeSector(p2d);
887 assert(sector->getPos() == p2d);
889 //TimeTaker timer("MapBlock deSerialize");
892 block = sector->getBlockNoCreateNoEx(p.Y);
896 Update an existing block
898 //infostream<<"Updating"<<std::endl;
899 block->deSerialize(istr, ser_version);
906 //infostream<<"Creating new"<<std::endl;
907 block = new MapBlock(&m_env.getMap(), p, this);
908 block->deSerialize(istr, ser_version);
909 sector->insertBlock(block);
923 u32 replysize = 2+1+6;
924 SharedBuffer<u8> reply(replysize);
925 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
927 writeV3S16(&reply[3], p);
929 m_con.Send(PEER_ID_SERVER, 1, reply, true);
933 Update Mesh of this block and blocks at x-, y- and z-.
934 Environment should not be locked as it interlocks with the
935 main thread, from which is will want to retrieve textures.
938 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
940 Add it to mesh update queue and set it to be acknowledged after update.
942 //infostream<<"Adding mesh update task for received block"<<std::endl;
943 addUpdateMeshTaskWithEdge(p, true);
945 else if(command == TOCLIENT_INVENTORY)
950 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
953 //TimeTaker t2("mutex locking", m_device);
954 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
957 //TimeTaker t3("istringstream init", m_device);
958 std::string datastring((char*)&data[2], datasize-2);
959 std::istringstream is(datastring, std::ios_base::binary);
962 //m_env.printPlayers(infostream);
964 //TimeTaker t4("player get", m_device);
965 Player *player = m_env.getLocalPlayer();
966 assert(player != NULL);
969 //TimeTaker t1("inventory.deSerialize()", m_device);
970 player->inventory.deSerialize(is, this);
973 m_inventory_updated = true;
975 //infostream<<"Client got player inventory:"<<std::endl;
976 //player->inventory.print(infostream);
979 else if(command == TOCLIENT_TIME_OF_DAY)
984 u16 time_of_day = readU16(&data[2]);
985 time_of_day = time_of_day % 24000;
986 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
994 m_env.setTimeOfDay(time_of_day);
996 u32 dr = m_env.getDayNightRatio();
998 infostream<<"Client: time_of_day="<<time_of_day
1004 else if(command == TOCLIENT_CHAT_MESSAGE)
1012 std::string datastring((char*)&data[2], datasize-2);
1013 std::istringstream is(datastring, std::ios_base::binary);
1016 is.read((char*)buf, 2);
1017 u16 len = readU16(buf);
1019 std::wstring message;
1020 for(u16 i=0; i<len; i++)
1022 is.read((char*)buf, 2);
1023 message += (wchar_t)readU16(buf);
1026 /*infostream<<"Client received chat message: "
1027 <<wide_to_narrow(message)<<std::endl;*/
1029 m_chat_queue.push_back(message);
1031 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1033 //if(g_settings->getBool("enable_experimental"))
1037 u16 count of removed objects
1038 for all removed objects {
1041 u16 count of added objects
1042 for all added objects {
1045 u32 initialization data length
1046 string initialization data
1051 // Get all data except the command number
1052 std::string datastring((char*)&data[2], datasize-2);
1053 // Throw them in an istringstream
1054 std::istringstream is(datastring, std::ios_base::binary);
1058 // Read removed objects
1060 u16 removed_count = readU16((u8*)buf);
1061 for(u16 i=0; i<removed_count; i++)
1064 u16 id = readU16((u8*)buf);
1067 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1068 m_env.removeActiveObject(id);
1072 // Read added objects
1074 u16 added_count = readU16((u8*)buf);
1075 for(u16 i=0; i<added_count; i++)
1078 u16 id = readU16((u8*)buf);
1080 u8 type = readU8((u8*)buf);
1081 std::string data = deSerializeLongString(is);
1084 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1085 m_env.addActiveObject(id, type, data);
1090 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1092 //if(g_settings->getBool("enable_experimental"))
1104 // Get all data except the command number
1105 std::string datastring((char*)&data[2], datasize-2);
1106 // Throw them in an istringstream
1107 std::istringstream is(datastring, std::ios_base::binary);
1109 while(is.eof() == false)
1113 u16 id = readU16((u8*)buf);
1117 u16 message_size = readU16((u8*)buf);
1118 std::string message;
1119 message.reserve(message_size);
1120 for(u16 i=0; i<message_size; i++)
1123 message.append(buf, 1);
1125 // Pass on to the environment
1127 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1128 m_env.processActiveObjectMessage(id, message);
1133 else if(command == TOCLIENT_HP)
1135 std::string datastring((char*)&data[2], datasize-2);
1136 std::istringstream is(datastring, std::ios_base::binary);
1137 Player *player = m_env.getLocalPlayer();
1138 assert(player != NULL);
1142 else if(command == TOCLIENT_MOVE_PLAYER)
1144 std::string datastring((char*)&data[2], datasize-2);
1145 std::istringstream is(datastring, std::ios_base::binary);
1146 Player *player = m_env.getLocalPlayer();
1147 assert(player != NULL);
1148 v3f pos = readV3F1000(is);
1149 f32 pitch = readF1000(is);
1150 f32 yaw = readF1000(is);
1151 player->setPosition(pos);
1152 /*player->setPitch(pitch);
1153 player->setYaw(yaw);*/
1155 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1156 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1162 Add to ClientEvent queue.
1163 This has to be sent to the main program because otherwise
1164 it would just force the pitch and yaw values to whatever
1165 the camera points to.
1168 event.type = CE_PLAYER_FORCE_MOVE;
1169 event.player_force_move.pitch = pitch;
1170 event.player_force_move.yaw = yaw;
1171 m_client_event_queue.push_back(event);
1173 // Ignore damage for a few seconds, so that the player doesn't
1174 // get damage from falling on ground
1175 m_ignore_damage_timer = 3.0;
1177 else if(command == TOCLIENT_PLAYERITEM)
1179 std::string datastring((char*)&data[2], datasize-2);
1180 std::istringstream is(datastring, std::ios_base::binary);
1182 u16 count = readU16(is);
1184 for (u16 i = 0; i < count; ++i) {
1185 u16 peer_id = readU16(is);
1186 Player *player = m_env.getPlayer(peer_id);
1190 infostream<<"Client: ignoring player item "
1191 << deSerializeString(is)
1192 << " for non-existing peer id " << peer_id
1195 } else if (player->isLocal()) {
1196 infostream<<"Client: ignoring player item "
1197 << deSerializeString(is)
1198 << " for local player" << std::endl;
1201 InventoryList *inv = player->inventory.getList("main");
1202 std::string itemstring(deSerializeString(is));
1203 if (itemstring.empty()) {
1206 <<"Client: empty player item for peer "
1207 << peer_id << std::endl;
1209 std::istringstream iss(itemstring);
1210 delete inv->changeItem(0,
1211 InventoryItem::deSerialize(iss, this));
1212 infostream<<"Client: player item for peer " << peer_id << ": ";
1213 player->getWieldItem()->serialize(infostream);
1214 infostream<<std::endl;
1219 else if(command == TOCLIENT_DEATHSCREEN)
1221 std::string datastring((char*)&data[2], datasize-2);
1222 std::istringstream is(datastring, std::ios_base::binary);
1224 bool set_camera_point_target = readU8(is);
1225 v3f camera_point_target = readV3F1000(is);
1228 event.type = CE_DEATHSCREEN;
1229 event.deathscreen.set_camera_point_target = set_camera_point_target;
1230 event.deathscreen.camera_point_target_x = camera_point_target.X;
1231 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1232 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1233 m_client_event_queue.push_back(event);
1235 else if(command == TOCLIENT_TEXTURES)
1237 io::IFileSystem *irrfs = m_device->getFileSystem();
1238 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1240 std::string datastring((char*)&data[2], datasize-2);
1241 std::istringstream is(datastring, std::ios_base::binary);
1243 // Stop threads while updating content definitions
1244 m_mesh_update_thread.setRun(false);
1245 // Process the remaining TextureSource queue to let MeshUpdateThread
1246 // get it's remaining textures and thus let it stop
1247 while(m_mesh_update_thread.IsRunning()){
1248 m_tsrc->processQueue();
1253 u16 total number of texture bunches
1254 u16 index of this bunch
1255 u32 number of textures in this bunch
1263 int num_bunches = readU16(is);
1264 int bunch_i = readU16(is);
1265 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1266 if(bunch_i == num_bunches - 1)
1267 m_textures_received = true;
1268 int num_textures = readU32(is);
1269 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1270 <<num_bunches<<" textures="<<num_textures
1271 <<" size="<<datasize<<std::endl;
1272 for(int i=0; i<num_textures; i++){
1273 std::string name = deSerializeString(is);
1274 std::string data = deSerializeLongString(is);
1275 // Silly irrlicht's const-incorrectness
1276 Buffer<char> data_rw(data.c_str(), data.size());
1277 // Create an irrlicht memory file
1278 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1279 *data_rw, data.size(), "_tempreadfile");
1282 video::IImage *img = vdrv->createImageFromFile(rfile);
1284 errorstream<<"Client: Cannot create image from data of "
1285 <<"received texture \""<<name<<"\""<<std::endl;
1289 m_tsrc->insertSourceImage(name, img);
1294 if(m_nodedef_received && m_textures_received){
1295 // Rebuild inherited images and recreate textures
1296 m_tsrc->rebuildImagesAndTextures();
1298 // Update texture atlas
1299 if(g_settings->getBool("enable_texture_atlas"))
1300 m_tsrc->buildMainAtlas(this);
1302 // Update node textures
1303 m_nodedef->updateTextures(m_tsrc);
1307 m_mesh_update_thread.setRun(true);
1308 m_mesh_update_thread.Start();
1311 event.type = CE_TEXTURES_UPDATED;
1312 m_client_event_queue.push_back(event);
1314 else if(command == TOCLIENT_TOOLDEF)
1316 infostream<<"Client: Received tool definitions: packet size: "
1317 <<datasize<<std::endl;
1319 std::string datastring((char*)&data[2], datasize-2);
1320 std::istringstream is(datastring, std::ios_base::binary);
1322 m_tooldef_received = true;
1324 // Stop threads while updating content definitions
1325 m_mesh_update_thread.setRun(false);
1326 // Process the remaining TextureSource queue to let MeshUpdateThread
1327 // get it's remaining textures and thus let it stop
1328 while(m_mesh_update_thread.IsRunning()){
1329 m_tsrc->processQueue();
1332 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1333 m_tooldef->deSerialize(tmp_is);
1336 m_mesh_update_thread.setRun(true);
1337 m_mesh_update_thread.Start();
1339 else if(command == TOCLIENT_NODEDEF)
1341 infostream<<"Client: Received node definitions: packet size: "
1342 <<datasize<<std::endl;
1344 std::string datastring((char*)&data[2], datasize-2);
1345 std::istringstream is(datastring, std::ios_base::binary);
1347 m_nodedef_received = true;
1349 // Stop threads while updating content definitions
1350 m_mesh_update_thread.stop();
1352 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1353 m_nodedef->deSerialize(tmp_is, this);
1355 if(m_textures_received){
1356 // Update texture atlas
1357 if(g_settings->getBool("enable_texture_atlas"))
1358 m_tsrc->buildMainAtlas(this);
1360 // Update node textures
1361 m_nodedef->updateTextures(m_tsrc);
1365 m_mesh_update_thread.setRun(true);
1366 m_mesh_update_thread.Start();
1368 else if(command == TOCLIENT_CRAFTITEMDEF)
1370 infostream<<"Client: Received CraftItem definitions: packet size: "
1371 <<datasize<<std::endl;
1373 std::string datastring((char*)&data[2], datasize-2);
1374 std::istringstream is(datastring, std::ios_base::binary);
1376 m_craftitemdef_received = true;
1378 // Stop threads while updating content definitions
1379 m_mesh_update_thread.setRun(false);
1380 // Process the remaining TextureSource queue to let MeshUpdateThread
1381 // get it's remaining textures and thus let it stop
1382 while(m_mesh_update_thread.IsRunning()){
1383 m_tsrc->processQueue();
1386 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1387 m_craftitemdef->deSerialize(tmp_is);
1390 m_mesh_update_thread.setRun(true);
1391 m_mesh_update_thread.Start();
1395 infostream<<"Client: Ignoring unknown command "
1396 <<command<<std::endl;
1400 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1402 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1403 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1406 void Client::interact(u8 action, const PointedThing& pointed)
1408 if(connectedAndInitialized() == false){
1409 infostream<<"Client::interact() "
1410 "cancelled (not connected)"
1415 std::ostringstream os(std::ios_base::binary);
1421 [5] u32 length of the next item
1422 [9] serialized PointedThing
1424 0: start digging (from undersurface) or use
1425 1: stop digging (all parameters ignored)
1426 2: digging completed
1427 3: place block or item (to abovesurface)
1430 writeU16(os, TOSERVER_INTERACT);
1431 writeU8(os, action);
1432 writeU16(os, getPlayerItem());
1433 std::ostringstream tmp_os(std::ios::binary);
1434 pointed.serialize(tmp_os);
1435 os<<serializeLongString(tmp_os.str());
1437 std::string s = os.str();
1438 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1441 Send(0, data, true);
1444 void Client::sendSignNodeText(v3s16 p, std::string text)
1452 std::ostringstream os(std::ios_base::binary);
1456 writeU16(buf, TOSERVER_SIGNNODETEXT);
1457 os.write((char*)buf, 2);
1461 os.write((char*)buf, 6);
1463 u16 textlen = text.size();
1464 // Write text length
1465 writeS16(buf, textlen);
1466 os.write((char*)buf, 2);
1469 os.write((char*)text.c_str(), textlen);
1472 std::string s = os.str();
1473 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1475 Send(0, data, true);
1478 void Client::sendInventoryAction(InventoryAction *a)
1480 std::ostringstream os(std::ios_base::binary);
1484 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1485 os.write((char*)buf, 2);
1490 std::string s = os.str();
1491 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1493 Send(0, data, true);
1496 void Client::sendChatMessage(const std::wstring &message)
1498 std::ostringstream os(std::ios_base::binary);
1502 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1503 os.write((char*)buf, 2);
1506 writeU16(buf, message.size());
1507 os.write((char*)buf, 2);
1510 for(u32 i=0; i<message.size(); i++)
1514 os.write((char*)buf, 2);
1518 std::string s = os.str();
1519 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1521 Send(0, data, true);
1524 void Client::sendChangePassword(const std::wstring oldpassword,
1525 const std::wstring newpassword)
1527 Player *player = m_env.getLocalPlayer();
1531 std::string playername = player->getName();
1532 std::string oldpwd = translatePassword(playername, oldpassword);
1533 std::string newpwd = translatePassword(playername, newpassword);
1535 std::ostringstream os(std::ios_base::binary);
1536 u8 buf[2+PASSWORD_SIZE*2];
1538 [0] u16 TOSERVER_PASSWORD
1539 [2] u8[28] old password
1540 [30] u8[28] new password
1543 writeU16(buf, TOSERVER_PASSWORD);
1544 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1546 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1547 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1549 buf[2+PASSWORD_SIZE-1] = 0;
1550 buf[30+PASSWORD_SIZE-1] = 0;
1551 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1554 std::string s = os.str();
1555 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1557 Send(0, data, true);
1561 void Client::sendDamage(u8 damage)
1563 DSTACK(__FUNCTION_NAME);
1564 std::ostringstream os(std::ios_base::binary);
1566 writeU16(os, TOSERVER_DAMAGE);
1567 writeU8(os, damage);
1570 std::string s = os.str();
1571 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1573 Send(0, data, true);
1576 void Client::sendRespawn()
1578 DSTACK(__FUNCTION_NAME);
1579 std::ostringstream os(std::ios_base::binary);
1581 writeU16(os, TOSERVER_RESPAWN);
1584 std::string s = os.str();
1585 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1587 Send(0, data, true);
1590 void Client::sendPlayerPos()
1592 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1594 Player *myplayer = m_env.getLocalPlayer();
1595 if(myplayer == NULL)
1600 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1601 our_peer_id = m_con.GetPeerID();
1604 // Set peer id if not set already
1605 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1606 myplayer->peer_id = our_peer_id;
1607 // Check that an existing peer_id is the same as the connection's
1608 assert(myplayer->peer_id == our_peer_id);
1610 v3f pf = myplayer->getPosition();
1611 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1612 v3f sf = myplayer->getSpeed();
1613 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1614 s32 pitch = myplayer->getPitch() * 100;
1615 s32 yaw = myplayer->getYaw() * 100;
1620 [2] v3s32 position*100
1621 [2+12] v3s32 speed*100
1622 [2+12+12] s32 pitch*100
1623 [2+12+12+4] s32 yaw*100
1626 SharedBuffer<u8> data(2+12+12+4+4);
1627 writeU16(&data[0], TOSERVER_PLAYERPOS);
1628 writeV3S32(&data[2], position);
1629 writeV3S32(&data[2+12], speed);
1630 writeS32(&data[2+12+12], pitch);
1631 writeS32(&data[2+12+12+4], yaw);
1633 // Send as unreliable
1634 Send(0, data, false);
1637 void Client::sendPlayerItem(u16 item)
1639 Player *myplayer = m_env.getLocalPlayer();
1640 if(myplayer == NULL)
1643 u16 our_peer_id = m_con.GetPeerID();
1645 // Set peer id if not set already
1646 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1647 myplayer->peer_id = our_peer_id;
1648 // Check that an existing peer_id is the same as the connection's
1649 assert(myplayer->peer_id == our_peer_id);
1651 SharedBuffer<u8> data(2+2);
1652 writeU16(&data[0], TOSERVER_PLAYERITEM);
1653 writeU16(&data[2], item);
1656 Send(0, data, true);
1659 void Client::removeNode(v3s16 p)
1661 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1663 core::map<v3s16, MapBlock*> modified_blocks;
1667 //TimeTaker t("removeNodeAndUpdate", m_device);
1668 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1670 catch(InvalidPositionException &e)
1674 for(core::map<v3s16, MapBlock * >::Iterator
1675 i = modified_blocks.getIterator();
1676 i.atEnd() == false; i++)
1678 v3s16 p = i.getNode()->getKey();
1679 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1680 addUpdateMeshTaskWithEdge(p);
1684 void Client::addNode(v3s16 p, MapNode n)
1686 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1688 TimeTaker timer1("Client::addNode()");
1690 core::map<v3s16, MapBlock*> modified_blocks;
1694 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1695 std::string st = std::string("");
1696 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1698 catch(InvalidPositionException &e)
1701 //TimeTaker timer2("Client::addNode(): updateMeshes");
1703 for(core::map<v3s16, MapBlock * >::Iterator
1704 i = modified_blocks.getIterator();
1705 i.atEnd() == false; i++)
1707 v3s16 p = i.getNode()->getKey();
1708 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1709 addUpdateMeshTaskWithEdge(p);
1713 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1715 m_env.getClientMap().updateCamera(pos, dir, fov);
1718 void Client::renderPostFx()
1720 m_env.getClientMap().renderPostFx();
1723 MapNode Client::getNode(v3s16 p)
1725 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1726 return m_env.getMap().getNode(p);
1729 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1731 return m_env.getMap().getNodeMetadata(p);
1734 LocalPlayer* Client::getLocalPlayer()
1736 return m_env.getLocalPlayer();
1739 void Client::setPlayerControl(PlayerControl &control)
1741 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1742 LocalPlayer *player = m_env.getLocalPlayer();
1743 assert(player != NULL);
1744 player->control = control;
1747 void Client::selectPlayerItem(u16 item)
1749 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1750 m_playeritem = item;
1751 m_inventory_updated = true;
1753 LocalPlayer *player = m_env.getLocalPlayer();
1754 assert(player != NULL);
1755 player->wieldItem(item);
1757 sendPlayerItem(item);
1760 // Returns true if the inventory of the local player has been
1761 // updated from the server. If it is true, it is set to false.
1762 bool Client::getLocalInventoryUpdated()
1764 // m_inventory_updated is behind envlock
1765 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1766 bool updated = m_inventory_updated;
1767 m_inventory_updated = false;
1771 // Copies the inventory of the local player to parameter
1772 void Client::getLocalInventory(Inventory &dst)
1774 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1775 Player *player = m_env.getLocalPlayer();
1776 assert(player != NULL);
1777 dst = player->inventory;
1780 InventoryContext *Client::getInventoryContext()
1782 return &m_inventory_context;
1785 Inventory* Client::getInventory(const InventoryLocation &loc)
1788 case InventoryLocation::UNDEFINED:
1791 case InventoryLocation::PLAYER:
1793 Player *player = m_env.getPlayer(loc.name.c_str());
1796 return &player->inventory;
1799 case InventoryLocation::NODEMETA:
1801 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1804 return meta->getInventory();
1813 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1815 if(id == "current_player")
1817 assert(c->current_player);
1818 return &(c->current_player->inventory);
1822 std::string id0 = fn.next(":");
1824 if(id0 == "nodemeta")
1827 p.X = stoi(fn.next(","));
1828 p.Y = stoi(fn.next(","));
1829 p.Z = stoi(fn.next(","));
1830 NodeMetadata* meta = getNodeMetadata(p);
1832 return meta->getInventory();
1833 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1834 <<"no metadata found"<<std::endl;
1838 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1842 void Client::inventoryAction(InventoryAction *a)
1844 sendInventoryAction(a);
1847 ClientActiveObject * Client::getSelectedActiveObject(
1849 v3f from_pos_f_on_map,
1850 core::line3d<f32> shootline_on_map
1853 core::array<DistanceSortedActiveObject> objects;
1855 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1857 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1860 // After this, the closest object is the first in the array.
1863 for(u32 i=0; i<objects.size(); i++)
1865 ClientActiveObject *obj = objects[i].obj;
1867 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1868 if(selection_box == NULL)
1871 v3f pos = obj->getPosition();
1873 core::aabbox3d<f32> offsetted_box(
1874 selection_box->MinEdge + pos,
1875 selection_box->MaxEdge + pos
1878 if(offsetted_box.intersectsWithLine(shootline_on_map))
1880 //infostream<<"Returning selected object"<<std::endl;
1885 //infostream<<"No object selected; returning NULL."<<std::endl;
1889 void Client::printDebugInfo(std::ostream &os)
1891 //JMutexAutoLock lock1(m_fetchblock_mutex);
1892 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1894 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1895 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1896 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1900 u32 Client::getDayNightRatio()
1902 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1903 return m_env.getDayNightRatio();
1908 Player *player = m_env.getLocalPlayer();
1909 assert(player != NULL);
1913 void Client::setTempMod(v3s16 p, NodeMod mod)
1915 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1916 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1918 core::map<v3s16, MapBlock*> affected_blocks;
1919 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
1922 for(core::map<v3s16, MapBlock*>::Iterator
1923 i = affected_blocks.getIterator();
1924 i.atEnd() == false; i++)
1926 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
1930 void Client::clearTempMod(v3s16 p)
1932 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1933 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1935 core::map<v3s16, MapBlock*> affected_blocks;
1936 ((ClientMap&)m_env.getMap()).clearTempMod(p,
1939 for(core::map<v3s16, MapBlock*>::Iterator
1940 i = affected_blocks.getIterator();
1941 i.atEnd() == false; i++)
1943 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
1947 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1949 /*infostream<<"Client::addUpdateMeshTask(): "
1950 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1953 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1958 Create a task to update the mesh of the block
1961 MeshMakeData *data = new MeshMakeData;
1964 //TimeTaker timer("data fill");
1966 // Debug: 1-6ms, avg=2ms
1967 data->fill(getDayNightRatio(), b);
1971 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
1973 // Add task to queue
1974 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
1976 /*infostream<<"Mesh update input queue size is "
1977 <<m_mesh_update_thread.m_queue_in.size()
1981 // Temporary test: make mesh directly in here
1983 //TimeTaker timer("make mesh");
1985 scene::SMesh *mesh_new = NULL;
1986 mesh_new = makeMapBlockMesh(data);
1987 b->replaceMesh(mesh_new);
1993 Mark mesh as non-expired at this point so that it can already
1994 be marked as expired again if the data changes
1996 b->setMeshExpired(false);
1999 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2003 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2004 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2009 v3s16 p = blockpos + v3s16(0,0,0);
2010 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2011 addUpdateMeshTask(p, ack_to_server);
2013 catch(InvalidPositionException &e){}
2016 v3s16 p = blockpos + v3s16(-1,0,0);
2017 addUpdateMeshTask(p);
2019 catch(InvalidPositionException &e){}
2021 v3s16 p = blockpos + v3s16(0,-1,0);
2022 addUpdateMeshTask(p);
2024 catch(InvalidPositionException &e){}
2026 v3s16 p = blockpos + v3s16(0,0,-1);
2027 addUpdateMeshTask(p);
2029 catch(InvalidPositionException &e){}
2032 ClientEvent Client::getClientEvent()
2034 if(m_client_event_queue.size() == 0)
2037 event.type = CE_NONE;
2040 return m_client_event_queue.pop_front();
2043 float Client::getRTT(void)
2046 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2047 } catch(con::PeerNotFoundException &e){
2052 // IGameDef interface
2054 IToolDefManager* Client::getToolDefManager()
2058 INodeDefManager* Client::getNodeDefManager()
2062 ICraftDefManager* Client::getCraftDefManager()
2065 //return m_craftdef;
2067 ICraftItemDefManager* Client::getCraftItemDefManager()
2069 return m_craftitemdef;
2071 ITextureSource* Client::getTextureSource()
2075 u16 Client::allocateUnknownNodeId(const std::string &name)
2077 errorstream<<"Client::allocateUnknownNodeId(): "
2078 <<"Client cannot allocate node IDs"<<std::endl;
2080 return CONTENT_IGNORE;