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 /*// Wait for output queue to flush.
139 // Allow 2 in queue, this makes less frametime jitter.
140 // Umm actually, there is no much difference
141 if(m_queue_out.size() >= 2)
147 QueuedMeshUpdate *q = m_queue_in.pop();
154 ScopeProfiler sp(&g_profiler, "mesh make");
156 scene::SMesh *mesh_new = NULL;
157 mesh_new = makeMapBlockMesh(q->data);
162 r.ack_block_to_server = q->ack_block_to_server;
164 /*dstream<<"MeshUpdateThread: Processed "
165 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
168 m_queue_out.push_back(r);
173 END_DEBUG_EXCEPTION_HANDLER
179 IrrlichtDevice *device,
180 const char *playername,
181 std::string password,
182 MapDrawControl &control):
183 m_mesh_update_thread(),
185 new ClientMap(this, control,
186 device->getSceneManager()->getRootSceneNode(),
187 device->getSceneManager(), 666),
188 device->getSceneManager()
190 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
192 camera_position(0,0,0),
193 camera_direction(0,0,1),
194 m_server_ser_ver(SER_FMT_VER_INVALID),
195 m_inventory_updated(false),
198 m_password(password),
199 m_access_denied(false)
201 m_packetcounter_timer = 0.0;
202 m_delete_unused_sectors_timer = 0.0;
203 m_connection_reinit_timer = 0.0;
204 m_avg_rtt_timer = 0.0;
205 m_playerpos_send_timer = 0.0;
206 m_ignore_damage_timer = 0.0;
208 //m_env_mutex.Init();
209 //m_con_mutex.Init();
211 m_mesh_update_thread.Start();
217 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
219 Player *player = new LocalPlayer();
221 player->updateName(playername);
223 m_env.addPlayer(player);
225 // Initialize player in the inventory context
226 m_inventory_context.current_player = player;
233 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
237 m_mesh_update_thread.setRun(false);
238 while(m_mesh_update_thread.IsRunning())
242 void Client::connect(Address address)
244 DSTACK(__FUNCTION_NAME);
245 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
246 m_con.setTimeoutMs(0);
247 m_con.Connect(address);
250 bool Client::connectedAndInitialized()
252 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
254 if(m_con.Connected() == false)
257 if(m_server_ser_ver == SER_FMT_VER_INVALID)
263 void Client::step(float dtime)
265 DSTACK(__FUNCTION_NAME);
271 if(m_ignore_damage_timer > dtime)
272 m_ignore_damage_timer -= dtime;
274 m_ignore_damage_timer = 0.0;
276 //dstream<<"Client steps "<<dtime<<std::endl;
279 //TimeTaker timer("ReceiveAll()", m_device);
285 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
287 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
288 m_con.RunTimeouts(dtime);
295 float &counter = m_packetcounter_timer;
301 dout_client<<"Client packetcounter (20s):"<<std::endl;
302 m_packetcounter.print(dout_client);
303 m_packetcounter.clear();
309 Delete unused sectors
311 NOTE: This jams the game for a while because deleting sectors
315 float &counter = m_delete_unused_sectors_timer;
323 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
325 core::list<v3s16> deleted_blocks;
327 float delete_unused_sectors_timeout =
328 g_settings.getFloat("client_delete_unused_sectors_timeout");
330 // Delete sector blocks
331 /*u32 num = m_env.getMap().unloadUnusedData
332 (delete_unused_sectors_timeout,
333 true, &deleted_blocks);*/
335 // Delete whole sectors
336 u32 num = m_env.getMap().unloadUnusedData
337 (delete_unused_sectors_timeout,
338 false, &deleted_blocks);
342 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
343 <<" unused sectors"<<std::endl;*/
344 dstream<<DTIME<<"Client: Deleted "<<num
345 <<" unused sectors"<<std::endl;
351 // Env is locked so con can be locked.
352 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
354 core::list<v3s16>::Iterator i = deleted_blocks.begin();
355 core::list<v3s16> sendlist;
358 if(sendlist.size() == 255 || i == deleted_blocks.end())
360 if(sendlist.size() == 0)
369 u32 replysize = 2+1+6*sendlist.size();
370 SharedBuffer<u8> reply(replysize);
371 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
372 reply[2] = sendlist.size();
374 for(core::list<v3s16>::Iterator
375 j = sendlist.begin();
376 j != sendlist.end(); j++)
378 writeV3S16(&reply[2+1+6*k], *j);
381 m_con.Send(PEER_ID_SERVER, 1, reply, true);
383 if(i == deleted_blocks.end())
389 sendlist.push_back(*i);
396 bool connected = connectedAndInitialized();
398 if(connected == false)
400 float &counter = m_connection_reinit_timer;
406 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
408 Player *myplayer = m_env.getLocalPlayer();
409 assert(myplayer != NULL);
411 // Send TOSERVER_INIT
412 // [0] u16 TOSERVER_INIT
413 // [2] u8 SER_FMT_VER_HIGHEST
414 // [3] u8[20] player_name
415 // [23] u8[28] password
416 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
417 writeU16(&data[0], TOSERVER_INIT);
418 writeU8(&data[2], SER_FMT_VER_HIGHEST);
420 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
421 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
423 /*dstream<<"Client: password hash is \""<<m_password<<"\""
426 memset((char*)&data[23], 0, PASSWORD_SIZE);
427 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
429 // Send as unreliable
430 Send(0, data, false);
433 // Not connected, return
438 Do stuff if connected
446 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
448 // Control local player (0ms)
449 LocalPlayer *player = m_env.getLocalPlayer();
450 assert(player != NULL);
451 player->applyControl(dtime);
453 //TimeTaker envtimer("env step", m_device);
457 // Step active blocks
458 for(core::map<v3s16, bool>::Iterator
459 i = m_active_blocks.getIterator();
460 i.atEnd() == false; i++)
462 v3s16 p = i.getNode()->getKey();
464 MapBlock *block = NULL;
467 block = m_env.getMap().getBlockNoCreate(p);
468 block->stepObjects(dtime, false, m_env.getDayNightRatio());
470 catch(InvalidPositionException &e)
480 ClientEnvEvent event = m_env.getClientEvent();
481 if(event.type == CEE_NONE)
485 else if(event.type == CEE_PLAYER_DAMAGE)
487 if(m_ignore_damage_timer <= 0)
489 u8 damage = event.player_damage.amount;
492 // Add to ClientEvent queue
494 event.type = CE_PLAYER_DAMAGE;
495 event.player_damage.amount = damage;
496 m_client_event_queue.push_back(event);
506 float &counter = m_avg_rtt_timer;
511 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
512 // connectedAndInitialized() is true, peer exists.
513 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
514 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
519 Send player position to server
522 float &counter = m_playerpos_send_timer;
532 Replace updated meshes
535 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
537 //TimeTaker timer("** Processing mesh update result queue");
540 /*dstream<<"Mesh update result queue size is "
541 <<m_mesh_update_thread.m_queue_out.size()
544 while(m_mesh_update_thread.m_queue_out.size() > 0)
546 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
547 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
550 block->replaceMesh(r.mesh);
552 if(r.ack_block_to_server)
554 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
555 <<","<<r.p.Z<<")"<<std::endl;*/
566 u32 replysize = 2+1+6;
567 SharedBuffer<u8> reply(replysize);
568 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
570 writeV3S16(&reply[3], r.p);
572 m_con.Send(PEER_ID_SERVER, 1, reply, true);
578 // Virtual methods from con::PeerHandler
579 void Client::peerAdded(con::Peer *peer)
581 derr_client<<"Client::peerAdded(): peer->id="
582 <<peer->id<<std::endl;
584 void Client::deletingPeer(con::Peer *peer, bool timeout)
586 derr_client<<"Client::deletingPeer(): "
587 "Server Peer is getting deleted "
588 <<"(timeout="<<timeout<<")"<<std::endl;
591 void Client::ReceiveAll()
593 DSTACK(__FUNCTION_NAME);
599 catch(con::NoIncomingDataException &e)
603 catch(con::InvalidIncomingDataException &e)
605 dout_client<<DTIME<<"Client::ReceiveAll(): "
606 "InvalidIncomingDataException: what()="
607 <<e.what()<<std::endl;
612 void Client::Receive()
614 DSTACK(__FUNCTION_NAME);
615 u32 data_maxsize = 200000;
616 Buffer<u8> data(data_maxsize);
620 //TimeTaker t1("con mutex and receive", m_device);
621 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
622 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
624 //TimeTaker t1("ProcessData", m_device);
625 ProcessData(*data, datasize, sender_peer_id);
629 sender_peer_id given to this shall be quaranteed to be a valid peer
631 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
633 DSTACK(__FUNCTION_NAME);
635 // Ignore packets that don't even fit a command
638 m_packetcounter.add(60000);
642 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
644 //dstream<<"Client: received command="<<command<<std::endl;
645 m_packetcounter.add((u16)command);
648 If this check is removed, be sure to change the queue
649 system to know the ids
651 if(sender_peer_id != PEER_ID_SERVER)
653 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
654 "coming from server: peer_id="<<sender_peer_id
661 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
662 // All data is coming from the server
663 // PeerNotFoundException is handled by caller.
664 peer = m_con.GetPeer(PEER_ID_SERVER);
667 u8 ser_version = m_server_ser_ver;
669 //dstream<<"Client received command="<<(int)command<<std::endl;
671 if(command == TOCLIENT_INIT)
676 u8 deployed = data[2];
678 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
679 "deployed="<<((int)deployed&0xff)<<std::endl;
681 if(deployed < SER_FMT_VER_LOWEST
682 || deployed > SER_FMT_VER_HIGHEST)
684 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
685 <<"unsupported ser_fmt_ver"<<std::endl;
689 m_server_ser_ver = deployed;
691 // Get player position
692 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
693 if(datasize >= 2+1+6)
694 playerpos_s16 = readV3S16(&data[2+1]);
695 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
698 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
700 // Set player position
701 Player *player = m_env.getLocalPlayer();
702 assert(player != NULL);
703 player->setPosition(playerpos_f);
706 if(datasize >= 2+1+6+8)
709 m_map_seed = readU64(&data[2+1+6]);
710 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
715 SharedBuffer<u8> reply(replysize);
716 writeU16(&reply[0], TOSERVER_INIT2);
718 m_con.Send(PEER_ID_SERVER, 1, reply, true);
723 if(command == TOCLIENT_ACCESS_DENIED)
725 // The server didn't like our password. Note, this needs
726 // to be processed even if the serialisation format has
727 // not been agreed yet, the same as TOCLIENT_INIT.
728 m_access_denied = true;
729 m_access_denied_reason = L"Unknown";
732 std::string datastring((char*)&data[2], datasize-2);
733 std::istringstream is(datastring, std::ios_base::binary);
734 m_access_denied_reason = deSerializeWideString(is);
739 if(ser_version == SER_FMT_VER_INVALID)
741 dout_client<<DTIME<<"WARNING: Client: Server serialization"
742 " format invalid or not initialized."
743 " Skipping incoming command="<<command<<std::endl;
747 // Just here to avoid putting the two if's together when
748 // making some copypasta
751 if(command == TOCLIENT_REMOVENODE)
756 p.X = readS16(&data[2]);
757 p.Y = readS16(&data[4]);
758 p.Z = readS16(&data[6]);
760 //TimeTaker t1("TOCLIENT_REMOVENODE");
762 // This will clear the cracking animation after digging
763 ((ClientMap&)m_env.getMap()).clearTempMod(p);
767 else if(command == TOCLIENT_ADDNODE)
769 if(datasize < 8 + MapNode::serializedLength(ser_version))
773 p.X = readS16(&data[2]);
774 p.Y = readS16(&data[4]);
775 p.Z = readS16(&data[6]);
777 //TimeTaker t1("TOCLIENT_ADDNODE");
780 n.deSerialize(&data[8], ser_version);
784 else if(command == TOCLIENT_BLOCKDATA)
786 // Ignore too small packet
791 p.X = readS16(&data[2]);
792 p.Y = readS16(&data[4]);
793 p.Z = readS16(&data[6]);
795 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
796 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
797 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
798 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
800 std::string datastring((char*)&data[8], datasize-8);
801 std::istringstream istr(datastring, std::ios_base::binary);
807 sector = m_env.getMap().emergeSector(p2d);
809 assert(sector->getPos() == p2d);
811 //TimeTaker timer("MapBlock deSerialize");
814 block = sector->getBlockNoCreateNoEx(p.Y);
818 Update an existing block
820 //dstream<<"Updating"<<std::endl;
821 block->deSerialize(istr, ser_version);
828 //dstream<<"Creating new"<<std::endl;
829 block = new MapBlock(&m_env.getMap(), p);
830 block->deSerialize(istr, ser_version);
831 sector->insertBlock(block);
835 mod.type = NODEMOD_CHANGECONTENT;
836 mod.param = CONTENT_MESE;
837 block->setTempMod(v3s16(8,10,8), mod);
838 block->setTempMod(v3s16(8,9,8), mod);
839 block->setTempMod(v3s16(8,8,8), mod);
840 block->setTempMod(v3s16(8,7,8), mod);
841 block->setTempMod(v3s16(8,6,8), mod);*/
855 u32 replysize = 2+1+6;
856 SharedBuffer<u8> reply(replysize);
857 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
859 writeV3S16(&reply[3], p);
861 m_con.Send(PEER_ID_SERVER, 1, reply, true);
865 Update Mesh of this block and blocks at x-, y- and z-.
866 Environment should not be locked as it interlocks with the
867 main thread, from which is will want to retrieve textures.
870 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
873 Add it to mesh update queue and set it to be acknowledged after update.
875 //std::cerr<<"Adding mesh update task for received block"<<std::endl;
876 addUpdateMeshTaskWithEdge(p, true);
878 else if(command == TOCLIENT_PLAYERPOS)
880 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
884 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
885 our_peer_id = m_con.GetPeerID();
887 // Cancel if we don't have a peer id
888 if(our_peer_id == PEER_ID_INEXISTENT){
889 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
896 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
898 u32 player_size = 2+12+12+4+4;
900 u32 player_count = (datasize-2) / player_size;
902 for(u32 i=0; i<player_count; i++)
904 u16 peer_id = readU16(&data[start]);
906 Player *player = m_env.getPlayer(peer_id);
908 // Skip if player doesn't exist
911 start += player_size;
915 // Skip if player is local player
916 if(player->isLocal())
918 start += player_size;
922 v3s32 ps = readV3S32(&data[start+2]);
923 v3s32 ss = readV3S32(&data[start+2+12]);
924 s32 pitch_i = readS32(&data[start+2+12+12]);
925 s32 yaw_i = readS32(&data[start+2+12+12+4]);
926 /*dstream<<"Client: got "
927 <<"pitch_i="<<pitch_i
928 <<" yaw_i="<<yaw_i<<std::endl;*/
929 f32 pitch = (f32)pitch_i / 100.0;
930 f32 yaw = (f32)yaw_i / 100.0;
931 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
932 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
933 player->setPosition(position);
934 player->setSpeed(speed);
935 player->setPitch(pitch);
938 /*dstream<<"Client: player "<<peer_id
940 <<" yaw="<<yaw<<std::endl;*/
942 start += player_size;
946 else if(command == TOCLIENT_PLAYERINFO)
950 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
951 our_peer_id = m_con.GetPeerID();
953 // Cancel if we don't have a peer id
954 if(our_peer_id == PEER_ID_INEXISTENT){
955 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
961 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
964 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
966 u32 item_size = 2+PLAYERNAME_SIZE;
967 u32 player_count = (datasize-2) / item_size;
970 core::list<u16> players_alive;
971 for(u32 i=0; i<player_count; i++)
973 // Make sure the name ends in '\0'
974 data[start+2+20-1] = 0;
976 u16 peer_id = readU16(&data[start]);
978 players_alive.push_back(peer_id);
980 /*dstream<<DTIME<<"peer_id="<<peer_id
981 <<" name="<<((char*)&data[start+2])<<std::endl;*/
983 // Don't update the info of the local player
984 if(peer_id == our_peer_id)
990 Player *player = m_env.getPlayer(peer_id);
992 // Create a player if it doesn't exist
995 player = new RemotePlayer(
996 m_device->getSceneManager()->getRootSceneNode(),
999 player->peer_id = peer_id;
1000 m_env.addPlayer(player);
1001 dout_client<<DTIME<<"Client: Adding new player "
1002 <<peer_id<<std::endl;
1005 player->updateName((char*)&data[start+2]);
1011 Remove those players from the environment that
1012 weren't listed by the server.
1014 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1015 core::list<Player*> players = m_env.getPlayers();
1016 core::list<Player*>::Iterator ip;
1017 for(ip=players.begin(); ip!=players.end(); ip++)
1019 // Ingore local player
1020 if((*ip)->isLocal())
1023 // Warn about a special case
1024 if((*ip)->peer_id == 0)
1026 dstream<<DTIME<<"WARNING: Client: Removing "
1027 "dead player with id=0"<<std::endl;
1030 bool is_alive = false;
1031 core::list<u16>::Iterator i;
1032 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1034 if((*ip)->peer_id == *i)
1040 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1041 <<" is_alive="<<is_alive<<std::endl;*/
1044 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1046 m_env.removePlayer((*ip)->peer_id);
1050 else if(command == TOCLIENT_SECTORMETA)
1052 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1057 [3...] v2s16 pos + sector metadata
1062 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1065 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1067 std::string datastring((char*)&data[2], datasize-2);
1068 std::istringstream is(datastring, std::ios_base::binary);
1072 is.read((char*)buf, 1);
1073 u16 sector_count = readU8(buf);
1075 //dstream<<"sector_count="<<sector_count<<std::endl;
1077 for(u16 i=0; i<sector_count; i++)
1080 is.read((char*)buf, 4);
1081 v2s16 pos = readV2S16(buf);
1082 /*dstream<<"Client: deserializing sector at "
1083 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1085 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1086 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1091 else if(command == TOCLIENT_INVENTORY)
1096 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1099 //TimeTaker t2("mutex locking", m_device);
1100 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1103 //TimeTaker t3("istringstream init", m_device);
1104 std::string datastring((char*)&data[2], datasize-2);
1105 std::istringstream is(datastring, std::ios_base::binary);
1108 //m_env.printPlayers(dstream);
1110 //TimeTaker t4("player get", m_device);
1111 Player *player = m_env.getLocalPlayer();
1112 assert(player != NULL);
1115 //TimeTaker t1("inventory.deSerialize()", m_device);
1116 player->inventory.deSerialize(is);
1119 m_inventory_updated = true;
1121 //dstream<<"Client got player inventory:"<<std::endl;
1122 //player->inventory.print(dstream);
1126 else if(command == TOCLIENT_OBJECTDATA)
1129 // Strip command word and create a stringstream
1130 std::string datastring((char*)&data[2], datasize-2);
1131 std::istringstream is(datastring, std::ios_base::binary);
1135 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1143 is.read((char*)buf, 2);
1144 u16 playercount = readU16(buf);
1146 for(u16 i=0; i<playercount; i++)
1148 is.read((char*)buf, 2);
1149 u16 peer_id = readU16(buf);
1150 is.read((char*)buf, 12);
1151 v3s32 p_i = readV3S32(buf);
1152 is.read((char*)buf, 12);
1153 v3s32 s_i = readV3S32(buf);
1154 is.read((char*)buf, 4);
1155 s32 pitch_i = readS32(buf);
1156 is.read((char*)buf, 4);
1157 s32 yaw_i = readS32(buf);
1159 Player *player = m_env.getPlayer(peer_id);
1161 // Skip if player doesn't exist
1167 // Skip if player is local player
1168 if(player->isLocal())
1173 f32 pitch = (f32)pitch_i / 100.0;
1174 f32 yaw = (f32)yaw_i / 100.0;
1175 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1176 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1178 player->setPosition(position);
1179 player->setSpeed(speed);
1180 player->setPitch(pitch);
1181 player->setYaw(yaw);
1188 // Read active block count
1189 is.read((char*)buf, 2);
1190 u16 blockcount = readU16(buf);
1192 // Initialize delete queue with all active blocks
1193 core::map<v3s16, bool> abs_to_delete;
1194 for(core::map<v3s16, bool>::Iterator
1195 i = m_active_blocks.getIterator();
1196 i.atEnd() == false; i++)
1198 v3s16 p = i.getNode()->getKey();
1199 /*dstream<<"adding "
1200 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1201 <<" to abs_to_delete"
1203 abs_to_delete.insert(p, true);
1206 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1209 for(u16 i=0; i<blockcount; i++)
1212 is.read((char*)buf, 6);
1213 v3s16 p = readV3S16(buf);
1214 // Get block from somewhere
1215 MapBlock *block = NULL;
1217 block = m_env.getMap().getBlockNoCreate(p);
1219 catch(InvalidPositionException &e)
1221 //TODO: Create a dummy block?
1225 dstream<<"WARNING: "
1226 <<"Could not get block at blockpos "
1227 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1228 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1229 <<"following block object data."
1234 /*dstream<<"Client updating objects for block "
1235 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1238 // Insert to active block list
1239 m_active_blocks.insert(p, true);
1241 // Remove from deletion queue
1242 if(abs_to_delete.find(p) != NULL)
1243 abs_to_delete.remove(p);
1246 Update objects of block
1248 NOTE: Be sure this is done in the main thread.
1250 block->updateObjects(is, m_server_ser_ver,
1251 m_device->getSceneManager(), m_env.getDayNightRatio());
1254 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1257 // Delete objects of blocks in delete queue
1258 for(core::map<v3s16, bool>::Iterator
1259 i = abs_to_delete.getIterator();
1260 i.atEnd() == false; i++)
1262 v3s16 p = i.getNode()->getKey();
1265 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1268 block->clearObjects();
1269 // Remove from active blocks list
1270 m_active_blocks.remove(p);
1272 catch(InvalidPositionException &e)
1274 dstream<<"WARNAING: Client: "
1275 <<"Couldn't clear objects of active->inactive"
1277 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1278 <<" because block was not found"
1286 else if(command == TOCLIENT_TIME_OF_DAY)
1291 u16 time_of_day = readU16(&data[2]);
1292 time_of_day = time_of_day % 24000;
1293 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1301 m_env.setTimeOfDay(time_of_day);
1303 u32 dr = m_env.getDayNightRatio();
1305 dstream<<"Client: time_of_day="<<time_of_day
1311 else if(command == TOCLIENT_CHAT_MESSAGE)
1319 std::string datastring((char*)&data[2], datasize-2);
1320 std::istringstream is(datastring, std::ios_base::binary);
1323 is.read((char*)buf, 2);
1324 u16 len = readU16(buf);
1326 std::wstring message;
1327 for(u16 i=0; i<len; i++)
1329 is.read((char*)buf, 2);
1330 message += (wchar_t)readU16(buf);
1333 /*dstream<<"Client received chat message: "
1334 <<wide_to_narrow(message)<<std::endl;*/
1336 m_chat_queue.push_back(message);
1338 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1340 //if(g_settings.getBool("enable_experimental"))
1344 u16 count of removed objects
1345 for all removed objects {
1348 u16 count of added objects
1349 for all added objects {
1352 u16 initialization data length
1353 string initialization data
1358 // Get all data except the command number
1359 std::string datastring((char*)&data[2], datasize-2);
1360 // Throw them in an istringstream
1361 std::istringstream is(datastring, std::ios_base::binary);
1365 // Read removed objects
1367 u16 removed_count = readU16((u8*)buf);
1368 for(u16 i=0; i<removed_count; i++)
1371 u16 id = readU16((u8*)buf);
1374 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1375 m_env.removeActiveObject(id);
1379 // Read added objects
1381 u16 added_count = readU16((u8*)buf);
1382 for(u16 i=0; i<added_count; i++)
1385 u16 id = readU16((u8*)buf);
1387 u8 type = readU8((u8*)buf);
1388 std::string data = deSerializeLongString(is);
1391 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1392 m_env.addActiveObject(id, type, data);
1397 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1399 //if(g_settings.getBool("enable_experimental"))
1411 // Get all data except the command number
1412 std::string datastring((char*)&data[2], datasize-2);
1413 // Throw them in an istringstream
1414 std::istringstream is(datastring, std::ios_base::binary);
1416 while(is.eof() == false)
1420 u16 id = readU16((u8*)buf);
1424 u16 message_size = readU16((u8*)buf);
1425 std::string message;
1426 message.reserve(message_size);
1427 for(u16 i=0; i<message_size; i++)
1430 message.append(buf, 1);
1432 // Pass on to the environment
1434 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1435 m_env.processActiveObjectMessage(id, message);
1440 else if(command == TOCLIENT_HP)
1442 std::string datastring((char*)&data[2], datasize-2);
1443 std::istringstream is(datastring, std::ios_base::binary);
1444 Player *player = m_env.getLocalPlayer();
1445 assert(player != NULL);
1449 else if(command == TOCLIENT_MOVE_PLAYER)
1451 std::string datastring((char*)&data[2], datasize-2);
1452 std::istringstream is(datastring, std::ios_base::binary);
1453 Player *player = m_env.getLocalPlayer();
1454 assert(player != NULL);
1455 v3f pos = readV3F1000(is);
1456 f32 pitch = readF1000(is);
1457 f32 yaw = readF1000(is);
1458 player->setPosition(pos);
1459 /*player->setPitch(pitch);
1460 player->setYaw(yaw);*/
1462 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1463 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1469 Add to ClientEvent queue.
1470 This has to be sent to the main program because otherwise
1471 it would just force the pitch and yaw values to whatever
1472 the camera points to.
1475 event.type = CE_PLAYER_FORCE_MOVE;
1476 event.player_force_move.pitch = pitch;
1477 event.player_force_move.yaw = yaw;
1478 m_client_event_queue.push_back(event);
1480 // Ignore damage for a few seconds, so that the player doesn't
1481 // get damage from falling on ground
1482 m_ignore_damage_timer = 3.0;
1486 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1487 <<command<<std::endl;
1491 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1493 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1494 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1497 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1498 v3s16 nodepos_oversurface, u16 item)
1500 if(connectedAndInitialized() == false){
1501 dout_client<<DTIME<<"Client::groundAction() "
1502 "cancelled (not connected)"
1511 [3] v3s16 nodepos_undersurface
1512 [9] v3s16 nodepos_abovesurface
1517 2: stop digging (all parameters ignored)
1518 3: digging completed
1520 u8 datasize = 2 + 1 + 6 + 6 + 2;
1521 SharedBuffer<u8> data(datasize);
1522 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1523 writeU8(&data[2], action);
1524 writeV3S16(&data[3], nodepos_undersurface);
1525 writeV3S16(&data[9], nodepos_oversurface);
1526 writeU16(&data[15], item);
1527 Send(0, data, true);
1530 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1532 if(connectedAndInitialized() == false){
1533 dout_client<<DTIME<<"Client::clickObject() "
1534 "cancelled (not connected)"
1540 [0] u16 command=TOSERVER_CLICK_OBJECT
1541 [2] u8 button (0=left, 1=right)
1546 u8 datasize = 2 + 1 + 6 + 2 + 2;
1547 SharedBuffer<u8> data(datasize);
1548 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1549 writeU8(&data[2], button);
1550 writeV3S16(&data[3], blockpos);
1551 writeS16(&data[9], id);
1552 writeU16(&data[11], item);
1553 Send(0, data, true);
1556 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1558 if(connectedAndInitialized() == false){
1559 dout_client<<DTIME<<"Client::clickActiveObject() "
1560 "cancelled (not connected)"
1568 [2] u8 button (0=left, 1=right)
1572 u8 datasize = 2 + 1 + 6 + 2 + 2;
1573 SharedBuffer<u8> data(datasize);
1574 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1575 writeU8(&data[2], button);
1576 writeU16(&data[3], id);
1577 writeU16(&data[5], item);
1578 Send(0, data, true);
1581 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1590 std::ostringstream os(std::ios_base::binary);
1594 writeU16(buf, TOSERVER_SIGNTEXT);
1595 os.write((char*)buf, 2);
1598 writeV3S16(buf, blockpos);
1599 os.write((char*)buf, 6);
1603 os.write((char*)buf, 2);
1605 u16 textlen = text.size();
1606 // Write text length
1607 writeS16(buf, textlen);
1608 os.write((char*)buf, 2);
1611 os.write((char*)text.c_str(), textlen);
1614 std::string s = os.str();
1615 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1617 Send(0, data, true);
1620 void Client::sendSignNodeText(v3s16 p, std::string text)
1628 std::ostringstream os(std::ios_base::binary);
1632 writeU16(buf, TOSERVER_SIGNNODETEXT);
1633 os.write((char*)buf, 2);
1637 os.write((char*)buf, 6);
1639 u16 textlen = text.size();
1640 // Write text length
1641 writeS16(buf, textlen);
1642 os.write((char*)buf, 2);
1645 os.write((char*)text.c_str(), textlen);
1648 std::string s = os.str();
1649 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1651 Send(0, data, true);
1654 void Client::sendInventoryAction(InventoryAction *a)
1656 std::ostringstream os(std::ios_base::binary);
1660 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1661 os.write((char*)buf, 2);
1666 std::string s = os.str();
1667 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1669 Send(0, data, true);
1672 void Client::sendChatMessage(const std::wstring &message)
1674 std::ostringstream os(std::ios_base::binary);
1678 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1679 os.write((char*)buf, 2);
1682 writeU16(buf, message.size());
1683 os.write((char*)buf, 2);
1686 for(u32 i=0; i<message.size(); i++)
1690 os.write((char*)buf, 2);
1694 std::string s = os.str();
1695 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1697 Send(0, data, true);
1700 void Client::sendChangePassword(const std::wstring oldpassword,
1701 const std::wstring newpassword)
1703 Player *player = m_env.getLocalPlayer();
1707 std::string playername = player->getName();
1708 std::string oldpwd = translatePassword(playername, oldpassword);
1709 std::string newpwd = translatePassword(playername, newpassword);
1711 std::ostringstream os(std::ios_base::binary);
1712 u8 buf[2+PASSWORD_SIZE*2];
1714 [0] u16 TOSERVER_PASSWORD
1715 [2] u8[28] old password
1716 [30] u8[28] new password
1719 writeU16(buf, TOSERVER_PASSWORD);
1720 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1722 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1723 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1725 buf[2+PASSWORD_SIZE-1] = 0;
1726 buf[30+PASSWORD_SIZE-1] = 0;
1727 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1730 std::string s = os.str();
1731 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1733 Send(0, data, true);
1737 void Client::sendDamage(u8 damage)
1739 DSTACK(__FUNCTION_NAME);
1740 std::ostringstream os(std::ios_base::binary);
1742 writeU16(os, TOSERVER_DAMAGE);
1743 writeU8(os, damage);
1746 std::string s = os.str();
1747 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1749 Send(0, data, true);
1752 void Client::sendPlayerPos()
1754 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1756 Player *myplayer = m_env.getLocalPlayer();
1757 if(myplayer == NULL)
1762 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1763 our_peer_id = m_con.GetPeerID();
1766 // Set peer id if not set already
1767 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1768 myplayer->peer_id = our_peer_id;
1769 // Check that an existing peer_id is the same as the connection's
1770 assert(myplayer->peer_id == our_peer_id);
1772 v3f pf = myplayer->getPosition();
1773 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1774 v3f sf = myplayer->getSpeed();
1775 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1776 s32 pitch = myplayer->getPitch() * 100;
1777 s32 yaw = myplayer->getYaw() * 100;
1782 [2] v3s32 position*100
1783 [2+12] v3s32 speed*100
1784 [2+12+12] s32 pitch*100
1785 [2+12+12+4] s32 yaw*100
1788 SharedBuffer<u8> data(2+12+12+4+4);
1789 writeU16(&data[0], TOSERVER_PLAYERPOS);
1790 writeV3S32(&data[2], position);
1791 writeV3S32(&data[2+12], speed);
1792 writeS32(&data[2+12+12], pitch);
1793 writeS32(&data[2+12+12+4], yaw);
1795 // Send as unreliable
1796 Send(0, data, false);
1799 void Client::removeNode(v3s16 p)
1801 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1803 core::map<v3s16, MapBlock*> modified_blocks;
1807 //TimeTaker t("removeNodeAndUpdate", m_device);
1808 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1810 catch(InvalidPositionException &e)
1814 for(core::map<v3s16, MapBlock * >::Iterator
1815 i = modified_blocks.getIterator();
1816 i.atEnd() == false; i++)
1818 v3s16 p = i.getNode()->getKey();
1819 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1820 addUpdateMeshTaskWithEdge(p);
1824 void Client::addNode(v3s16 p, MapNode n)
1826 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1828 TimeTaker timer1("Client::addNode()");
1830 core::map<v3s16, MapBlock*> modified_blocks;
1834 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1835 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1837 catch(InvalidPositionException &e)
1840 //TimeTaker timer2("Client::addNode(): updateMeshes");
1842 for(core::map<v3s16, MapBlock * >::Iterator
1843 i = modified_blocks.getIterator();
1844 i.atEnd() == false; i++)
1846 v3s16 p = i.getNode()->getKey();
1847 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1848 addUpdateMeshTaskWithEdge(p);
1852 void Client::updateCamera(v3f pos, v3f dir)
1854 m_env.getClientMap().updateCamera(pos, dir);
1855 camera_position = pos;
1856 camera_direction = dir;
1859 MapNode Client::getNode(v3s16 p)
1861 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1862 return m_env.getMap().getNode(p);
1865 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1867 return m_env.getMap().getNodeMetadata(p);
1870 v3f Client::getPlayerPosition()
1872 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1873 LocalPlayer *player = m_env.getLocalPlayer();
1874 assert(player != NULL);
1875 return player->getPosition();
1878 void Client::setPlayerControl(PlayerControl &control)
1880 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1881 LocalPlayer *player = m_env.getLocalPlayer();
1882 assert(player != NULL);
1883 player->control = control;
1886 // Returns true if the inventory of the local player has been
1887 // updated from the server. If it is true, it is set to false.
1888 bool Client::getLocalInventoryUpdated()
1890 // m_inventory_updated is behind envlock
1891 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1892 bool updated = m_inventory_updated;
1893 m_inventory_updated = false;
1897 // Copies the inventory of the local player to parameter
1898 void Client::getLocalInventory(Inventory &dst)
1900 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1901 Player *player = m_env.getLocalPlayer();
1902 assert(player != NULL);
1903 dst = player->inventory;
1906 InventoryContext *Client::getInventoryContext()
1908 return &m_inventory_context;
1911 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1913 if(id == "current_player")
1915 assert(c->current_player);
1916 return &(c->current_player->inventory);
1920 std::string id0 = fn.next(":");
1922 if(id0 == "nodemeta")
1925 p.X = stoi(fn.next(","));
1926 p.Y = stoi(fn.next(","));
1927 p.Z = stoi(fn.next(","));
1928 NodeMetadata* meta = getNodeMetadata(p);
1930 return meta->getInventory();
1931 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1932 <<"no metadata found"<<std::endl;
1936 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1939 void Client::inventoryAction(InventoryAction *a)
1941 sendInventoryAction(a);
1944 MapBlockObject * Client::getSelectedObject(
1946 v3f from_pos_f_on_map,
1947 core::line3d<f32> shootline_on_map
1950 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1952 core::array<DistanceSortedObject> objects;
1954 for(core::map<v3s16, bool>::Iterator
1955 i = m_active_blocks.getIterator();
1956 i.atEnd() == false; i++)
1958 v3s16 p = i.getNode()->getKey();
1960 MapBlock *block = NULL;
1963 block = m_env.getMap().getBlockNoCreate(p);
1965 catch(InvalidPositionException &e)
1970 // Calculate from_pos relative to block
1971 v3s16 block_pos_i_on_map = block->getPosRelative();
1972 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1973 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1975 block->getObjects(from_pos_f_on_block, max_d, objects);
1976 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1979 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1982 // After this, the closest object is the first in the array.
1985 for(u32 i=0; i<objects.size(); i++)
1987 MapBlockObject *obj = objects[i].obj;
1988 MapBlock *block = obj->getBlock();
1990 // Calculate shootline relative to block
1991 v3s16 block_pos_i_on_map = block->getPosRelative();
1992 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1993 core::line3d<f32> shootline_on_block(
1994 shootline_on_map.start - block_pos_f_on_map,
1995 shootline_on_map.end - block_pos_f_on_map
1998 if(obj->isSelected(shootline_on_block))
2000 //dstream<<"Returning selected object"<<std::endl;
2005 //dstream<<"No object selected; returning NULL."<<std::endl;
2009 ClientActiveObject * Client::getSelectedActiveObject(
2011 v3f from_pos_f_on_map,
2012 core::line3d<f32> shootline_on_map
2015 core::array<DistanceSortedActiveObject> objects;
2017 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2019 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2022 // After this, the closest object is the first in the array.
2025 for(u32 i=0; i<objects.size(); i++)
2027 ClientActiveObject *obj = objects[i].obj;
2029 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2030 if(selection_box == NULL)
2033 v3f pos = obj->getPosition();
2035 core::aabbox3d<f32> offsetted_box(
2036 selection_box->MinEdge + pos,
2037 selection_box->MaxEdge + pos
2040 if(offsetted_box.intersectsWithLine(shootline_on_map))
2042 //dstream<<"Returning selected object"<<std::endl;
2047 //dstream<<"No object selected; returning NULL."<<std::endl;
2051 void Client::printDebugInfo(std::ostream &os)
2053 //JMutexAutoLock lock1(m_fetchblock_mutex);
2054 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2056 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2057 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2058 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2062 u32 Client::getDayNightRatio()
2064 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2065 return m_env.getDayNightRatio();
2070 Player *player = m_env.getLocalPlayer();
2071 assert(player != NULL);
2075 void Client::setTempMod(v3s16 p, NodeMod mod)
2077 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2078 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2080 core::map<v3s16, MapBlock*> affected_blocks;
2081 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2084 for(core::map<v3s16, MapBlock*>::Iterator
2085 i = affected_blocks.getIterator();
2086 i.atEnd() == false; i++)
2088 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2092 void Client::clearTempMod(v3s16 p)
2094 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2095 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2097 core::map<v3s16, MapBlock*> affected_blocks;
2098 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2101 for(core::map<v3s16, MapBlock*>::Iterator
2102 i = affected_blocks.getIterator();
2103 i.atEnd() == false; i++)
2105 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2109 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2111 /*dstream<<"Client::addUpdateMeshTask(): "
2112 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2115 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2120 Create a task to update the mesh of the block
2123 MeshMakeData *data = new MeshMakeData;
2126 //TimeTaker timer("data fill");
2128 // Debug: 1-6ms, avg=2ms
2129 data->fill(getDayNightRatio(), b);
2133 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2135 // Add task to queue
2136 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2138 /*dstream<<"Mesh update input queue size is "
2139 <<m_mesh_update_thread.m_queue_in.size()
2143 // Temporary test: make mesh directly in here
2145 //TimeTaker timer("make mesh");
2147 scene::SMesh *mesh_new = NULL;
2148 mesh_new = makeMapBlockMesh(data);
2149 b->replaceMesh(mesh_new);
2155 Mark mesh as non-expired at this point so that it can already
2156 be marked as expired again if the data changes
2158 b->setMeshExpired(false);
2161 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2165 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2166 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2171 v3s16 p = blockpos + v3s16(0,0,0);
2172 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2173 addUpdateMeshTask(p, ack_to_server);
2175 catch(InvalidPositionException &e){}
2178 v3s16 p = blockpos + v3s16(-1,0,0);
2179 addUpdateMeshTask(p);
2181 catch(InvalidPositionException &e){}
2183 v3s16 p = blockpos + v3s16(0,-1,0);
2184 addUpdateMeshTask(p);
2186 catch(InvalidPositionException &e){}
2188 v3s16 p = blockpos + v3s16(0,0,-1);
2189 addUpdateMeshTask(p);
2191 catch(InvalidPositionException &e){}
2194 ClientEvent Client::getClientEvent()
2196 if(m_client_event_queue.size() == 0)
2199 event.type = CE_NONE;
2202 return m_client_event_queue.pop_front();