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();
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 g_settings.getFloat("client_unload_unused_data_timeout");
333 // Delete sector blocks
334 /*u32 num = m_env.getMap().unloadUnusedData
335 (delete_unused_sectors_timeout,
336 true, &deleted_blocks);*/
338 // Delete whole sectors
339 m_env.getMap().unloadUnusedData
340 (delete_unused_sectors_timeout,
343 if(deleted_blocks.size() > 0)
345 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
346 <<" unused sectors"<<std::endl;*/
347 /*dstream<<DTIME<<"Client: Deleted "<<num
348 <<" unused sectors"<<std::endl;*/
354 // Env is locked so con can be locked.
355 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
357 core::list<v3s16>::Iterator i = deleted_blocks.begin();
358 core::list<v3s16> sendlist;
361 if(sendlist.size() == 255 || i == deleted_blocks.end())
363 if(sendlist.size() == 0)
372 u32 replysize = 2+1+6*sendlist.size();
373 SharedBuffer<u8> reply(replysize);
374 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
375 reply[2] = sendlist.size();
377 for(core::list<v3s16>::Iterator
378 j = sendlist.begin();
379 j != sendlist.end(); j++)
381 writeV3S16(&reply[2+1+6*k], *j);
384 m_con.Send(PEER_ID_SERVER, 1, reply, true);
386 if(i == deleted_blocks.end())
392 sendlist.push_back(*i);
400 if(connected == false)
402 float &counter = m_connection_reinit_timer;
408 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
410 Player *myplayer = m_env.getLocalPlayer();
411 assert(myplayer != NULL);
413 // Send TOSERVER_INIT
414 // [0] u16 TOSERVER_INIT
415 // [2] u8 SER_FMT_VER_HIGHEST
416 // [3] u8[20] player_name
417 // [23] u8[28] password
418 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
419 writeU16(&data[0], TOSERVER_INIT);
420 writeU8(&data[2], SER_FMT_VER_HIGHEST);
422 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
423 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
425 /*dstream<<"Client: password hash is \""<<m_password<<"\""
428 memset((char*)&data[23], 0, PASSWORD_SIZE);
429 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
431 // Send as unreliable
432 Send(0, data, false);
435 // Not connected, return
440 Do stuff if connected
444 Run Map's timers and unload unused data
446 const float map_timer_and_unload_dtime = 5.25;
447 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
449 ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
450 core::list<v3s16> deleted_blocks;
451 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
452 g_settings.getFloat("client_unload_unused_data_timeout"),
455 /*if(deleted_blocks.size() > 0)
456 dstream<<"Client: Unloaded "<<deleted_blocks.size()
457 <<" unused blocks"<<std::endl;*/
461 NOTE: This loop is intentionally iterated the way it is.
464 core::list<v3s16>::Iterator i = deleted_blocks.begin();
465 core::list<v3s16> sendlist;
468 if(sendlist.size() == 255 || i == deleted_blocks.end())
470 if(sendlist.size() == 0)
479 u32 replysize = 2+1+6*sendlist.size();
480 SharedBuffer<u8> reply(replysize);
481 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
482 reply[2] = sendlist.size();
484 for(core::list<v3s16>::Iterator
485 j = sendlist.begin();
486 j != sendlist.end(); j++)
488 writeV3S16(&reply[2+1+6*k], *j);
491 m_con.Send(PEER_ID_SERVER, 1, reply, true);
493 if(i == deleted_blocks.end())
499 sendlist.push_back(*i);
509 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
511 // Control local player (0ms)
512 LocalPlayer *player = m_env.getLocalPlayer();
513 assert(player != NULL);
514 player->applyControl(dtime);
516 //TimeTaker envtimer("env step", m_device);
522 NOTE: These old objects are DEPRECATED. TODO: Remove
524 for(core::map<v3s16, bool>::Iterator
525 i = m_active_blocks.getIterator();
526 i.atEnd() == false; i++)
528 v3s16 p = i.getNode()->getKey();
530 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
534 // Step MapBlockObjects
535 block->stepObjects(dtime, false, m_env.getDayNightRatio());
543 ClientEnvEvent event = m_env.getClientEvent();
544 if(event.type == CEE_NONE)
548 else if(event.type == CEE_PLAYER_DAMAGE)
550 if(m_ignore_damage_timer <= 0)
552 u8 damage = event.player_damage.amount;
555 // Add to ClientEvent queue
557 event.type = CE_PLAYER_DAMAGE;
558 event.player_damage.amount = damage;
559 m_client_event_queue.push_back(event);
569 float &counter = m_avg_rtt_timer;
574 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
575 // connectedAndInitialized() is true, peer exists.
576 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
577 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
582 Send player position to server
585 float &counter = m_playerpos_send_timer;
595 Replace updated meshes
598 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
600 //TimeTaker timer("** Processing mesh update result queue");
603 /*dstream<<"Mesh update result queue size is "
604 <<m_mesh_update_thread.m_queue_out.size()
607 while(m_mesh_update_thread.m_queue_out.size() > 0)
609 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
610 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
613 block->replaceMesh(r.mesh);
615 if(r.ack_block_to_server)
617 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
618 <<","<<r.p.Z<<")"<<std::endl;*/
629 u32 replysize = 2+1+6;
630 SharedBuffer<u8> reply(replysize);
631 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
633 writeV3S16(&reply[3], r.p);
635 m_con.Send(PEER_ID_SERVER, 1, reply, true);
641 // Virtual methods from con::PeerHandler
642 void Client::peerAdded(con::Peer *peer)
644 derr_client<<"Client::peerAdded(): peer->id="
645 <<peer->id<<std::endl;
647 void Client::deletingPeer(con::Peer *peer, bool timeout)
649 derr_client<<"Client::deletingPeer(): "
650 "Server Peer is getting deleted "
651 <<"(timeout="<<timeout<<")"<<std::endl;
654 void Client::ReceiveAll()
656 DSTACK(__FUNCTION_NAME);
662 catch(con::NoIncomingDataException &e)
666 catch(con::InvalidIncomingDataException &e)
668 dout_client<<DTIME<<"Client::ReceiveAll(): "
669 "InvalidIncomingDataException: what()="
670 <<e.what()<<std::endl;
675 void Client::Receive()
677 DSTACK(__FUNCTION_NAME);
678 u32 data_maxsize = 200000;
679 Buffer<u8> data(data_maxsize);
683 //TimeTaker t1("con mutex and receive", m_device);
684 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
685 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
687 //TimeTaker t1("ProcessData", m_device);
688 ProcessData(*data, datasize, sender_peer_id);
692 sender_peer_id given to this shall be quaranteed to be a valid peer
694 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
696 DSTACK(__FUNCTION_NAME);
698 // Ignore packets that don't even fit a command
701 m_packetcounter.add(60000);
705 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
707 //dstream<<"Client: received command="<<command<<std::endl;
708 m_packetcounter.add((u16)command);
711 If this check is removed, be sure to change the queue
712 system to know the ids
714 if(sender_peer_id != PEER_ID_SERVER)
716 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
717 "coming from server: peer_id="<<sender_peer_id
724 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
725 // All data is coming from the server
726 // PeerNotFoundException is handled by caller.
727 peer = m_con.GetPeer(PEER_ID_SERVER);
730 u8 ser_version = m_server_ser_ver;
732 //dstream<<"Client received command="<<(int)command<<std::endl;
734 if(command == TOCLIENT_INIT)
739 u8 deployed = data[2];
741 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
742 "deployed="<<((int)deployed&0xff)<<std::endl;
744 if(deployed < SER_FMT_VER_LOWEST
745 || deployed > SER_FMT_VER_HIGHEST)
747 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
748 <<"unsupported ser_fmt_ver"<<std::endl;
752 m_server_ser_ver = deployed;
754 // Get player position
755 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
756 if(datasize >= 2+1+6)
757 playerpos_s16 = readV3S16(&data[2+1]);
758 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
761 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
763 // Set player position
764 Player *player = m_env.getLocalPlayer();
765 assert(player != NULL);
766 player->setPosition(playerpos_f);
769 if(datasize >= 2+1+6+8)
772 m_map_seed = readU64(&data[2+1+6]);
773 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
778 SharedBuffer<u8> reply(replysize);
779 writeU16(&reply[0], TOSERVER_INIT2);
781 m_con.Send(PEER_ID_SERVER, 1, reply, true);
786 if(command == TOCLIENT_ACCESS_DENIED)
788 // The server didn't like our password. Note, this needs
789 // to be processed even if the serialisation format has
790 // not been agreed yet, the same as TOCLIENT_INIT.
791 m_access_denied = true;
792 m_access_denied_reason = L"Unknown";
795 std::string datastring((char*)&data[2], datasize-2);
796 std::istringstream is(datastring, std::ios_base::binary);
797 m_access_denied_reason = deSerializeWideString(is);
802 if(ser_version == SER_FMT_VER_INVALID)
804 dout_client<<DTIME<<"WARNING: Client: Server serialization"
805 " format invalid or not initialized."
806 " Skipping incoming command="<<command<<std::endl;
810 // Just here to avoid putting the two if's together when
811 // making some copypasta
814 if(command == TOCLIENT_REMOVENODE)
819 p.X = readS16(&data[2]);
820 p.Y = readS16(&data[4]);
821 p.Z = readS16(&data[6]);
823 //TimeTaker t1("TOCLIENT_REMOVENODE");
825 // This will clear the cracking animation after digging
826 ((ClientMap&)m_env.getMap()).clearTempMod(p);
830 else if(command == TOCLIENT_ADDNODE)
832 if(datasize < 8 + MapNode::serializedLength(ser_version))
836 p.X = readS16(&data[2]);
837 p.Y = readS16(&data[4]);
838 p.Z = readS16(&data[6]);
840 //TimeTaker t1("TOCLIENT_ADDNODE");
843 n.deSerialize(&data[8], ser_version);
847 else if(command == TOCLIENT_BLOCKDATA)
849 // Ignore too small packet
854 p.X = readS16(&data[2]);
855 p.Y = readS16(&data[4]);
856 p.Z = readS16(&data[6]);
858 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
859 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
860 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
861 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
863 std::string datastring((char*)&data[8], datasize-8);
864 std::istringstream istr(datastring, std::ios_base::binary);
870 sector = m_env.getMap().emergeSector(p2d);
872 assert(sector->getPos() == p2d);
874 //TimeTaker timer("MapBlock deSerialize");
877 block = sector->getBlockNoCreateNoEx(p.Y);
881 Update an existing block
883 //dstream<<"Updating"<<std::endl;
884 block->deSerialize(istr, ser_version);
891 //dstream<<"Creating new"<<std::endl;
892 block = new MapBlock(&m_env.getMap(), p);
893 block->deSerialize(istr, ser_version);
894 sector->insertBlock(block);
898 mod.type = NODEMOD_CHANGECONTENT;
899 mod.param = CONTENT_MESE;
900 block->setTempMod(v3s16(8,10,8), mod);
901 block->setTempMod(v3s16(8,9,8), mod);
902 block->setTempMod(v3s16(8,8,8), mod);
903 block->setTempMod(v3s16(8,7,8), mod);
904 block->setTempMod(v3s16(8,6,8), mod);*/
918 u32 replysize = 2+1+6;
919 SharedBuffer<u8> reply(replysize);
920 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
922 writeV3S16(&reply[3], p);
924 m_con.Send(PEER_ID_SERVER, 1, reply, true);
928 Update Mesh of this block and blocks at x-, y- and z-.
929 Environment should not be locked as it interlocks with the
930 main thread, from which is will want to retrieve textures.
933 //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();