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"
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();
307 // Get connection status
308 bool connected = connectedAndInitialized();
313 Delete unused sectors
315 NOTE: This jams the game for a while because deleting sectors
319 float &counter = m_delete_unused_sectors_timer;
327 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
329 core::list<v3s16> deleted_blocks;
331 float delete_unused_sectors_timeout =
332 g_settings.getFloat("client_delete_unused_sectors_timeout");
334 // Delete sector blocks
335 /*u32 num = m_env.getMap().unloadUnusedData
336 (delete_unused_sectors_timeout,
337 true, &deleted_blocks);*/
339 // Delete whole sectors
340 m_env.getMap().unloadUnusedData
341 (delete_unused_sectors_timeout,
344 if(deleted_blocks.size() > 0)
346 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
347 <<" unused sectors"<<std::endl;*/
348 /*dstream<<DTIME<<"Client: Deleted "<<num
349 <<" unused sectors"<<std::endl;*/
355 // Env is locked so con can be locked.
356 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
358 core::list<v3s16>::Iterator i = deleted_blocks.begin();
359 core::list<v3s16> sendlist;
362 if(sendlist.size() == 255 || i == deleted_blocks.end())
364 if(sendlist.size() == 0)
373 u32 replysize = 2+1+6*sendlist.size();
374 SharedBuffer<u8> reply(replysize);
375 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
376 reply[2] = sendlist.size();
378 for(core::list<v3s16>::Iterator
379 j = sendlist.begin();
380 j != sendlist.end(); j++)
382 writeV3S16(&reply[2+1+6*k], *j);
385 m_con.Send(PEER_ID_SERVER, 1, reply, true);
387 if(i == deleted_blocks.end())
393 sendlist.push_back(*i);
401 if(connected == false)
403 float &counter = m_connection_reinit_timer;
409 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
411 Player *myplayer = m_env.getLocalPlayer();
412 assert(myplayer != NULL);
414 // Send TOSERVER_INIT
415 // [0] u16 TOSERVER_INIT
416 // [2] u8 SER_FMT_VER_HIGHEST
417 // [3] u8[20] player_name
418 // [23] u8[28] password
419 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
420 writeU16(&data[0], TOSERVER_INIT);
421 writeU8(&data[2], SER_FMT_VER_HIGHEST);
423 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
424 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
426 /*dstream<<"Client: password hash is \""<<m_password<<"\""
429 memset((char*)&data[23], 0, PASSWORD_SIZE);
430 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
432 // Send as unreliable
433 Send(0, data, false);
436 // Not connected, return
441 Do stuff if connected
445 Run Map's timers and unload unused data
447 const float map_timer_and_unload_dtime = 5.25;
448 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
450 ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
451 core::list<v3s16> deleted_blocks;
452 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
453 g_settings.getFloat("client_unload_unused_data_timeout"),
456 /*if(deleted_blocks.size() > 0)
457 dstream<<"Client: Unloaded "<<deleted_blocks.size()
458 <<" unused blocks"<<std::endl;*/
462 NOTE: This loop is intentionally iterated the way it is.
465 core::list<v3s16>::Iterator i = deleted_blocks.begin();
466 core::list<v3s16> sendlist;
469 if(sendlist.size() == 255 || i == deleted_blocks.end())
471 if(sendlist.size() == 0)
480 u32 replysize = 2+1+6*sendlist.size();
481 SharedBuffer<u8> reply(replysize);
482 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
483 reply[2] = sendlist.size();
485 for(core::list<v3s16>::Iterator
486 j = sendlist.begin();
487 j != sendlist.end(); j++)
489 writeV3S16(&reply[2+1+6*k], *j);
492 m_con.Send(PEER_ID_SERVER, 1, reply, true);
494 if(i == deleted_blocks.end())
500 sendlist.push_back(*i);
510 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
512 // Control local player (0ms)
513 LocalPlayer *player = m_env.getLocalPlayer();
514 assert(player != NULL);
515 player->applyControl(dtime);
517 //TimeTaker envtimer("env step", m_device);
523 NOTE: These old objects are DEPRECATED. TODO: Remove
525 for(core::map<v3s16, bool>::Iterator
526 i = m_active_blocks.getIterator();
527 i.atEnd() == false; i++)
529 v3s16 p = i.getNode()->getKey();
531 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
535 // Step MapBlockObjects
536 block->stepObjects(dtime, false, m_env.getDayNightRatio());
544 ClientEnvEvent event = m_env.getClientEvent();
545 if(event.type == CEE_NONE)
549 else if(event.type == CEE_PLAYER_DAMAGE)
551 if(m_ignore_damage_timer <= 0)
553 u8 damage = event.player_damage.amount;
556 // Add to ClientEvent queue
558 event.type = CE_PLAYER_DAMAGE;
559 event.player_damage.amount = damage;
560 m_client_event_queue.push_back(event);
570 float &counter = m_avg_rtt_timer;
575 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
576 // connectedAndInitialized() is true, peer exists.
577 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
578 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
583 Send player position to server
586 float &counter = m_playerpos_send_timer;
596 Replace updated meshes
599 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
601 //TimeTaker timer("** Processing mesh update result queue");
604 /*dstream<<"Mesh update result queue size is "
605 <<m_mesh_update_thread.m_queue_out.size()
608 while(m_mesh_update_thread.m_queue_out.size() > 0)
610 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
611 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
614 block->replaceMesh(r.mesh);
616 if(r.ack_block_to_server)
618 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
619 <<","<<r.p.Z<<")"<<std::endl;*/
630 u32 replysize = 2+1+6;
631 SharedBuffer<u8> reply(replysize);
632 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
634 writeV3S16(&reply[3], r.p);
636 m_con.Send(PEER_ID_SERVER, 1, reply, true);
642 // Virtual methods from con::PeerHandler
643 void Client::peerAdded(con::Peer *peer)
645 derr_client<<"Client::peerAdded(): peer->id="
646 <<peer->id<<std::endl;
648 void Client::deletingPeer(con::Peer *peer, bool timeout)
650 derr_client<<"Client::deletingPeer(): "
651 "Server Peer is getting deleted "
652 <<"(timeout="<<timeout<<")"<<std::endl;
655 void Client::ReceiveAll()
657 DSTACK(__FUNCTION_NAME);
663 catch(con::NoIncomingDataException &e)
667 catch(con::InvalidIncomingDataException &e)
669 dout_client<<DTIME<<"Client::ReceiveAll(): "
670 "InvalidIncomingDataException: what()="
671 <<e.what()<<std::endl;
676 void Client::Receive()
678 DSTACK(__FUNCTION_NAME);
679 u32 data_maxsize = 200000;
680 Buffer<u8> data(data_maxsize);
684 //TimeTaker t1("con mutex and receive", m_device);
685 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
686 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
688 //TimeTaker t1("ProcessData", m_device);
689 ProcessData(*data, datasize, sender_peer_id);
693 sender_peer_id given to this shall be quaranteed to be a valid peer
695 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
697 DSTACK(__FUNCTION_NAME);
699 // Ignore packets that don't even fit a command
702 m_packetcounter.add(60000);
706 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
708 //dstream<<"Client: received command="<<command<<std::endl;
709 m_packetcounter.add((u16)command);
712 If this check is removed, be sure to change the queue
713 system to know the ids
715 if(sender_peer_id != PEER_ID_SERVER)
717 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
718 "coming from server: peer_id="<<sender_peer_id
725 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
726 // All data is coming from the server
727 // PeerNotFoundException is handled by caller.
728 peer = m_con.GetPeer(PEER_ID_SERVER);
731 u8 ser_version = m_server_ser_ver;
733 //dstream<<"Client received command="<<(int)command<<std::endl;
735 if(command == TOCLIENT_INIT)
740 u8 deployed = data[2];
742 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
743 "deployed="<<((int)deployed&0xff)<<std::endl;
745 if(deployed < SER_FMT_VER_LOWEST
746 || deployed > SER_FMT_VER_HIGHEST)
748 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
749 <<"unsupported ser_fmt_ver"<<std::endl;
753 m_server_ser_ver = deployed;
755 // Get player position
756 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
757 if(datasize >= 2+1+6)
758 playerpos_s16 = readV3S16(&data[2+1]);
759 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
762 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
764 // Set player position
765 Player *player = m_env.getLocalPlayer();
766 assert(player != NULL);
767 player->setPosition(playerpos_f);
770 if(datasize >= 2+1+6+8)
773 m_map_seed = readU64(&data[2+1+6]);
774 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
779 SharedBuffer<u8> reply(replysize);
780 writeU16(&reply[0], TOSERVER_INIT2);
782 m_con.Send(PEER_ID_SERVER, 1, reply, true);
787 if(command == TOCLIENT_ACCESS_DENIED)
789 // The server didn't like our password. Note, this needs
790 // to be processed even if the serialisation format has
791 // not been agreed yet, the same as TOCLIENT_INIT.
792 m_access_denied = true;
793 m_access_denied_reason = L"Unknown";
796 std::string datastring((char*)&data[2], datasize-2);
797 std::istringstream is(datastring, std::ios_base::binary);
798 m_access_denied_reason = deSerializeWideString(is);
803 if(ser_version == SER_FMT_VER_INVALID)
805 dout_client<<DTIME<<"WARNING: Client: Server serialization"
806 " format invalid or not initialized."
807 " Skipping incoming command="<<command<<std::endl;
811 // Just here to avoid putting the two if's together when
812 // making some copypasta
815 if(command == TOCLIENT_REMOVENODE)
820 p.X = readS16(&data[2]);
821 p.Y = readS16(&data[4]);
822 p.Z = readS16(&data[6]);
824 //TimeTaker t1("TOCLIENT_REMOVENODE");
826 // This will clear the cracking animation after digging
827 ((ClientMap&)m_env.getMap()).clearTempMod(p);
831 else if(command == TOCLIENT_ADDNODE)
833 if(datasize < 8 + MapNode::serializedLength(ser_version))
837 p.X = readS16(&data[2]);
838 p.Y = readS16(&data[4]);
839 p.Z = readS16(&data[6]);
841 //TimeTaker t1("TOCLIENT_ADDNODE");
844 n.deSerialize(&data[8], ser_version);
848 else if(command == TOCLIENT_BLOCKDATA)
850 // Ignore too small packet
855 p.X = readS16(&data[2]);
856 p.Y = readS16(&data[4]);
857 p.Z = readS16(&data[6]);
859 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
860 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
861 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
862 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
864 std::string datastring((char*)&data[8], datasize-8);
865 std::istringstream istr(datastring, std::ios_base::binary);
871 sector = m_env.getMap().emergeSector(p2d);
873 assert(sector->getPos() == p2d);
875 //TimeTaker timer("MapBlock deSerialize");
878 block = sector->getBlockNoCreateNoEx(p.Y);
882 Update an existing block
884 //dstream<<"Updating"<<std::endl;
885 block->deSerialize(istr, ser_version);
892 //dstream<<"Creating new"<<std::endl;
893 block = new MapBlock(&m_env.getMap(), p);
894 block->deSerialize(istr, ser_version);
895 sector->insertBlock(block);
899 mod.type = NODEMOD_CHANGECONTENT;
900 mod.param = CONTENT_MESE;
901 block->setTempMod(v3s16(8,10,8), mod);
902 block->setTempMod(v3s16(8,9,8), mod);
903 block->setTempMod(v3s16(8,8,8), mod);
904 block->setTempMod(v3s16(8,7,8), mod);
905 block->setTempMod(v3s16(8,6,8), mod);*/
919 u32 replysize = 2+1+6;
920 SharedBuffer<u8> reply(replysize);
921 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
923 writeV3S16(&reply[3], p);
925 m_con.Send(PEER_ID_SERVER, 1, reply, true);
929 Update Mesh of this block and blocks at x-, y- and z-.
930 Environment should not be locked as it interlocks with the
931 main thread, from which is will want to retrieve textures.
934 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
937 Add it to mesh update queue and set it to be acknowledged after update.
939 //std::cerr<<"Adding mesh update task for received block"<<std::endl;
940 addUpdateMeshTaskWithEdge(p, true);
942 else if(command == TOCLIENT_PLAYERPOS)
944 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
948 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
949 our_peer_id = m_con.GetPeerID();
951 // Cancel if we don't have a peer id
952 if(our_peer_id == PEER_ID_INEXISTENT){
953 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
960 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
962 u32 player_size = 2+12+12+4+4;
964 u32 player_count = (datasize-2) / player_size;
966 for(u32 i=0; i<player_count; i++)
968 u16 peer_id = readU16(&data[start]);
970 Player *player = m_env.getPlayer(peer_id);
972 // Skip if player doesn't exist
975 start += player_size;
979 // Skip if player is local player
980 if(player->isLocal())
982 start += player_size;
986 v3s32 ps = readV3S32(&data[start+2]);
987 v3s32 ss = readV3S32(&data[start+2+12]);
988 s32 pitch_i = readS32(&data[start+2+12+12]);
989 s32 yaw_i = readS32(&data[start+2+12+12+4]);
990 /*dstream<<"Client: got "
991 <<"pitch_i="<<pitch_i
992 <<" yaw_i="<<yaw_i<<std::endl;*/
993 f32 pitch = (f32)pitch_i / 100.0;
994 f32 yaw = (f32)yaw_i / 100.0;
995 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
996 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
997 player->setPosition(position);
998 player->setSpeed(speed);
999 player->setPitch(pitch);
1000 player->setYaw(yaw);
1002 /*dstream<<"Client: player "<<peer_id
1004 <<" yaw="<<yaw<<std::endl;*/
1006 start += player_size;
1010 else if(command == TOCLIENT_PLAYERINFO)
1014 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1015 our_peer_id = m_con.GetPeerID();
1017 // Cancel if we don't have a peer id
1018 if(our_peer_id == PEER_ID_INEXISTENT){
1019 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
1020 "we have no peer id"
1025 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
1028 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1030 u32 item_size = 2+PLAYERNAME_SIZE;
1031 u32 player_count = (datasize-2) / item_size;
1034 core::list<u16> players_alive;
1035 for(u32 i=0; i<player_count; i++)
1037 // Make sure the name ends in '\0'
1038 data[start+2+20-1] = 0;
1040 u16 peer_id = readU16(&data[start]);
1042 players_alive.push_back(peer_id);
1044 /*dstream<<DTIME<<"peer_id="<<peer_id
1045 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1047 // Don't update the info of the local player
1048 if(peer_id == our_peer_id)
1054 Player *player = m_env.getPlayer(peer_id);
1056 // Create a player if it doesn't exist
1059 player = new RemotePlayer(
1060 m_device->getSceneManager()->getRootSceneNode(),
1063 player->peer_id = peer_id;
1064 m_env.addPlayer(player);
1065 dout_client<<DTIME<<"Client: Adding new player "
1066 <<peer_id<<std::endl;
1069 player->updateName((char*)&data[start+2]);
1075 Remove those players from the environment that
1076 weren't listed by the server.
1078 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1079 core::list<Player*> players = m_env.getPlayers();
1080 core::list<Player*>::Iterator ip;
1081 for(ip=players.begin(); ip!=players.end(); ip++)
1083 // Ingore local player
1084 if((*ip)->isLocal())
1087 // Warn about a special case
1088 if((*ip)->peer_id == 0)
1090 dstream<<DTIME<<"WARNING: Client: Removing "
1091 "dead player with id=0"<<std::endl;
1094 bool is_alive = false;
1095 core::list<u16>::Iterator i;
1096 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1098 if((*ip)->peer_id == *i)
1104 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1105 <<" is_alive="<<is_alive<<std::endl;*/
1108 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1110 m_env.removePlayer((*ip)->peer_id);
1114 else if(command == TOCLIENT_SECTORMETA)
1116 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1121 [3...] v2s16 pos + sector metadata
1126 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1129 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1131 std::string datastring((char*)&data[2], datasize-2);
1132 std::istringstream is(datastring, std::ios_base::binary);
1136 is.read((char*)buf, 1);
1137 u16 sector_count = readU8(buf);
1139 //dstream<<"sector_count="<<sector_count<<std::endl;
1141 for(u16 i=0; i<sector_count; i++)
1144 is.read((char*)buf, 4);
1145 v2s16 pos = readV2S16(buf);
1146 /*dstream<<"Client: deserializing sector at "
1147 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1149 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1150 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1155 else if(command == TOCLIENT_INVENTORY)
1160 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1163 //TimeTaker t2("mutex locking", m_device);
1164 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1167 //TimeTaker t3("istringstream init", m_device);
1168 std::string datastring((char*)&data[2], datasize-2);
1169 std::istringstream is(datastring, std::ios_base::binary);
1172 //m_env.printPlayers(dstream);
1174 //TimeTaker t4("player get", m_device);
1175 Player *player = m_env.getLocalPlayer();
1176 assert(player != NULL);
1179 //TimeTaker t1("inventory.deSerialize()", m_device);
1180 player->inventory.deSerialize(is);
1183 m_inventory_updated = true;
1185 //dstream<<"Client got player inventory:"<<std::endl;
1186 //player->inventory.print(dstream);
1190 else if(command == TOCLIENT_OBJECTDATA)
1193 // Strip command word and create a stringstream
1194 std::string datastring((char*)&data[2], datasize-2);
1195 std::istringstream is(datastring, std::ios_base::binary);
1199 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1207 is.read((char*)buf, 2);
1208 u16 playercount = readU16(buf);
1210 for(u16 i=0; i<playercount; i++)
1212 is.read((char*)buf, 2);
1213 u16 peer_id = readU16(buf);
1214 is.read((char*)buf, 12);
1215 v3s32 p_i = readV3S32(buf);
1216 is.read((char*)buf, 12);
1217 v3s32 s_i = readV3S32(buf);
1218 is.read((char*)buf, 4);
1219 s32 pitch_i = readS32(buf);
1220 is.read((char*)buf, 4);
1221 s32 yaw_i = readS32(buf);
1223 Player *player = m_env.getPlayer(peer_id);
1225 // Skip if player doesn't exist
1231 // Skip if player is local player
1232 if(player->isLocal())
1237 f32 pitch = (f32)pitch_i / 100.0;
1238 f32 yaw = (f32)yaw_i / 100.0;
1239 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1240 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1242 player->setPosition(position);
1243 player->setSpeed(speed);
1244 player->setPitch(pitch);
1245 player->setYaw(yaw);
1250 NOTE: Deprecated stuff here, TODO: Remove
1253 // Read active block count
1254 is.read((char*)buf, 2);
1255 u16 blockcount = readU16(buf);
1257 // Initialize delete queue with all active blocks
1258 core::map<v3s16, bool> abs_to_delete;
1259 for(core::map<v3s16, bool>::Iterator
1260 i = m_active_blocks.getIterator();
1261 i.atEnd() == false; i++)
1263 v3s16 p = i.getNode()->getKey();
1264 /*dstream<<"adding "
1265 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1266 <<" to abs_to_delete"
1268 abs_to_delete.insert(p, true);
1271 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1274 for(u16 i=0; i<blockcount; i++)
1277 is.read((char*)buf, 6);
1278 v3s16 p = readV3S16(buf);
1279 // Get block from somewhere
1280 MapBlock *block = NULL;
1282 block = m_env.getMap().getBlockNoCreate(p);
1284 catch(InvalidPositionException &e)
1286 //TODO: Create a dummy block?
1290 dstream<<"WARNING: "
1291 <<"Could not get block at blockpos "
1292 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1293 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1294 <<"following block object data."
1299 /*dstream<<"Client updating objects for block "
1300 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1303 // Insert to active block list
1304 m_active_blocks.insert(p, true);
1306 // Remove from deletion queue
1307 if(abs_to_delete.find(p) != NULL)
1308 abs_to_delete.remove(p);
1311 Update objects of block
1313 NOTE: Be sure this is done in the main thread.
1315 block->updateObjects(is, m_server_ser_ver,
1316 m_device->getSceneManager(), m_env.getDayNightRatio());
1319 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1322 // Delete objects of blocks in delete queue
1323 for(core::map<v3s16, bool>::Iterator
1324 i = abs_to_delete.getIterator();
1325 i.atEnd() == false; i++)
1327 v3s16 p = i.getNode()->getKey();
1330 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1333 block->clearObjects();
1334 // Remove from active blocks list
1335 m_active_blocks.remove(p);
1337 catch(InvalidPositionException &e)
1339 dstream<<"WARNAING: Client: "
1340 <<"Couldn't clear objects of active->inactive"
1342 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1343 <<" because block was not found"
1351 else if(command == TOCLIENT_TIME_OF_DAY)
1356 u16 time_of_day = readU16(&data[2]);
1357 time_of_day = time_of_day % 24000;
1358 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1366 m_env.setTimeOfDay(time_of_day);
1368 u32 dr = m_env.getDayNightRatio();
1370 dstream<<"Client: time_of_day="<<time_of_day
1376 else if(command == TOCLIENT_CHAT_MESSAGE)
1384 std::string datastring((char*)&data[2], datasize-2);
1385 std::istringstream is(datastring, std::ios_base::binary);
1388 is.read((char*)buf, 2);
1389 u16 len = readU16(buf);
1391 std::wstring message;
1392 for(u16 i=0; i<len; i++)
1394 is.read((char*)buf, 2);
1395 message += (wchar_t)readU16(buf);
1398 /*dstream<<"Client received chat message: "
1399 <<wide_to_narrow(message)<<std::endl;*/
1401 m_chat_queue.push_back(message);
1403 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1405 //if(g_settings.getBool("enable_experimental"))
1409 u16 count of removed objects
1410 for all removed objects {
1413 u16 count of added objects
1414 for all added objects {
1417 u16 initialization data length
1418 string initialization data
1423 // Get all data except the command number
1424 std::string datastring((char*)&data[2], datasize-2);
1425 // Throw them in an istringstream
1426 std::istringstream is(datastring, std::ios_base::binary);
1430 // Read removed objects
1432 u16 removed_count = readU16((u8*)buf);
1433 for(u16 i=0; i<removed_count; i++)
1436 u16 id = readU16((u8*)buf);
1439 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1440 m_env.removeActiveObject(id);
1444 // Read added objects
1446 u16 added_count = readU16((u8*)buf);
1447 for(u16 i=0; i<added_count; i++)
1450 u16 id = readU16((u8*)buf);
1452 u8 type = readU8((u8*)buf);
1453 std::string data = deSerializeLongString(is);
1456 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1457 m_env.addActiveObject(id, type, data);
1462 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1464 //if(g_settings.getBool("enable_experimental"))
1476 // Get all data except the command number
1477 std::string datastring((char*)&data[2], datasize-2);
1478 // Throw them in an istringstream
1479 std::istringstream is(datastring, std::ios_base::binary);
1481 while(is.eof() == false)
1485 u16 id = readU16((u8*)buf);
1489 u16 message_size = readU16((u8*)buf);
1490 std::string message;
1491 message.reserve(message_size);
1492 for(u16 i=0; i<message_size; i++)
1495 message.append(buf, 1);
1497 // Pass on to the environment
1499 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1500 m_env.processActiveObjectMessage(id, message);
1505 else if(command == TOCLIENT_HP)
1507 std::string datastring((char*)&data[2], datasize-2);
1508 std::istringstream is(datastring, std::ios_base::binary);
1509 Player *player = m_env.getLocalPlayer();
1510 assert(player != NULL);
1514 else if(command == TOCLIENT_MOVE_PLAYER)
1516 std::string datastring((char*)&data[2], datasize-2);
1517 std::istringstream is(datastring, std::ios_base::binary);
1518 Player *player = m_env.getLocalPlayer();
1519 assert(player != NULL);
1520 v3f pos = readV3F1000(is);
1521 f32 pitch = readF1000(is);
1522 f32 yaw = readF1000(is);
1523 player->setPosition(pos);
1524 /*player->setPitch(pitch);
1525 player->setYaw(yaw);*/
1527 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1528 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1534 Add to ClientEvent queue.
1535 This has to be sent to the main program because otherwise
1536 it would just force the pitch and yaw values to whatever
1537 the camera points to.
1540 event.type = CE_PLAYER_FORCE_MOVE;
1541 event.player_force_move.pitch = pitch;
1542 event.player_force_move.yaw = yaw;
1543 m_client_event_queue.push_back(event);
1545 // Ignore damage for a few seconds, so that the player doesn't
1546 // get damage from falling on ground
1547 m_ignore_damage_timer = 3.0;
1551 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1552 <<command<<std::endl;
1556 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1558 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1559 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1562 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1563 v3s16 nodepos_oversurface, u16 item)
1565 if(connectedAndInitialized() == false){
1566 dout_client<<DTIME<<"Client::groundAction() "
1567 "cancelled (not connected)"
1576 [3] v3s16 nodepos_undersurface
1577 [9] v3s16 nodepos_abovesurface
1582 2: stop digging (all parameters ignored)
1583 3: digging completed
1585 u8 datasize = 2 + 1 + 6 + 6 + 2;
1586 SharedBuffer<u8> data(datasize);
1587 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1588 writeU8(&data[2], action);
1589 writeV3S16(&data[3], nodepos_undersurface);
1590 writeV3S16(&data[9], nodepos_oversurface);
1591 writeU16(&data[15], item);
1592 Send(0, data, true);
1595 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1597 if(connectedAndInitialized() == false){
1598 dout_client<<DTIME<<"Client::clickObject() "
1599 "cancelled (not connected)"
1605 [0] u16 command=TOSERVER_CLICK_OBJECT
1606 [2] u8 button (0=left, 1=right)
1611 u8 datasize = 2 + 1 + 6 + 2 + 2;
1612 SharedBuffer<u8> data(datasize);
1613 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1614 writeU8(&data[2], button);
1615 writeV3S16(&data[3], blockpos);
1616 writeS16(&data[9], id);
1617 writeU16(&data[11], item);
1618 Send(0, data, true);
1621 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1623 if(connectedAndInitialized() == false){
1624 dout_client<<DTIME<<"Client::clickActiveObject() "
1625 "cancelled (not connected)"
1633 [2] u8 button (0=left, 1=right)
1637 u8 datasize = 2 + 1 + 6 + 2 + 2;
1638 SharedBuffer<u8> data(datasize);
1639 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1640 writeU8(&data[2], button);
1641 writeU16(&data[3], id);
1642 writeU16(&data[5], item);
1643 Send(0, data, true);
1646 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1655 std::ostringstream os(std::ios_base::binary);
1659 writeU16(buf, TOSERVER_SIGNTEXT);
1660 os.write((char*)buf, 2);
1663 writeV3S16(buf, blockpos);
1664 os.write((char*)buf, 6);
1668 os.write((char*)buf, 2);
1670 u16 textlen = text.size();
1671 // Write text length
1672 writeS16(buf, textlen);
1673 os.write((char*)buf, 2);
1676 os.write((char*)text.c_str(), textlen);
1679 std::string s = os.str();
1680 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1682 Send(0, data, true);
1685 void Client::sendSignNodeText(v3s16 p, std::string text)
1693 std::ostringstream os(std::ios_base::binary);
1697 writeU16(buf, TOSERVER_SIGNNODETEXT);
1698 os.write((char*)buf, 2);
1702 os.write((char*)buf, 6);
1704 u16 textlen = text.size();
1705 // Write text length
1706 writeS16(buf, textlen);
1707 os.write((char*)buf, 2);
1710 os.write((char*)text.c_str(), textlen);
1713 std::string s = os.str();
1714 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1716 Send(0, data, true);
1719 void Client::sendInventoryAction(InventoryAction *a)
1721 std::ostringstream os(std::ios_base::binary);
1725 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1726 os.write((char*)buf, 2);
1731 std::string s = os.str();
1732 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1734 Send(0, data, true);
1737 void Client::sendChatMessage(const std::wstring &message)
1739 std::ostringstream os(std::ios_base::binary);
1743 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1744 os.write((char*)buf, 2);
1747 writeU16(buf, message.size());
1748 os.write((char*)buf, 2);
1751 for(u32 i=0; i<message.size(); i++)
1755 os.write((char*)buf, 2);
1759 std::string s = os.str();
1760 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1762 Send(0, data, true);
1765 void Client::sendChangePassword(const std::wstring oldpassword,
1766 const std::wstring newpassword)
1768 Player *player = m_env.getLocalPlayer();
1772 std::string playername = player->getName();
1773 std::string oldpwd = translatePassword(playername, oldpassword);
1774 std::string newpwd = translatePassword(playername, newpassword);
1776 std::ostringstream os(std::ios_base::binary);
1777 u8 buf[2+PASSWORD_SIZE*2];
1779 [0] u16 TOSERVER_PASSWORD
1780 [2] u8[28] old password
1781 [30] u8[28] new password
1784 writeU16(buf, TOSERVER_PASSWORD);
1785 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1787 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1788 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1790 buf[2+PASSWORD_SIZE-1] = 0;
1791 buf[30+PASSWORD_SIZE-1] = 0;
1792 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1795 std::string s = os.str();
1796 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1798 Send(0, data, true);
1802 void Client::sendDamage(u8 damage)
1804 DSTACK(__FUNCTION_NAME);
1805 std::ostringstream os(std::ios_base::binary);
1807 writeU16(os, TOSERVER_DAMAGE);
1808 writeU8(os, damage);
1811 std::string s = os.str();
1812 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1814 Send(0, data, true);
1817 void Client::sendPlayerPos()
1819 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1821 Player *myplayer = m_env.getLocalPlayer();
1822 if(myplayer == NULL)
1827 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1828 our_peer_id = m_con.GetPeerID();
1831 // Set peer id if not set already
1832 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1833 myplayer->peer_id = our_peer_id;
1834 // Check that an existing peer_id is the same as the connection's
1835 assert(myplayer->peer_id == our_peer_id);
1837 v3f pf = myplayer->getPosition();
1838 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1839 v3f sf = myplayer->getSpeed();
1840 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1841 s32 pitch = myplayer->getPitch() * 100;
1842 s32 yaw = myplayer->getYaw() * 100;
1847 [2] v3s32 position*100
1848 [2+12] v3s32 speed*100
1849 [2+12+12] s32 pitch*100
1850 [2+12+12+4] s32 yaw*100
1853 SharedBuffer<u8> data(2+12+12+4+4);
1854 writeU16(&data[0], TOSERVER_PLAYERPOS);
1855 writeV3S32(&data[2], position);
1856 writeV3S32(&data[2+12], speed);
1857 writeS32(&data[2+12+12], pitch);
1858 writeS32(&data[2+12+12+4], yaw);
1860 // Send as unreliable
1861 Send(0, data, false);
1864 void Client::removeNode(v3s16 p)
1866 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1868 core::map<v3s16, MapBlock*> modified_blocks;
1872 //TimeTaker t("removeNodeAndUpdate", m_device);
1873 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1875 catch(InvalidPositionException &e)
1879 for(core::map<v3s16, MapBlock * >::Iterator
1880 i = modified_blocks.getIterator();
1881 i.atEnd() == false; i++)
1883 v3s16 p = i.getNode()->getKey();
1884 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1885 addUpdateMeshTaskWithEdge(p);
1889 void Client::addNode(v3s16 p, MapNode n)
1891 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1893 TimeTaker timer1("Client::addNode()");
1895 core::map<v3s16, MapBlock*> modified_blocks;
1899 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1900 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1902 catch(InvalidPositionException &e)
1905 //TimeTaker timer2("Client::addNode(): updateMeshes");
1907 for(core::map<v3s16, MapBlock * >::Iterator
1908 i = modified_blocks.getIterator();
1909 i.atEnd() == false; i++)
1911 v3s16 p = i.getNode()->getKey();
1912 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1913 addUpdateMeshTaskWithEdge(p);
1917 void Client::updateCamera(v3f pos, v3f dir)
1919 m_env.getClientMap().updateCamera(pos, dir);
1920 camera_position = pos;
1921 camera_direction = dir;
1924 MapNode Client::getNode(v3s16 p)
1926 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1927 return m_env.getMap().getNode(p);
1930 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1932 return m_env.getMap().getNodeMetadata(p);
1935 v3f Client::getPlayerPosition()
1937 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1938 LocalPlayer *player = m_env.getLocalPlayer();
1939 assert(player != NULL);
1940 return player->getPosition();
1943 void Client::setPlayerControl(PlayerControl &control)
1945 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1946 LocalPlayer *player = m_env.getLocalPlayer();
1947 assert(player != NULL);
1948 player->control = control;
1951 // Returns true if the inventory of the local player has been
1952 // updated from the server. If it is true, it is set to false.
1953 bool Client::getLocalInventoryUpdated()
1955 // m_inventory_updated is behind envlock
1956 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1957 bool updated = m_inventory_updated;
1958 m_inventory_updated = false;
1962 // Copies the inventory of the local player to parameter
1963 void Client::getLocalInventory(Inventory &dst)
1965 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1966 Player *player = m_env.getLocalPlayer();
1967 assert(player != NULL);
1968 dst = player->inventory;
1971 InventoryContext *Client::getInventoryContext()
1973 return &m_inventory_context;
1976 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1978 if(id == "current_player")
1980 assert(c->current_player);
1981 return &(c->current_player->inventory);
1985 std::string id0 = fn.next(":");
1987 if(id0 == "nodemeta")
1990 p.X = stoi(fn.next(","));
1991 p.Y = stoi(fn.next(","));
1992 p.Z = stoi(fn.next(","));
1993 NodeMetadata* meta = getNodeMetadata(p);
1995 return meta->getInventory();
1996 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1997 <<"no metadata found"<<std::endl;
2001 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2004 void Client::inventoryAction(InventoryAction *a)
2006 sendInventoryAction(a);
2009 MapBlockObject * Client::getSelectedObject(
2011 v3f from_pos_f_on_map,
2012 core::line3d<f32> shootline_on_map
2015 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2017 core::array<DistanceSortedObject> objects;
2019 for(core::map<v3s16, bool>::Iterator
2020 i = m_active_blocks.getIterator();
2021 i.atEnd() == false; i++)
2023 v3s16 p = i.getNode()->getKey();
2025 MapBlock *block = NULL;
2028 block = m_env.getMap().getBlockNoCreate(p);
2030 catch(InvalidPositionException &e)
2035 // Calculate from_pos relative to block
2036 v3s16 block_pos_i_on_map = block->getPosRelative();
2037 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2038 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
2040 block->getObjects(from_pos_f_on_block, max_d, objects);
2041 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
2044 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2047 // After this, the closest object is the first in the array.
2050 for(u32 i=0; i<objects.size(); i++)
2052 MapBlockObject *obj = objects[i].obj;
2053 MapBlock *block = obj->getBlock();
2055 // Calculate shootline relative to block
2056 v3s16 block_pos_i_on_map = block->getPosRelative();
2057 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2058 core::line3d<f32> shootline_on_block(
2059 shootline_on_map.start - block_pos_f_on_map,
2060 shootline_on_map.end - block_pos_f_on_map
2063 if(obj->isSelected(shootline_on_block))
2065 //dstream<<"Returning selected object"<<std::endl;
2070 //dstream<<"No object selected; returning NULL."<<std::endl;
2074 ClientActiveObject * Client::getSelectedActiveObject(
2076 v3f from_pos_f_on_map,
2077 core::line3d<f32> shootline_on_map
2080 core::array<DistanceSortedActiveObject> objects;
2082 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2084 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2087 // After this, the closest object is the first in the array.
2090 for(u32 i=0; i<objects.size(); i++)
2092 ClientActiveObject *obj = objects[i].obj;
2094 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2095 if(selection_box == NULL)
2098 v3f pos = obj->getPosition();
2100 core::aabbox3d<f32> offsetted_box(
2101 selection_box->MinEdge + pos,
2102 selection_box->MaxEdge + pos
2105 if(offsetted_box.intersectsWithLine(shootline_on_map))
2107 //dstream<<"Returning selected object"<<std::endl;
2112 //dstream<<"No object selected; returning NULL."<<std::endl;
2116 void Client::printDebugInfo(std::ostream &os)
2118 //JMutexAutoLock lock1(m_fetchblock_mutex);
2119 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2121 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2122 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2123 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2127 u32 Client::getDayNightRatio()
2129 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2130 return m_env.getDayNightRatio();
2135 Player *player = m_env.getLocalPlayer();
2136 assert(player != NULL);
2140 void Client::setTempMod(v3s16 p, NodeMod mod)
2142 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2143 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2145 core::map<v3s16, MapBlock*> affected_blocks;
2146 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2149 for(core::map<v3s16, MapBlock*>::Iterator
2150 i = affected_blocks.getIterator();
2151 i.atEnd() == false; i++)
2153 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2157 void Client::clearTempMod(v3s16 p)
2159 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2160 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2162 core::map<v3s16, MapBlock*> affected_blocks;
2163 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2166 for(core::map<v3s16, MapBlock*>::Iterator
2167 i = affected_blocks.getIterator();
2168 i.atEnd() == false; i++)
2170 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2174 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2176 /*dstream<<"Client::addUpdateMeshTask(): "
2177 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2180 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2185 Create a task to update the mesh of the block
2188 MeshMakeData *data = new MeshMakeData;
2191 //TimeTaker timer("data fill");
2193 // Debug: 1-6ms, avg=2ms
2194 data->fill(getDayNightRatio(), b);
2198 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2200 // Add task to queue
2201 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2203 /*dstream<<"Mesh update input queue size is "
2204 <<m_mesh_update_thread.m_queue_in.size()
2208 // Temporary test: make mesh directly in here
2210 //TimeTaker timer("make mesh");
2212 scene::SMesh *mesh_new = NULL;
2213 mesh_new = makeMapBlockMesh(data);
2214 b->replaceMesh(mesh_new);
2220 Mark mesh as non-expired at this point so that it can already
2221 be marked as expired again if the data changes
2223 b->setMeshExpired(false);
2226 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2230 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2231 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2236 v3s16 p = blockpos + v3s16(0,0,0);
2237 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2238 addUpdateMeshTask(p, ack_to_server);
2240 catch(InvalidPositionException &e){}
2243 v3s16 p = blockpos + v3s16(-1,0,0);
2244 addUpdateMeshTask(p);
2246 catch(InvalidPositionException &e){}
2248 v3s16 p = blockpos + v3s16(0,-1,0);
2249 addUpdateMeshTask(p);
2251 catch(InvalidPositionException &e){}
2253 v3s16 p = blockpos + v3s16(0,0,-1);
2254 addUpdateMeshTask(p);
2256 catch(InvalidPositionException &e){}
2259 ClientEvent Client::getClientEvent()
2261 if(m_client_event_queue.size() == 0)
2264 event.type = CE_NONE;
2267 return m_client_event_queue.pop_front();