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"
39 QueuedMeshUpdate::QueuedMeshUpdate():
42 ack_block_to_server(false)
46 QueuedMeshUpdate::~QueuedMeshUpdate()
56 MeshUpdateQueue::MeshUpdateQueue()
61 MeshUpdateQueue::~MeshUpdateQueue()
63 JMutexAutoLock lock(m_mutex);
65 core::list<QueuedMeshUpdate*>::Iterator i;
66 for(i=m_queue.begin(); i!=m_queue.end(); i++)
68 QueuedMeshUpdate *q = *i;
74 peer_id=0 adds with nobody to send to
76 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
78 DSTACK(__FUNCTION_NAME);
82 JMutexAutoLock lock(m_mutex);
85 Find if block is already in queue.
86 If it is, update the data and quit.
88 core::list<QueuedMeshUpdate*>::Iterator i;
89 for(i=m_queue.begin(); i!=m_queue.end(); i++)
91 QueuedMeshUpdate *q = *i;
97 if(ack_block_to_server)
98 q->ack_block_to_server = true;
106 QueuedMeshUpdate *q = new QueuedMeshUpdate;
109 q->ack_block_to_server = ack_block_to_server;
110 m_queue.push_back(q);
113 // Returned pointer must be deleted
114 // Returns NULL if queue is empty
115 QueuedMeshUpdate * MeshUpdateQueue::pop()
117 JMutexAutoLock lock(m_mutex);
119 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
120 if(i == m_queue.end())
122 QueuedMeshUpdate *q = *i;
131 void * MeshUpdateThread::Thread()
135 log_register_thread("MeshUpdateThread");
137 DSTACK(__FUNCTION_NAME);
139 BEGIN_DEBUG_EXCEPTION_HANDLER
143 /*// Wait for output queue to flush.
144 // Allow 2 in queue, this makes less frametime jitter.
145 // Umm actually, there is no much difference
146 if(m_queue_out.size() >= 2)
152 QueuedMeshUpdate *q = m_queue_in.pop();
159 ScopeProfiler sp(g_profiler, "Client: Mesh making");
161 scene::SMesh *mesh_new = NULL;
162 mesh_new = makeMapBlockMesh(q->data);
167 r.ack_block_to_server = q->ack_block_to_server;
169 /*infostream<<"MeshUpdateThread: Processed "
170 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
173 m_queue_out.push_back(r);
178 END_DEBUG_EXCEPTION_HANDLER(errorstream)
184 IrrlichtDevice *device,
185 const char *playername,
186 std::string password,
187 MapDrawControl &control):
188 m_mesh_update_thread(),
190 new ClientMap(this, control,
191 device->getSceneManager()->getRootSceneNode(),
192 device->getSceneManager(), 666),
193 device->getSceneManager()
195 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
197 m_server_ser_ver(SER_FMT_VER_INVALID),
198 m_inventory_updated(false),
201 m_password(password),
202 m_access_denied(false)
204 m_packetcounter_timer = 0.0;
205 //m_delete_unused_sectors_timer = 0.0;
206 m_connection_reinit_timer = 0.0;
207 m_avg_rtt_timer = 0.0;
208 m_playerpos_send_timer = 0.0;
209 m_ignore_damage_timer = 0.0;
211 //m_env_mutex.Init();
212 //m_con_mutex.Init();
214 m_mesh_update_thread.Start();
220 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
222 Player *player = new LocalPlayer();
224 player->updateName(playername);
226 m_env.addPlayer(player);
228 // Initialize player in the inventory context
229 m_inventory_context.current_player = player;
236 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
240 m_mesh_update_thread.setRun(false);
241 while(m_mesh_update_thread.IsRunning())
245 void Client::connect(Address address)
247 DSTACK(__FUNCTION_NAME);
248 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
249 m_con.SetTimeoutMs(0);
250 m_con.Connect(address);
253 bool Client::connectedAndInitialized()
255 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
257 if(m_con.Connected() == false)
260 if(m_server_ser_ver == SER_FMT_VER_INVALID)
266 void Client::step(float dtime)
268 DSTACK(__FUNCTION_NAME);
274 if(m_ignore_damage_timer > dtime)
275 m_ignore_damage_timer -= dtime;
277 m_ignore_damage_timer = 0.0;
279 //infostream<<"Client steps "<<dtime<<std::endl;
282 //TimeTaker timer("ReceiveAll()", m_device);
288 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
290 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
291 m_con.RunTimeouts(dtime);
298 float &counter = m_packetcounter_timer;
304 infostream<<"Client packetcounter (20s):"<<std::endl;
305 m_packetcounter.print(infostream);
306 m_packetcounter.clear();
310 // Get connection status
311 bool connected = connectedAndInitialized();
316 Delete unused sectors
318 NOTE: This jams the game for a while because deleting sectors
322 float &counter = m_delete_unused_sectors_timer;
330 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
332 core::list<v3s16> deleted_blocks;
334 float delete_unused_sectors_timeout =
335 g_settings->getFloat("client_delete_unused_sectors_timeout");
337 // Delete sector blocks
338 /*u32 num = m_env.getMap().unloadUnusedData
339 (delete_unused_sectors_timeout,
340 true, &deleted_blocks);*/
342 // Delete whole sectors
343 m_env.getMap().unloadUnusedData
344 (delete_unused_sectors_timeout,
347 if(deleted_blocks.size() > 0)
349 /*infostream<<"Client: Deleted blocks of "<<num
350 <<" unused sectors"<<std::endl;*/
351 /*infostream<<"Client: Deleted "<<num
352 <<" unused sectors"<<std::endl;*/
358 // Env is locked so con can be locked.
359 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
361 core::list<v3s16>::Iterator i = deleted_blocks.begin();
362 core::list<v3s16> sendlist;
365 if(sendlist.size() == 255 || i == deleted_blocks.end())
367 if(sendlist.size() == 0)
376 u32 replysize = 2+1+6*sendlist.size();
377 SharedBuffer<u8> reply(replysize);
378 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
379 reply[2] = sendlist.size();
381 for(core::list<v3s16>::Iterator
382 j = sendlist.begin();
383 j != sendlist.end(); j++)
385 writeV3S16(&reply[2+1+6*k], *j);
388 m_con.Send(PEER_ID_SERVER, 1, reply, true);
390 if(i == deleted_blocks.end())
396 sendlist.push_back(*i);
404 if(connected == false)
406 float &counter = m_connection_reinit_timer;
412 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
414 Player *myplayer = m_env.getLocalPlayer();
415 assert(myplayer != NULL);
417 // Send TOSERVER_INIT
418 // [0] u16 TOSERVER_INIT
419 // [2] u8 SER_FMT_VER_HIGHEST
420 // [3] u8[20] player_name
421 // [23] u8[28] password (new in some version)
422 // [51] u16 client network protocol version (new in some version)
423 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
424 writeU16(&data[0], TOSERVER_INIT);
425 writeU8(&data[2], SER_FMT_VER_HIGHEST);
427 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
428 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
430 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
433 memset((char*)&data[23], 0, PASSWORD_SIZE);
434 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
436 // This should be incremented in each version
437 writeU16(&data[51], 3);
439 // Send as unreliable
440 Send(0, data, false);
443 // Not connected, return
448 Do stuff if connected
452 Run Map's timers and unload unused data
454 const float map_timer_and_unload_dtime = 5.25;
455 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
457 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
458 core::list<v3s16> deleted_blocks;
459 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
460 g_settings->getFloat("client_unload_unused_data_timeout"),
463 /*if(deleted_blocks.size() > 0)
464 infostream<<"Client: Unloaded "<<deleted_blocks.size()
465 <<" unused blocks"<<std::endl;*/
469 NOTE: This loop is intentionally iterated the way it is.
472 core::list<v3s16>::Iterator i = deleted_blocks.begin();
473 core::list<v3s16> sendlist;
476 if(sendlist.size() == 255 || i == deleted_blocks.end())
478 if(sendlist.size() == 0)
487 u32 replysize = 2+1+6*sendlist.size();
488 SharedBuffer<u8> reply(replysize);
489 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
490 reply[2] = sendlist.size();
492 for(core::list<v3s16>::Iterator
493 j = sendlist.begin();
494 j != sendlist.end(); j++)
496 writeV3S16(&reply[2+1+6*k], *j);
499 m_con.Send(PEER_ID_SERVER, 1, reply, true);
501 if(i == deleted_blocks.end())
507 sendlist.push_back(*i);
517 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
519 // Control local player (0ms)
520 LocalPlayer *player = m_env.getLocalPlayer();
521 assert(player != NULL);
522 player->applyControl(dtime);
524 //TimeTaker envtimer("env step", m_device);
533 ClientEnvEvent event = m_env.getClientEvent();
534 if(event.type == CEE_NONE)
538 else if(event.type == CEE_PLAYER_DAMAGE)
540 if(m_ignore_damage_timer <= 0)
542 u8 damage = event.player_damage.amount;
545 // Add to ClientEvent queue
547 event.type = CE_PLAYER_DAMAGE;
548 event.player_damage.amount = damage;
549 m_client_event_queue.push_back(event);
559 float &counter = m_avg_rtt_timer;
564 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
565 // connectedAndInitialized() is true, peer exists.
566 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
567 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
572 Send player position to server
575 float &counter = m_playerpos_send_timer;
585 Replace updated meshes
588 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
590 //TimeTaker timer("** Processing mesh update result queue");
593 /*infostream<<"Mesh update result queue size is "
594 <<m_mesh_update_thread.m_queue_out.size()
597 while(m_mesh_update_thread.m_queue_out.size() > 0)
599 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
600 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
603 block->replaceMesh(r.mesh);
605 if(r.ack_block_to_server)
607 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
608 <<","<<r.p.Z<<")"<<std::endl;*/
619 u32 replysize = 2+1+6;
620 SharedBuffer<u8> reply(replysize);
621 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
623 writeV3S16(&reply[3], r.p);
625 m_con.Send(PEER_ID_SERVER, 1, reply, true);
631 // Virtual methods from con::PeerHandler
632 void Client::peerAdded(con::Peer *peer)
634 infostream<<"Client::peerAdded(): peer->id="
635 <<peer->id<<std::endl;
637 void Client::deletingPeer(con::Peer *peer, bool timeout)
639 infostream<<"Client::deletingPeer(): "
640 "Server Peer is getting deleted "
641 <<"(timeout="<<timeout<<")"<<std::endl;
644 void Client::ReceiveAll()
646 DSTACK(__FUNCTION_NAME);
652 catch(con::NoIncomingDataException &e)
656 catch(con::InvalidIncomingDataException &e)
658 infostream<<"Client::ReceiveAll(): "
659 "InvalidIncomingDataException: what()="
660 <<e.what()<<std::endl;
665 void Client::Receive()
667 DSTACK(__FUNCTION_NAME);
668 u32 data_maxsize = 200000;
669 Buffer<u8> data(data_maxsize);
673 //TimeTaker t1("con mutex and receive", m_device);
674 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
675 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
677 //TimeTaker t1("ProcessData", m_device);
678 ProcessData(*data, datasize, sender_peer_id);
682 sender_peer_id given to this shall be quaranteed to be a valid peer
684 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
686 DSTACK(__FUNCTION_NAME);
688 // Ignore packets that don't even fit a command
691 m_packetcounter.add(60000);
695 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
697 //infostream<<"Client: received command="<<command<<std::endl;
698 m_packetcounter.add((u16)command);
701 If this check is removed, be sure to change the queue
702 system to know the ids
704 if(sender_peer_id != PEER_ID_SERVER)
706 infostream<<"Client::ProcessData(): Discarding data not "
707 "coming from server: peer_id="<<sender_peer_id
712 u8 ser_version = m_server_ser_ver;
714 //infostream<<"Client received command="<<(int)command<<std::endl;
716 if(command == TOCLIENT_INIT)
721 u8 deployed = data[2];
723 infostream<<"Client: TOCLIENT_INIT received with "
724 "deployed="<<((int)deployed&0xff)<<std::endl;
726 if(deployed < SER_FMT_VER_LOWEST
727 || deployed > SER_FMT_VER_HIGHEST)
729 infostream<<"Client: TOCLIENT_INIT: Server sent "
730 <<"unsupported ser_fmt_ver"<<std::endl;
734 m_server_ser_ver = deployed;
736 // Get player position
737 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
738 if(datasize >= 2+1+6)
739 playerpos_s16 = readV3S16(&data[2+1]);
740 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
743 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
745 // Set player position
746 Player *player = m_env.getLocalPlayer();
747 assert(player != NULL);
748 player->setPosition(playerpos_f);
751 if(datasize >= 2+1+6+8)
754 m_map_seed = readU64(&data[2+1+6]);
755 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
760 SharedBuffer<u8> reply(replysize);
761 writeU16(&reply[0], TOSERVER_INIT2);
763 m_con.Send(PEER_ID_SERVER, 1, reply, true);
768 if(command == TOCLIENT_ACCESS_DENIED)
770 // The server didn't like our password. Note, this needs
771 // to be processed even if the serialisation format has
772 // not been agreed yet, the same as TOCLIENT_INIT.
773 m_access_denied = true;
774 m_access_denied_reason = L"Unknown";
777 std::string datastring((char*)&data[2], datasize-2);
778 std::istringstream is(datastring, std::ios_base::binary);
779 m_access_denied_reason = deSerializeWideString(is);
784 if(ser_version == SER_FMT_VER_INVALID)
786 infostream<<"Client: Server serialization"
787 " format invalid or not initialized."
788 " Skipping incoming command="<<command<<std::endl;
792 // Just here to avoid putting the two if's together when
793 // making some copypasta
796 if(command == TOCLIENT_REMOVENODE)
801 p.X = readS16(&data[2]);
802 p.Y = readS16(&data[4]);
803 p.Z = readS16(&data[6]);
805 //TimeTaker t1("TOCLIENT_REMOVENODE");
807 // This will clear the cracking animation after digging
808 ((ClientMap&)m_env.getMap()).clearTempMod(p);
812 else if(command == TOCLIENT_ADDNODE)
814 if(datasize < 8 + MapNode::serializedLength(ser_version))
818 p.X = readS16(&data[2]);
819 p.Y = readS16(&data[4]);
820 p.Z = readS16(&data[6]);
822 //TimeTaker t1("TOCLIENT_ADDNODE");
825 n.deSerialize(&data[8], ser_version);
829 else if(command == TOCLIENT_BLOCKDATA)
831 // Ignore too small packet
836 p.X = readS16(&data[2]);
837 p.Y = readS16(&data[4]);
838 p.Z = readS16(&data[6]);
840 /*infostream<<"Client: Thread: BLOCKDATA for ("
841 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
842 /*infostream<<"Client: Thread: BLOCKDATA for ("
843 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
845 std::string datastring((char*)&data[8], datasize-8);
846 std::istringstream istr(datastring, std::ios_base::binary);
852 sector = m_env.getMap().emergeSector(p2d);
854 assert(sector->getPos() == p2d);
856 //TimeTaker timer("MapBlock deSerialize");
859 block = sector->getBlockNoCreateNoEx(p.Y);
863 Update an existing block
865 //infostream<<"Updating"<<std::endl;
866 block->deSerialize(istr, ser_version);
873 //infostream<<"Creating new"<<std::endl;
874 block = new MapBlock(&m_env.getMap(), p);
875 block->deSerialize(istr, ser_version);
876 sector->insertBlock(block);
880 mod.type = NODEMOD_CHANGECONTENT;
881 mod.param = CONTENT_MESE;
882 block->setTempMod(v3s16(8,10,8), mod);
883 block->setTempMod(v3s16(8,9,8), mod);
884 block->setTempMod(v3s16(8,8,8), mod);
885 block->setTempMod(v3s16(8,7,8), mod);
886 block->setTempMod(v3s16(8,6,8), mod);*/
900 u32 replysize = 2+1+6;
901 SharedBuffer<u8> reply(replysize);
902 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
904 writeV3S16(&reply[3], p);
906 m_con.Send(PEER_ID_SERVER, 1, reply, true);
910 Update Mesh of this block and blocks at x-, y- and z-.
911 Environment should not be locked as it interlocks with the
912 main thread, from which is will want to retrieve textures.
915 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
917 Add it to mesh update queue and set it to be acknowledged after update.
919 //infostream<<"Adding mesh update task for received block"<<std::endl;
920 addUpdateMeshTaskWithEdge(p, true);
922 else if(command == TOCLIENT_PLAYERPOS)
924 infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
928 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
929 our_peer_id = m_con.GetPeerID();
931 // Cancel if we don't have a peer id
932 if(our_peer_id == PEER_ID_INEXISTENT){
933 infostream<<"TOCLIENT_PLAYERPOS cancelled: "
940 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
942 u32 player_size = 2+12+12+4+4;
944 u32 player_count = (datasize-2) / player_size;
946 for(u32 i=0; i<player_count; i++)
948 u16 peer_id = readU16(&data[start]);
950 Player *player = m_env.getPlayer(peer_id);
952 // Skip if player doesn't exist
955 start += player_size;
959 // Skip if player is local player
960 if(player->isLocal())
962 start += player_size;
966 v3s32 ps = readV3S32(&data[start+2]);
967 v3s32 ss = readV3S32(&data[start+2+12]);
968 s32 pitch_i = readS32(&data[start+2+12+12]);
969 s32 yaw_i = readS32(&data[start+2+12+12+4]);
970 /*infostream<<"Client: got "
971 <<"pitch_i="<<pitch_i
972 <<" yaw_i="<<yaw_i<<std::endl;*/
973 f32 pitch = (f32)pitch_i / 100.0;
974 f32 yaw = (f32)yaw_i / 100.0;
975 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
976 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
977 player->setPosition(position);
978 player->setSpeed(speed);
979 player->setPitch(pitch);
982 /*infostream<<"Client: player "<<peer_id
984 <<" yaw="<<yaw<<std::endl;*/
986 start += player_size;
990 else if(command == TOCLIENT_PLAYERINFO)
994 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
995 our_peer_id = m_con.GetPeerID();
997 // Cancel if we don't have a peer id
998 if(our_peer_id == PEER_ID_INEXISTENT){
999 infostream<<"TOCLIENT_PLAYERINFO cancelled: "
1000 "we have no peer id"
1005 //infostream<<"Client: Server reports players:"<<std::endl;
1008 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1010 u32 item_size = 2+PLAYERNAME_SIZE;
1011 u32 player_count = (datasize-2) / item_size;
1014 core::list<u16> players_alive;
1015 for(u32 i=0; i<player_count; i++)
1017 // Make sure the name ends in '\0'
1018 data[start+2+20-1] = 0;
1020 u16 peer_id = readU16(&data[start]);
1022 players_alive.push_back(peer_id);
1024 /*infostream<<"peer_id="<<peer_id
1025 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1027 // Don't update the info of the local player
1028 if(peer_id == our_peer_id)
1034 Player *player = m_env.getPlayer(peer_id);
1036 // Create a player if it doesn't exist
1039 player = new RemotePlayer(
1040 m_device->getSceneManager()->getRootSceneNode(),
1043 player->peer_id = peer_id;
1044 m_env.addPlayer(player);
1045 infostream<<"Client: Adding new player "
1046 <<peer_id<<std::endl;
1049 player->updateName((char*)&data[start+2]);
1055 Remove those players from the environment that
1056 weren't listed by the server.
1058 //infostream<<"Removing dead players"<<std::endl;
1059 core::list<Player*> players = m_env.getPlayers();
1060 core::list<Player*>::Iterator ip;
1061 for(ip=players.begin(); ip!=players.end(); ip++)
1063 // Ingore local player
1064 if((*ip)->isLocal())
1067 // Warn about a special case
1068 if((*ip)->peer_id == 0)
1070 infostream<<"Client: Removing "
1071 "dead player with id=0"<<std::endl;
1074 bool is_alive = false;
1075 core::list<u16>::Iterator i;
1076 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1078 if((*ip)->peer_id == *i)
1084 /*infostream<<"peer_id="<<((*ip)->peer_id)
1085 <<" is_alive="<<is_alive<<std::endl;*/
1088 infostream<<"Removing dead player "<<(*ip)->peer_id
1090 m_env.removePlayer((*ip)->peer_id);
1094 else if(command == TOCLIENT_SECTORMETA)
1096 infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1101 [3...] v2s16 pos + sector metadata
1106 //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1109 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1111 std::string datastring((char*)&data[2], datasize-2);
1112 std::istringstream is(datastring, std::ios_base::binary);
1116 is.read((char*)buf, 1);
1117 u16 sector_count = readU8(buf);
1119 //infostream<<"sector_count="<<sector_count<<std::endl;
1121 for(u16 i=0; i<sector_count; i++)
1124 is.read((char*)buf, 4);
1125 v2s16 pos = readV2S16(buf);
1126 /*infostream<<"Client: deserializing sector at "
1127 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1129 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1130 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1135 else if(command == TOCLIENT_INVENTORY)
1140 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1143 //TimeTaker t2("mutex locking", m_device);
1144 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1147 //TimeTaker t3("istringstream init", m_device);
1148 std::string datastring((char*)&data[2], datasize-2);
1149 std::istringstream is(datastring, std::ios_base::binary);
1152 //m_env.printPlayers(infostream);
1154 //TimeTaker t4("player get", m_device);
1155 Player *player = m_env.getLocalPlayer();
1156 assert(player != NULL);
1159 //TimeTaker t1("inventory.deSerialize()", m_device);
1160 player->inventory.deSerialize(is);
1163 m_inventory_updated = true;
1165 //infostream<<"Client got player inventory:"<<std::endl;
1166 //player->inventory.print(infostream);
1170 else if(command == TOCLIENT_OBJECTDATA)
1172 // Strip command word and create a stringstream
1173 std::string datastring((char*)&data[2], datasize-2);
1174 std::istringstream is(datastring, std::ios_base::binary);
1182 is.read((char*)buf, 2);
1183 u16 playercount = readU16(buf);
1185 for(u16 i=0; i<playercount; i++)
1187 is.read((char*)buf, 2);
1188 u16 peer_id = readU16(buf);
1189 is.read((char*)buf, 12);
1190 v3s32 p_i = readV3S32(buf);
1191 is.read((char*)buf, 12);
1192 v3s32 s_i = readV3S32(buf);
1193 is.read((char*)buf, 4);
1194 s32 pitch_i = readS32(buf);
1195 is.read((char*)buf, 4);
1196 s32 yaw_i = readS32(buf);
1198 Player *player = m_env.getPlayer(peer_id);
1200 // Skip if player doesn't exist
1206 // Skip if player is local player
1207 if(player->isLocal())
1212 f32 pitch = (f32)pitch_i / 100.0;
1213 f32 yaw = (f32)yaw_i / 100.0;
1214 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1215 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1217 player->setPosition(position);
1218 player->setSpeed(speed);
1219 player->setPitch(pitch);
1220 player->setYaw(yaw);
1225 NOTE: Deprecated stuff
1228 // Read active block count
1229 u16 blockcount = readU16(is);
1230 if(blockcount != 0){
1231 infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
1232 "not supported"<<std::endl;
1236 else if(command == TOCLIENT_TIME_OF_DAY)
1241 u16 time_of_day = readU16(&data[2]);
1242 time_of_day = time_of_day % 24000;
1243 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1251 m_env.setTimeOfDay(time_of_day);
1253 u32 dr = m_env.getDayNightRatio();
1255 infostream<<"Client: time_of_day="<<time_of_day
1261 else if(command == TOCLIENT_CHAT_MESSAGE)
1269 std::string datastring((char*)&data[2], datasize-2);
1270 std::istringstream is(datastring, std::ios_base::binary);
1273 is.read((char*)buf, 2);
1274 u16 len = readU16(buf);
1276 std::wstring message;
1277 for(u16 i=0; i<len; i++)
1279 is.read((char*)buf, 2);
1280 message += (wchar_t)readU16(buf);
1283 /*infostream<<"Client received chat message: "
1284 <<wide_to_narrow(message)<<std::endl;*/
1286 m_chat_queue.push_back(message);
1288 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1290 //if(g_settings->getBool("enable_experimental"))
1294 u16 count of removed objects
1295 for all removed objects {
1298 u16 count of added objects
1299 for all added objects {
1302 u32 initialization data length
1303 string initialization data
1308 // Get all data except the command number
1309 std::string datastring((char*)&data[2], datasize-2);
1310 // Throw them in an istringstream
1311 std::istringstream is(datastring, std::ios_base::binary);
1315 // Read removed objects
1317 u16 removed_count = readU16((u8*)buf);
1318 for(u16 i=0; i<removed_count; i++)
1321 u16 id = readU16((u8*)buf);
1324 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1325 m_env.removeActiveObject(id);
1329 // Read added objects
1331 u16 added_count = readU16((u8*)buf);
1332 for(u16 i=0; i<added_count; i++)
1335 u16 id = readU16((u8*)buf);
1337 u8 type = readU8((u8*)buf);
1338 std::string data = deSerializeLongString(is);
1341 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1342 m_env.addActiveObject(id, type, data);
1347 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1349 //if(g_settings->getBool("enable_experimental"))
1361 // Get all data except the command number
1362 std::string datastring((char*)&data[2], datasize-2);
1363 // Throw them in an istringstream
1364 std::istringstream is(datastring, std::ios_base::binary);
1366 while(is.eof() == false)
1370 u16 id = readU16((u8*)buf);
1374 u16 message_size = readU16((u8*)buf);
1375 std::string message;
1376 message.reserve(message_size);
1377 for(u16 i=0; i<message_size; i++)
1380 message.append(buf, 1);
1382 // Pass on to the environment
1384 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1385 m_env.processActiveObjectMessage(id, message);
1390 else if(command == TOCLIENT_HP)
1392 std::string datastring((char*)&data[2], datasize-2);
1393 std::istringstream is(datastring, std::ios_base::binary);
1394 Player *player = m_env.getLocalPlayer();
1395 assert(player != NULL);
1399 else if(command == TOCLIENT_MOVE_PLAYER)
1401 std::string datastring((char*)&data[2], datasize-2);
1402 std::istringstream is(datastring, std::ios_base::binary);
1403 Player *player = m_env.getLocalPlayer();
1404 assert(player != NULL);
1405 v3f pos = readV3F1000(is);
1406 f32 pitch = readF1000(is);
1407 f32 yaw = readF1000(is);
1408 player->setPosition(pos);
1409 /*player->setPitch(pitch);
1410 player->setYaw(yaw);*/
1412 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1413 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1419 Add to ClientEvent queue.
1420 This has to be sent to the main program because otherwise
1421 it would just force the pitch and yaw values to whatever
1422 the camera points to.
1425 event.type = CE_PLAYER_FORCE_MOVE;
1426 event.player_force_move.pitch = pitch;
1427 event.player_force_move.yaw = yaw;
1428 m_client_event_queue.push_back(event);
1430 // Ignore damage for a few seconds, so that the player doesn't
1431 // get damage from falling on ground
1432 m_ignore_damage_timer = 3.0;
1434 else if(command == TOCLIENT_PLAYERITEM)
1436 std::string datastring((char*)&data[2], datasize-2);
1437 std::istringstream is(datastring, std::ios_base::binary);
1439 u16 count = readU16(is);
1441 for (u16 i = 0; i < count; ++i) {
1442 u16 peer_id = readU16(is);
1443 Player *player = m_env.getPlayer(peer_id);
1447 infostream<<"Client: ignoring player item "
1448 << deSerializeString(is)
1449 << " for non-existing peer id " << peer_id
1452 } else if (player->isLocal()) {
1453 infostream<<"Client: ignoring player item "
1454 << deSerializeString(is)
1455 << " for local player" << std::endl;
1458 InventoryList *inv = player->inventory.getList("main");
1459 std::string itemstring(deSerializeString(is));
1460 if (itemstring.empty()) {
1463 <<"Client: empty player item for peer "
1464 << peer_id << std::endl;
1466 std::istringstream iss(itemstring);
1467 delete inv->changeItem(0, InventoryItem::deSerialize(iss));
1468 infostream<<"Client: player item for peer " << peer_id << ": ";
1469 player->getWieldItem()->serialize(infostream);
1470 infostream<<std::endl;
1475 else if(command == TOCLIENT_DEATHSCREEN)
1477 std::string datastring((char*)&data[2], datasize-2);
1478 std::istringstream is(datastring, std::ios_base::binary);
1480 bool set_camera_point_target = readU8(is);
1481 v3f camera_point_target = readV3F1000(is);
1484 event.type = CE_DEATHSCREEN;
1485 event.deathscreen.set_camera_point_target = set_camera_point_target;
1486 event.deathscreen.camera_point_target_x = camera_point_target.X;
1487 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1488 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1489 m_client_event_queue.push_back(event);
1493 infostream<<"Client: Ignoring unknown command "
1494 <<command<<std::endl;
1498 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1500 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1501 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1504 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1505 v3s16 nodepos_oversurface, u16 item)
1507 if(connectedAndInitialized() == false){
1508 infostream<<"Client::groundAction() "
1509 "cancelled (not connected)"
1518 [3] v3s16 nodepos_undersurface
1519 [9] v3s16 nodepos_abovesurface
1524 2: stop digging (all parameters ignored)
1525 3: digging completed
1527 u8 datasize = 2 + 1 + 6 + 6 + 2;
1528 SharedBuffer<u8> data(datasize);
1529 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1530 writeU8(&data[2], action);
1531 writeV3S16(&data[3], nodepos_undersurface);
1532 writeV3S16(&data[9], nodepos_oversurface);
1533 writeU16(&data[15], item);
1534 Send(0, data, true);
1537 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1539 if(connectedAndInitialized() == false){
1540 infostream<<"Client::clickActiveObject() "
1541 "cancelled (not connected)"
1546 Player *player = m_env.getLocalPlayer();
1550 ClientActiveObject *obj = m_env.getActiveObject(id);
1553 ToolItem *titem = NULL;
1554 std::string toolname = "";
1556 InventoryList *mlist = player->inventory.getList("main");
1559 InventoryItem *item = mlist->getItem(item_i);
1560 if(item && (std::string)item->getName() == "ToolItem")
1562 titem = (ToolItem*)item;
1563 toolname = titem->getToolName();
1567 v3f playerpos = player->getPosition();
1568 v3f objpos = obj->getPosition();
1569 v3f dir = (objpos - playerpos).normalize();
1571 bool disable_send = obj->directReportPunch(toolname, dir);
1581 [2] u8 button (0=left, 1=right)
1585 u8 datasize = 2 + 1 + 6 + 2 + 2;
1586 SharedBuffer<u8> data(datasize);
1587 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1588 writeU8(&data[2], button);
1589 writeU16(&data[3], id);
1590 writeU16(&data[5], item_i);
1591 Send(0, data, true);
1594 void Client::sendSignNodeText(v3s16 p, std::string text)
1602 std::ostringstream os(std::ios_base::binary);
1606 writeU16(buf, TOSERVER_SIGNNODETEXT);
1607 os.write((char*)buf, 2);
1611 os.write((char*)buf, 6);
1613 u16 textlen = text.size();
1614 // Write text length
1615 writeS16(buf, textlen);
1616 os.write((char*)buf, 2);
1619 os.write((char*)text.c_str(), textlen);
1622 std::string s = os.str();
1623 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1625 Send(0, data, true);
1628 void Client::sendInventoryAction(InventoryAction *a)
1630 std::ostringstream os(std::ios_base::binary);
1634 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1635 os.write((char*)buf, 2);
1640 std::string s = os.str();
1641 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1643 Send(0, data, true);
1646 void Client::sendChatMessage(const std::wstring &message)
1648 std::ostringstream os(std::ios_base::binary);
1652 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1653 os.write((char*)buf, 2);
1656 writeU16(buf, message.size());
1657 os.write((char*)buf, 2);
1660 for(u32 i=0; i<message.size(); i++)
1664 os.write((char*)buf, 2);
1668 std::string s = os.str();
1669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1671 Send(0, data, true);
1674 void Client::sendChangePassword(const std::wstring oldpassword,
1675 const std::wstring newpassword)
1677 Player *player = m_env.getLocalPlayer();
1681 std::string playername = player->getName();
1682 std::string oldpwd = translatePassword(playername, oldpassword);
1683 std::string newpwd = translatePassword(playername, newpassword);
1685 std::ostringstream os(std::ios_base::binary);
1686 u8 buf[2+PASSWORD_SIZE*2];
1688 [0] u16 TOSERVER_PASSWORD
1689 [2] u8[28] old password
1690 [30] u8[28] new password
1693 writeU16(buf, TOSERVER_PASSWORD);
1694 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1696 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1697 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1699 buf[2+PASSWORD_SIZE-1] = 0;
1700 buf[30+PASSWORD_SIZE-1] = 0;
1701 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1704 std::string s = os.str();
1705 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1707 Send(0, data, true);
1711 void Client::sendDamage(u8 damage)
1713 DSTACK(__FUNCTION_NAME);
1714 std::ostringstream os(std::ios_base::binary);
1716 writeU16(os, TOSERVER_DAMAGE);
1717 writeU8(os, damage);
1720 std::string s = os.str();
1721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1723 Send(0, data, true);
1726 void Client::sendRespawn()
1728 DSTACK(__FUNCTION_NAME);
1729 std::ostringstream os(std::ios_base::binary);
1731 writeU16(os, TOSERVER_RESPAWN);
1734 std::string s = os.str();
1735 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1737 Send(0, data, true);
1740 void Client::sendPlayerPos()
1742 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1744 Player *myplayer = m_env.getLocalPlayer();
1745 if(myplayer == NULL)
1750 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1751 our_peer_id = m_con.GetPeerID();
1754 // Set peer id if not set already
1755 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1756 myplayer->peer_id = our_peer_id;
1757 // Check that an existing peer_id is the same as the connection's
1758 assert(myplayer->peer_id == our_peer_id);
1760 v3f pf = myplayer->getPosition();
1761 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1762 v3f sf = myplayer->getSpeed();
1763 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1764 s32 pitch = myplayer->getPitch() * 100;
1765 s32 yaw = myplayer->getYaw() * 100;
1770 [2] v3s32 position*100
1771 [2+12] v3s32 speed*100
1772 [2+12+12] s32 pitch*100
1773 [2+12+12+4] s32 yaw*100
1776 SharedBuffer<u8> data(2+12+12+4+4);
1777 writeU16(&data[0], TOSERVER_PLAYERPOS);
1778 writeV3S32(&data[2], position);
1779 writeV3S32(&data[2+12], speed);
1780 writeS32(&data[2+12+12], pitch);
1781 writeS32(&data[2+12+12+4], yaw);
1783 // Send as unreliable
1784 Send(0, data, false);
1787 void Client::sendPlayerItem(u16 item)
1789 Player *myplayer = m_env.getLocalPlayer();
1790 if(myplayer == NULL)
1793 u16 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 SharedBuffer<u8> data(2+2);
1802 writeU16(&data[0], TOSERVER_PLAYERITEM);
1803 writeU16(&data[2], item);
1806 Send(0, data, true);
1809 void Client::removeNode(v3s16 p)
1811 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1813 core::map<v3s16, MapBlock*> modified_blocks;
1817 //TimeTaker t("removeNodeAndUpdate", m_device);
1818 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1820 catch(InvalidPositionException &e)
1824 for(core::map<v3s16, MapBlock * >::Iterator
1825 i = modified_blocks.getIterator();
1826 i.atEnd() == false; i++)
1828 v3s16 p = i.getNode()->getKey();
1829 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1830 addUpdateMeshTaskWithEdge(p);
1834 void Client::addNode(v3s16 p, MapNode n)
1836 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1838 TimeTaker timer1("Client::addNode()");
1840 core::map<v3s16, MapBlock*> modified_blocks;
1844 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1845 std::string st = std::string("");
1846 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1848 catch(InvalidPositionException &e)
1851 //TimeTaker timer2("Client::addNode(): updateMeshes");
1853 for(core::map<v3s16, MapBlock * >::Iterator
1854 i = modified_blocks.getIterator();
1855 i.atEnd() == false; i++)
1857 v3s16 p = i.getNode()->getKey();
1858 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1859 addUpdateMeshTaskWithEdge(p);
1863 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1865 m_env.getClientMap().updateCamera(pos, dir, fov);
1868 void Client::renderPostFx()
1870 m_env.getClientMap().renderPostFx();
1873 MapNode Client::getNode(v3s16 p)
1875 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1876 return m_env.getMap().getNode(p);
1879 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1881 return m_env.getMap().getNodeMetadata(p);
1884 LocalPlayer* Client::getLocalPlayer()
1886 return m_env.getLocalPlayer();
1889 void Client::setPlayerControl(PlayerControl &control)
1891 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1892 LocalPlayer *player = m_env.getLocalPlayer();
1893 assert(player != NULL);
1894 player->control = control;
1897 void Client::selectPlayerItem(u16 item)
1899 LocalPlayer *player = m_env.getLocalPlayer();
1900 assert(player != NULL);
1902 player->wieldItem(item);
1904 sendPlayerItem(item);
1907 // Returns true if the inventory of the local player has been
1908 // updated from the server. If it is true, it is set to false.
1909 bool Client::getLocalInventoryUpdated()
1911 // m_inventory_updated is behind envlock
1912 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1913 bool updated = m_inventory_updated;
1914 m_inventory_updated = false;
1918 // Copies the inventory of the local player to parameter
1919 void Client::getLocalInventory(Inventory &dst)
1921 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1922 Player *player = m_env.getLocalPlayer();
1923 assert(player != NULL);
1924 dst = player->inventory;
1927 InventoryContext *Client::getInventoryContext()
1929 return &m_inventory_context;
1932 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1934 if(id == "current_player")
1936 assert(c->current_player);
1937 return &(c->current_player->inventory);
1941 std::string id0 = fn.next(":");
1943 if(id0 == "nodemeta")
1946 p.X = stoi(fn.next(","));
1947 p.Y = stoi(fn.next(","));
1948 p.Z = stoi(fn.next(","));
1949 NodeMetadata* meta = getNodeMetadata(p);
1951 return meta->getInventory();
1952 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1953 <<"no metadata found"<<std::endl;
1957 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1960 void Client::inventoryAction(InventoryAction *a)
1962 sendInventoryAction(a);
1965 ClientActiveObject * Client::getSelectedActiveObject(
1967 v3f from_pos_f_on_map,
1968 core::line3d<f32> shootline_on_map
1971 core::array<DistanceSortedActiveObject> objects;
1973 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1975 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1978 // After this, the closest object is the first in the array.
1981 for(u32 i=0; i<objects.size(); i++)
1983 ClientActiveObject *obj = objects[i].obj;
1985 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1986 if(selection_box == NULL)
1989 v3f pos = obj->getPosition();
1991 core::aabbox3d<f32> offsetted_box(
1992 selection_box->MinEdge + pos,
1993 selection_box->MaxEdge + pos
1996 if(offsetted_box.intersectsWithLine(shootline_on_map))
1998 //infostream<<"Returning selected object"<<std::endl;
2003 //infostream<<"No object selected; returning NULL."<<std::endl;
2007 void Client::printDebugInfo(std::ostream &os)
2009 //JMutexAutoLock lock1(m_fetchblock_mutex);
2010 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2012 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2013 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2014 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2018 u32 Client::getDayNightRatio()
2020 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2021 return m_env.getDayNightRatio();
2026 Player *player = m_env.getLocalPlayer();
2027 assert(player != NULL);
2031 void Client::setTempMod(v3s16 p, NodeMod mod)
2033 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2034 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2036 core::map<v3s16, MapBlock*> affected_blocks;
2037 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2040 for(core::map<v3s16, MapBlock*>::Iterator
2041 i = affected_blocks.getIterator();
2042 i.atEnd() == false; i++)
2044 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2048 void Client::clearTempMod(v3s16 p)
2050 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2051 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2053 core::map<v3s16, MapBlock*> affected_blocks;
2054 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2057 for(core::map<v3s16, MapBlock*>::Iterator
2058 i = affected_blocks.getIterator();
2059 i.atEnd() == false; i++)
2061 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2065 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2067 /*infostream<<"Client::addUpdateMeshTask(): "
2068 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2071 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2076 Create a task to update the mesh of the block
2079 MeshMakeData *data = new MeshMakeData;
2082 //TimeTaker timer("data fill");
2084 // Debug: 1-6ms, avg=2ms
2085 data->fill(getDayNightRatio(), b);
2089 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2091 // Add task to queue
2092 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2094 /*infostream<<"Mesh update input queue size is "
2095 <<m_mesh_update_thread.m_queue_in.size()
2099 // Temporary test: make mesh directly in here
2101 //TimeTaker timer("make mesh");
2103 scene::SMesh *mesh_new = NULL;
2104 mesh_new = makeMapBlockMesh(data);
2105 b->replaceMesh(mesh_new);
2111 Mark mesh as non-expired at this point so that it can already
2112 be marked as expired again if the data changes
2114 b->setMeshExpired(false);
2117 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2121 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2122 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2127 v3s16 p = blockpos + v3s16(0,0,0);
2128 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2129 addUpdateMeshTask(p, ack_to_server);
2131 catch(InvalidPositionException &e){}
2134 v3s16 p = blockpos + v3s16(-1,0,0);
2135 addUpdateMeshTask(p);
2137 catch(InvalidPositionException &e){}
2139 v3s16 p = blockpos + v3s16(0,-1,0);
2140 addUpdateMeshTask(p);
2142 catch(InvalidPositionException &e){}
2144 v3s16 p = blockpos + v3s16(0,0,-1);
2145 addUpdateMeshTask(p);
2147 catch(InvalidPositionException &e){}
2150 ClientEvent Client::getClientEvent()
2152 if(m_client_event_queue.size() == 0)
2155 event.type = CE_NONE;
2158 return m_client_event_queue.pop_front();
2161 float Client::getRTT(void)
2164 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2165 } catch(con::PeerNotFoundException &e){