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 MeshUpdateQueue::(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"
36 QueuedMeshUpdate::QueuedMeshUpdate():
39 ack_block_to_server(false)
43 QueuedMeshUpdate::~QueuedMeshUpdate()
53 MeshUpdateQueue::MeshUpdateQueue()
58 MeshUpdateQueue::~MeshUpdateQueue()
60 JMutexAutoLock lock(m_mutex);
62 core::list<QueuedMeshUpdate*>::Iterator i;
63 for(i=m_queue.begin(); i!=m_queue.end(); i++)
65 QueuedMeshUpdate *q = *i;
71 peer_id=0 adds with nobody to send to
73 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
75 DSTACK(__FUNCTION_NAME);
79 JMutexAutoLock lock(m_mutex);
82 Find if block is already in queue.
83 If it is, update the data and quit.
85 core::list<QueuedMeshUpdate*>::Iterator i;
86 for(i=m_queue.begin(); i!=m_queue.end(); i++)
88 QueuedMeshUpdate *q = *i;
94 if(ack_block_to_server)
95 q->ack_block_to_server = true;
103 QueuedMeshUpdate *q = new QueuedMeshUpdate;
106 q->ack_block_to_server = ack_block_to_server;
107 m_queue.push_back(q);
110 // Returned pointer must be deleted
111 // Returns NULL if queue is empty
112 QueuedMeshUpdate * MeshUpdateQueue::pop()
114 JMutexAutoLock lock(m_mutex);
116 core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
117 if(i == m_queue.end())
119 QueuedMeshUpdate *q = *i;
128 void * MeshUpdateThread::Thread()
132 DSTACK(__FUNCTION_NAME);
134 BEGIN_DEBUG_EXCEPTION_HANDLER
138 QueuedMeshUpdate *q = m_queue_in.pop();
145 ScopeProfiler sp(&g_profiler, "mesh make");
147 scene::SMesh *mesh_new = NULL;
148 mesh_new = makeMapBlockMesh(q->data);
153 r.ack_block_to_server = q->ack_block_to_server;
155 /*dstream<<"MeshUpdateThread: Processed "
156 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
159 m_queue_out.push_back(r);
164 END_DEBUG_EXCEPTION_HANDLER
170 IrrlichtDevice *device,
171 const char *playername,
172 std::string password,
173 MapDrawControl &control):
174 m_mesh_update_thread(),
176 new ClientMap(this, control,
177 device->getSceneManager()->getRootSceneNode(),
178 device->getSceneManager(), 666),
179 device->getSceneManager()
181 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
183 camera_position(0,0,0),
184 camera_direction(0,0,1),
185 m_server_ser_ver(SER_FMT_VER_INVALID),
186 m_inventory_updated(false),
189 m_password(password),
190 m_access_denied(false)
192 m_packetcounter_timer = 0.0;
193 m_delete_unused_sectors_timer = 0.0;
194 m_connection_reinit_timer = 0.0;
195 m_avg_rtt_timer = 0.0;
196 m_playerpos_send_timer = 0.0;
197 m_ignore_damage_timer = 0.0;
199 //m_env_mutex.Init();
200 //m_con_mutex.Init();
202 m_mesh_update_thread.Start();
208 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
210 Player *player = new LocalPlayer();
212 player->updateName(playername);
214 m_env.addPlayer(player);
216 // Initialize player in the inventory context
217 m_inventory_context.current_player = player;
224 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
228 m_mesh_update_thread.setRun(false);
229 while(m_mesh_update_thread.IsRunning())
233 void Client::connect(Address address)
235 DSTACK(__FUNCTION_NAME);
236 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
237 m_con.setTimeoutMs(0);
238 m_con.Connect(address);
241 bool Client::connectedAndInitialized()
243 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
245 if(m_con.Connected() == false)
248 if(m_server_ser_ver == SER_FMT_VER_INVALID)
254 void Client::step(float dtime)
256 DSTACK(__FUNCTION_NAME);
262 if(m_ignore_damage_timer > dtime)
263 m_ignore_damage_timer -= dtime;
265 m_ignore_damage_timer = 0.0;
267 //dstream<<"Client steps "<<dtime<<std::endl;
270 //TimeTaker timer("ReceiveAll()", m_device);
276 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
278 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
279 m_con.RunTimeouts(dtime);
286 float &counter = m_packetcounter_timer;
292 dout_client<<"Client packetcounter (20s):"<<std::endl;
293 m_packetcounter.print(dout_client);
294 m_packetcounter.clear();
300 Delete unused sectors
302 NOTE: This jams the game for a while because deleting sectors
306 float &counter = m_delete_unused_sectors_timer;
314 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
316 core::list<v3s16> deleted_blocks;
318 float delete_unused_sectors_timeout =
319 g_settings.getFloat("client_delete_unused_sectors_timeout");
321 // Delete sector blocks
322 /*u32 num = m_env.getMap().unloadUnusedData
323 (delete_unused_sectors_timeout,
324 true, &deleted_blocks);*/
326 // Delete whole sectors
327 u32 num = m_env.getMap().unloadUnusedData
328 (delete_unused_sectors_timeout,
329 false, &deleted_blocks);
333 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
334 <<" unused sectors"<<std::endl;*/
335 dstream<<DTIME<<"Client: Deleted "<<num
336 <<" unused sectors"<<std::endl;
342 // Env is locked so con can be locked.
343 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
345 core::list<v3s16>::Iterator i = deleted_blocks.begin();
346 core::list<v3s16> sendlist;
349 if(sendlist.size() == 255 || i == deleted_blocks.end())
351 if(sendlist.size() == 0)
360 u32 replysize = 2+1+6*sendlist.size();
361 SharedBuffer<u8> reply(replysize);
362 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
363 reply[2] = sendlist.size();
365 for(core::list<v3s16>::Iterator
366 j = sendlist.begin();
367 j != sendlist.end(); j++)
369 writeV3S16(&reply[2+1+6*k], *j);
372 m_con.Send(PEER_ID_SERVER, 1, reply, true);
374 if(i == deleted_blocks.end())
380 sendlist.push_back(*i);
387 bool connected = connectedAndInitialized();
389 if(connected == false)
391 float &counter = m_connection_reinit_timer;
397 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
399 Player *myplayer = m_env.getLocalPlayer();
400 assert(myplayer != NULL);
402 // Send TOSERVER_INIT
403 // [0] u16 TOSERVER_INIT
404 // [2] u8 SER_FMT_VER_HIGHEST
405 // [3] u8[20] player_name
406 // [23] u8[28] password
407 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
408 writeU16(&data[0], TOSERVER_INIT);
409 writeU8(&data[2], SER_FMT_VER_HIGHEST);
411 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
412 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
414 /*dstream<<"Client: password hash is \""<<m_password<<"\""
417 memset((char*)&data[23], 0, PASSWORD_SIZE);
418 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
420 // Send as unreliable
421 Send(0, data, false);
424 // Not connected, return
429 Do stuff if connected
437 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
439 // Control local player (0ms)
440 LocalPlayer *player = m_env.getLocalPlayer();
441 assert(player != NULL);
442 player->applyControl(dtime);
444 //TimeTaker envtimer("env step", m_device);
448 // Step active blocks
449 for(core::map<v3s16, bool>::Iterator
450 i = m_active_blocks.getIterator();
451 i.atEnd() == false; i++)
453 v3s16 p = i.getNode()->getKey();
455 MapBlock *block = NULL;
458 block = m_env.getMap().getBlockNoCreate(p);
459 block->stepObjects(dtime, false, m_env.getDayNightRatio());
461 catch(InvalidPositionException &e)
471 ClientEnvEvent event = m_env.getClientEvent();
472 if(event.type == CEE_NONE)
476 else if(event.type == CEE_PLAYER_DAMAGE)
478 if(m_ignore_damage_timer <= 0)
480 u8 damage = event.player_damage.amount;
483 // Add to ClientEvent queue
485 event.type = CE_PLAYER_DAMAGE;
486 event.player_damage.amount = damage;
487 m_client_event_queue.push_back(event);
497 float &counter = m_avg_rtt_timer;
502 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
503 // connectedAndInitialized() is true, peer exists.
504 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
505 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
510 Send player position to server
513 float &counter = m_playerpos_send_timer;
523 Replace updated meshes
526 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
528 //TimeTaker timer("** Processing mesh update result queue");
531 /*dstream<<"Mesh update result queue size is "
532 <<m_mesh_update_thread.m_queue_out.size()
535 while(m_mesh_update_thread.m_queue_out.size() > 0)
537 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
538 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
541 block->replaceMesh(r.mesh);
543 if(r.ack_block_to_server)
545 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
546 <<","<<r.p.Z<<")"<<std::endl;*/
557 u32 replysize = 2+1+6;
558 SharedBuffer<u8> reply(replysize);
559 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
561 writeV3S16(&reply[3], r.p);
563 m_con.Send(PEER_ID_SERVER, 1, reply, true);
569 // Virtual methods from con::PeerHandler
570 void Client::peerAdded(con::Peer *peer)
572 derr_client<<"Client::peerAdded(): peer->id="
573 <<peer->id<<std::endl;
575 void Client::deletingPeer(con::Peer *peer, bool timeout)
577 derr_client<<"Client::deletingPeer(): "
578 "Server Peer is getting deleted "
579 <<"(timeout="<<timeout<<")"<<std::endl;
582 void Client::ReceiveAll()
584 DSTACK(__FUNCTION_NAME);
590 catch(con::NoIncomingDataException &e)
594 catch(con::InvalidIncomingDataException &e)
596 dout_client<<DTIME<<"Client::ReceiveAll(): "
597 "InvalidIncomingDataException: what()="
598 <<e.what()<<std::endl;
603 void Client::Receive()
605 DSTACK(__FUNCTION_NAME);
606 u32 data_maxsize = 200000;
607 Buffer<u8> data(data_maxsize);
611 //TimeTaker t1("con mutex and receive", m_device);
612 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
613 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
615 //TimeTaker t1("ProcessData", m_device);
616 ProcessData(*data, datasize, sender_peer_id);
620 sender_peer_id given to this shall be quaranteed to be a valid peer
622 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
624 DSTACK(__FUNCTION_NAME);
626 // Ignore packets that don't even fit a command
629 m_packetcounter.add(60000);
633 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
635 //dstream<<"Client: received command="<<command<<std::endl;
636 m_packetcounter.add((u16)command);
639 If this check is removed, be sure to change the queue
640 system to know the ids
642 if(sender_peer_id != PEER_ID_SERVER)
644 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
645 "coming from server: peer_id="<<sender_peer_id
652 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
653 // All data is coming from the server
654 // PeerNotFoundException is handled by caller.
655 peer = m_con.GetPeer(PEER_ID_SERVER);
658 u8 ser_version = m_server_ser_ver;
660 //dstream<<"Client received command="<<(int)command<<std::endl;
662 if(command == TOCLIENT_INIT)
667 u8 deployed = data[2];
669 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
670 "deployed="<<((int)deployed&0xff)<<std::endl;
672 if(deployed < SER_FMT_VER_LOWEST
673 || deployed > SER_FMT_VER_HIGHEST)
675 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
676 <<"unsupported ser_fmt_ver"<<std::endl;
680 m_server_ser_ver = deployed;
682 // Get player position
683 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
684 if(datasize >= 2+1+6)
685 playerpos_s16 = readV3S16(&data[2+1]);
686 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
689 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
691 // Set player position
692 Player *player = m_env.getLocalPlayer();
693 assert(player != NULL);
694 player->setPosition(playerpos_f);
697 if(datasize >= 2+1+6+8)
700 m_map_seed = readU64(&data[2+1+6]);
701 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
706 SharedBuffer<u8> reply(replysize);
707 writeU16(&reply[0], TOSERVER_INIT2);
709 m_con.Send(PEER_ID_SERVER, 1, reply, true);
714 if(command == TOCLIENT_ACCESS_DENIED)
716 // The server didn't like our password. Note, this needs
717 // to be processed even if the serialisation format has
718 // not been agreed yet, the same as TOCLIENT_INIT.
719 m_access_denied = true;
720 m_access_denied_reason = L"Unknown";
723 std::string datastring((char*)&data[2], datasize-2);
724 std::istringstream is(datastring, std::ios_base::binary);
725 m_access_denied_reason = deSerializeWideString(is);
730 if(ser_version == SER_FMT_VER_INVALID)
732 dout_client<<DTIME<<"WARNING: Client: Server serialization"
733 " format invalid or not initialized."
734 " Skipping incoming command="<<command<<std::endl;
738 // Just here to avoid putting the two if's together when
739 // making some copypasta
742 if(command == TOCLIENT_REMOVENODE)
747 p.X = readS16(&data[2]);
748 p.Y = readS16(&data[4]);
749 p.Z = readS16(&data[6]);
751 //TimeTaker t1("TOCLIENT_REMOVENODE");
753 // This will clear the cracking animation after digging
754 ((ClientMap&)m_env.getMap()).clearTempMod(p);
758 else if(command == TOCLIENT_ADDNODE)
760 if(datasize < 8 + MapNode::serializedLength(ser_version))
764 p.X = readS16(&data[2]);
765 p.Y = readS16(&data[4]);
766 p.Z = readS16(&data[6]);
768 //TimeTaker t1("TOCLIENT_ADDNODE");
771 n.deSerialize(&data[8], ser_version);
775 else if(command == TOCLIENT_BLOCKDATA)
777 // Ignore too small packet
782 p.X = readS16(&data[2]);
783 p.Y = readS16(&data[4]);
784 p.Z = readS16(&data[6]);
786 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
787 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
788 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
789 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
791 std::string datastring((char*)&data[8], datasize-8);
792 std::istringstream istr(datastring, std::ios_base::binary);
798 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
801 sector = m_env.getMap().emergeSector(p2d);
803 v2s16 sp = sector->getPos();
806 dstream<<"ERROR: Got sector with getPos()="
807 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
808 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
812 //assert(sector->getPos() == p2d);
814 //TimeTaker timer("MapBlock deSerialize");
817 block = sector->getBlockNoCreateNoEx(p.Y);
821 Update an existing block
823 //dstream<<"Updating"<<std::endl;
824 block->deSerialize(istr, ser_version);
831 //dstream<<"Creating new"<<std::endl;
832 block = new MapBlock(&m_env.getMap(), p);
833 block->deSerialize(istr, ser_version);
834 sector->insertBlock(block);
838 mod.type = NODEMOD_CHANGECONTENT;
839 mod.param = CONTENT_MESE;
840 block->setTempMod(v3s16(8,10,8), mod);
841 block->setTempMod(v3s16(8,9,8), mod);
842 block->setTempMod(v3s16(8,8,8), mod);
843 block->setTempMod(v3s16(8,7,8), mod);
844 block->setTempMod(v3s16(8,6,8), mod);*/
859 u32 replysize = 2+1+6;
860 SharedBuffer<u8> reply(replysize);
861 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
863 writeV3S16(&reply[3], p);
865 m_con.Send(PEER_ID_SERVER, 1, reply, true);
869 Update Mesh of this block and blocks at x-, y- and z-.
870 Environment should not be locked as it interlocks with the
871 main thread, from which is will want to retrieve textures.
874 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
877 Add it to mesh update queue and set it to be acknowledged after update.
879 addUpdateMeshTaskWithEdge(p, true);
881 else if(command == TOCLIENT_PLAYERPOS)
883 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
887 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
888 our_peer_id = m_con.GetPeerID();
890 // Cancel if we don't have a peer id
891 if(our_peer_id == PEER_ID_INEXISTENT){
892 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
899 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
901 u32 player_size = 2+12+12+4+4;
903 u32 player_count = (datasize-2) / player_size;
905 for(u32 i=0; i<player_count; i++)
907 u16 peer_id = readU16(&data[start]);
909 Player *player = m_env.getPlayer(peer_id);
911 // Skip if player doesn't exist
914 start += player_size;
918 // Skip if player is local player
919 if(player->isLocal())
921 start += player_size;
925 v3s32 ps = readV3S32(&data[start+2]);
926 v3s32 ss = readV3S32(&data[start+2+12]);
927 s32 pitch_i = readS32(&data[start+2+12+12]);
928 s32 yaw_i = readS32(&data[start+2+12+12+4]);
929 /*dstream<<"Client: got "
930 <<"pitch_i="<<pitch_i
931 <<" yaw_i="<<yaw_i<<std::endl;*/
932 f32 pitch = (f32)pitch_i / 100.0;
933 f32 yaw = (f32)yaw_i / 100.0;
934 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
935 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
936 player->setPosition(position);
937 player->setSpeed(speed);
938 player->setPitch(pitch);
941 /*dstream<<"Client: player "<<peer_id
943 <<" yaw="<<yaw<<std::endl;*/
945 start += player_size;
949 else if(command == TOCLIENT_PLAYERINFO)
953 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
954 our_peer_id = m_con.GetPeerID();
956 // Cancel if we don't have a peer id
957 if(our_peer_id == PEER_ID_INEXISTENT){
958 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
964 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
967 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
969 u32 item_size = 2+PLAYERNAME_SIZE;
970 u32 player_count = (datasize-2) / item_size;
973 core::list<u16> players_alive;
974 for(u32 i=0; i<player_count; i++)
976 // Make sure the name ends in '\0'
977 data[start+2+20-1] = 0;
979 u16 peer_id = readU16(&data[start]);
981 players_alive.push_back(peer_id);
983 /*dstream<<DTIME<<"peer_id="<<peer_id
984 <<" name="<<((char*)&data[start+2])<<std::endl;*/
986 // Don't update the info of the local player
987 if(peer_id == our_peer_id)
993 Player *player = m_env.getPlayer(peer_id);
995 // Create a player if it doesn't exist
998 player = new RemotePlayer(
999 m_device->getSceneManager()->getRootSceneNode(),
1002 player->peer_id = peer_id;
1003 m_env.addPlayer(player);
1004 dout_client<<DTIME<<"Client: Adding new player "
1005 <<peer_id<<std::endl;
1008 player->updateName((char*)&data[start+2]);
1014 Remove those players from the environment that
1015 weren't listed by the server.
1017 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1018 core::list<Player*> players = m_env.getPlayers();
1019 core::list<Player*>::Iterator ip;
1020 for(ip=players.begin(); ip!=players.end(); ip++)
1022 // Ingore local player
1023 if((*ip)->isLocal())
1026 // Warn about a special case
1027 if((*ip)->peer_id == 0)
1029 dstream<<DTIME<<"WARNING: Client: Removing "
1030 "dead player with id=0"<<std::endl;
1033 bool is_alive = false;
1034 core::list<u16>::Iterator i;
1035 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1037 if((*ip)->peer_id == *i)
1043 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1044 <<" is_alive="<<is_alive<<std::endl;*/
1047 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1049 m_env.removePlayer((*ip)->peer_id);
1053 else if(command == TOCLIENT_SECTORMETA)
1055 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1060 [3...] v2s16 pos + sector metadata
1065 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1068 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1070 std::string datastring((char*)&data[2], datasize-2);
1071 std::istringstream is(datastring, std::ios_base::binary);
1075 is.read((char*)buf, 1);
1076 u16 sector_count = readU8(buf);
1078 //dstream<<"sector_count="<<sector_count<<std::endl;
1080 for(u16 i=0; i<sector_count; i++)
1083 is.read((char*)buf, 4);
1084 v2s16 pos = readV2S16(buf);
1085 /*dstream<<"Client: deserializing sector at "
1086 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1088 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1089 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1094 else if(command == TOCLIENT_INVENTORY)
1099 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1102 //TimeTaker t2("mutex locking", m_device);
1103 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1106 //TimeTaker t3("istringstream init", m_device);
1107 std::string datastring((char*)&data[2], datasize-2);
1108 std::istringstream is(datastring, std::ios_base::binary);
1111 //m_env.printPlayers(dstream);
1113 //TimeTaker t4("player get", m_device);
1114 Player *player = m_env.getLocalPlayer();
1115 assert(player != NULL);
1118 //TimeTaker t1("inventory.deSerialize()", m_device);
1119 player->inventory.deSerialize(is);
1122 m_inventory_updated = true;
1124 //dstream<<"Client got player inventory:"<<std::endl;
1125 //player->inventory.print(dstream);
1129 else if(command == TOCLIENT_OBJECTDATA)
1132 // Strip command word and create a stringstream
1133 std::string datastring((char*)&data[2], datasize-2);
1134 std::istringstream is(datastring, std::ios_base::binary);
1138 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1146 is.read((char*)buf, 2);
1147 u16 playercount = readU16(buf);
1149 for(u16 i=0; i<playercount; i++)
1151 is.read((char*)buf, 2);
1152 u16 peer_id = readU16(buf);
1153 is.read((char*)buf, 12);
1154 v3s32 p_i = readV3S32(buf);
1155 is.read((char*)buf, 12);
1156 v3s32 s_i = readV3S32(buf);
1157 is.read((char*)buf, 4);
1158 s32 pitch_i = readS32(buf);
1159 is.read((char*)buf, 4);
1160 s32 yaw_i = readS32(buf);
1162 Player *player = m_env.getPlayer(peer_id);
1164 // Skip if player doesn't exist
1170 // Skip if player is local player
1171 if(player->isLocal())
1176 f32 pitch = (f32)pitch_i / 100.0;
1177 f32 yaw = (f32)yaw_i / 100.0;
1178 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1179 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1181 player->setPosition(position);
1182 player->setSpeed(speed);
1183 player->setPitch(pitch);
1184 player->setYaw(yaw);
1191 // Read active block count
1192 is.read((char*)buf, 2);
1193 u16 blockcount = readU16(buf);
1195 // Initialize delete queue with all active blocks
1196 core::map<v3s16, bool> abs_to_delete;
1197 for(core::map<v3s16, bool>::Iterator
1198 i = m_active_blocks.getIterator();
1199 i.atEnd() == false; i++)
1201 v3s16 p = i.getNode()->getKey();
1202 /*dstream<<"adding "
1203 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1204 <<" to abs_to_delete"
1206 abs_to_delete.insert(p, true);
1209 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1212 for(u16 i=0; i<blockcount; i++)
1215 is.read((char*)buf, 6);
1216 v3s16 p = readV3S16(buf);
1217 // Get block from somewhere
1218 MapBlock *block = NULL;
1220 block = m_env.getMap().getBlockNoCreate(p);
1222 catch(InvalidPositionException &e)
1224 //TODO: Create a dummy block?
1228 dstream<<"WARNING: "
1229 <<"Could not get block at blockpos "
1230 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1231 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1232 <<"following block object data."
1237 /*dstream<<"Client updating objects for block "
1238 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1241 // Insert to active block list
1242 m_active_blocks.insert(p, true);
1244 // Remove from deletion queue
1245 if(abs_to_delete.find(p) != NULL)
1246 abs_to_delete.remove(p);
1249 Update objects of block
1251 NOTE: Be sure this is done in the main thread.
1253 block->updateObjects(is, m_server_ser_ver,
1254 m_device->getSceneManager(), m_env.getDayNightRatio());
1257 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1260 // Delete objects of blocks in delete queue
1261 for(core::map<v3s16, bool>::Iterator
1262 i = abs_to_delete.getIterator();
1263 i.atEnd() == false; i++)
1265 v3s16 p = i.getNode()->getKey();
1268 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1271 block->clearObjects();
1272 // Remove from active blocks list
1273 m_active_blocks.remove(p);
1275 catch(InvalidPositionException &e)
1277 dstream<<"WARNAING: Client: "
1278 <<"Couldn't clear objects of active->inactive"
1280 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1281 <<" because block was not found"
1289 else if(command == TOCLIENT_TIME_OF_DAY)
1294 u16 time_of_day = readU16(&data[2]);
1295 time_of_day = time_of_day % 24000;
1296 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1304 m_env.setTimeOfDay(time_of_day);
1306 u32 dr = m_env.getDayNightRatio();
1308 dstream<<"Client: time_of_day="<<time_of_day
1314 else if(command == TOCLIENT_CHAT_MESSAGE)
1322 std::string datastring((char*)&data[2], datasize-2);
1323 std::istringstream is(datastring, std::ios_base::binary);
1326 is.read((char*)buf, 2);
1327 u16 len = readU16(buf);
1329 std::wstring message;
1330 for(u16 i=0; i<len; i++)
1332 is.read((char*)buf, 2);
1333 message += (wchar_t)readU16(buf);
1336 /*dstream<<"Client received chat message: "
1337 <<wide_to_narrow(message)<<std::endl;*/
1339 m_chat_queue.push_back(message);
1341 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1343 //if(g_settings.getBool("enable_experimental"))
1347 u16 count of removed objects
1348 for all removed objects {
1351 u16 count of added objects
1352 for all added objects {
1355 u16 initialization data length
1356 string initialization data
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);
1368 // Read removed objects
1370 u16 removed_count = readU16((u8*)buf);
1371 for(u16 i=0; i<removed_count; i++)
1374 u16 id = readU16((u8*)buf);
1377 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1378 m_env.removeActiveObject(id);
1382 // Read added objects
1384 u16 added_count = readU16((u8*)buf);
1385 for(u16 i=0; i<added_count; i++)
1388 u16 id = readU16((u8*)buf);
1390 u8 type = readU8((u8*)buf);
1391 std::string data = deSerializeLongString(is);
1394 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1395 m_env.addActiveObject(id, type, data);
1400 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1402 //if(g_settings.getBool("enable_experimental"))
1414 // Get all data except the command number
1415 std::string datastring((char*)&data[2], datasize-2);
1416 // Throw them in an istringstream
1417 std::istringstream is(datastring, std::ios_base::binary);
1419 while(is.eof() == false)
1423 u16 id = readU16((u8*)buf);
1427 u16 message_size = readU16((u8*)buf);
1428 std::string message;
1429 message.reserve(message_size);
1430 for(u16 i=0; i<message_size; i++)
1433 message.append(buf, 1);
1435 // Pass on to the environment
1437 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1438 m_env.processActiveObjectMessage(id, message);
1443 else if(command == TOCLIENT_HP)
1445 std::string datastring((char*)&data[2], datasize-2);
1446 std::istringstream is(datastring, std::ios_base::binary);
1447 Player *player = m_env.getLocalPlayer();
1448 assert(player != NULL);
1452 else if(command == TOCLIENT_MOVE_PLAYER)
1454 std::string datastring((char*)&data[2], datasize-2);
1455 std::istringstream is(datastring, std::ios_base::binary);
1456 Player *player = m_env.getLocalPlayer();
1457 assert(player != NULL);
1458 v3f pos = readV3F1000(is);
1459 f32 pitch = readF1000(is);
1460 f32 yaw = readF1000(is);
1461 player->setPosition(pos);
1462 /*player->setPitch(pitch);
1463 player->setYaw(yaw);*/
1465 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1466 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1472 Add to ClientEvent queue.
1473 This has to be sent to the main program because otherwise
1474 it would just force the pitch and yaw values to whatever
1475 the camera points to.
1478 event.type = CE_PLAYER_FORCE_MOVE;
1479 event.player_force_move.pitch = pitch;
1480 event.player_force_move.yaw = yaw;
1481 m_client_event_queue.push_back(event);
1483 // Ignore damage for a few seconds, so that the player doesn't
1484 // get damage from falling on ground
1485 m_ignore_damage_timer = 3.0;
1489 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1490 <<command<<std::endl;
1494 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1496 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1497 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1500 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1501 v3s16 nodepos_oversurface, u16 item)
1503 if(connectedAndInitialized() == false){
1504 dout_client<<DTIME<<"Client::groundAction() "
1505 "cancelled (not connected)"
1514 [3] v3s16 nodepos_undersurface
1515 [9] v3s16 nodepos_abovesurface
1520 2: stop digging (all parameters ignored)
1521 3: digging completed
1523 u8 datasize = 2 + 1 + 6 + 6 + 2;
1524 SharedBuffer<u8> data(datasize);
1525 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1526 writeU8(&data[2], action);
1527 writeV3S16(&data[3], nodepos_undersurface);
1528 writeV3S16(&data[9], nodepos_oversurface);
1529 writeU16(&data[15], item);
1530 Send(0, data, true);
1533 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1535 if(connectedAndInitialized() == false){
1536 dout_client<<DTIME<<"Client::clickObject() "
1537 "cancelled (not connected)"
1543 [0] u16 command=TOSERVER_CLICK_OBJECT
1544 [2] u8 button (0=left, 1=right)
1549 u8 datasize = 2 + 1 + 6 + 2 + 2;
1550 SharedBuffer<u8> data(datasize);
1551 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1552 writeU8(&data[2], button);
1553 writeV3S16(&data[3], blockpos);
1554 writeS16(&data[9], id);
1555 writeU16(&data[11], item);
1556 Send(0, data, true);
1559 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1561 if(connectedAndInitialized() == false){
1562 dout_client<<DTIME<<"Client::clickActiveObject() "
1563 "cancelled (not connected)"
1571 [2] u8 button (0=left, 1=right)
1575 u8 datasize = 2 + 1 + 6 + 2 + 2;
1576 SharedBuffer<u8> data(datasize);
1577 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1578 writeU8(&data[2], button);
1579 writeU16(&data[3], id);
1580 writeU16(&data[5], item);
1581 Send(0, data, true);
1584 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1593 std::ostringstream os(std::ios_base::binary);
1597 writeU16(buf, TOSERVER_SIGNTEXT);
1598 os.write((char*)buf, 2);
1601 writeV3S16(buf, blockpos);
1602 os.write((char*)buf, 6);
1606 os.write((char*)buf, 2);
1608 u16 textlen = text.size();
1609 // Write text length
1610 writeS16(buf, textlen);
1611 os.write((char*)buf, 2);
1614 os.write((char*)text.c_str(), textlen);
1617 std::string s = os.str();
1618 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1620 Send(0, data, true);
1623 void Client::sendSignNodeText(v3s16 p, std::string text)
1631 std::ostringstream os(std::ios_base::binary);
1635 writeU16(buf, TOSERVER_SIGNNODETEXT);
1636 os.write((char*)buf, 2);
1640 os.write((char*)buf, 6);
1642 u16 textlen = text.size();
1643 // Write text length
1644 writeS16(buf, textlen);
1645 os.write((char*)buf, 2);
1648 os.write((char*)text.c_str(), textlen);
1651 std::string s = os.str();
1652 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1654 Send(0, data, true);
1657 void Client::sendInventoryAction(InventoryAction *a)
1659 std::ostringstream os(std::ios_base::binary);
1663 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1664 os.write((char*)buf, 2);
1669 std::string s = os.str();
1670 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1672 Send(0, data, true);
1675 void Client::sendChatMessage(const std::wstring &message)
1677 std::ostringstream os(std::ios_base::binary);
1681 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1682 os.write((char*)buf, 2);
1685 writeU16(buf, message.size());
1686 os.write((char*)buf, 2);
1689 for(u32 i=0; i<message.size(); i++)
1693 os.write((char*)buf, 2);
1697 std::string s = os.str();
1698 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1700 Send(0, data, true);
1703 void Client::sendChangePassword(const std::wstring oldpassword,
1704 const std::wstring newpassword)
1706 Player *player = m_env.getLocalPlayer();
1710 std::string playername = player->getName();
1711 std::string oldpwd = translatePassword(playername, oldpassword);
1712 std::string newpwd = translatePassword(playername, newpassword);
1714 std::ostringstream os(std::ios_base::binary);
1715 u8 buf[2+PASSWORD_SIZE*2];
1717 [0] u16 TOSERVER_PASSWORD
1718 [2] u8[28] old password
1719 [30] u8[28] new password
1722 writeU16(buf, TOSERVER_PASSWORD);
1723 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1725 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1726 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1728 buf[2+PASSWORD_SIZE-1] = 0;
1729 buf[30+PASSWORD_SIZE-1] = 0;
1730 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1733 std::string s = os.str();
1734 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1736 Send(0, data, true);
1740 void Client::sendDamage(u8 damage)
1742 DSTACK(__FUNCTION_NAME);
1743 std::ostringstream os(std::ios_base::binary);
1745 writeU16(os, TOSERVER_DAMAGE);
1746 writeU8(os, damage);
1749 std::string s = os.str();
1750 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1752 Send(0, data, true);
1755 void Client::sendPlayerPos()
1757 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1759 Player *myplayer = m_env.getLocalPlayer();
1760 if(myplayer == NULL)
1765 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1766 our_peer_id = m_con.GetPeerID();
1769 // Set peer id if not set already
1770 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1771 myplayer->peer_id = our_peer_id;
1772 // Check that an existing peer_id is the same as the connection's
1773 assert(myplayer->peer_id == our_peer_id);
1775 v3f pf = myplayer->getPosition();
1776 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1777 v3f sf = myplayer->getSpeed();
1778 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1779 s32 pitch = myplayer->getPitch() * 100;
1780 s32 yaw = myplayer->getYaw() * 100;
1785 [2] v3s32 position*100
1786 [2+12] v3s32 speed*100
1787 [2+12+12] s32 pitch*100
1788 [2+12+12+4] s32 yaw*100
1791 SharedBuffer<u8> data(2+12+12+4+4);
1792 writeU16(&data[0], TOSERVER_PLAYERPOS);
1793 writeV3S32(&data[2], position);
1794 writeV3S32(&data[2+12], speed);
1795 writeS32(&data[2+12+12], pitch);
1796 writeS32(&data[2+12+12+4], yaw);
1798 // Send as unreliable
1799 Send(0, data, false);
1802 void Client::removeNode(v3s16 p)
1804 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1806 core::map<v3s16, MapBlock*> modified_blocks;
1810 //TimeTaker t("removeNodeAndUpdate", m_device);
1811 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1813 catch(InvalidPositionException &e)
1817 for(core::map<v3s16, MapBlock * >::Iterator
1818 i = modified_blocks.getIterator();
1819 i.atEnd() == false; i++)
1821 v3s16 p = i.getNode()->getKey();
1822 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1823 addUpdateMeshTaskWithEdge(p);
1827 void Client::addNode(v3s16 p, MapNode n)
1829 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1831 TimeTaker timer1("Client::addNode()");
1833 core::map<v3s16, MapBlock*> modified_blocks;
1837 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1838 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1840 catch(InvalidPositionException &e)
1843 //TimeTaker timer2("Client::addNode(): updateMeshes");
1845 for(core::map<v3s16, MapBlock * >::Iterator
1846 i = modified_blocks.getIterator();
1847 i.atEnd() == false; i++)
1849 v3s16 p = i.getNode()->getKey();
1850 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1851 addUpdateMeshTaskWithEdge(p);
1855 void Client::updateCamera(v3f pos, v3f dir)
1857 m_env.getClientMap().updateCamera(pos, dir);
1858 camera_position = pos;
1859 camera_direction = dir;
1862 MapNode Client::getNode(v3s16 p)
1864 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1865 return m_env.getMap().getNode(p);
1868 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1870 return m_env.getMap().getNodeMetadata(p);
1873 v3f Client::getPlayerPosition()
1875 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1876 LocalPlayer *player = m_env.getLocalPlayer();
1877 assert(player != NULL);
1878 return player->getPosition();
1881 void Client::setPlayerControl(PlayerControl &control)
1883 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1884 LocalPlayer *player = m_env.getLocalPlayer();
1885 assert(player != NULL);
1886 player->control = control;
1889 // Returns true if the inventory of the local player has been
1890 // updated from the server. If it is true, it is set to false.
1891 bool Client::getLocalInventoryUpdated()
1893 // m_inventory_updated is behind envlock
1894 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1895 bool updated = m_inventory_updated;
1896 m_inventory_updated = false;
1900 // Copies the inventory of the local player to parameter
1901 void Client::getLocalInventory(Inventory &dst)
1903 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1904 Player *player = m_env.getLocalPlayer();
1905 assert(player != NULL);
1906 dst = player->inventory;
1909 InventoryContext *Client::getInventoryContext()
1911 return &m_inventory_context;
1914 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1916 if(id == "current_player")
1918 assert(c->current_player);
1919 return &(c->current_player->inventory);
1923 std::string id0 = fn.next(":");
1925 if(id0 == "nodemeta")
1928 p.X = stoi(fn.next(","));
1929 p.Y = stoi(fn.next(","));
1930 p.Z = stoi(fn.next(","));
1931 NodeMetadata* meta = getNodeMetadata(p);
1933 return meta->getInventory();
1934 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1935 <<"no metadata found"<<std::endl;
1939 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1942 void Client::inventoryAction(InventoryAction *a)
1944 sendInventoryAction(a);
1947 MapBlockObject * Client::getSelectedObject(
1949 v3f from_pos_f_on_map,
1950 core::line3d<f32> shootline_on_map
1953 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1955 core::array<DistanceSortedObject> objects;
1957 for(core::map<v3s16, bool>::Iterator
1958 i = m_active_blocks.getIterator();
1959 i.atEnd() == false; i++)
1961 v3s16 p = i.getNode()->getKey();
1963 MapBlock *block = NULL;
1966 block = m_env.getMap().getBlockNoCreate(p);
1968 catch(InvalidPositionException &e)
1973 // Calculate from_pos relative to block
1974 v3s16 block_pos_i_on_map = block->getPosRelative();
1975 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1976 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1978 block->getObjects(from_pos_f_on_block, max_d, objects);
1979 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1982 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1985 // After this, the closest object is the first in the array.
1988 for(u32 i=0; i<objects.size(); i++)
1990 MapBlockObject *obj = objects[i].obj;
1991 MapBlock *block = obj->getBlock();
1993 // Calculate shootline relative to block
1994 v3s16 block_pos_i_on_map = block->getPosRelative();
1995 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1996 core::line3d<f32> shootline_on_block(
1997 shootline_on_map.start - block_pos_f_on_map,
1998 shootline_on_map.end - block_pos_f_on_map
2001 if(obj->isSelected(shootline_on_block))
2003 //dstream<<"Returning selected object"<<std::endl;
2008 //dstream<<"No object selected; returning NULL."<<std::endl;
2012 ClientActiveObject * Client::getSelectedActiveObject(
2014 v3f from_pos_f_on_map,
2015 core::line3d<f32> shootline_on_map
2018 core::array<DistanceSortedActiveObject> objects;
2020 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2022 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2025 // After this, the closest object is the first in the array.
2028 for(u32 i=0; i<objects.size(); i++)
2030 ClientActiveObject *obj = objects[i].obj;
2032 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2033 if(selection_box == NULL)
2036 v3f pos = obj->getPosition();
2038 core::aabbox3d<f32> offsetted_box(
2039 selection_box->MinEdge + pos,
2040 selection_box->MaxEdge + pos
2043 if(offsetted_box.intersectsWithLine(shootline_on_map))
2045 //dstream<<"Returning selected object"<<std::endl;
2050 //dstream<<"No object selected; returning NULL."<<std::endl;
2054 void Client::printDebugInfo(std::ostream &os)
2056 //JMutexAutoLock lock1(m_fetchblock_mutex);
2057 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2059 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2060 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2061 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2065 u32 Client::getDayNightRatio()
2067 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2068 return m_env.getDayNightRatio();
2073 Player *player = m_env.getLocalPlayer();
2074 assert(player != NULL);
2078 void Client::setTempMod(v3s16 p, NodeMod mod)
2080 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2081 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2083 core::map<v3s16, MapBlock*> affected_blocks;
2084 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2087 for(core::map<v3s16, MapBlock*>::Iterator
2088 i = affected_blocks.getIterator();
2089 i.atEnd() == false; i++)
2091 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2095 void Client::clearTempMod(v3s16 p)
2097 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2098 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2100 core::map<v3s16, MapBlock*> affected_blocks;
2101 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2104 for(core::map<v3s16, MapBlock*>::Iterator
2105 i = affected_blocks.getIterator();
2106 i.atEnd() == false; i++)
2108 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2112 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2114 /*dstream<<"Client::addUpdateMeshTask(): "
2115 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2118 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2123 Create a task to update the mesh of the block
2126 MeshMakeData *data = new MeshMakeData;
2129 //TimeTaker timer("data fill");
2131 data->fill(getDayNightRatio(), b);
2135 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2137 // Add task to queue
2138 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2140 /*dstream<<"Mesh update input queue size is "
2141 <<m_mesh_update_thread.m_queue_in.size()
2145 // Temporary test: make mesh directly in here
2147 //TimeTaker timer("make mesh");
2149 scene::SMesh *mesh_new = NULL;
2150 mesh_new = makeMapBlockMesh(data);
2151 b->replaceMesh(mesh_new);
2156 b->setMeshExpired(false);
2159 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2163 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2164 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2169 v3s16 p = blockpos + v3s16(0,0,0);
2170 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2171 addUpdateMeshTask(p, ack_to_server);
2173 catch(InvalidPositionException &e){}
2176 v3s16 p = blockpos + v3s16(-1,0,0);
2177 addUpdateMeshTask(p);
2179 catch(InvalidPositionException &e){}
2181 v3s16 p = blockpos + v3s16(0,-1,0);
2182 addUpdateMeshTask(p);
2184 catch(InvalidPositionException &e){}
2186 v3s16 p = blockpos + v3s16(0,0,-1);
2187 addUpdateMeshTask(p);
2189 catch(InvalidPositionException &e){}
2192 ClientEvent Client::getClientEvent()
2194 if(m_client_event_queue.size() == 0)
2197 event.type = CE_NONE;
2200 return m_client_event_queue.pop_front();