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 m_server_ser_ver(SER_FMT_VER_INVALID),
193 m_inventory_updated(false),
196 m_password(password),
197 m_access_denied(false)
199 m_packetcounter_timer = 0.0;
200 //m_delete_unused_sectors_timer = 0.0;
201 m_connection_reinit_timer = 0.0;
202 m_avg_rtt_timer = 0.0;
203 m_playerpos_send_timer = 0.0;
204 m_ignore_damage_timer = 0.0;
206 //m_env_mutex.Init();
207 //m_con_mutex.Init();
209 m_mesh_update_thread.Start();
215 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
217 Player *player = new LocalPlayer();
219 player->updateName(playername);
221 m_env.addPlayer(player);
223 // Initialize player in the inventory context
224 m_inventory_context.current_player = player;
231 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
235 m_mesh_update_thread.setRun(false);
236 while(m_mesh_update_thread.IsRunning())
240 void Client::connect(Address address)
242 DSTACK(__FUNCTION_NAME);
243 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
244 m_con.setTimeoutMs(0);
245 m_con.Connect(address);
248 bool Client::connectedAndInitialized()
250 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
252 if(m_con.Connected() == false)
255 if(m_server_ser_ver == SER_FMT_VER_INVALID)
261 void Client::step(float dtime)
263 DSTACK(__FUNCTION_NAME);
269 if(m_ignore_damage_timer > dtime)
270 m_ignore_damage_timer -= dtime;
272 m_ignore_damage_timer = 0.0;
274 //dstream<<"Client steps "<<dtime<<std::endl;
277 //TimeTaker timer("ReceiveAll()", m_device);
283 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
285 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
286 m_con.RunTimeouts(dtime);
293 float &counter = m_packetcounter_timer;
299 dout_client<<"Client packetcounter (20s):"<<std::endl;
300 m_packetcounter.print(dout_client);
301 m_packetcounter.clear();
305 // Get connection status
306 bool connected = connectedAndInitialized();
311 Delete unused sectors
313 NOTE: This jams the game for a while because deleting sectors
317 float &counter = m_delete_unused_sectors_timer;
325 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
327 core::list<v3s16> deleted_blocks;
329 float delete_unused_sectors_timeout =
330 g_settings.getFloat("client_delete_unused_sectors_timeout");
332 // Delete sector blocks
333 /*u32 num = m_env.getMap().unloadUnusedData
334 (delete_unused_sectors_timeout,
335 true, &deleted_blocks);*/
337 // Delete whole sectors
338 m_env.getMap().unloadUnusedData
339 (delete_unused_sectors_timeout,
342 if(deleted_blocks.size() > 0)
344 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
345 <<" unused sectors"<<std::endl;*/
346 /*dstream<<DTIME<<"Client: Deleted "<<num
347 <<" unused sectors"<<std::endl;*/
353 // Env is locked so con can be locked.
354 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
356 core::list<v3s16>::Iterator i = deleted_blocks.begin();
357 core::list<v3s16> sendlist;
360 if(sendlist.size() == 255 || i == deleted_blocks.end())
362 if(sendlist.size() == 0)
371 u32 replysize = 2+1+6*sendlist.size();
372 SharedBuffer<u8> reply(replysize);
373 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
374 reply[2] = sendlist.size();
376 for(core::list<v3s16>::Iterator
377 j = sendlist.begin();
378 j != sendlist.end(); j++)
380 writeV3S16(&reply[2+1+6*k], *j);
383 m_con.Send(PEER_ID_SERVER, 1, reply, true);
385 if(i == deleted_blocks.end())
391 sendlist.push_back(*i);
399 if(connected == false)
401 float &counter = m_connection_reinit_timer;
407 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
409 Player *myplayer = m_env.getLocalPlayer();
410 assert(myplayer != NULL);
412 // Send TOSERVER_INIT
413 // [0] u16 TOSERVER_INIT
414 // [2] u8 SER_FMT_VER_HIGHEST
415 // [3] u8[20] player_name
416 // [23] u8[28] password (new in some version)
417 // [51] u16 client network protocol version (new in some version)
418 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
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: sending initial password hash: \""<<m_password<<"\""
428 memset((char*)&data[23], 0, PASSWORD_SIZE);
429 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
431 // This should be incremented in each version
432 writeU16(&data[51], 1);
434 // Send as unreliable
435 Send(0, data, false);
438 // Not connected, return
443 Do stuff if connected
447 Run Map's timers and unload unused data
449 const float map_timer_and_unload_dtime = 5.25;
450 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
452 ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
453 core::list<v3s16> deleted_blocks;
454 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
455 g_settings.getFloat("client_unload_unused_data_timeout"),
458 /*if(deleted_blocks.size() > 0)
459 dstream<<"Client: Unloaded "<<deleted_blocks.size()
460 <<" unused blocks"<<std::endl;*/
464 NOTE: This loop is intentionally iterated the way it is.
467 core::list<v3s16>::Iterator i = deleted_blocks.begin();
468 core::list<v3s16> sendlist;
471 if(sendlist.size() == 255 || i == deleted_blocks.end())
473 if(sendlist.size() == 0)
482 u32 replysize = 2+1+6*sendlist.size();
483 SharedBuffer<u8> reply(replysize);
484 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
485 reply[2] = sendlist.size();
487 for(core::list<v3s16>::Iterator
488 j = sendlist.begin();
489 j != sendlist.end(); j++)
491 writeV3S16(&reply[2+1+6*k], *j);
494 m_con.Send(PEER_ID_SERVER, 1, reply, true);
496 if(i == deleted_blocks.end())
502 sendlist.push_back(*i);
512 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
514 // Control local player (0ms)
515 LocalPlayer *player = m_env.getLocalPlayer();
516 assert(player != NULL);
517 player->applyControl(dtime);
519 //TimeTaker envtimer("env step", m_device);
525 NOTE: These old objects are DEPRECATED. TODO: Remove
527 for(core::map<v3s16, bool>::Iterator
528 i = m_active_blocks.getIterator();
529 i.atEnd() == false; i++)
531 v3s16 p = i.getNode()->getKey();
533 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
537 // Step MapBlockObjects
538 block->stepObjects(dtime, false, m_env.getDayNightRatio());
546 ClientEnvEvent event = m_env.getClientEvent();
547 if(event.type == CEE_NONE)
551 else if(event.type == CEE_PLAYER_DAMAGE)
553 if(m_ignore_damage_timer <= 0)
555 u8 damage = event.player_damage.amount;
558 // Add to ClientEvent queue
560 event.type = CE_PLAYER_DAMAGE;
561 event.player_damage.amount = damage;
562 m_client_event_queue.push_back(event);
572 float &counter = m_avg_rtt_timer;
577 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
578 // connectedAndInitialized() is true, peer exists.
579 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
580 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
585 Send player position to server
588 float &counter = m_playerpos_send_timer;
598 Replace updated meshes
601 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
603 //TimeTaker timer("** Processing mesh update result queue");
606 /*dstream<<"Mesh update result queue size is "
607 <<m_mesh_update_thread.m_queue_out.size()
610 while(m_mesh_update_thread.m_queue_out.size() > 0)
612 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
613 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
616 block->replaceMesh(r.mesh);
618 if(r.ack_block_to_server)
620 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
621 <<","<<r.p.Z<<")"<<std::endl;*/
632 u32 replysize = 2+1+6;
633 SharedBuffer<u8> reply(replysize);
634 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
636 writeV3S16(&reply[3], r.p);
638 m_con.Send(PEER_ID_SERVER, 1, reply, true);
644 // Virtual methods from con::PeerHandler
645 void Client::peerAdded(con::Peer *peer)
647 derr_client<<"Client::peerAdded(): peer->id="
648 <<peer->id<<std::endl;
650 void Client::deletingPeer(con::Peer *peer, bool timeout)
652 derr_client<<"Client::deletingPeer(): "
653 "Server Peer is getting deleted "
654 <<"(timeout="<<timeout<<")"<<std::endl;
657 void Client::ReceiveAll()
659 DSTACK(__FUNCTION_NAME);
665 catch(con::NoIncomingDataException &e)
669 catch(con::InvalidIncomingDataException &e)
671 dout_client<<DTIME<<"Client::ReceiveAll(): "
672 "InvalidIncomingDataException: what()="
673 <<e.what()<<std::endl;
678 void Client::Receive()
680 DSTACK(__FUNCTION_NAME);
681 u32 data_maxsize = 200000;
682 Buffer<u8> data(data_maxsize);
686 //TimeTaker t1("con mutex and receive", m_device);
687 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
688 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
690 //TimeTaker t1("ProcessData", m_device);
691 ProcessData(*data, datasize, sender_peer_id);
695 sender_peer_id given to this shall be quaranteed to be a valid peer
697 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
699 DSTACK(__FUNCTION_NAME);
701 // Ignore packets that don't even fit a command
704 m_packetcounter.add(60000);
708 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
710 //dstream<<"Client: received command="<<command<<std::endl;
711 m_packetcounter.add((u16)command);
714 If this check is removed, be sure to change the queue
715 system to know the ids
717 if(sender_peer_id != PEER_ID_SERVER)
719 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
720 "coming from server: peer_id="<<sender_peer_id
727 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
728 // All data is coming from the server
729 // PeerNotFoundException is handled by caller.
730 peer = m_con.GetPeer(PEER_ID_SERVER);
733 u8 ser_version = m_server_ser_ver;
735 //dstream<<"Client received command="<<(int)command<<std::endl;
737 if(command == TOCLIENT_INIT)
742 u8 deployed = data[2];
744 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
745 "deployed="<<((int)deployed&0xff)<<std::endl;
747 if(deployed < SER_FMT_VER_LOWEST
748 || deployed > SER_FMT_VER_HIGHEST)
750 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
751 <<"unsupported ser_fmt_ver"<<std::endl;
755 m_server_ser_ver = deployed;
757 // Get player position
758 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
759 if(datasize >= 2+1+6)
760 playerpos_s16 = readV3S16(&data[2+1]);
761 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
764 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
766 // Set player position
767 Player *player = m_env.getLocalPlayer();
768 assert(player != NULL);
769 player->setPosition(playerpos_f);
772 if(datasize >= 2+1+6+8)
775 m_map_seed = readU64(&data[2+1+6]);
776 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
781 SharedBuffer<u8> reply(replysize);
782 writeU16(&reply[0], TOSERVER_INIT2);
784 m_con.Send(PEER_ID_SERVER, 1, reply, true);
789 if(command == TOCLIENT_ACCESS_DENIED)
791 // The server didn't like our password. Note, this needs
792 // to be processed even if the serialisation format has
793 // not been agreed yet, the same as TOCLIENT_INIT.
794 m_access_denied = true;
795 m_access_denied_reason = L"Unknown";
798 std::string datastring((char*)&data[2], datasize-2);
799 std::istringstream is(datastring, std::ios_base::binary);
800 m_access_denied_reason = deSerializeWideString(is);
805 if(ser_version == SER_FMT_VER_INVALID)
807 dout_client<<DTIME<<"WARNING: Client: Server serialization"
808 " format invalid or not initialized."
809 " Skipping incoming command="<<command<<std::endl;
813 // Just here to avoid putting the two if's together when
814 // making some copypasta
817 if(command == TOCLIENT_REMOVENODE)
822 p.X = readS16(&data[2]);
823 p.Y = readS16(&data[4]);
824 p.Z = readS16(&data[6]);
826 //TimeTaker t1("TOCLIENT_REMOVENODE");
828 // This will clear the cracking animation after digging
829 ((ClientMap&)m_env.getMap()).clearTempMod(p);
833 else if(command == TOCLIENT_ADDNODE)
835 if(datasize < 8 + MapNode::serializedLength(ser_version))
839 p.X = readS16(&data[2]);
840 p.Y = readS16(&data[4]);
841 p.Z = readS16(&data[6]);
843 //TimeTaker t1("TOCLIENT_ADDNODE");
846 n.deSerialize(&data[8], ser_version);
850 else if(command == TOCLIENT_BLOCKDATA)
852 // Ignore too small packet
857 p.X = readS16(&data[2]);
858 p.Y = readS16(&data[4]);
859 p.Z = readS16(&data[6]);
861 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
862 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
863 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
864 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
866 std::string datastring((char*)&data[8], datasize-8);
867 std::istringstream istr(datastring, std::ios_base::binary);
873 sector = m_env.getMap().emergeSector(p2d);
875 assert(sector->getPos() == p2d);
877 //TimeTaker timer("MapBlock deSerialize");
880 block = sector->getBlockNoCreateNoEx(p.Y);
884 Update an existing block
886 //dstream<<"Updating"<<std::endl;
887 block->deSerialize(istr, ser_version);
894 //dstream<<"Creating new"<<std::endl;
895 block = new MapBlock(&m_env.getMap(), p);
896 block->deSerialize(istr, ser_version);
897 sector->insertBlock(block);
901 mod.type = NODEMOD_CHANGECONTENT;
902 mod.param = CONTENT_MESE;
903 block->setTempMod(v3s16(8,10,8), mod);
904 block->setTempMod(v3s16(8,9,8), mod);
905 block->setTempMod(v3s16(8,8,8), mod);
906 block->setTempMod(v3s16(8,7,8), mod);
907 block->setTempMod(v3s16(8,6,8), mod);*/
921 u32 replysize = 2+1+6;
922 SharedBuffer<u8> reply(replysize);
923 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
925 writeV3S16(&reply[3], p);
927 m_con.Send(PEER_ID_SERVER, 1, reply, true);
931 Update Mesh of this block and blocks at x-, y- and z-.
932 Environment should not be locked as it interlocks with the
933 main thread, from which is will want to retrieve textures.
936 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
938 Add it to mesh update queue and set it to be acknowledged after update.
940 //std::cerr<<"Adding mesh update task for received block"<<std::endl;
941 addUpdateMeshTaskWithEdge(p, true);
943 else if(command == TOCLIENT_PLAYERPOS)
945 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
949 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
950 our_peer_id = m_con.GetPeerID();
952 // Cancel if we don't have a peer id
953 if(our_peer_id == PEER_ID_INEXISTENT){
954 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
961 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
963 u32 player_size = 2+12+12+4+4;
965 u32 player_count = (datasize-2) / player_size;
967 for(u32 i=0; i<player_count; i++)
969 u16 peer_id = readU16(&data[start]);
971 Player *player = m_env.getPlayer(peer_id);
973 // Skip if player doesn't exist
976 start += player_size;
980 // Skip if player is local player
981 if(player->isLocal())
983 start += player_size;
987 v3s32 ps = readV3S32(&data[start+2]);
988 v3s32 ss = readV3S32(&data[start+2+12]);
989 s32 pitch_i = readS32(&data[start+2+12+12]);
990 s32 yaw_i = readS32(&data[start+2+12+12+4]);
991 /*dstream<<"Client: got "
992 <<"pitch_i="<<pitch_i
993 <<" yaw_i="<<yaw_i<<std::endl;*/
994 f32 pitch = (f32)pitch_i / 100.0;
995 f32 yaw = (f32)yaw_i / 100.0;
996 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
997 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
998 player->setPosition(position);
999 player->setSpeed(speed);
1000 player->setPitch(pitch);
1001 player->setYaw(yaw);
1003 /*dstream<<"Client: player "<<peer_id
1005 <<" yaw="<<yaw<<std::endl;*/
1007 start += player_size;
1011 else if(command == TOCLIENT_PLAYERINFO)
1015 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1016 our_peer_id = m_con.GetPeerID();
1018 // Cancel if we don't have a peer id
1019 if(our_peer_id == PEER_ID_INEXISTENT){
1020 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
1021 "we have no peer id"
1026 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
1029 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1031 u32 item_size = 2+PLAYERNAME_SIZE;
1032 u32 player_count = (datasize-2) / item_size;
1035 core::list<u16> players_alive;
1036 for(u32 i=0; i<player_count; i++)
1038 // Make sure the name ends in '\0'
1039 data[start+2+20-1] = 0;
1041 u16 peer_id = readU16(&data[start]);
1043 players_alive.push_back(peer_id);
1045 /*dstream<<DTIME<<"peer_id="<<peer_id
1046 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1048 // Don't update the info of the local player
1049 if(peer_id == our_peer_id)
1055 Player *player = m_env.getPlayer(peer_id);
1057 // Create a player if it doesn't exist
1060 player = new RemotePlayer(
1061 m_device->getSceneManager()->getRootSceneNode(),
1064 player->peer_id = peer_id;
1065 m_env.addPlayer(player);
1066 dout_client<<DTIME<<"Client: Adding new player "
1067 <<peer_id<<std::endl;
1070 player->updateName((char*)&data[start+2]);
1076 Remove those players from the environment that
1077 weren't listed by the server.
1079 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1080 core::list<Player*> players = m_env.getPlayers();
1081 core::list<Player*>::Iterator ip;
1082 for(ip=players.begin(); ip!=players.end(); ip++)
1084 // Ingore local player
1085 if((*ip)->isLocal())
1088 // Warn about a special case
1089 if((*ip)->peer_id == 0)
1091 dstream<<DTIME<<"WARNING: Client: Removing "
1092 "dead player with id=0"<<std::endl;
1095 bool is_alive = false;
1096 core::list<u16>::Iterator i;
1097 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1099 if((*ip)->peer_id == *i)
1105 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1106 <<" is_alive="<<is_alive<<std::endl;*/
1109 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1111 m_env.removePlayer((*ip)->peer_id);
1115 else if(command == TOCLIENT_SECTORMETA)
1117 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1122 [3...] v2s16 pos + sector metadata
1127 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1130 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1132 std::string datastring((char*)&data[2], datasize-2);
1133 std::istringstream is(datastring, std::ios_base::binary);
1137 is.read((char*)buf, 1);
1138 u16 sector_count = readU8(buf);
1140 //dstream<<"sector_count="<<sector_count<<std::endl;
1142 for(u16 i=0; i<sector_count; i++)
1145 is.read((char*)buf, 4);
1146 v2s16 pos = readV2S16(buf);
1147 /*dstream<<"Client: deserializing sector at "
1148 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1150 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1151 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1156 else if(command == TOCLIENT_INVENTORY)
1161 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1164 //TimeTaker t2("mutex locking", m_device);
1165 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1168 //TimeTaker t3("istringstream init", m_device);
1169 std::string datastring((char*)&data[2], datasize-2);
1170 std::istringstream is(datastring, std::ios_base::binary);
1173 //m_env.printPlayers(dstream);
1175 //TimeTaker t4("player get", m_device);
1176 Player *player = m_env.getLocalPlayer();
1177 assert(player != NULL);
1180 //TimeTaker t1("inventory.deSerialize()", m_device);
1181 player->inventory.deSerialize(is);
1184 m_inventory_updated = true;
1186 //dstream<<"Client got player inventory:"<<std::endl;
1187 //player->inventory.print(dstream);
1191 else if(command == TOCLIENT_OBJECTDATA)
1194 // Strip command word and create a stringstream
1195 std::string datastring((char*)&data[2], datasize-2);
1196 std::istringstream is(datastring, std::ios_base::binary);
1200 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1208 is.read((char*)buf, 2);
1209 u16 playercount = readU16(buf);
1211 for(u16 i=0; i<playercount; i++)
1213 is.read((char*)buf, 2);
1214 u16 peer_id = readU16(buf);
1215 is.read((char*)buf, 12);
1216 v3s32 p_i = readV3S32(buf);
1217 is.read((char*)buf, 12);
1218 v3s32 s_i = readV3S32(buf);
1219 is.read((char*)buf, 4);
1220 s32 pitch_i = readS32(buf);
1221 is.read((char*)buf, 4);
1222 s32 yaw_i = readS32(buf);
1224 Player *player = m_env.getPlayer(peer_id);
1226 // Skip if player doesn't exist
1232 // Skip if player is local player
1233 if(player->isLocal())
1238 f32 pitch = (f32)pitch_i / 100.0;
1239 f32 yaw = (f32)yaw_i / 100.0;
1240 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1241 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1243 player->setPosition(position);
1244 player->setSpeed(speed);
1245 player->setPitch(pitch);
1246 player->setYaw(yaw);
1251 NOTE: Deprecated stuff here, TODO: Remove
1254 // Read active block count
1255 is.read((char*)buf, 2);
1256 u16 blockcount = readU16(buf);
1258 // Initialize delete queue with all active blocks
1259 core::map<v3s16, bool> abs_to_delete;
1260 for(core::map<v3s16, bool>::Iterator
1261 i = m_active_blocks.getIterator();
1262 i.atEnd() == false; i++)
1264 v3s16 p = i.getNode()->getKey();
1265 /*dstream<<"adding "
1266 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1267 <<" to abs_to_delete"
1269 abs_to_delete.insert(p, true);
1272 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1275 for(u16 i=0; i<blockcount; i++)
1278 is.read((char*)buf, 6);
1279 v3s16 p = readV3S16(buf);
1280 // Get block from somewhere
1281 MapBlock *block = NULL;
1283 block = m_env.getMap().getBlockNoCreate(p);
1285 catch(InvalidPositionException &e)
1287 //TODO: Create a dummy block?
1291 dstream<<"WARNING: "
1292 <<"Could not get block at blockpos "
1293 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1294 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1295 <<"following block object data."
1300 /*dstream<<"Client updating objects for block "
1301 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1304 // Insert to active block list
1305 m_active_blocks.insert(p, true);
1307 // Remove from deletion queue
1308 if(abs_to_delete.find(p) != NULL)
1309 abs_to_delete.remove(p);
1312 Update objects of block
1314 NOTE: Be sure this is done in the main thread.
1316 block->updateObjects(is, m_server_ser_ver,
1317 m_device->getSceneManager(), m_env.getDayNightRatio());
1320 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1323 // Delete objects of blocks in delete queue
1324 for(core::map<v3s16, bool>::Iterator
1325 i = abs_to_delete.getIterator();
1326 i.atEnd() == false; i++)
1328 v3s16 p = i.getNode()->getKey();
1331 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1334 block->clearObjects();
1335 // Remove from active blocks list
1336 m_active_blocks.remove(p);
1338 catch(InvalidPositionException &e)
1340 dstream<<"WARNAING: Client: "
1341 <<"Couldn't clear objects of active->inactive"
1343 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1344 <<" because block was not found"
1352 else if(command == TOCLIENT_TIME_OF_DAY)
1357 u16 time_of_day = readU16(&data[2]);
1358 time_of_day = time_of_day % 24000;
1359 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1367 m_env.setTimeOfDay(time_of_day);
1369 u32 dr = m_env.getDayNightRatio();
1371 dstream<<"Client: time_of_day="<<time_of_day
1377 else if(command == TOCLIENT_CHAT_MESSAGE)
1385 std::string datastring((char*)&data[2], datasize-2);
1386 std::istringstream is(datastring, std::ios_base::binary);
1389 is.read((char*)buf, 2);
1390 u16 len = readU16(buf);
1392 std::wstring message;
1393 for(u16 i=0; i<len; i++)
1395 is.read((char*)buf, 2);
1396 message += (wchar_t)readU16(buf);
1399 /*dstream<<"Client received chat message: "
1400 <<wide_to_narrow(message)<<std::endl;*/
1402 m_chat_queue.push_back(message);
1404 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1406 //if(g_settings.getBool("enable_experimental"))
1410 u16 count of removed objects
1411 for all removed objects {
1414 u16 count of added objects
1415 for all added objects {
1418 u32 initialization data length
1419 string initialization data
1424 // Get all data except the command number
1425 std::string datastring((char*)&data[2], datasize-2);
1426 // Throw them in an istringstream
1427 std::istringstream is(datastring, std::ios_base::binary);
1431 // Read removed objects
1433 u16 removed_count = readU16((u8*)buf);
1434 for(u16 i=0; i<removed_count; i++)
1437 u16 id = readU16((u8*)buf);
1440 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1441 m_env.removeActiveObject(id);
1445 // Read added objects
1447 u16 added_count = readU16((u8*)buf);
1448 for(u16 i=0; i<added_count; i++)
1451 u16 id = readU16((u8*)buf);
1453 u8 type = readU8((u8*)buf);
1454 std::string data = deSerializeLongString(is);
1457 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1458 m_env.addActiveObject(id, type, data);
1463 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1465 //if(g_settings.getBool("enable_experimental"))
1477 // Get all data except the command number
1478 std::string datastring((char*)&data[2], datasize-2);
1479 // Throw them in an istringstream
1480 std::istringstream is(datastring, std::ios_base::binary);
1482 while(is.eof() == false)
1486 u16 id = readU16((u8*)buf);
1490 u16 message_size = readU16((u8*)buf);
1491 std::string message;
1492 message.reserve(message_size);
1493 for(u16 i=0; i<message_size; i++)
1496 message.append(buf, 1);
1498 // Pass on to the environment
1500 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1501 m_env.processActiveObjectMessage(id, message);
1506 else if(command == TOCLIENT_HP)
1508 std::string datastring((char*)&data[2], datasize-2);
1509 std::istringstream is(datastring, std::ios_base::binary);
1510 Player *player = m_env.getLocalPlayer();
1511 assert(player != NULL);
1515 else if(command == TOCLIENT_MOVE_PLAYER)
1517 std::string datastring((char*)&data[2], datasize-2);
1518 std::istringstream is(datastring, std::ios_base::binary);
1519 Player *player = m_env.getLocalPlayer();
1520 assert(player != NULL);
1521 v3f pos = readV3F1000(is);
1522 f32 pitch = readF1000(is);
1523 f32 yaw = readF1000(is);
1524 player->setPosition(pos);
1525 /*player->setPitch(pitch);
1526 player->setYaw(yaw);*/
1528 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1529 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1535 Add to ClientEvent queue.
1536 This has to be sent to the main program because otherwise
1537 it would just force the pitch and yaw values to whatever
1538 the camera points to.
1541 event.type = CE_PLAYER_FORCE_MOVE;
1542 event.player_force_move.pitch = pitch;
1543 event.player_force_move.yaw = yaw;
1544 m_client_event_queue.push_back(event);
1546 // Ignore damage for a few seconds, so that the player doesn't
1547 // get damage from falling on ground
1548 m_ignore_damage_timer = 3.0;
1550 else if(command == TOCLIENT_PLAYERITEM)
1552 std::string datastring((char*)&data[2], datasize-2);
1553 std::istringstream is(datastring, std::ios_base::binary);
1555 u16 count = readU16(is);
1557 for (u16 i = 0; i < count; ++i) {
1558 u16 peer_id = readU16(is);
1559 Player *player = m_env.getPlayer(peer_id);
1563 dout_client<<DTIME<<"Client: ignoring player item "
1564 << deSerializeString(is)
1565 << " for non-existing peer id " << peer_id
1568 } else if (player->isLocal()) {
1569 dout_client<<DTIME<<"Client: ignoring player item "
1570 << deSerializeString(is)
1571 << " for local player" << std::endl;
1574 InventoryList *inv = player->inventory.getList("main");
1575 std::string itemstring(deSerializeString(is));
1576 if (itemstring.empty()) {
1579 <<"Client: empty player item for peer "
1580 << peer_id << std::endl;
1582 std::istringstream iss(itemstring);
1583 delete inv->changeItem(0, InventoryItem::deSerialize(iss));
1584 dout_client<<DTIME<<"Client: player item for peer " << peer_id << ": ";
1585 player->getWieldItem()->serialize(dout_client);
1586 dout_client<<std::endl;
1593 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1594 <<command<<std::endl;
1598 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1600 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1601 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1604 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1605 v3s16 nodepos_oversurface, u16 item)
1607 if(connectedAndInitialized() == false){
1608 dout_client<<DTIME<<"Client::groundAction() "
1609 "cancelled (not connected)"
1618 [3] v3s16 nodepos_undersurface
1619 [9] v3s16 nodepos_abovesurface
1624 2: stop digging (all parameters ignored)
1625 3: digging completed
1627 u8 datasize = 2 + 1 + 6 + 6 + 2;
1628 SharedBuffer<u8> data(datasize);
1629 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1630 writeU8(&data[2], action);
1631 writeV3S16(&data[3], nodepos_undersurface);
1632 writeV3S16(&data[9], nodepos_oversurface);
1633 writeU16(&data[15], item);
1634 Send(0, data, true);
1637 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1639 if(connectedAndInitialized() == false){
1640 dout_client<<DTIME<<"Client::clickObject() "
1641 "cancelled (not connected)"
1647 [0] u16 command=TOSERVER_CLICK_OBJECT
1648 [2] u8 button (0=left, 1=right)
1653 u8 datasize = 2 + 1 + 6 + 2 + 2;
1654 SharedBuffer<u8> data(datasize);
1655 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1656 writeU8(&data[2], button);
1657 writeV3S16(&data[3], blockpos);
1658 writeS16(&data[9], id);
1659 writeU16(&data[11], item);
1660 Send(0, data, true);
1663 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1665 if(connectedAndInitialized() == false){
1666 dout_client<<DTIME<<"Client::clickActiveObject() "
1667 "cancelled (not connected)"
1675 [2] u8 button (0=left, 1=right)
1679 u8 datasize = 2 + 1 + 6 + 2 + 2;
1680 SharedBuffer<u8> data(datasize);
1681 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1682 writeU8(&data[2], button);
1683 writeU16(&data[3], id);
1684 writeU16(&data[5], item);
1685 Send(0, data, true);
1688 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1697 std::ostringstream os(std::ios_base::binary);
1701 writeU16(buf, TOSERVER_SIGNTEXT);
1702 os.write((char*)buf, 2);
1705 writeV3S16(buf, blockpos);
1706 os.write((char*)buf, 6);
1710 os.write((char*)buf, 2);
1712 u16 textlen = text.size();
1713 // Write text length
1714 writeS16(buf, textlen);
1715 os.write((char*)buf, 2);
1718 os.write((char*)text.c_str(), textlen);
1721 std::string s = os.str();
1722 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1724 Send(0, data, true);
1727 void Client::sendSignNodeText(v3s16 p, std::string text)
1735 std::ostringstream os(std::ios_base::binary);
1739 writeU16(buf, TOSERVER_SIGNNODETEXT);
1740 os.write((char*)buf, 2);
1744 os.write((char*)buf, 6);
1746 u16 textlen = text.size();
1747 // Write text length
1748 writeS16(buf, textlen);
1749 os.write((char*)buf, 2);
1752 os.write((char*)text.c_str(), textlen);
1755 std::string s = os.str();
1756 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1758 Send(0, data, true);
1761 void Client::sendInventoryAction(InventoryAction *a)
1763 std::ostringstream os(std::ios_base::binary);
1767 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1768 os.write((char*)buf, 2);
1773 std::string s = os.str();
1774 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1776 Send(0, data, true);
1779 void Client::sendChatMessage(const std::wstring &message)
1781 std::ostringstream os(std::ios_base::binary);
1785 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1786 os.write((char*)buf, 2);
1789 writeU16(buf, message.size());
1790 os.write((char*)buf, 2);
1793 for(u32 i=0; i<message.size(); i++)
1797 os.write((char*)buf, 2);
1801 std::string s = os.str();
1802 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1804 Send(0, data, true);
1807 void Client::sendChangePassword(const std::wstring oldpassword,
1808 const std::wstring newpassword)
1810 Player *player = m_env.getLocalPlayer();
1814 std::string playername = player->getName();
1815 std::string oldpwd = translatePassword(playername, oldpassword);
1816 std::string newpwd = translatePassword(playername, newpassword);
1818 std::ostringstream os(std::ios_base::binary);
1819 u8 buf[2+PASSWORD_SIZE*2];
1821 [0] u16 TOSERVER_PASSWORD
1822 [2] u8[28] old password
1823 [30] u8[28] new password
1826 writeU16(buf, TOSERVER_PASSWORD);
1827 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1829 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1830 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1832 buf[2+PASSWORD_SIZE-1] = 0;
1833 buf[30+PASSWORD_SIZE-1] = 0;
1834 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1837 std::string s = os.str();
1838 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1840 Send(0, data, true);
1844 void Client::sendDamage(u8 damage)
1846 DSTACK(__FUNCTION_NAME);
1847 std::ostringstream os(std::ios_base::binary);
1849 writeU16(os, TOSERVER_DAMAGE);
1850 writeU8(os, damage);
1853 std::string s = os.str();
1854 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1856 Send(0, data, true);
1859 void Client::sendPlayerPos()
1861 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1863 Player *myplayer = m_env.getLocalPlayer();
1864 if(myplayer == NULL)
1869 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1870 our_peer_id = m_con.GetPeerID();
1873 // Set peer id if not set already
1874 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1875 myplayer->peer_id = our_peer_id;
1876 // Check that an existing peer_id is the same as the connection's
1877 assert(myplayer->peer_id == our_peer_id);
1879 v3f pf = myplayer->getPosition();
1880 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1881 v3f sf = myplayer->getSpeed();
1882 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1883 s32 pitch = myplayer->getPitch() * 100;
1884 s32 yaw = myplayer->getYaw() * 100;
1889 [2] v3s32 position*100
1890 [2+12] v3s32 speed*100
1891 [2+12+12] s32 pitch*100
1892 [2+12+12+4] s32 yaw*100
1895 SharedBuffer<u8> data(2+12+12+4+4);
1896 writeU16(&data[0], TOSERVER_PLAYERPOS);
1897 writeV3S32(&data[2], position);
1898 writeV3S32(&data[2+12], speed);
1899 writeS32(&data[2+12+12], pitch);
1900 writeS32(&data[2+12+12+4], yaw);
1902 // Send as unreliable
1903 Send(0, data, false);
1906 void Client::sendPlayerItem(u16 item)
1908 Player *myplayer = m_env.getLocalPlayer();
1909 if(myplayer == NULL)
1912 u16 our_peer_id = m_con.GetPeerID();
1914 // Set peer id if not set already
1915 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1916 myplayer->peer_id = our_peer_id;
1917 // Check that an existing peer_id is the same as the connection's
1918 assert(myplayer->peer_id == our_peer_id);
1920 SharedBuffer<u8> data(2+2);
1921 writeU16(&data[0], TOSERVER_PLAYERITEM);
1922 writeU16(&data[2], item);
1925 Send(0, data, true);
1928 void Client::removeNode(v3s16 p)
1930 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1932 core::map<v3s16, MapBlock*> modified_blocks;
1936 //TimeTaker t("removeNodeAndUpdate", m_device);
1937 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1939 catch(InvalidPositionException &e)
1943 for(core::map<v3s16, MapBlock * >::Iterator
1944 i = modified_blocks.getIterator();
1945 i.atEnd() == false; i++)
1947 v3s16 p = i.getNode()->getKey();
1948 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1949 addUpdateMeshTaskWithEdge(p);
1953 void Client::addNode(v3s16 p, MapNode n)
1955 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1957 TimeTaker timer1("Client::addNode()");
1959 core::map<v3s16, MapBlock*> modified_blocks;
1963 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1964 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1966 catch(InvalidPositionException &e)
1969 //TimeTaker timer2("Client::addNode(): updateMeshes");
1971 for(core::map<v3s16, MapBlock * >::Iterator
1972 i = modified_blocks.getIterator();
1973 i.atEnd() == false; i++)
1975 v3s16 p = i.getNode()->getKey();
1976 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1977 addUpdateMeshTaskWithEdge(p);
1981 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1983 m_env.getClientMap().updateCamera(pos, dir, fov);
1986 void Client::renderPostFx()
1988 m_env.getClientMap().renderPostFx();
1991 MapNode Client::getNode(v3s16 p)
1993 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1994 return m_env.getMap().getNode(p);
1997 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1999 return m_env.getMap().getNodeMetadata(p);
2002 LocalPlayer* Client::getLocalPlayer()
2004 return m_env.getLocalPlayer();
2007 void Client::setPlayerWield(scene::ISceneNode *wield)
2009 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2010 LocalPlayer *player = m_env.getLocalPlayer();
2011 assert(player != NULL);
2012 player->wield = wield;
2015 void Client::setPlayerControl(PlayerControl &control)
2017 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2018 LocalPlayer *player = m_env.getLocalPlayer();
2019 assert(player != NULL);
2020 player->control = control;
2023 void Client::selectPlayerItem(u16 item)
2025 LocalPlayer *player = m_env.getLocalPlayer();
2026 assert(player != NULL);
2028 player->wieldItem(item);
2030 sendPlayerItem(item);
2033 // Returns true if the inventory of the local player has been
2034 // updated from the server. If it is true, it is set to false.
2035 bool Client::getLocalInventoryUpdated()
2037 // m_inventory_updated is behind envlock
2038 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2039 bool updated = m_inventory_updated;
2040 m_inventory_updated = false;
2044 // Copies the inventory of the local player to parameter
2045 void Client::getLocalInventory(Inventory &dst)
2047 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2048 Player *player = m_env.getLocalPlayer();
2049 assert(player != NULL);
2050 dst = player->inventory;
2053 InventoryContext *Client::getInventoryContext()
2055 return &m_inventory_context;
2058 Inventory* Client::getInventory(InventoryContext *c, std::string id)
2060 if(id == "current_player")
2062 assert(c->current_player);
2063 return &(c->current_player->inventory);
2067 std::string id0 = fn.next(":");
2069 if(id0 == "nodemeta")
2072 p.X = stoi(fn.next(","));
2073 p.Y = stoi(fn.next(","));
2074 p.Z = stoi(fn.next(","));
2075 NodeMetadata* meta = getNodeMetadata(p);
2077 return meta->getInventory();
2078 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2079 <<"no metadata found"<<std::endl;
2083 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2086 void Client::inventoryAction(InventoryAction *a)
2088 sendInventoryAction(a);
2091 MapBlockObject * Client::getSelectedObject(
2093 v3f from_pos_f_on_map,
2094 core::line3d<f32> shootline_on_map
2097 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2099 core::array<DistanceSortedObject> objects;
2101 for(core::map<v3s16, bool>::Iterator
2102 i = m_active_blocks.getIterator();
2103 i.atEnd() == false; i++)
2105 v3s16 p = i.getNode()->getKey();
2107 MapBlock *block = NULL;
2110 block = m_env.getMap().getBlockNoCreate(p);
2112 catch(InvalidPositionException &e)
2117 // Calculate from_pos relative to block
2118 v3s16 block_pos_i_on_map = block->getPosRelative();
2119 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2120 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
2122 block->getObjects(from_pos_f_on_block, max_d, objects);
2123 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
2126 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2129 // After this, the closest object is the first in the array.
2132 for(u32 i=0; i<objects.size(); i++)
2134 MapBlockObject *obj = objects[i].obj;
2135 MapBlock *block = obj->getBlock();
2137 // Calculate shootline relative to block
2138 v3s16 block_pos_i_on_map = block->getPosRelative();
2139 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2140 core::line3d<f32> shootline_on_block(
2141 shootline_on_map.start - block_pos_f_on_map,
2142 shootline_on_map.end - block_pos_f_on_map
2145 if(obj->isSelected(shootline_on_block))
2147 //dstream<<"Returning selected object"<<std::endl;
2152 //dstream<<"No object selected; returning NULL."<<std::endl;
2156 ClientActiveObject * Client::getSelectedActiveObject(
2158 v3f from_pos_f_on_map,
2159 core::line3d<f32> shootline_on_map
2162 core::array<DistanceSortedActiveObject> objects;
2164 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2166 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2169 // After this, the closest object is the first in the array.
2172 for(u32 i=0; i<objects.size(); i++)
2174 ClientActiveObject *obj = objects[i].obj;
2176 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2177 if(selection_box == NULL)
2180 v3f pos = obj->getPosition();
2182 core::aabbox3d<f32> offsetted_box(
2183 selection_box->MinEdge + pos,
2184 selection_box->MaxEdge + pos
2187 if(offsetted_box.intersectsWithLine(shootline_on_map))
2189 //dstream<<"Returning selected object"<<std::endl;
2194 //dstream<<"No object selected; returning NULL."<<std::endl;
2198 void Client::printDebugInfo(std::ostream &os)
2200 //JMutexAutoLock lock1(m_fetchblock_mutex);
2201 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2203 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2204 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2205 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2209 u32 Client::getDayNightRatio()
2211 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2212 return m_env.getDayNightRatio();
2217 Player *player = m_env.getLocalPlayer();
2218 assert(player != NULL);
2222 void Client::setTempMod(v3s16 p, NodeMod mod)
2224 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2225 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2227 core::map<v3s16, MapBlock*> affected_blocks;
2228 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2231 for(core::map<v3s16, MapBlock*>::Iterator
2232 i = affected_blocks.getIterator();
2233 i.atEnd() == false; i++)
2235 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2239 void Client::clearTempMod(v3s16 p)
2241 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2242 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2244 core::map<v3s16, MapBlock*> affected_blocks;
2245 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2248 for(core::map<v3s16, MapBlock*>::Iterator
2249 i = affected_blocks.getIterator();
2250 i.atEnd() == false; i++)
2252 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2256 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2258 /*dstream<<"Client::addUpdateMeshTask(): "
2259 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2262 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2267 Create a task to update the mesh of the block
2270 MeshMakeData *data = new MeshMakeData;
2273 //TimeTaker timer("data fill");
2275 // Debug: 1-6ms, avg=2ms
2276 data->fill(getDayNightRatio(), b);
2280 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2282 // Add task to queue
2283 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2285 /*dstream<<"Mesh update input queue size is "
2286 <<m_mesh_update_thread.m_queue_in.size()
2290 // Temporary test: make mesh directly in here
2292 //TimeTaker timer("make mesh");
2294 scene::SMesh *mesh_new = NULL;
2295 mesh_new = makeMapBlockMesh(data);
2296 b->replaceMesh(mesh_new);
2302 Mark mesh as non-expired at this point so that it can already
2303 be marked as expired again if the data changes
2305 b->setMeshExpired(false);
2308 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2312 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2313 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2318 v3s16 p = blockpos + v3s16(0,0,0);
2319 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2320 addUpdateMeshTask(p, ack_to_server);
2322 catch(InvalidPositionException &e){}
2325 v3s16 p = blockpos + v3s16(-1,0,0);
2326 addUpdateMeshTask(p);
2328 catch(InvalidPositionException &e){}
2330 v3s16 p = blockpos + v3s16(0,-1,0);
2331 addUpdateMeshTask(p);
2333 catch(InvalidPositionException &e){}
2335 v3s16 p = blockpos + v3s16(0,0,-1);
2336 addUpdateMeshTask(p);
2338 catch(InvalidPositionException &e){}
2341 ClientEvent Client::getClientEvent()
2343 if(m_client_event_queue.size() == 0)
2346 event.type = CE_NONE;
2349 return m_client_event_queue.pop_front();