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 SharedBuffer<u8> data;
672 //TimeTaker t1("con mutex and receive", m_device);
673 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
674 datasize = m_con.Receive(sender_peer_id, data);
676 //TimeTaker t1("ProcessData", m_device);
677 ProcessData(*data, datasize, sender_peer_id);
681 sender_peer_id given to this shall be quaranteed to be a valid peer
683 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
685 DSTACK(__FUNCTION_NAME);
687 // Ignore packets that don't even fit a command
690 m_packetcounter.add(60000);
694 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
696 //infostream<<"Client: received command="<<command<<std::endl;
697 m_packetcounter.add((u16)command);
700 If this check is removed, be sure to change the queue
701 system to know the ids
703 if(sender_peer_id != PEER_ID_SERVER)
705 infostream<<"Client::ProcessData(): Discarding data not "
706 "coming from server: peer_id="<<sender_peer_id
711 u8 ser_version = m_server_ser_ver;
713 //infostream<<"Client received command="<<(int)command<<std::endl;
715 if(command == TOCLIENT_INIT)
720 u8 deployed = data[2];
722 infostream<<"Client: TOCLIENT_INIT received with "
723 "deployed="<<((int)deployed&0xff)<<std::endl;
725 if(deployed < SER_FMT_VER_LOWEST
726 || deployed > SER_FMT_VER_HIGHEST)
728 infostream<<"Client: TOCLIENT_INIT: Server sent "
729 <<"unsupported ser_fmt_ver"<<std::endl;
733 m_server_ser_ver = deployed;
735 // Get player position
736 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
737 if(datasize >= 2+1+6)
738 playerpos_s16 = readV3S16(&data[2+1]);
739 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
742 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
744 // Set player position
745 Player *player = m_env.getLocalPlayer();
746 assert(player != NULL);
747 player->setPosition(playerpos_f);
750 if(datasize >= 2+1+6+8)
753 m_map_seed = readU64(&data[2+1+6]);
754 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
759 SharedBuffer<u8> reply(replysize);
760 writeU16(&reply[0], TOSERVER_INIT2);
762 m_con.Send(PEER_ID_SERVER, 1, reply, true);
767 if(command == TOCLIENT_ACCESS_DENIED)
769 // The server didn't like our password. Note, this needs
770 // to be processed even if the serialisation format has
771 // not been agreed yet, the same as TOCLIENT_INIT.
772 m_access_denied = true;
773 m_access_denied_reason = L"Unknown";
776 std::string datastring((char*)&data[2], datasize-2);
777 std::istringstream is(datastring, std::ios_base::binary);
778 m_access_denied_reason = deSerializeWideString(is);
783 if(ser_version == SER_FMT_VER_INVALID)
785 infostream<<"Client: Server serialization"
786 " format invalid or not initialized."
787 " Skipping incoming command="<<command<<std::endl;
791 // Just here to avoid putting the two if's together when
792 // making some copypasta
795 if(command == TOCLIENT_REMOVENODE)
800 p.X = readS16(&data[2]);
801 p.Y = readS16(&data[4]);
802 p.Z = readS16(&data[6]);
804 //TimeTaker t1("TOCLIENT_REMOVENODE");
806 // This will clear the cracking animation after digging
807 ((ClientMap&)m_env.getMap()).clearTempMod(p);
811 else if(command == TOCLIENT_ADDNODE)
813 if(datasize < 8 + MapNode::serializedLength(ser_version))
817 p.X = readS16(&data[2]);
818 p.Y = readS16(&data[4]);
819 p.Z = readS16(&data[6]);
821 //TimeTaker t1("TOCLIENT_ADDNODE");
824 n.deSerialize(&data[8], ser_version);
828 else if(command == TOCLIENT_BLOCKDATA)
830 // Ignore too small packet
835 p.X = readS16(&data[2]);
836 p.Y = readS16(&data[4]);
837 p.Z = readS16(&data[6]);
839 /*infostream<<"Client: Thread: BLOCKDATA for ("
840 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
841 /*infostream<<"Client: Thread: BLOCKDATA for ("
842 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
844 std::string datastring((char*)&data[8], datasize-8);
845 std::istringstream istr(datastring, std::ios_base::binary);
851 sector = m_env.getMap().emergeSector(p2d);
853 assert(sector->getPos() == p2d);
855 //TimeTaker timer("MapBlock deSerialize");
858 block = sector->getBlockNoCreateNoEx(p.Y);
862 Update an existing block
864 //infostream<<"Updating"<<std::endl;
865 block->deSerialize(istr, ser_version);
872 //infostream<<"Creating new"<<std::endl;
873 block = new MapBlock(&m_env.getMap(), p);
874 block->deSerialize(istr, ser_version);
875 sector->insertBlock(block);
879 mod.type = NODEMOD_CHANGECONTENT;
880 mod.param = CONTENT_MESE;
881 block->setTempMod(v3s16(8,10,8), mod);
882 block->setTempMod(v3s16(8,9,8), mod);
883 block->setTempMod(v3s16(8,8,8), mod);
884 block->setTempMod(v3s16(8,7,8), mod);
885 block->setTempMod(v3s16(8,6,8), mod);*/
899 u32 replysize = 2+1+6;
900 SharedBuffer<u8> reply(replysize);
901 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
903 writeV3S16(&reply[3], p);
905 m_con.Send(PEER_ID_SERVER, 1, reply, true);
909 Update Mesh of this block and blocks at x-, y- and z-.
910 Environment should not be locked as it interlocks with the
911 main thread, from which is will want to retrieve textures.
914 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
916 Add it to mesh update queue and set it to be acknowledged after update.
918 //infostream<<"Adding mesh update task for received block"<<std::endl;
919 addUpdateMeshTaskWithEdge(p, true);
921 else if(command == TOCLIENT_PLAYERPOS)
923 infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
927 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
928 our_peer_id = m_con.GetPeerID();
930 // Cancel if we don't have a peer id
931 if(our_peer_id == PEER_ID_INEXISTENT){
932 infostream<<"TOCLIENT_PLAYERPOS cancelled: "
939 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
941 u32 player_size = 2+12+12+4+4;
943 u32 player_count = (datasize-2) / player_size;
945 for(u32 i=0; i<player_count; i++)
947 u16 peer_id = readU16(&data[start]);
949 Player *player = m_env.getPlayer(peer_id);
951 // Skip if player doesn't exist
954 start += player_size;
958 // Skip if player is local player
959 if(player->isLocal())
961 start += player_size;
965 v3s32 ps = readV3S32(&data[start+2]);
966 v3s32 ss = readV3S32(&data[start+2+12]);
967 s32 pitch_i = readS32(&data[start+2+12+12]);
968 s32 yaw_i = readS32(&data[start+2+12+12+4]);
969 /*infostream<<"Client: got "
970 <<"pitch_i="<<pitch_i
971 <<" yaw_i="<<yaw_i<<std::endl;*/
972 f32 pitch = (f32)pitch_i / 100.0;
973 f32 yaw = (f32)yaw_i / 100.0;
974 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
975 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
976 player->setPosition(position);
977 player->setSpeed(speed);
978 player->setPitch(pitch);
981 /*infostream<<"Client: player "<<peer_id
983 <<" yaw="<<yaw<<std::endl;*/
985 start += player_size;
989 else if(command == TOCLIENT_PLAYERINFO)
993 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
994 our_peer_id = m_con.GetPeerID();
996 // Cancel if we don't have a peer id
997 if(our_peer_id == PEER_ID_INEXISTENT){
998 infostream<<"TOCLIENT_PLAYERINFO cancelled: "
1004 //infostream<<"Client: Server reports players:"<<std::endl;
1007 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1009 u32 item_size = 2+PLAYERNAME_SIZE;
1010 u32 player_count = (datasize-2) / item_size;
1013 core::list<u16> players_alive;
1014 for(u32 i=0; i<player_count; i++)
1016 // Make sure the name ends in '\0'
1017 data[start+2+20-1] = 0;
1019 u16 peer_id = readU16(&data[start]);
1021 players_alive.push_back(peer_id);
1023 /*infostream<<"peer_id="<<peer_id
1024 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1026 // Don't update the info of the local player
1027 if(peer_id == our_peer_id)
1033 Player *player = m_env.getPlayer(peer_id);
1035 // Create a player if it doesn't exist
1038 player = new RemotePlayer(
1039 m_device->getSceneManager()->getRootSceneNode(),
1042 player->peer_id = peer_id;
1043 m_env.addPlayer(player);
1044 infostream<<"Client: Adding new player "
1045 <<peer_id<<std::endl;
1048 player->updateName((char*)&data[start+2]);
1054 Remove those players from the environment that
1055 weren't listed by the server.
1057 //infostream<<"Removing dead players"<<std::endl;
1058 core::list<Player*> players = m_env.getPlayers();
1059 core::list<Player*>::Iterator ip;
1060 for(ip=players.begin(); ip!=players.end(); ip++)
1062 // Ingore local player
1063 if((*ip)->isLocal())
1066 // Warn about a special case
1067 if((*ip)->peer_id == 0)
1069 infostream<<"Client: Removing "
1070 "dead player with id=0"<<std::endl;
1073 bool is_alive = false;
1074 core::list<u16>::Iterator i;
1075 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1077 if((*ip)->peer_id == *i)
1083 /*infostream<<"peer_id="<<((*ip)->peer_id)
1084 <<" is_alive="<<is_alive<<std::endl;*/
1087 infostream<<"Removing dead player "<<(*ip)->peer_id
1089 m_env.removePlayer((*ip)->peer_id);
1093 else if(command == TOCLIENT_SECTORMETA)
1095 infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1100 [3...] v2s16 pos + sector metadata
1105 //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1108 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1110 std::string datastring((char*)&data[2], datasize-2);
1111 std::istringstream is(datastring, std::ios_base::binary);
1115 is.read((char*)buf, 1);
1116 u16 sector_count = readU8(buf);
1118 //infostream<<"sector_count="<<sector_count<<std::endl;
1120 for(u16 i=0; i<sector_count; i++)
1123 is.read((char*)buf, 4);
1124 v2s16 pos = readV2S16(buf);
1125 /*infostream<<"Client: deserializing sector at "
1126 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1128 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1129 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1134 else if(command == TOCLIENT_INVENTORY)
1139 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1142 //TimeTaker t2("mutex locking", m_device);
1143 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1146 //TimeTaker t3("istringstream init", m_device);
1147 std::string datastring((char*)&data[2], datasize-2);
1148 std::istringstream is(datastring, std::ios_base::binary);
1151 //m_env.printPlayers(infostream);
1153 //TimeTaker t4("player get", m_device);
1154 Player *player = m_env.getLocalPlayer();
1155 assert(player != NULL);
1158 //TimeTaker t1("inventory.deSerialize()", m_device);
1159 player->inventory.deSerialize(is);
1162 m_inventory_updated = true;
1164 //infostream<<"Client got player inventory:"<<std::endl;
1165 //player->inventory.print(infostream);
1169 else if(command == TOCLIENT_OBJECTDATA)
1171 // Strip command word and create a stringstream
1172 std::string datastring((char*)&data[2], datasize-2);
1173 std::istringstream is(datastring, std::ios_base::binary);
1181 is.read((char*)buf, 2);
1182 u16 playercount = readU16(buf);
1184 for(u16 i=0; i<playercount; i++)
1186 is.read((char*)buf, 2);
1187 u16 peer_id = readU16(buf);
1188 is.read((char*)buf, 12);
1189 v3s32 p_i = readV3S32(buf);
1190 is.read((char*)buf, 12);
1191 v3s32 s_i = readV3S32(buf);
1192 is.read((char*)buf, 4);
1193 s32 pitch_i = readS32(buf);
1194 is.read((char*)buf, 4);
1195 s32 yaw_i = readS32(buf);
1197 Player *player = m_env.getPlayer(peer_id);
1199 // Skip if player doesn't exist
1205 // Skip if player is local player
1206 if(player->isLocal())
1211 f32 pitch = (f32)pitch_i / 100.0;
1212 f32 yaw = (f32)yaw_i / 100.0;
1213 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1214 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1216 player->setPosition(position);
1217 player->setSpeed(speed);
1218 player->setPitch(pitch);
1219 player->setYaw(yaw);
1224 NOTE: Deprecated stuff
1227 // Read active block count
1228 u16 blockcount = readU16(is);
1229 if(blockcount != 0){
1230 infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
1231 "not supported"<<std::endl;
1235 else if(command == TOCLIENT_TIME_OF_DAY)
1240 u16 time_of_day = readU16(&data[2]);
1241 time_of_day = time_of_day % 24000;
1242 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1250 m_env.setTimeOfDay(time_of_day);
1252 u32 dr = m_env.getDayNightRatio();
1254 infostream<<"Client: time_of_day="<<time_of_day
1260 else if(command == TOCLIENT_CHAT_MESSAGE)
1268 std::string datastring((char*)&data[2], datasize-2);
1269 std::istringstream is(datastring, std::ios_base::binary);
1272 is.read((char*)buf, 2);
1273 u16 len = readU16(buf);
1275 std::wstring message;
1276 for(u16 i=0; i<len; i++)
1278 is.read((char*)buf, 2);
1279 message += (wchar_t)readU16(buf);
1282 /*infostream<<"Client received chat message: "
1283 <<wide_to_narrow(message)<<std::endl;*/
1285 m_chat_queue.push_back(message);
1287 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1289 //if(g_settings->getBool("enable_experimental"))
1293 u16 count of removed objects
1294 for all removed objects {
1297 u16 count of added objects
1298 for all added objects {
1301 u32 initialization data length
1302 string initialization data
1307 // Get all data except the command number
1308 std::string datastring((char*)&data[2], datasize-2);
1309 // Throw them in an istringstream
1310 std::istringstream is(datastring, std::ios_base::binary);
1314 // Read removed objects
1316 u16 removed_count = readU16((u8*)buf);
1317 for(u16 i=0; i<removed_count; i++)
1320 u16 id = readU16((u8*)buf);
1323 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1324 m_env.removeActiveObject(id);
1328 // Read added objects
1330 u16 added_count = readU16((u8*)buf);
1331 for(u16 i=0; i<added_count; i++)
1334 u16 id = readU16((u8*)buf);
1336 u8 type = readU8((u8*)buf);
1337 std::string data = deSerializeLongString(is);
1340 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1341 m_env.addActiveObject(id, type, data);
1346 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1348 //if(g_settings->getBool("enable_experimental"))
1360 // Get all data except the command number
1361 std::string datastring((char*)&data[2], datasize-2);
1362 // Throw them in an istringstream
1363 std::istringstream is(datastring, std::ios_base::binary);
1365 while(is.eof() == false)
1369 u16 id = readU16((u8*)buf);
1373 u16 message_size = readU16((u8*)buf);
1374 std::string message;
1375 message.reserve(message_size);
1376 for(u16 i=0; i<message_size; i++)
1379 message.append(buf, 1);
1381 // Pass on to the environment
1383 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1384 m_env.processActiveObjectMessage(id, message);
1389 else if(command == TOCLIENT_HP)
1391 std::string datastring((char*)&data[2], datasize-2);
1392 std::istringstream is(datastring, std::ios_base::binary);
1393 Player *player = m_env.getLocalPlayer();
1394 assert(player != NULL);
1398 else if(command == TOCLIENT_MOVE_PLAYER)
1400 std::string datastring((char*)&data[2], datasize-2);
1401 std::istringstream is(datastring, std::ios_base::binary);
1402 Player *player = m_env.getLocalPlayer();
1403 assert(player != NULL);
1404 v3f pos = readV3F1000(is);
1405 f32 pitch = readF1000(is);
1406 f32 yaw = readF1000(is);
1407 player->setPosition(pos);
1408 /*player->setPitch(pitch);
1409 player->setYaw(yaw);*/
1411 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1412 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1418 Add to ClientEvent queue.
1419 This has to be sent to the main program because otherwise
1420 it would just force the pitch and yaw values to whatever
1421 the camera points to.
1424 event.type = CE_PLAYER_FORCE_MOVE;
1425 event.player_force_move.pitch = pitch;
1426 event.player_force_move.yaw = yaw;
1427 m_client_event_queue.push_back(event);
1429 // Ignore damage for a few seconds, so that the player doesn't
1430 // get damage from falling on ground
1431 m_ignore_damage_timer = 3.0;
1433 else if(command == TOCLIENT_PLAYERITEM)
1435 std::string datastring((char*)&data[2], datasize-2);
1436 std::istringstream is(datastring, std::ios_base::binary);
1438 u16 count = readU16(is);
1440 for (u16 i = 0; i < count; ++i) {
1441 u16 peer_id = readU16(is);
1442 Player *player = m_env.getPlayer(peer_id);
1446 infostream<<"Client: ignoring player item "
1447 << deSerializeString(is)
1448 << " for non-existing peer id " << peer_id
1451 } else if (player->isLocal()) {
1452 infostream<<"Client: ignoring player item "
1453 << deSerializeString(is)
1454 << " for local player" << std::endl;
1457 InventoryList *inv = player->inventory.getList("main");
1458 std::string itemstring(deSerializeString(is));
1459 if (itemstring.empty()) {
1462 <<"Client: empty player item for peer "
1463 << peer_id << std::endl;
1465 std::istringstream iss(itemstring);
1466 delete inv->changeItem(0, InventoryItem::deSerialize(iss));
1467 infostream<<"Client: player item for peer " << peer_id << ": ";
1468 player->getWieldItem()->serialize(infostream);
1469 infostream<<std::endl;
1474 else if(command == TOCLIENT_DEATHSCREEN)
1476 std::string datastring((char*)&data[2], datasize-2);
1477 std::istringstream is(datastring, std::ios_base::binary);
1479 bool set_camera_point_target = readU8(is);
1480 v3f camera_point_target = readV3F1000(is);
1483 event.type = CE_DEATHSCREEN;
1484 event.deathscreen.set_camera_point_target = set_camera_point_target;
1485 event.deathscreen.camera_point_target_x = camera_point_target.X;
1486 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1487 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1488 m_client_event_queue.push_back(event);
1492 infostream<<"Client: Ignoring unknown command "
1493 <<command<<std::endl;
1497 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1499 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1500 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1503 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1504 v3s16 nodepos_oversurface, u16 item)
1506 if(connectedAndInitialized() == false){
1507 infostream<<"Client::groundAction() "
1508 "cancelled (not connected)"
1517 [3] v3s16 nodepos_undersurface
1518 [9] v3s16 nodepos_abovesurface
1523 2: stop digging (all parameters ignored)
1524 3: digging completed
1526 u8 datasize = 2 + 1 + 6 + 6 + 2;
1527 SharedBuffer<u8> data(datasize);
1528 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1529 writeU8(&data[2], action);
1530 writeV3S16(&data[3], nodepos_undersurface);
1531 writeV3S16(&data[9], nodepos_oversurface);
1532 writeU16(&data[15], item);
1533 Send(0, data, true);
1536 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1538 if(connectedAndInitialized() == false){
1539 infostream<<"Client::clickActiveObject() "
1540 "cancelled (not connected)"
1545 Player *player = m_env.getLocalPlayer();
1549 ClientActiveObject *obj = m_env.getActiveObject(id);
1552 ToolItem *titem = NULL;
1553 std::string toolname = "";
1555 InventoryList *mlist = player->inventory.getList("main");
1558 InventoryItem *item = mlist->getItem(item_i);
1559 if(item && (std::string)item->getName() == "ToolItem")
1561 titem = (ToolItem*)item;
1562 toolname = titem->getToolName();
1566 v3f playerpos = player->getPosition();
1567 v3f objpos = obj->getPosition();
1568 v3f dir = (objpos - playerpos).normalize();
1570 bool disable_send = obj->directReportPunch(toolname, dir);
1580 [2] u8 button (0=left, 1=right)
1584 u8 datasize = 2 + 1 + 6 + 2 + 2;
1585 SharedBuffer<u8> data(datasize);
1586 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1587 writeU8(&data[2], button);
1588 writeU16(&data[3], id);
1589 writeU16(&data[5], item_i);
1590 Send(0, data, true);
1593 void Client::sendSignNodeText(v3s16 p, std::string text)
1601 std::ostringstream os(std::ios_base::binary);
1605 writeU16(buf, TOSERVER_SIGNNODETEXT);
1606 os.write((char*)buf, 2);
1610 os.write((char*)buf, 6);
1612 u16 textlen = text.size();
1613 // Write text length
1614 writeS16(buf, textlen);
1615 os.write((char*)buf, 2);
1618 os.write((char*)text.c_str(), textlen);
1621 std::string s = os.str();
1622 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1624 Send(0, data, true);
1627 void Client::sendInventoryAction(InventoryAction *a)
1629 std::ostringstream os(std::ios_base::binary);
1633 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1634 os.write((char*)buf, 2);
1639 std::string s = os.str();
1640 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1642 Send(0, data, true);
1645 void Client::sendChatMessage(const std::wstring &message)
1647 std::ostringstream os(std::ios_base::binary);
1651 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1652 os.write((char*)buf, 2);
1655 writeU16(buf, message.size());
1656 os.write((char*)buf, 2);
1659 for(u32 i=0; i<message.size(); i++)
1663 os.write((char*)buf, 2);
1667 std::string s = os.str();
1668 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1670 Send(0, data, true);
1673 void Client::sendChangePassword(const std::wstring oldpassword,
1674 const std::wstring newpassword)
1676 Player *player = m_env.getLocalPlayer();
1680 std::string playername = player->getName();
1681 std::string oldpwd = translatePassword(playername, oldpassword);
1682 std::string newpwd = translatePassword(playername, newpassword);
1684 std::ostringstream os(std::ios_base::binary);
1685 u8 buf[2+PASSWORD_SIZE*2];
1687 [0] u16 TOSERVER_PASSWORD
1688 [2] u8[28] old password
1689 [30] u8[28] new password
1692 writeU16(buf, TOSERVER_PASSWORD);
1693 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1695 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1696 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1698 buf[2+PASSWORD_SIZE-1] = 0;
1699 buf[30+PASSWORD_SIZE-1] = 0;
1700 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1703 std::string s = os.str();
1704 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1706 Send(0, data, true);
1710 void Client::sendDamage(u8 damage)
1712 DSTACK(__FUNCTION_NAME);
1713 std::ostringstream os(std::ios_base::binary);
1715 writeU16(os, TOSERVER_DAMAGE);
1716 writeU8(os, damage);
1719 std::string s = os.str();
1720 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1722 Send(0, data, true);
1725 void Client::sendRespawn()
1727 DSTACK(__FUNCTION_NAME);
1728 std::ostringstream os(std::ios_base::binary);
1730 writeU16(os, TOSERVER_RESPAWN);
1733 std::string s = os.str();
1734 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1736 Send(0, data, true);
1739 void Client::sendPlayerPos()
1741 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1743 Player *myplayer = m_env.getLocalPlayer();
1744 if(myplayer == NULL)
1749 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1750 our_peer_id = m_con.GetPeerID();
1753 // Set peer id if not set already
1754 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1755 myplayer->peer_id = our_peer_id;
1756 // Check that an existing peer_id is the same as the connection's
1757 assert(myplayer->peer_id == our_peer_id);
1759 v3f pf = myplayer->getPosition();
1760 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1761 v3f sf = myplayer->getSpeed();
1762 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1763 s32 pitch = myplayer->getPitch() * 100;
1764 s32 yaw = myplayer->getYaw() * 100;
1769 [2] v3s32 position*100
1770 [2+12] v3s32 speed*100
1771 [2+12+12] s32 pitch*100
1772 [2+12+12+4] s32 yaw*100
1775 SharedBuffer<u8> data(2+12+12+4+4);
1776 writeU16(&data[0], TOSERVER_PLAYERPOS);
1777 writeV3S32(&data[2], position);
1778 writeV3S32(&data[2+12], speed);
1779 writeS32(&data[2+12+12], pitch);
1780 writeS32(&data[2+12+12+4], yaw);
1782 // Send as unreliable
1783 Send(0, data, false);
1786 void Client::sendPlayerItem(u16 item)
1788 Player *myplayer = m_env.getLocalPlayer();
1789 if(myplayer == NULL)
1792 u16 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 SharedBuffer<u8> data(2+2);
1801 writeU16(&data[0], TOSERVER_PLAYERITEM);
1802 writeU16(&data[2], item);
1805 Send(0, data, true);
1808 void Client::removeNode(v3s16 p)
1810 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1812 core::map<v3s16, MapBlock*> modified_blocks;
1816 //TimeTaker t("removeNodeAndUpdate", m_device);
1817 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1819 catch(InvalidPositionException &e)
1823 for(core::map<v3s16, MapBlock * >::Iterator
1824 i = modified_blocks.getIterator();
1825 i.atEnd() == false; i++)
1827 v3s16 p = i.getNode()->getKey();
1828 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1829 addUpdateMeshTaskWithEdge(p);
1833 void Client::addNode(v3s16 p, MapNode n)
1835 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1837 TimeTaker timer1("Client::addNode()");
1839 core::map<v3s16, MapBlock*> modified_blocks;
1843 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1844 std::string st = std::string("");
1845 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1847 catch(InvalidPositionException &e)
1850 //TimeTaker timer2("Client::addNode(): updateMeshes");
1852 for(core::map<v3s16, MapBlock * >::Iterator
1853 i = modified_blocks.getIterator();
1854 i.atEnd() == false; i++)
1856 v3s16 p = i.getNode()->getKey();
1857 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1858 addUpdateMeshTaskWithEdge(p);
1862 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1864 m_env.getClientMap().updateCamera(pos, dir, fov);
1867 void Client::renderPostFx()
1869 m_env.getClientMap().renderPostFx();
1872 MapNode Client::getNode(v3s16 p)
1874 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1875 return m_env.getMap().getNode(p);
1878 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1880 return m_env.getMap().getNodeMetadata(p);
1883 LocalPlayer* Client::getLocalPlayer()
1885 return m_env.getLocalPlayer();
1888 void Client::setPlayerControl(PlayerControl &control)
1890 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1891 LocalPlayer *player = m_env.getLocalPlayer();
1892 assert(player != NULL);
1893 player->control = control;
1896 void Client::selectPlayerItem(u16 item)
1898 LocalPlayer *player = m_env.getLocalPlayer();
1899 assert(player != NULL);
1901 player->wieldItem(item);
1903 sendPlayerItem(item);
1906 // Returns true if the inventory of the local player has been
1907 // updated from the server. If it is true, it is set to false.
1908 bool Client::getLocalInventoryUpdated()
1910 // m_inventory_updated is behind envlock
1911 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1912 bool updated = m_inventory_updated;
1913 m_inventory_updated = false;
1917 // Copies the inventory of the local player to parameter
1918 void Client::getLocalInventory(Inventory &dst)
1920 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1921 Player *player = m_env.getLocalPlayer();
1922 assert(player != NULL);
1923 dst = player->inventory;
1926 InventoryContext *Client::getInventoryContext()
1928 return &m_inventory_context;
1931 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1933 if(id == "current_player")
1935 assert(c->current_player);
1936 return &(c->current_player->inventory);
1940 std::string id0 = fn.next(":");
1942 if(id0 == "nodemeta")
1945 p.X = stoi(fn.next(","));
1946 p.Y = stoi(fn.next(","));
1947 p.Z = stoi(fn.next(","));
1948 NodeMetadata* meta = getNodeMetadata(p);
1950 return meta->getInventory();
1951 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1952 <<"no metadata found"<<std::endl;
1956 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1959 void Client::inventoryAction(InventoryAction *a)
1961 sendInventoryAction(a);
1964 ClientActiveObject * Client::getSelectedActiveObject(
1966 v3f from_pos_f_on_map,
1967 core::line3d<f32> shootline_on_map
1970 core::array<DistanceSortedActiveObject> objects;
1972 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1974 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1977 // After this, the closest object is the first in the array.
1980 for(u32 i=0; i<objects.size(); i++)
1982 ClientActiveObject *obj = objects[i].obj;
1984 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1985 if(selection_box == NULL)
1988 v3f pos = obj->getPosition();
1990 core::aabbox3d<f32> offsetted_box(
1991 selection_box->MinEdge + pos,
1992 selection_box->MaxEdge + pos
1995 if(offsetted_box.intersectsWithLine(shootline_on_map))
1997 //infostream<<"Returning selected object"<<std::endl;
2002 //infostream<<"No object selected; returning NULL."<<std::endl;
2006 void Client::printDebugInfo(std::ostream &os)
2008 //JMutexAutoLock lock1(m_fetchblock_mutex);
2009 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2011 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2012 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2013 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2017 u32 Client::getDayNightRatio()
2019 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2020 return m_env.getDayNightRatio();
2025 Player *player = m_env.getLocalPlayer();
2026 assert(player != NULL);
2030 void Client::setTempMod(v3s16 p, NodeMod mod)
2032 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2033 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2035 core::map<v3s16, MapBlock*> affected_blocks;
2036 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2039 for(core::map<v3s16, MapBlock*>::Iterator
2040 i = affected_blocks.getIterator();
2041 i.atEnd() == false; i++)
2043 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2047 void Client::clearTempMod(v3s16 p)
2049 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2050 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2052 core::map<v3s16, MapBlock*> affected_blocks;
2053 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2056 for(core::map<v3s16, MapBlock*>::Iterator
2057 i = affected_blocks.getIterator();
2058 i.atEnd() == false; i++)
2060 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2064 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2066 /*infostream<<"Client::addUpdateMeshTask(): "
2067 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2070 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2075 Create a task to update the mesh of the block
2078 MeshMakeData *data = new MeshMakeData;
2081 //TimeTaker timer("data fill");
2083 // Debug: 1-6ms, avg=2ms
2084 data->fill(getDayNightRatio(), b);
2088 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2090 // Add task to queue
2091 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2093 /*infostream<<"Mesh update input queue size is "
2094 <<m_mesh_update_thread.m_queue_in.size()
2098 // Temporary test: make mesh directly in here
2100 //TimeTaker timer("make mesh");
2102 scene::SMesh *mesh_new = NULL;
2103 mesh_new = makeMapBlockMesh(data);
2104 b->replaceMesh(mesh_new);
2110 Mark mesh as non-expired at this point so that it can already
2111 be marked as expired again if the data changes
2113 b->setMeshExpired(false);
2116 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2120 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2121 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2126 v3s16 p = blockpos + v3s16(0,0,0);
2127 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2128 addUpdateMeshTask(p, ack_to_server);
2130 catch(InvalidPositionException &e){}
2133 v3s16 p = blockpos + v3s16(-1,0,0);
2134 addUpdateMeshTask(p);
2136 catch(InvalidPositionException &e){}
2138 v3s16 p = blockpos + v3s16(0,-1,0);
2139 addUpdateMeshTask(p);
2141 catch(InvalidPositionException &e){}
2143 v3s16 p = blockpos + v3s16(0,0,-1);
2144 addUpdateMeshTask(p);
2146 catch(InvalidPositionException &e){}
2149 ClientEvent Client::getClientEvent()
2151 if(m_client_event_queue.size() == 0)
2154 event.type = CE_NONE;
2157 return m_client_event_queue.pop_front();
2160 float Client::getRTT(void)
2163 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2164 } catch(con::PeerNotFoundException &e){