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"
42 QueuedMeshUpdate::QueuedMeshUpdate():
45 ack_block_to_server(false)
49 QueuedMeshUpdate::~QueuedMeshUpdate()
59 MeshUpdateQueue::MeshUpdateQueue()
64 MeshUpdateQueue::~MeshUpdateQueue()
66 JMutexAutoLock lock(m_mutex);
68 core::list<QueuedMeshUpdate*>::Iterator i;
69 for(i=m_queue.begin(); i!=m_queue.end(); i++)
71 QueuedMeshUpdate *q = *i;
77 peer_id=0 adds with nobody to send to
79 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
81 DSTACK(__FUNCTION_NAME);
85 JMutexAutoLock lock(m_mutex);
88 Find if block is already in queue.
89 If it is, update the data and quit.
91 core::list<QueuedMeshUpdate*>::Iterator i;
92 for(i=m_queue.begin(); i!=m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 if(ack_block_to_server)
101 q->ack_block_to_server = true;
109 QueuedMeshUpdate *q = new QueuedMeshUpdate;
112 q->ack_block_to_server = ack_block_to_server;
113 m_queue.push_back(q);
116 // Returned pointer must be deleted
117 // Returns NULL if queue is empty
118 QueuedMeshUpdate * MeshUpdateQueue::pop()
120 JMutexAutoLock lock(m_mutex);
122 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
123 if(i == m_queue.end())
125 QueuedMeshUpdate *q = *i;
134 void * MeshUpdateThread::Thread()
138 log_register_thread("MeshUpdateThread");
140 DSTACK(__FUNCTION_NAME);
142 BEGIN_DEBUG_EXCEPTION_HANDLER
146 /*// Wait for output queue to flush.
147 // Allow 2 in queue, this makes less frametime jitter.
148 // Umm actually, there is no much difference
149 if(m_queue_out.size() >= 2)
155 QueuedMeshUpdate *q = m_queue_in.pop();
162 ScopeProfiler sp(g_profiler, "Client: Mesh making");
164 scene::SMesh *mesh_new = NULL;
165 mesh_new = makeMapBlockMesh(q->data, m_gamedef);
170 r.ack_block_to_server = q->ack_block_to_server;
172 /*infostream<<"MeshUpdateThread: Processed "
173 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
176 m_queue_out.push_back(r);
181 END_DEBUG_EXCEPTION_HANDLER(errorstream)
187 IrrlichtDevice *device,
188 const char *playername,
189 std::string password,
190 MapDrawControl &control,
191 IWritableTextureSource *tsrc,
192 IWritableToolDefManager *tooldef,
193 IWritableNodeDefManager *nodedef
198 m_mesh_update_thread(this),
200 new ClientMap(this, this, control,
201 device->getSceneManager()->getRootSceneNode(),
202 device->getSceneManager(), 666),
203 device->getSceneManager(),
206 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
208 m_server_ser_ver(SER_FMT_VER_INVALID),
209 m_inventory_updated(false),
212 m_password(password),
213 m_access_denied(false)
215 m_packetcounter_timer = 0.0;
216 //m_delete_unused_sectors_timer = 0.0;
217 m_connection_reinit_timer = 0.0;
218 m_avg_rtt_timer = 0.0;
219 m_playerpos_send_timer = 0.0;
220 m_ignore_damage_timer = 0.0;
222 // Build main texture atlas, now that the GameDef exists (that is, us)
223 if(g_settings->getBool("enable_texture_atlas"))
224 tsrc->buildMainAtlas(this);
226 infostream<<"Not building texture atlas."<<std::endl;
229 m_nodedef->updateTextures(tsrc);
231 // NOTE: This should be done only after getting possible dynamic
232 // game definitions from the server, or at least shut down and
233 // restarted when doing so
234 m_mesh_update_thread.Start();
240 Player *player = new LocalPlayer(this);
242 player->updateName(playername);
244 m_env.addPlayer(player);
246 // Initialize player in the inventory context
247 m_inventory_context.current_player = player;
254 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
258 m_mesh_update_thread.setRun(false);
259 while(m_mesh_update_thread.IsRunning())
263 void Client::connect(Address address)
265 DSTACK(__FUNCTION_NAME);
266 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
267 m_con.SetTimeoutMs(0);
268 m_con.Connect(address);
271 bool Client::connectedAndInitialized()
273 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
275 if(m_con.Connected() == false)
278 if(m_server_ser_ver == SER_FMT_VER_INVALID)
284 void Client::step(float dtime)
286 DSTACK(__FUNCTION_NAME);
292 if(m_ignore_damage_timer > dtime)
293 m_ignore_damage_timer -= dtime;
295 m_ignore_damage_timer = 0.0;
297 //infostream<<"Client steps "<<dtime<<std::endl;
300 //TimeTaker timer("ReceiveAll()", m_device);
306 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
308 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
309 m_con.RunTimeouts(dtime);
316 float &counter = m_packetcounter_timer;
322 infostream<<"Client packetcounter (20s):"<<std::endl;
323 m_packetcounter.print(infostream);
324 m_packetcounter.clear();
328 // Get connection status
329 bool connected = connectedAndInitialized();
334 Delete unused sectors
336 NOTE: This jams the game for a while because deleting sectors
340 float &counter = m_delete_unused_sectors_timer;
348 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
350 core::list<v3s16> deleted_blocks;
352 float delete_unused_sectors_timeout =
353 g_settings->getFloat("client_delete_unused_sectors_timeout");
355 // Delete sector blocks
356 /*u32 num = m_env.getMap().unloadUnusedData
357 (delete_unused_sectors_timeout,
358 true, &deleted_blocks);*/
360 // Delete whole sectors
361 m_env.getMap().unloadUnusedData
362 (delete_unused_sectors_timeout,
365 if(deleted_blocks.size() > 0)
367 /*infostream<<"Client: Deleted blocks of "<<num
368 <<" unused sectors"<<std::endl;*/
369 /*infostream<<"Client: Deleted "<<num
370 <<" unused sectors"<<std::endl;*/
376 // Env is locked so con can be locked.
377 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
379 core::list<v3s16>::Iterator i = deleted_blocks.begin();
380 core::list<v3s16> sendlist;
383 if(sendlist.size() == 255 || i == deleted_blocks.end())
385 if(sendlist.size() == 0)
394 u32 replysize = 2+1+6*sendlist.size();
395 SharedBuffer<u8> reply(replysize);
396 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
397 reply[2] = sendlist.size();
399 for(core::list<v3s16>::Iterator
400 j = sendlist.begin();
401 j != sendlist.end(); j++)
403 writeV3S16(&reply[2+1+6*k], *j);
406 m_con.Send(PEER_ID_SERVER, 1, reply, true);
408 if(i == deleted_blocks.end())
414 sendlist.push_back(*i);
422 if(connected == false)
424 float &counter = m_connection_reinit_timer;
430 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
432 Player *myplayer = m_env.getLocalPlayer();
433 assert(myplayer != NULL);
435 // Send TOSERVER_INIT
436 // [0] u16 TOSERVER_INIT
437 // [2] u8 SER_FMT_VER_HIGHEST
438 // [3] u8[20] player_name
439 // [23] u8[28] password (new in some version)
440 // [51] u16 client network protocol version (new in some version)
441 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
442 writeU16(&data[0], TOSERVER_INIT);
443 writeU8(&data[2], SER_FMT_VER_HIGHEST);
445 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
446 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
448 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
451 memset((char*)&data[23], 0, PASSWORD_SIZE);
452 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
454 // This should be incremented in each version
455 writeU16(&data[51], PROTOCOL_VERSION);
457 // Send as unreliable
458 Send(0, data, false);
461 // Not connected, return
466 Do stuff if connected
470 Run Map's timers and unload unused data
472 const float map_timer_and_unload_dtime = 5.25;
473 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
475 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
476 core::list<v3s16> deleted_blocks;
477 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
478 g_settings->getFloat("client_unload_unused_data_timeout"),
481 /*if(deleted_blocks.size() > 0)
482 infostream<<"Client: Unloaded "<<deleted_blocks.size()
483 <<" unused blocks"<<std::endl;*/
487 NOTE: This loop is intentionally iterated the way it is.
490 core::list<v3s16>::Iterator i = deleted_blocks.begin();
491 core::list<v3s16> sendlist;
494 if(sendlist.size() == 255 || i == deleted_blocks.end())
496 if(sendlist.size() == 0)
505 u32 replysize = 2+1+6*sendlist.size();
506 SharedBuffer<u8> reply(replysize);
507 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
508 reply[2] = sendlist.size();
510 for(core::list<v3s16>::Iterator
511 j = sendlist.begin();
512 j != sendlist.end(); j++)
514 writeV3S16(&reply[2+1+6*k], *j);
517 m_con.Send(PEER_ID_SERVER, 1, reply, true);
519 if(i == deleted_blocks.end())
525 sendlist.push_back(*i);
535 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
537 // Control local player (0ms)
538 LocalPlayer *player = m_env.getLocalPlayer();
539 assert(player != NULL);
540 player->applyControl(dtime);
542 //TimeTaker envtimer("env step", m_device);
551 ClientEnvEvent event = m_env.getClientEvent();
552 if(event.type == CEE_NONE)
556 else if(event.type == CEE_PLAYER_DAMAGE)
558 if(m_ignore_damage_timer <= 0)
560 u8 damage = event.player_damage.amount;
563 // Add to ClientEvent queue
565 event.type = CE_PLAYER_DAMAGE;
566 event.player_damage.amount = damage;
567 m_client_event_queue.push_back(event);
577 float &counter = m_avg_rtt_timer;
582 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
583 // connectedAndInitialized() is true, peer exists.
584 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
585 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
590 Send player position to server
593 float &counter = m_playerpos_send_timer;
603 Replace updated meshes
606 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
608 //TimeTaker timer("** Processing mesh update result queue");
611 /*infostream<<"Mesh update result queue size is "
612 <<m_mesh_update_thread.m_queue_out.size()
615 while(m_mesh_update_thread.m_queue_out.size() > 0)
617 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
618 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
621 block->replaceMesh(r.mesh);
623 if(r.ack_block_to_server)
625 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
626 <<","<<r.p.Z<<")"<<std::endl;*/
637 u32 replysize = 2+1+6;
638 SharedBuffer<u8> reply(replysize);
639 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
641 writeV3S16(&reply[3], r.p);
643 m_con.Send(PEER_ID_SERVER, 1, reply, true);
649 // Virtual methods from con::PeerHandler
650 void Client::peerAdded(con::Peer *peer)
652 infostream<<"Client::peerAdded(): peer->id="
653 <<peer->id<<std::endl;
655 void Client::deletingPeer(con::Peer *peer, bool timeout)
657 infostream<<"Client::deletingPeer(): "
658 "Server Peer is getting deleted "
659 <<"(timeout="<<timeout<<")"<<std::endl;
662 void Client::ReceiveAll()
664 DSTACK(__FUNCTION_NAME);
670 catch(con::NoIncomingDataException &e)
674 catch(con::InvalidIncomingDataException &e)
676 infostream<<"Client::ReceiveAll(): "
677 "InvalidIncomingDataException: what()="
678 <<e.what()<<std::endl;
683 void Client::Receive()
685 DSTACK(__FUNCTION_NAME);
686 SharedBuffer<u8> data;
690 //TimeTaker t1("con mutex and receive", m_device);
691 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
692 datasize = m_con.Receive(sender_peer_id, data);
694 //TimeTaker t1("ProcessData", m_device);
695 ProcessData(*data, datasize, sender_peer_id);
699 sender_peer_id given to this shall be quaranteed to be a valid peer
701 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
703 DSTACK(__FUNCTION_NAME);
705 // Ignore packets that don't even fit a command
708 m_packetcounter.add(60000);
712 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
714 //infostream<<"Client: received command="<<command<<std::endl;
715 m_packetcounter.add((u16)command);
718 If this check is removed, be sure to change the queue
719 system to know the ids
721 if(sender_peer_id != PEER_ID_SERVER)
723 infostream<<"Client::ProcessData(): Discarding data not "
724 "coming from server: peer_id="<<sender_peer_id
729 u8 ser_version = m_server_ser_ver;
731 //infostream<<"Client received command="<<(int)command<<std::endl;
733 if(command == TOCLIENT_INIT)
738 u8 deployed = data[2];
740 infostream<<"Client: TOCLIENT_INIT received with "
741 "deployed="<<((int)deployed&0xff)<<std::endl;
743 if(deployed < SER_FMT_VER_LOWEST
744 || deployed > SER_FMT_VER_HIGHEST)
746 infostream<<"Client: TOCLIENT_INIT: Server sent "
747 <<"unsupported ser_fmt_ver"<<std::endl;
751 m_server_ser_ver = deployed;
753 // Get player position
754 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
755 if(datasize >= 2+1+6)
756 playerpos_s16 = readV3S16(&data[2+1]);
757 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
760 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
762 // Set player position
763 Player *player = m_env.getLocalPlayer();
764 assert(player != NULL);
765 player->setPosition(playerpos_f);
768 if(datasize >= 2+1+6+8)
771 m_map_seed = readU64(&data[2+1+6]);
772 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
777 SharedBuffer<u8> reply(replysize);
778 writeU16(&reply[0], TOSERVER_INIT2);
780 m_con.Send(PEER_ID_SERVER, 1, reply, true);
785 if(command == TOCLIENT_ACCESS_DENIED)
787 // The server didn't like our password. Note, this needs
788 // to be processed even if the serialisation format has
789 // not been agreed yet, the same as TOCLIENT_INIT.
790 m_access_denied = true;
791 m_access_denied_reason = L"Unknown";
794 std::string datastring((char*)&data[2], datasize-2);
795 std::istringstream is(datastring, std::ios_base::binary);
796 m_access_denied_reason = deSerializeWideString(is);
801 if(ser_version == SER_FMT_VER_INVALID)
803 infostream<<"Client: Server serialization"
804 " format invalid or not initialized."
805 " Skipping incoming command="<<command<<std::endl;
809 // Just here to avoid putting the two if's together when
810 // making some copypasta
813 if(command == TOCLIENT_REMOVENODE)
818 p.X = readS16(&data[2]);
819 p.Y = readS16(&data[4]);
820 p.Z = readS16(&data[6]);
822 //TimeTaker t1("TOCLIENT_REMOVENODE");
824 // This will clear the cracking animation after digging
825 ((ClientMap&)m_env.getMap()).clearTempMod(p);
829 else if(command == TOCLIENT_ADDNODE)
831 if(datasize < 8 + MapNode::serializedLength(ser_version))
835 p.X = readS16(&data[2]);
836 p.Y = readS16(&data[4]);
837 p.Z = readS16(&data[6]);
839 //TimeTaker t1("TOCLIENT_ADDNODE");
842 n.deSerialize(&data[8], ser_version, m_nodedef);
846 else if(command == TOCLIENT_BLOCKDATA)
848 // Ignore too small packet
853 p.X = readS16(&data[2]);
854 p.Y = readS16(&data[4]);
855 p.Z = readS16(&data[6]);
857 /*infostream<<"Client: Thread: BLOCKDATA for ("
858 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
859 /*infostream<<"Client: Thread: BLOCKDATA for ("
860 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
862 std::string datastring((char*)&data[8], datasize-8);
863 std::istringstream istr(datastring, std::ios_base::binary);
869 sector = m_env.getMap().emergeSector(p2d);
871 assert(sector->getPos() == p2d);
873 //TimeTaker timer("MapBlock deSerialize");
876 block = sector->getBlockNoCreateNoEx(p.Y);
880 Update an existing block
882 //infostream<<"Updating"<<std::endl;
883 block->deSerialize(istr, ser_version);
890 //infostream<<"Creating new"<<std::endl;
891 block = new MapBlock(&m_env.getMap(), p, this);
892 block->deSerialize(istr, ser_version);
893 sector->insertBlock(block);
897 mod.type = NODEMOD_CHANGECONTENT;
898 mod.param = CONTENT_MESE;
899 block->setTempMod(v3s16(8,10,8), mod);
900 block->setTempMod(v3s16(8,9,8), mod);
901 block->setTempMod(v3s16(8,8,8), mod);
902 block->setTempMod(v3s16(8,7,8), mod);
903 block->setTempMod(v3s16(8,6,8), mod);*/
917 u32 replysize = 2+1+6;
918 SharedBuffer<u8> reply(replysize);
919 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
921 writeV3S16(&reply[3], p);
923 m_con.Send(PEER_ID_SERVER, 1, reply, true);
927 Update Mesh of this block and blocks at x-, y- and z-.
928 Environment should not be locked as it interlocks with the
929 main thread, from which is will want to retrieve textures.
932 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
934 Add it to mesh update queue and set it to be acknowledged after update.
936 //infostream<<"Adding mesh update task for received block"<<std::endl;
937 addUpdateMeshTaskWithEdge(p, true);
939 else if(command == TOCLIENT_PLAYERPOS)
941 infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
945 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
946 our_peer_id = m_con.GetPeerID();
948 // Cancel if we don't have a peer id
949 if(our_peer_id == PEER_ID_INEXISTENT){
950 infostream<<"TOCLIENT_PLAYERPOS cancelled: "
957 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
959 u32 player_size = 2+12+12+4+4;
961 u32 player_count = (datasize-2) / player_size;
963 for(u32 i=0; i<player_count; i++)
965 u16 peer_id = readU16(&data[start]);
967 Player *player = m_env.getPlayer(peer_id);
969 // Skip if player doesn't exist
972 start += player_size;
976 // Skip if player is local player
977 if(player->isLocal())
979 start += player_size;
983 v3s32 ps = readV3S32(&data[start+2]);
984 v3s32 ss = readV3S32(&data[start+2+12]);
985 s32 pitch_i = readS32(&data[start+2+12+12]);
986 s32 yaw_i = readS32(&data[start+2+12+12+4]);
987 /*infostream<<"Client: got "
988 <<"pitch_i="<<pitch_i
989 <<" yaw_i="<<yaw_i<<std::endl;*/
990 f32 pitch = (f32)pitch_i / 100.0;
991 f32 yaw = (f32)yaw_i / 100.0;
992 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
993 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
994 player->setPosition(position);
995 player->setSpeed(speed);
996 player->setPitch(pitch);
999 /*infostream<<"Client: player "<<peer_id
1001 <<" yaw="<<yaw<<std::endl;*/
1003 start += player_size;
1007 else if(command == TOCLIENT_PLAYERINFO)
1011 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1012 our_peer_id = m_con.GetPeerID();
1014 // Cancel if we don't have a peer id
1015 if(our_peer_id == PEER_ID_INEXISTENT){
1016 infostream<<"TOCLIENT_PLAYERINFO cancelled: "
1017 "we have no peer id"
1022 //infostream<<"Client: Server reports players:"<<std::endl;
1025 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1027 u32 item_size = 2+PLAYERNAME_SIZE;
1028 u32 player_count = (datasize-2) / item_size;
1031 core::list<u16> players_alive;
1032 for(u32 i=0; i<player_count; i++)
1034 // Make sure the name ends in '\0'
1035 data[start+2+20-1] = 0;
1037 u16 peer_id = readU16(&data[start]);
1039 players_alive.push_back(peer_id);
1041 /*infostream<<"peer_id="<<peer_id
1042 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1044 // Don't update the info of the local player
1045 if(peer_id == our_peer_id)
1051 Player *player = m_env.getPlayer(peer_id);
1053 // Create a player if it doesn't exist
1056 player = new RemotePlayer(this,
1057 m_device->getSceneManager()->getRootSceneNode(),
1060 player->peer_id = peer_id;
1061 m_env.addPlayer(player);
1062 infostream<<"Client: Adding new player "
1063 <<peer_id<<std::endl;
1066 player->updateName((char*)&data[start+2]);
1072 Remove those players from the environment that
1073 weren't listed by the server.
1075 //infostream<<"Removing dead players"<<std::endl;
1076 core::list<Player*> players = m_env.getPlayers();
1077 core::list<Player*>::Iterator ip;
1078 for(ip=players.begin(); ip!=players.end(); ip++)
1080 // Ingore local player
1081 if((*ip)->isLocal())
1084 // Warn about a special case
1085 if((*ip)->peer_id == 0)
1087 infostream<<"Client: Removing "
1088 "dead player with id=0"<<std::endl;
1091 bool is_alive = false;
1092 core::list<u16>::Iterator i;
1093 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1095 if((*ip)->peer_id == *i)
1101 /*infostream<<"peer_id="<<((*ip)->peer_id)
1102 <<" is_alive="<<is_alive<<std::endl;*/
1105 infostream<<"Removing dead player "<<(*ip)->peer_id
1107 m_env.removePlayer((*ip)->peer_id);
1111 else if(command == TOCLIENT_SECTORMETA)
1113 infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1118 [3...] v2s16 pos + sector metadata
1123 //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1126 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1128 std::string datastring((char*)&data[2], datasize-2);
1129 std::istringstream is(datastring, std::ios_base::binary);
1133 is.read((char*)buf, 1);
1134 u16 sector_count = readU8(buf);
1136 //infostream<<"sector_count="<<sector_count<<std::endl;
1138 for(u16 i=0; i<sector_count; i++)
1141 is.read((char*)buf, 4);
1142 v2s16 pos = readV2S16(buf);
1143 /*infostream<<"Client: deserializing sector at "
1144 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1146 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1147 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1152 else if(command == TOCLIENT_INVENTORY)
1157 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1160 //TimeTaker t2("mutex locking", m_device);
1161 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1164 //TimeTaker t3("istringstream init", m_device);
1165 std::string datastring((char*)&data[2], datasize-2);
1166 std::istringstream is(datastring, std::ios_base::binary);
1169 //m_env.printPlayers(infostream);
1171 //TimeTaker t4("player get", m_device);
1172 Player *player = m_env.getLocalPlayer();
1173 assert(player != NULL);
1176 //TimeTaker t1("inventory.deSerialize()", m_device);
1177 player->inventory.deSerialize(is, this);
1180 m_inventory_updated = true;
1182 //infostream<<"Client got player inventory:"<<std::endl;
1183 //player->inventory.print(infostream);
1187 else if(command == TOCLIENT_OBJECTDATA)
1189 // Strip command word and create a stringstream
1190 std::string datastring((char*)&data[2], datasize-2);
1191 std::istringstream is(datastring, std::ios_base::binary);
1199 is.read((char*)buf, 2);
1200 u16 playercount = readU16(buf);
1202 for(u16 i=0; i<playercount; i++)
1204 is.read((char*)buf, 2);
1205 u16 peer_id = readU16(buf);
1206 is.read((char*)buf, 12);
1207 v3s32 p_i = readV3S32(buf);
1208 is.read((char*)buf, 12);
1209 v3s32 s_i = readV3S32(buf);
1210 is.read((char*)buf, 4);
1211 s32 pitch_i = readS32(buf);
1212 is.read((char*)buf, 4);
1213 s32 yaw_i = readS32(buf);
1215 Player *player = m_env.getPlayer(peer_id);
1217 // Skip if player doesn't exist
1223 // Skip if player is local player
1224 if(player->isLocal())
1229 f32 pitch = (f32)pitch_i / 100.0;
1230 f32 yaw = (f32)yaw_i / 100.0;
1231 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1232 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1234 player->setPosition(position);
1235 player->setSpeed(speed);
1236 player->setPitch(pitch);
1237 player->setYaw(yaw);
1242 NOTE: Deprecated stuff
1245 // Read active block count
1246 u16 blockcount = readU16(is);
1247 if(blockcount != 0){
1248 infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
1249 "not supported"<<std::endl;
1253 else if(command == TOCLIENT_TIME_OF_DAY)
1258 u16 time_of_day = readU16(&data[2]);
1259 time_of_day = time_of_day % 24000;
1260 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1268 m_env.setTimeOfDay(time_of_day);
1270 u32 dr = m_env.getDayNightRatio();
1272 infostream<<"Client: time_of_day="<<time_of_day
1278 else if(command == TOCLIENT_CHAT_MESSAGE)
1286 std::string datastring((char*)&data[2], datasize-2);
1287 std::istringstream is(datastring, std::ios_base::binary);
1290 is.read((char*)buf, 2);
1291 u16 len = readU16(buf);
1293 std::wstring message;
1294 for(u16 i=0; i<len; i++)
1296 is.read((char*)buf, 2);
1297 message += (wchar_t)readU16(buf);
1300 /*infostream<<"Client received chat message: "
1301 <<wide_to_narrow(message)<<std::endl;*/
1303 m_chat_queue.push_back(message);
1305 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1307 //if(g_settings->getBool("enable_experimental"))
1311 u16 count of removed objects
1312 for all removed objects {
1315 u16 count of added objects
1316 for all added objects {
1319 u32 initialization data length
1320 string initialization data
1325 // Get all data except the command number
1326 std::string datastring((char*)&data[2], datasize-2);
1327 // Throw them in an istringstream
1328 std::istringstream is(datastring, std::ios_base::binary);
1332 // Read removed objects
1334 u16 removed_count = readU16((u8*)buf);
1335 for(u16 i=0; i<removed_count; i++)
1338 u16 id = readU16((u8*)buf);
1341 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1342 m_env.removeActiveObject(id);
1346 // Read added objects
1348 u16 added_count = readU16((u8*)buf);
1349 for(u16 i=0; i<added_count; i++)
1352 u16 id = readU16((u8*)buf);
1354 u8 type = readU8((u8*)buf);
1355 std::string data = deSerializeLongString(is);
1358 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1359 m_env.addActiveObject(id, type, data);
1364 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1366 //if(g_settings->getBool("enable_experimental"))
1378 // Get all data except the command number
1379 std::string datastring((char*)&data[2], datasize-2);
1380 // Throw them in an istringstream
1381 std::istringstream is(datastring, std::ios_base::binary);
1383 while(is.eof() == false)
1387 u16 id = readU16((u8*)buf);
1391 u16 message_size = readU16((u8*)buf);
1392 std::string message;
1393 message.reserve(message_size);
1394 for(u16 i=0; i<message_size; i++)
1397 message.append(buf, 1);
1399 // Pass on to the environment
1401 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1402 m_env.processActiveObjectMessage(id, message);
1407 else if(command == TOCLIENT_HP)
1409 std::string datastring((char*)&data[2], datasize-2);
1410 std::istringstream is(datastring, std::ios_base::binary);
1411 Player *player = m_env.getLocalPlayer();
1412 assert(player != NULL);
1416 else if(command == TOCLIENT_MOVE_PLAYER)
1418 std::string datastring((char*)&data[2], datasize-2);
1419 std::istringstream is(datastring, std::ios_base::binary);
1420 Player *player = m_env.getLocalPlayer();
1421 assert(player != NULL);
1422 v3f pos = readV3F1000(is);
1423 f32 pitch = readF1000(is);
1424 f32 yaw = readF1000(is);
1425 player->setPosition(pos);
1426 /*player->setPitch(pitch);
1427 player->setYaw(yaw);*/
1429 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1430 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1436 Add to ClientEvent queue.
1437 This has to be sent to the main program because otherwise
1438 it would just force the pitch and yaw values to whatever
1439 the camera points to.
1442 event.type = CE_PLAYER_FORCE_MOVE;
1443 event.player_force_move.pitch = pitch;
1444 event.player_force_move.yaw = yaw;
1445 m_client_event_queue.push_back(event);
1447 // Ignore damage for a few seconds, so that the player doesn't
1448 // get damage from falling on ground
1449 m_ignore_damage_timer = 3.0;
1451 else if(command == TOCLIENT_PLAYERITEM)
1453 std::string datastring((char*)&data[2], datasize-2);
1454 std::istringstream is(datastring, std::ios_base::binary);
1456 u16 count = readU16(is);
1458 for (u16 i = 0; i < count; ++i) {
1459 u16 peer_id = readU16(is);
1460 Player *player = m_env.getPlayer(peer_id);
1464 infostream<<"Client: ignoring player item "
1465 << deSerializeString(is)
1466 << " for non-existing peer id " << peer_id
1469 } else if (player->isLocal()) {
1470 infostream<<"Client: ignoring player item "
1471 << deSerializeString(is)
1472 << " for local player" << std::endl;
1475 InventoryList *inv = player->inventory.getList("main");
1476 std::string itemstring(deSerializeString(is));
1477 if (itemstring.empty()) {
1480 <<"Client: empty player item for peer "
1481 << peer_id << std::endl;
1483 std::istringstream iss(itemstring);
1484 delete inv->changeItem(0,
1485 InventoryItem::deSerialize(iss, this));
1486 infostream<<"Client: player item for peer " << peer_id << ": ";
1487 player->getWieldItem()->serialize(infostream);
1488 infostream<<std::endl;
1493 else if(command == TOCLIENT_DEATHSCREEN)
1495 std::string datastring((char*)&data[2], datasize-2);
1496 std::istringstream is(datastring, std::ios_base::binary);
1498 bool set_camera_point_target = readU8(is);
1499 v3f camera_point_target = readV3F1000(is);
1502 event.type = CE_DEATHSCREEN;
1503 event.deathscreen.set_camera_point_target = set_camera_point_target;
1504 event.deathscreen.camera_point_target_x = camera_point_target.X;
1505 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1506 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1507 m_client_event_queue.push_back(event);
1509 else if(command == TOCLIENT_TOOLDEF)
1511 infostream<<"Client: Received tool definitions"<<std::endl;
1513 std::string datastring((char*)&data[2], datasize-2);
1514 std::istringstream is(datastring, std::ios_base::binary);
1516 // Stop threads while updating content definitions
1517 m_mesh_update_thread.stop();
1519 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1520 m_tooldef->deSerialize(tmp_is);
1523 m_mesh_update_thread.setRun(true);
1524 m_mesh_update_thread.Start();
1528 infostream<<"Client: Ignoring unknown command "
1529 <<command<<std::endl;
1533 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1535 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1536 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1539 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1540 v3s16 nodepos_oversurface, u16 item)
1542 if(connectedAndInitialized() == false){
1543 infostream<<"Client::groundAction() "
1544 "cancelled (not connected)"
1553 [3] v3s16 nodepos_undersurface
1554 [9] v3s16 nodepos_abovesurface
1559 2: stop digging (all parameters ignored)
1560 3: digging completed
1562 u8 datasize = 2 + 1 + 6 + 6 + 2;
1563 SharedBuffer<u8> data(datasize);
1564 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1565 writeU8(&data[2], action);
1566 writeV3S16(&data[3], nodepos_undersurface);
1567 writeV3S16(&data[9], nodepos_oversurface);
1568 writeU16(&data[15], item);
1569 Send(0, data, true);
1572 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1574 if(connectedAndInitialized() == false){
1575 infostream<<"Client::clickActiveObject() "
1576 "cancelled (not connected)"
1581 Player *player = m_env.getLocalPlayer();
1585 ClientActiveObject *obj = m_env.getActiveObject(id);
1588 ToolItem *titem = NULL;
1589 std::string toolname = "";
1591 InventoryList *mlist = player->inventory.getList("main");
1594 InventoryItem *item = mlist->getItem(item_i);
1595 if(item && (std::string)item->getName() == "ToolItem")
1597 titem = (ToolItem*)item;
1598 toolname = titem->getToolName();
1602 v3f playerpos = player->getPosition();
1603 v3f objpos = obj->getPosition();
1604 v3f dir = (objpos - playerpos).normalize();
1606 bool disable_send = obj->directReportPunch(toolname, dir);
1616 [2] u8 button (0=left, 1=right)
1620 u8 datasize = 2 + 1 + 6 + 2 + 2;
1621 SharedBuffer<u8> data(datasize);
1622 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1623 writeU8(&data[2], button);
1624 writeU16(&data[3], id);
1625 writeU16(&data[5], item_i);
1626 Send(0, data, true);
1629 void Client::sendSignNodeText(v3s16 p, std::string text)
1637 std::ostringstream os(std::ios_base::binary);
1641 writeU16(buf, TOSERVER_SIGNNODETEXT);
1642 os.write((char*)buf, 2);
1646 os.write((char*)buf, 6);
1648 u16 textlen = text.size();
1649 // Write text length
1650 writeS16(buf, textlen);
1651 os.write((char*)buf, 2);
1654 os.write((char*)text.c_str(), textlen);
1657 std::string s = os.str();
1658 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1660 Send(0, data, true);
1663 void Client::sendInventoryAction(InventoryAction *a)
1665 std::ostringstream os(std::ios_base::binary);
1669 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1670 os.write((char*)buf, 2);
1675 std::string s = os.str();
1676 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1678 Send(0, data, true);
1681 void Client::sendChatMessage(const std::wstring &message)
1683 std::ostringstream os(std::ios_base::binary);
1687 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1688 os.write((char*)buf, 2);
1691 writeU16(buf, message.size());
1692 os.write((char*)buf, 2);
1695 for(u32 i=0; i<message.size(); i++)
1699 os.write((char*)buf, 2);
1703 std::string s = os.str();
1704 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1706 Send(0, data, true);
1709 void Client::sendChangePassword(const std::wstring oldpassword,
1710 const std::wstring newpassword)
1712 Player *player = m_env.getLocalPlayer();
1716 std::string playername = player->getName();
1717 std::string oldpwd = translatePassword(playername, oldpassword);
1718 std::string newpwd = translatePassword(playername, newpassword);
1720 std::ostringstream os(std::ios_base::binary);
1721 u8 buf[2+PASSWORD_SIZE*2];
1723 [0] u16 TOSERVER_PASSWORD
1724 [2] u8[28] old password
1725 [30] u8[28] new password
1728 writeU16(buf, TOSERVER_PASSWORD);
1729 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1731 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1732 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1734 buf[2+PASSWORD_SIZE-1] = 0;
1735 buf[30+PASSWORD_SIZE-1] = 0;
1736 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1739 std::string s = os.str();
1740 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1742 Send(0, data, true);
1746 void Client::sendDamage(u8 damage)
1748 DSTACK(__FUNCTION_NAME);
1749 std::ostringstream os(std::ios_base::binary);
1751 writeU16(os, TOSERVER_DAMAGE);
1752 writeU8(os, damage);
1755 std::string s = os.str();
1756 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1758 Send(0, data, true);
1761 void Client::sendRespawn()
1763 DSTACK(__FUNCTION_NAME);
1764 std::ostringstream os(std::ios_base::binary);
1766 writeU16(os, TOSERVER_RESPAWN);
1769 std::string s = os.str();
1770 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1772 Send(0, data, true);
1775 void Client::sendPlayerPos()
1777 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1779 Player *myplayer = m_env.getLocalPlayer();
1780 if(myplayer == NULL)
1785 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1786 our_peer_id = m_con.GetPeerID();
1789 // Set peer id if not set already
1790 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1791 myplayer->peer_id = our_peer_id;
1792 // Check that an existing peer_id is the same as the connection's
1793 assert(myplayer->peer_id == our_peer_id);
1795 v3f pf = myplayer->getPosition();
1796 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1797 v3f sf = myplayer->getSpeed();
1798 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1799 s32 pitch = myplayer->getPitch() * 100;
1800 s32 yaw = myplayer->getYaw() * 100;
1805 [2] v3s32 position*100
1806 [2+12] v3s32 speed*100
1807 [2+12+12] s32 pitch*100
1808 [2+12+12+4] s32 yaw*100
1811 SharedBuffer<u8> data(2+12+12+4+4);
1812 writeU16(&data[0], TOSERVER_PLAYERPOS);
1813 writeV3S32(&data[2], position);
1814 writeV3S32(&data[2+12], speed);
1815 writeS32(&data[2+12+12], pitch);
1816 writeS32(&data[2+12+12+4], yaw);
1818 // Send as unreliable
1819 Send(0, data, false);
1822 void Client::sendPlayerItem(u16 item)
1824 Player *myplayer = m_env.getLocalPlayer();
1825 if(myplayer == NULL)
1828 u16 our_peer_id = m_con.GetPeerID();
1830 // Set peer id if not set already
1831 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1832 myplayer->peer_id = our_peer_id;
1833 // Check that an existing peer_id is the same as the connection's
1834 assert(myplayer->peer_id == our_peer_id);
1836 SharedBuffer<u8> data(2+2);
1837 writeU16(&data[0], TOSERVER_PLAYERITEM);
1838 writeU16(&data[2], item);
1841 Send(0, data, true);
1844 void Client::removeNode(v3s16 p)
1846 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1848 core::map<v3s16, MapBlock*> modified_blocks;
1852 //TimeTaker t("removeNodeAndUpdate", m_device);
1853 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1855 catch(InvalidPositionException &e)
1859 for(core::map<v3s16, MapBlock * >::Iterator
1860 i = modified_blocks.getIterator();
1861 i.atEnd() == false; i++)
1863 v3s16 p = i.getNode()->getKey();
1864 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1865 addUpdateMeshTaskWithEdge(p);
1869 void Client::addNode(v3s16 p, MapNode n)
1871 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1873 TimeTaker timer1("Client::addNode()");
1875 core::map<v3s16, MapBlock*> modified_blocks;
1879 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1880 std::string st = std::string("");
1881 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1883 catch(InvalidPositionException &e)
1886 //TimeTaker timer2("Client::addNode(): updateMeshes");
1888 for(core::map<v3s16, MapBlock * >::Iterator
1889 i = modified_blocks.getIterator();
1890 i.atEnd() == false; i++)
1892 v3s16 p = i.getNode()->getKey();
1893 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1894 addUpdateMeshTaskWithEdge(p);
1898 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1900 m_env.getClientMap().updateCamera(pos, dir, fov);
1903 void Client::renderPostFx()
1905 m_env.getClientMap().renderPostFx();
1908 MapNode Client::getNode(v3s16 p)
1910 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1911 return m_env.getMap().getNode(p);
1914 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1916 return m_env.getMap().getNodeMetadata(p);
1919 LocalPlayer* Client::getLocalPlayer()
1921 return m_env.getLocalPlayer();
1924 void Client::setPlayerControl(PlayerControl &control)
1926 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1927 LocalPlayer *player = m_env.getLocalPlayer();
1928 assert(player != NULL);
1929 player->control = control;
1932 void Client::selectPlayerItem(u16 item)
1934 LocalPlayer *player = m_env.getLocalPlayer();
1935 assert(player != NULL);
1937 player->wieldItem(item);
1939 sendPlayerItem(item);
1942 // Returns true if the inventory of the local player has been
1943 // updated from the server. If it is true, it is set to false.
1944 bool Client::getLocalInventoryUpdated()
1946 // m_inventory_updated is behind envlock
1947 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1948 bool updated = m_inventory_updated;
1949 m_inventory_updated = false;
1953 // Copies the inventory of the local player to parameter
1954 void Client::getLocalInventory(Inventory &dst)
1956 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1957 Player *player = m_env.getLocalPlayer();
1958 assert(player != NULL);
1959 dst = player->inventory;
1962 InventoryContext *Client::getInventoryContext()
1964 return &m_inventory_context;
1967 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1969 if(id == "current_player")
1971 assert(c->current_player);
1972 return &(c->current_player->inventory);
1976 std::string id0 = fn.next(":");
1978 if(id0 == "nodemeta")
1981 p.X = stoi(fn.next(","));
1982 p.Y = stoi(fn.next(","));
1983 p.Z = stoi(fn.next(","));
1984 NodeMetadata* meta = getNodeMetadata(p);
1986 return meta->getInventory();
1987 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1988 <<"no metadata found"<<std::endl;
1992 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1995 void Client::inventoryAction(InventoryAction *a)
1997 sendInventoryAction(a);
2000 ClientActiveObject * Client::getSelectedActiveObject(
2002 v3f from_pos_f_on_map,
2003 core::line3d<f32> shootline_on_map
2006 core::array<DistanceSortedActiveObject> objects;
2008 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2010 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2013 // After this, the closest object is the first in the array.
2016 for(u32 i=0; i<objects.size(); i++)
2018 ClientActiveObject *obj = objects[i].obj;
2020 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2021 if(selection_box == NULL)
2024 v3f pos = obj->getPosition();
2026 core::aabbox3d<f32> offsetted_box(
2027 selection_box->MinEdge + pos,
2028 selection_box->MaxEdge + pos
2031 if(offsetted_box.intersectsWithLine(shootline_on_map))
2033 //infostream<<"Returning selected object"<<std::endl;
2038 //infostream<<"No object selected; returning NULL."<<std::endl;
2042 void Client::printDebugInfo(std::ostream &os)
2044 //JMutexAutoLock lock1(m_fetchblock_mutex);
2045 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2047 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2048 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2049 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2053 u32 Client::getDayNightRatio()
2055 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2056 return m_env.getDayNightRatio();
2061 Player *player = m_env.getLocalPlayer();
2062 assert(player != NULL);
2066 void Client::setTempMod(v3s16 p, NodeMod mod)
2068 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2069 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2071 core::map<v3s16, MapBlock*> affected_blocks;
2072 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2075 for(core::map<v3s16, MapBlock*>::Iterator
2076 i = affected_blocks.getIterator();
2077 i.atEnd() == false; i++)
2079 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2083 void Client::clearTempMod(v3s16 p)
2085 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2086 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2088 core::map<v3s16, MapBlock*> affected_blocks;
2089 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2092 for(core::map<v3s16, MapBlock*>::Iterator
2093 i = affected_blocks.getIterator();
2094 i.atEnd() == false; i++)
2096 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2100 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2102 /*infostream<<"Client::addUpdateMeshTask(): "
2103 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2106 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2111 Create a task to update the mesh of the block
2114 MeshMakeData *data = new MeshMakeData;
2117 //TimeTaker timer("data fill");
2119 // Debug: 1-6ms, avg=2ms
2120 data->fill(getDayNightRatio(), b);
2124 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2126 // Add task to queue
2127 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2129 /*infostream<<"Mesh update input queue size is "
2130 <<m_mesh_update_thread.m_queue_in.size()
2134 // Temporary test: make mesh directly in here
2136 //TimeTaker timer("make mesh");
2138 scene::SMesh *mesh_new = NULL;
2139 mesh_new = makeMapBlockMesh(data);
2140 b->replaceMesh(mesh_new);
2146 Mark mesh as non-expired at this point so that it can already
2147 be marked as expired again if the data changes
2149 b->setMeshExpired(false);
2152 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2156 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2157 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2162 v3s16 p = blockpos + v3s16(0,0,0);
2163 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2164 addUpdateMeshTask(p, ack_to_server);
2166 catch(InvalidPositionException &e){}
2169 v3s16 p = blockpos + v3s16(-1,0,0);
2170 addUpdateMeshTask(p);
2172 catch(InvalidPositionException &e){}
2174 v3s16 p = blockpos + v3s16(0,-1,0);
2175 addUpdateMeshTask(p);
2177 catch(InvalidPositionException &e){}
2179 v3s16 p = blockpos + v3s16(0,0,-1);
2180 addUpdateMeshTask(p);
2182 catch(InvalidPositionException &e){}
2185 ClientEvent Client::getClientEvent()
2187 if(m_client_event_queue.size() == 0)
2190 event.type = CE_NONE;
2193 return m_client_event_queue.pop_front();
2196 float Client::getRTT(void)
2199 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2200 } catch(con::PeerNotFoundException &e){
2205 // IGameDef interface
2207 IToolDefManager* Client::getToolDefManager()
2211 INodeDefManager* Client::getNodeDefManager()
2215 ITextureSource* Client::getTextureSource()