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());
936 Add it to mesh update queue and set it to be acknowledged after update.
938 //std::cerr<<"Adding mesh update task for received block"<<std::endl;
939 addUpdateMeshTaskWithEdge(p, true);
941 else if(command == TOCLIENT_PLAYERPOS)
943 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
947 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
948 our_peer_id = m_con.GetPeerID();
950 // Cancel if we don't have a peer id
951 if(our_peer_id == PEER_ID_INEXISTENT){
952 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
959 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
961 u32 player_size = 2+12+12+4+4;
963 u32 player_count = (datasize-2) / player_size;
965 for(u32 i=0; i<player_count; i++)
967 u16 peer_id = readU16(&data[start]);
969 Player *player = m_env.getPlayer(peer_id);
971 // Skip if player doesn't exist
974 start += player_size;
978 // Skip if player is local player
979 if(player->isLocal())
981 start += player_size;
985 v3s32 ps = readV3S32(&data[start+2]);
986 v3s32 ss = readV3S32(&data[start+2+12]);
987 s32 pitch_i = readS32(&data[start+2+12+12]);
988 s32 yaw_i = readS32(&data[start+2+12+12+4]);
989 /*dstream<<"Client: got "
990 <<"pitch_i="<<pitch_i
991 <<" yaw_i="<<yaw_i<<std::endl;*/
992 f32 pitch = (f32)pitch_i / 100.0;
993 f32 yaw = (f32)yaw_i / 100.0;
994 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
995 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
996 player->setPosition(position);
997 player->setSpeed(speed);
998 player->setPitch(pitch);
1001 /*dstream<<"Client: player "<<peer_id
1003 <<" yaw="<<yaw<<std::endl;*/
1005 start += player_size;
1009 else if(command == TOCLIENT_PLAYERINFO)
1013 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1014 our_peer_id = m_con.GetPeerID();
1016 // Cancel if we don't have a peer id
1017 if(our_peer_id == PEER_ID_INEXISTENT){
1018 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
1019 "we have no peer id"
1024 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
1027 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1029 u32 item_size = 2+PLAYERNAME_SIZE;
1030 u32 player_count = (datasize-2) / item_size;
1033 core::list<u16> players_alive;
1034 for(u32 i=0; i<player_count; i++)
1036 // Make sure the name ends in '\0'
1037 data[start+2+20-1] = 0;
1039 u16 peer_id = readU16(&data[start]);
1041 players_alive.push_back(peer_id);
1043 /*dstream<<DTIME<<"peer_id="<<peer_id
1044 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1046 // Don't update the info of the local player
1047 if(peer_id == our_peer_id)
1053 Player *player = m_env.getPlayer(peer_id);
1055 // Create a player if it doesn't exist
1058 player = new RemotePlayer(
1059 m_device->getSceneManager()->getRootSceneNode(),
1062 player->peer_id = peer_id;
1063 m_env.addPlayer(player);
1064 dout_client<<DTIME<<"Client: Adding new player "
1065 <<peer_id<<std::endl;
1068 player->updateName((char*)&data[start+2]);
1074 Remove those players from the environment that
1075 weren't listed by the server.
1077 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1078 core::list<Player*> players = m_env.getPlayers();
1079 core::list<Player*>::Iterator ip;
1080 for(ip=players.begin(); ip!=players.end(); ip++)
1082 // Ingore local player
1083 if((*ip)->isLocal())
1086 // Warn about a special case
1087 if((*ip)->peer_id == 0)
1089 dstream<<DTIME<<"WARNING: Client: Removing "
1090 "dead player with id=0"<<std::endl;
1093 bool is_alive = false;
1094 core::list<u16>::Iterator i;
1095 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1097 if((*ip)->peer_id == *i)
1103 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1104 <<" is_alive="<<is_alive<<std::endl;*/
1107 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1109 m_env.removePlayer((*ip)->peer_id);
1113 else if(command == TOCLIENT_SECTORMETA)
1115 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1120 [3...] v2s16 pos + sector metadata
1125 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1128 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1130 std::string datastring((char*)&data[2], datasize-2);
1131 std::istringstream is(datastring, std::ios_base::binary);
1135 is.read((char*)buf, 1);
1136 u16 sector_count = readU8(buf);
1138 //dstream<<"sector_count="<<sector_count<<std::endl;
1140 for(u16 i=0; i<sector_count; i++)
1143 is.read((char*)buf, 4);
1144 v2s16 pos = readV2S16(buf);
1145 /*dstream<<"Client: deserializing sector at "
1146 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1148 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1149 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1154 else if(command == TOCLIENT_INVENTORY)
1159 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1162 //TimeTaker t2("mutex locking", m_device);
1163 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1166 //TimeTaker t3("istringstream init", m_device);
1167 std::string datastring((char*)&data[2], datasize-2);
1168 std::istringstream is(datastring, std::ios_base::binary);
1171 //m_env.printPlayers(dstream);
1173 //TimeTaker t4("player get", m_device);
1174 Player *player = m_env.getLocalPlayer();
1175 assert(player != NULL);
1178 //TimeTaker t1("inventory.deSerialize()", m_device);
1179 player->inventory.deSerialize(is);
1182 m_inventory_updated = true;
1184 //dstream<<"Client got player inventory:"<<std::endl;
1185 //player->inventory.print(dstream);
1189 else if(command == TOCLIENT_OBJECTDATA)
1192 // Strip command word and create a stringstream
1193 std::string datastring((char*)&data[2], datasize-2);
1194 std::istringstream is(datastring, std::ios_base::binary);
1198 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1206 is.read((char*)buf, 2);
1207 u16 playercount = readU16(buf);
1209 for(u16 i=0; i<playercount; i++)
1211 is.read((char*)buf, 2);
1212 u16 peer_id = readU16(buf);
1213 is.read((char*)buf, 12);
1214 v3s32 p_i = readV3S32(buf);
1215 is.read((char*)buf, 12);
1216 v3s32 s_i = readV3S32(buf);
1217 is.read((char*)buf, 4);
1218 s32 pitch_i = readS32(buf);
1219 is.read((char*)buf, 4);
1220 s32 yaw_i = readS32(buf);
1222 Player *player = m_env.getPlayer(peer_id);
1224 // Skip if player doesn't exist
1230 // Skip if player is local player
1231 if(player->isLocal())
1236 f32 pitch = (f32)pitch_i / 100.0;
1237 f32 yaw = (f32)yaw_i / 100.0;
1238 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1239 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1241 player->setPosition(position);
1242 player->setSpeed(speed);
1243 player->setPitch(pitch);
1244 player->setYaw(yaw);
1249 NOTE: Deprecated stuff here, TODO: Remove
1252 // Read active block count
1253 is.read((char*)buf, 2);
1254 u16 blockcount = readU16(buf);
1256 // Initialize delete queue with all active blocks
1257 core::map<v3s16, bool> abs_to_delete;
1258 for(core::map<v3s16, bool>::Iterator
1259 i = m_active_blocks.getIterator();
1260 i.atEnd() == false; i++)
1262 v3s16 p = i.getNode()->getKey();
1263 /*dstream<<"adding "
1264 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1265 <<" to abs_to_delete"
1267 abs_to_delete.insert(p, true);
1270 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1273 for(u16 i=0; i<blockcount; i++)
1276 is.read((char*)buf, 6);
1277 v3s16 p = readV3S16(buf);
1278 // Get block from somewhere
1279 MapBlock *block = NULL;
1281 block = m_env.getMap().getBlockNoCreate(p);
1283 catch(InvalidPositionException &e)
1285 //TODO: Create a dummy block?
1289 dstream<<"WARNING: "
1290 <<"Could not get block at blockpos "
1291 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1292 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1293 <<"following block object data."
1298 /*dstream<<"Client updating objects for block "
1299 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1302 // Insert to active block list
1303 m_active_blocks.insert(p, true);
1305 // Remove from deletion queue
1306 if(abs_to_delete.find(p) != NULL)
1307 abs_to_delete.remove(p);
1310 Update objects of block
1312 NOTE: Be sure this is done in the main thread.
1314 block->updateObjects(is, m_server_ser_ver,
1315 m_device->getSceneManager(), m_env.getDayNightRatio());
1318 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1321 // Delete objects of blocks in delete queue
1322 for(core::map<v3s16, bool>::Iterator
1323 i = abs_to_delete.getIterator();
1324 i.atEnd() == false; i++)
1326 v3s16 p = i.getNode()->getKey();
1329 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1332 block->clearObjects();
1333 // Remove from active blocks list
1334 m_active_blocks.remove(p);
1336 catch(InvalidPositionException &e)
1338 dstream<<"WARNAING: Client: "
1339 <<"Couldn't clear objects of active->inactive"
1341 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1342 <<" because block was not found"
1350 else if(command == TOCLIENT_TIME_OF_DAY)
1355 u16 time_of_day = readU16(&data[2]);
1356 time_of_day = time_of_day % 24000;
1357 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1365 m_env.setTimeOfDay(time_of_day);
1367 u32 dr = m_env.getDayNightRatio();
1369 dstream<<"Client: time_of_day="<<time_of_day
1375 else if(command == TOCLIENT_CHAT_MESSAGE)
1383 std::string datastring((char*)&data[2], datasize-2);
1384 std::istringstream is(datastring, std::ios_base::binary);
1387 is.read((char*)buf, 2);
1388 u16 len = readU16(buf);
1390 std::wstring message;
1391 for(u16 i=0; i<len; i++)
1393 is.read((char*)buf, 2);
1394 message += (wchar_t)readU16(buf);
1397 /*dstream<<"Client received chat message: "
1398 <<wide_to_narrow(message)<<std::endl;*/
1400 m_chat_queue.push_back(message);
1402 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1404 //if(g_settings.getBool("enable_experimental"))
1408 u16 count of removed objects
1409 for all removed objects {
1412 u16 count of added objects
1413 for all added objects {
1416 u16 initialization data length
1417 string initialization data
1422 // Get all data except the command number
1423 std::string datastring((char*)&data[2], datasize-2);
1424 // Throw them in an istringstream
1425 std::istringstream is(datastring, std::ios_base::binary);
1429 // Read removed objects
1431 u16 removed_count = readU16((u8*)buf);
1432 for(u16 i=0; i<removed_count; i++)
1435 u16 id = readU16((u8*)buf);
1438 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1439 m_env.removeActiveObject(id);
1443 // Read added objects
1445 u16 added_count = readU16((u8*)buf);
1446 for(u16 i=0; i<added_count; i++)
1449 u16 id = readU16((u8*)buf);
1451 u8 type = readU8((u8*)buf);
1452 std::string data = deSerializeLongString(is);
1455 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1456 m_env.addActiveObject(id, type, data);
1461 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1463 //if(g_settings.getBool("enable_experimental"))
1475 // Get all data except the command number
1476 std::string datastring((char*)&data[2], datasize-2);
1477 // Throw them in an istringstream
1478 std::istringstream is(datastring, std::ios_base::binary);
1480 while(is.eof() == false)
1484 u16 id = readU16((u8*)buf);
1488 u16 message_size = readU16((u8*)buf);
1489 std::string message;
1490 message.reserve(message_size);
1491 for(u16 i=0; i<message_size; i++)
1494 message.append(buf, 1);
1496 // Pass on to the environment
1498 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1499 m_env.processActiveObjectMessage(id, message);
1504 else if(command == TOCLIENT_HP)
1506 std::string datastring((char*)&data[2], datasize-2);
1507 std::istringstream is(datastring, std::ios_base::binary);
1508 Player *player = m_env.getLocalPlayer();
1509 assert(player != NULL);
1513 else if(command == TOCLIENT_MOVE_PLAYER)
1515 std::string datastring((char*)&data[2], datasize-2);
1516 std::istringstream is(datastring, std::ios_base::binary);
1517 Player *player = m_env.getLocalPlayer();
1518 assert(player != NULL);
1519 v3f pos = readV3F1000(is);
1520 f32 pitch = readF1000(is);
1521 f32 yaw = readF1000(is);
1522 player->setPosition(pos);
1523 /*player->setPitch(pitch);
1524 player->setYaw(yaw);*/
1526 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1527 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1533 Add to ClientEvent queue.
1534 This has to be sent to the main program because otherwise
1535 it would just force the pitch and yaw values to whatever
1536 the camera points to.
1539 event.type = CE_PLAYER_FORCE_MOVE;
1540 event.player_force_move.pitch = pitch;
1541 event.player_force_move.yaw = yaw;
1542 m_client_event_queue.push_back(event);
1544 // Ignore damage for a few seconds, so that the player doesn't
1545 // get damage from falling on ground
1546 m_ignore_damage_timer = 3.0;
1550 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1551 <<command<<std::endl;
1555 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1557 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1558 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1561 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1562 v3s16 nodepos_oversurface, u16 item)
1564 if(connectedAndInitialized() == false){
1565 dout_client<<DTIME<<"Client::groundAction() "
1566 "cancelled (not connected)"
1575 [3] v3s16 nodepos_undersurface
1576 [9] v3s16 nodepos_abovesurface
1581 2: stop digging (all parameters ignored)
1582 3: digging completed
1584 u8 datasize = 2 + 1 + 6 + 6 + 2;
1585 SharedBuffer<u8> data(datasize);
1586 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1587 writeU8(&data[2], action);
1588 writeV3S16(&data[3], nodepos_undersurface);
1589 writeV3S16(&data[9], nodepos_oversurface);
1590 writeU16(&data[15], item);
1591 Send(0, data, true);
1594 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1596 if(connectedAndInitialized() == false){
1597 dout_client<<DTIME<<"Client::clickObject() "
1598 "cancelled (not connected)"
1604 [0] u16 command=TOSERVER_CLICK_OBJECT
1605 [2] u8 button (0=left, 1=right)
1610 u8 datasize = 2 + 1 + 6 + 2 + 2;
1611 SharedBuffer<u8> data(datasize);
1612 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1613 writeU8(&data[2], button);
1614 writeV3S16(&data[3], blockpos);
1615 writeS16(&data[9], id);
1616 writeU16(&data[11], item);
1617 Send(0, data, true);
1620 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1622 if(connectedAndInitialized() == false){
1623 dout_client<<DTIME<<"Client::clickActiveObject() "
1624 "cancelled (not connected)"
1632 [2] u8 button (0=left, 1=right)
1636 u8 datasize = 2 + 1 + 6 + 2 + 2;
1637 SharedBuffer<u8> data(datasize);
1638 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1639 writeU8(&data[2], button);
1640 writeU16(&data[3], id);
1641 writeU16(&data[5], item);
1642 Send(0, data, true);
1645 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1654 std::ostringstream os(std::ios_base::binary);
1658 writeU16(buf, TOSERVER_SIGNTEXT);
1659 os.write((char*)buf, 2);
1662 writeV3S16(buf, blockpos);
1663 os.write((char*)buf, 6);
1667 os.write((char*)buf, 2);
1669 u16 textlen = text.size();
1670 // Write text length
1671 writeS16(buf, textlen);
1672 os.write((char*)buf, 2);
1675 os.write((char*)text.c_str(), textlen);
1678 std::string s = os.str();
1679 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1681 Send(0, data, true);
1684 void Client::sendSignNodeText(v3s16 p, std::string text)
1692 std::ostringstream os(std::ios_base::binary);
1696 writeU16(buf, TOSERVER_SIGNNODETEXT);
1697 os.write((char*)buf, 2);
1701 os.write((char*)buf, 6);
1703 u16 textlen = text.size();
1704 // Write text length
1705 writeS16(buf, textlen);
1706 os.write((char*)buf, 2);
1709 os.write((char*)text.c_str(), textlen);
1712 std::string s = os.str();
1713 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1715 Send(0, data, true);
1718 void Client::sendInventoryAction(InventoryAction *a)
1720 std::ostringstream os(std::ios_base::binary);
1724 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1725 os.write((char*)buf, 2);
1730 std::string s = os.str();
1731 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1733 Send(0, data, true);
1736 void Client::sendChatMessage(const std::wstring &message)
1738 std::ostringstream os(std::ios_base::binary);
1742 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1743 os.write((char*)buf, 2);
1746 writeU16(buf, message.size());
1747 os.write((char*)buf, 2);
1750 for(u32 i=0; i<message.size(); i++)
1754 os.write((char*)buf, 2);
1758 std::string s = os.str();
1759 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1761 Send(0, data, true);
1764 void Client::sendChangePassword(const std::wstring oldpassword,
1765 const std::wstring newpassword)
1767 Player *player = m_env.getLocalPlayer();
1771 std::string playername = player->getName();
1772 std::string oldpwd = translatePassword(playername, oldpassword);
1773 std::string newpwd = translatePassword(playername, newpassword);
1775 std::ostringstream os(std::ios_base::binary);
1776 u8 buf[2+PASSWORD_SIZE*2];
1778 [0] u16 TOSERVER_PASSWORD
1779 [2] u8[28] old password
1780 [30] u8[28] new password
1783 writeU16(buf, TOSERVER_PASSWORD);
1784 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1786 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1787 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1789 buf[2+PASSWORD_SIZE-1] = 0;
1790 buf[30+PASSWORD_SIZE-1] = 0;
1791 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1794 std::string s = os.str();
1795 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1797 Send(0, data, true);
1801 void Client::sendDamage(u8 damage)
1803 DSTACK(__FUNCTION_NAME);
1804 std::ostringstream os(std::ios_base::binary);
1806 writeU16(os, TOSERVER_DAMAGE);
1807 writeU8(os, damage);
1810 std::string s = os.str();
1811 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1813 Send(0, data, true);
1816 void Client::sendPlayerPos()
1818 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1820 Player *myplayer = m_env.getLocalPlayer();
1821 if(myplayer == NULL)
1826 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1827 our_peer_id = m_con.GetPeerID();
1830 // Set peer id if not set already
1831 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1832 myplayer->peer_id = our_peer_id;
1833 // Check that an existing peer_id is the same as the connection's
1834 assert(myplayer->peer_id == our_peer_id);
1836 v3f pf = myplayer->getPosition();
1837 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1838 v3f sf = myplayer->getSpeed();
1839 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1840 s32 pitch = myplayer->getPitch() * 100;
1841 s32 yaw = myplayer->getYaw() * 100;
1846 [2] v3s32 position*100
1847 [2+12] v3s32 speed*100
1848 [2+12+12] s32 pitch*100
1849 [2+12+12+4] s32 yaw*100
1852 SharedBuffer<u8> data(2+12+12+4+4);
1853 writeU16(&data[0], TOSERVER_PLAYERPOS);
1854 writeV3S32(&data[2], position);
1855 writeV3S32(&data[2+12], speed);
1856 writeS32(&data[2+12+12], pitch);
1857 writeS32(&data[2+12+12+4], yaw);
1859 // Send as unreliable
1860 Send(0, data, false);
1863 void Client::removeNode(v3s16 p)
1865 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1867 core::map<v3s16, MapBlock*> modified_blocks;
1871 //TimeTaker t("removeNodeAndUpdate", m_device);
1872 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1874 catch(InvalidPositionException &e)
1878 for(core::map<v3s16, MapBlock * >::Iterator
1879 i = modified_blocks.getIterator();
1880 i.atEnd() == false; i++)
1882 v3s16 p = i.getNode()->getKey();
1883 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1884 addUpdateMeshTaskWithEdge(p);
1888 void Client::addNode(v3s16 p, MapNode n)
1890 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1892 TimeTaker timer1("Client::addNode()");
1894 core::map<v3s16, MapBlock*> modified_blocks;
1898 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1899 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1901 catch(InvalidPositionException &e)
1904 //TimeTaker timer2("Client::addNode(): updateMeshes");
1906 for(core::map<v3s16, MapBlock * >::Iterator
1907 i = modified_blocks.getIterator();
1908 i.atEnd() == false; i++)
1910 v3s16 p = i.getNode()->getKey();
1911 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1912 addUpdateMeshTaskWithEdge(p);
1916 void Client::updateCamera(v3f pos, v3f dir)
1918 m_env.getClientMap().updateCamera(pos, dir);
1919 camera_position = pos;
1920 camera_direction = dir;
1923 MapNode Client::getNode(v3s16 p)
1925 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1926 return m_env.getMap().getNode(p);
1929 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1931 return m_env.getMap().getNodeMetadata(p);
1934 v3f Client::getPlayerPosition()
1936 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1937 LocalPlayer *player = m_env.getLocalPlayer();
1938 assert(player != NULL);
1939 return player->getPosition();
1942 void Client::setPlayerControl(PlayerControl &control)
1944 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1945 LocalPlayer *player = m_env.getLocalPlayer();
1946 assert(player != NULL);
1947 player->control = control;
1950 // Returns true if the inventory of the local player has been
1951 // updated from the server. If it is true, it is set to false.
1952 bool Client::getLocalInventoryUpdated()
1954 // m_inventory_updated is behind envlock
1955 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1956 bool updated = m_inventory_updated;
1957 m_inventory_updated = false;
1961 // Copies the inventory of the local player to parameter
1962 void Client::getLocalInventory(Inventory &dst)
1964 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1965 Player *player = m_env.getLocalPlayer();
1966 assert(player != NULL);
1967 dst = player->inventory;
1970 InventoryContext *Client::getInventoryContext()
1972 return &m_inventory_context;
1975 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1977 if(id == "current_player")
1979 assert(c->current_player);
1980 return &(c->current_player->inventory);
1984 std::string id0 = fn.next(":");
1986 if(id0 == "nodemeta")
1989 p.X = stoi(fn.next(","));
1990 p.Y = stoi(fn.next(","));
1991 p.Z = stoi(fn.next(","));
1992 NodeMetadata* meta = getNodeMetadata(p);
1994 return meta->getInventory();
1995 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1996 <<"no metadata found"<<std::endl;
2000 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2003 void Client::inventoryAction(InventoryAction *a)
2005 sendInventoryAction(a);
2008 MapBlockObject * Client::getSelectedObject(
2010 v3f from_pos_f_on_map,
2011 core::line3d<f32> shootline_on_map
2014 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2016 core::array<DistanceSortedObject> objects;
2018 for(core::map<v3s16, bool>::Iterator
2019 i = m_active_blocks.getIterator();
2020 i.atEnd() == false; i++)
2022 v3s16 p = i.getNode()->getKey();
2024 MapBlock *block = NULL;
2027 block = m_env.getMap().getBlockNoCreate(p);
2029 catch(InvalidPositionException &e)
2034 // Calculate from_pos relative to block
2035 v3s16 block_pos_i_on_map = block->getPosRelative();
2036 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2037 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
2039 block->getObjects(from_pos_f_on_block, max_d, objects);
2040 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
2043 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2046 // After this, the closest object is the first in the array.
2049 for(u32 i=0; i<objects.size(); i++)
2051 MapBlockObject *obj = objects[i].obj;
2052 MapBlock *block = obj->getBlock();
2054 // Calculate shootline relative to block
2055 v3s16 block_pos_i_on_map = block->getPosRelative();
2056 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2057 core::line3d<f32> shootline_on_block(
2058 shootline_on_map.start - block_pos_f_on_map,
2059 shootline_on_map.end - block_pos_f_on_map
2062 if(obj->isSelected(shootline_on_block))
2064 //dstream<<"Returning selected object"<<std::endl;
2069 //dstream<<"No object selected; returning NULL."<<std::endl;
2073 ClientActiveObject * Client::getSelectedActiveObject(
2075 v3f from_pos_f_on_map,
2076 core::line3d<f32> shootline_on_map
2079 core::array<DistanceSortedActiveObject> objects;
2081 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2083 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2086 // After this, the closest object is the first in the array.
2089 for(u32 i=0; i<objects.size(); i++)
2091 ClientActiveObject *obj = objects[i].obj;
2093 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2094 if(selection_box == NULL)
2097 v3f pos = obj->getPosition();
2099 core::aabbox3d<f32> offsetted_box(
2100 selection_box->MinEdge + pos,
2101 selection_box->MaxEdge + pos
2104 if(offsetted_box.intersectsWithLine(shootline_on_map))
2106 //dstream<<"Returning selected object"<<std::endl;
2111 //dstream<<"No object selected; returning NULL."<<std::endl;
2115 void Client::printDebugInfo(std::ostream &os)
2117 //JMutexAutoLock lock1(m_fetchblock_mutex);
2118 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2120 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2121 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2122 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2126 u32 Client::getDayNightRatio()
2128 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2129 return m_env.getDayNightRatio();
2134 Player *player = m_env.getLocalPlayer();
2135 assert(player != NULL);
2139 void Client::setTempMod(v3s16 p, NodeMod mod)
2141 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2142 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2144 core::map<v3s16, MapBlock*> affected_blocks;
2145 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2148 for(core::map<v3s16, MapBlock*>::Iterator
2149 i = affected_blocks.getIterator();
2150 i.atEnd() == false; i++)
2152 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2156 void Client::clearTempMod(v3s16 p)
2158 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2159 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2161 core::map<v3s16, MapBlock*> affected_blocks;
2162 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2165 for(core::map<v3s16, MapBlock*>::Iterator
2166 i = affected_blocks.getIterator();
2167 i.atEnd() == false; i++)
2169 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2173 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2175 /*dstream<<"Client::addUpdateMeshTask(): "
2176 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2179 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2184 Create a task to update the mesh of the block
2187 MeshMakeData *data = new MeshMakeData;
2190 //TimeTaker timer("data fill");
2192 // Debug: 1-6ms, avg=2ms
2193 data->fill(getDayNightRatio(), b);
2197 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2199 // Add task to queue
2200 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2202 /*dstream<<"Mesh update input queue size is "
2203 <<m_mesh_update_thread.m_queue_in.size()
2207 // Temporary test: make mesh directly in here
2209 //TimeTaker timer("make mesh");
2211 scene::SMesh *mesh_new = NULL;
2212 mesh_new = makeMapBlockMesh(data);
2213 b->replaceMesh(mesh_new);
2219 Mark mesh as non-expired at this point so that it can already
2220 be marked as expired again if the data changes
2222 b->setMeshExpired(false);
2225 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2229 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2230 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2235 v3s16 p = blockpos + v3s16(0,0,0);
2236 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2237 addUpdateMeshTask(p, ack_to_server);
2239 catch(InvalidPositionException &e){}
2242 v3s16 p = blockpos + v3s16(-1,0,0);
2243 addUpdateMeshTask(p);
2245 catch(InvalidPositionException &e){}
2247 v3s16 p = blockpos + v3s16(0,-1,0);
2248 addUpdateMeshTask(p);
2250 catch(InvalidPositionException &e){}
2252 v3s16 p = blockpos + v3s16(0,0,-1);
2253 addUpdateMeshTask(p);
2255 catch(InvalidPositionException &e){}
2258 ClientEvent Client::getClientEvent()
2260 if(m_client_event_queue.size() == 0)
2263 event.type = CE_NONE;
2266 return m_client_event_queue.pop_front();