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 (new in some version)
419 // [51] u16 client network protocol version (new in some version)
420 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
421 writeU16(&data[0], TOSERVER_INIT);
422 writeU8(&data[2], SER_FMT_VER_HIGHEST);
424 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
425 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
427 /*dstream<<"Client: sending initial password hash: \""<<m_password<<"\""
430 memset((char*)&data[23], 0, PASSWORD_SIZE);
431 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
433 // This should be incremented in each version
434 writeU16(&data[51], 1);
436 // Send as unreliable
437 Send(0, data, false);
440 // Not connected, return
445 Do stuff if connected
449 Run Map's timers and unload unused data
451 const float map_timer_and_unload_dtime = 5.25;
452 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
454 ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
455 core::list<v3s16> deleted_blocks;
456 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
457 g_settings.getFloat("client_unload_unused_data_timeout"),
460 /*if(deleted_blocks.size() > 0)
461 dstream<<"Client: Unloaded "<<deleted_blocks.size()
462 <<" unused blocks"<<std::endl;*/
466 NOTE: This loop is intentionally iterated the way it is.
469 core::list<v3s16>::Iterator i = deleted_blocks.begin();
470 core::list<v3s16> sendlist;
473 if(sendlist.size() == 255 || i == deleted_blocks.end())
475 if(sendlist.size() == 0)
484 u32 replysize = 2+1+6*sendlist.size();
485 SharedBuffer<u8> reply(replysize);
486 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
487 reply[2] = sendlist.size();
489 for(core::list<v3s16>::Iterator
490 j = sendlist.begin();
491 j != sendlist.end(); j++)
493 writeV3S16(&reply[2+1+6*k], *j);
496 m_con.Send(PEER_ID_SERVER, 1, reply, true);
498 if(i == deleted_blocks.end())
504 sendlist.push_back(*i);
514 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
516 // Control local player (0ms)
517 LocalPlayer *player = m_env.getLocalPlayer();
518 assert(player != NULL);
519 player->applyControl(dtime);
521 //TimeTaker envtimer("env step", m_device);
527 NOTE: These old objects are DEPRECATED. TODO: Remove
529 for(core::map<v3s16, bool>::Iterator
530 i = m_active_blocks.getIterator();
531 i.atEnd() == false; i++)
533 v3s16 p = i.getNode()->getKey();
535 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
539 // Step MapBlockObjects
540 block->stepObjects(dtime, false, m_env.getDayNightRatio());
548 ClientEnvEvent event = m_env.getClientEvent();
549 if(event.type == CEE_NONE)
553 else if(event.type == CEE_PLAYER_DAMAGE)
555 if(m_ignore_damage_timer <= 0)
557 u8 damage = event.player_damage.amount;
560 // Add to ClientEvent queue
562 event.type = CE_PLAYER_DAMAGE;
563 event.player_damage.amount = damage;
564 m_client_event_queue.push_back(event);
574 float &counter = m_avg_rtt_timer;
579 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
580 // connectedAndInitialized() is true, peer exists.
581 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
582 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
587 Send player position to server
590 float &counter = m_playerpos_send_timer;
600 Replace updated meshes
603 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
605 //TimeTaker timer("** Processing mesh update result queue");
608 /*dstream<<"Mesh update result queue size is "
609 <<m_mesh_update_thread.m_queue_out.size()
612 while(m_mesh_update_thread.m_queue_out.size() > 0)
614 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
615 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
618 block->replaceMesh(r.mesh);
620 if(r.ack_block_to_server)
622 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
623 <<","<<r.p.Z<<")"<<std::endl;*/
634 u32 replysize = 2+1+6;
635 SharedBuffer<u8> reply(replysize);
636 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
638 writeV3S16(&reply[3], r.p);
640 m_con.Send(PEER_ID_SERVER, 1, reply, true);
646 // Virtual methods from con::PeerHandler
647 void Client::peerAdded(con::Peer *peer)
649 derr_client<<"Client::peerAdded(): peer->id="
650 <<peer->id<<std::endl;
652 void Client::deletingPeer(con::Peer *peer, bool timeout)
654 derr_client<<"Client::deletingPeer(): "
655 "Server Peer is getting deleted "
656 <<"(timeout="<<timeout<<")"<<std::endl;
659 void Client::ReceiveAll()
661 DSTACK(__FUNCTION_NAME);
667 catch(con::NoIncomingDataException &e)
671 catch(con::InvalidIncomingDataException &e)
673 dout_client<<DTIME<<"Client::ReceiveAll(): "
674 "InvalidIncomingDataException: what()="
675 <<e.what()<<std::endl;
680 void Client::Receive()
682 DSTACK(__FUNCTION_NAME);
683 u32 data_maxsize = 200000;
684 Buffer<u8> data(data_maxsize);
688 //TimeTaker t1("con mutex and receive", m_device);
689 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
690 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
692 //TimeTaker t1("ProcessData", m_device);
693 ProcessData(*data, datasize, sender_peer_id);
697 sender_peer_id given to this shall be quaranteed to be a valid peer
699 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
701 DSTACK(__FUNCTION_NAME);
703 // Ignore packets that don't even fit a command
706 m_packetcounter.add(60000);
710 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
712 //dstream<<"Client: received command="<<command<<std::endl;
713 m_packetcounter.add((u16)command);
716 If this check is removed, be sure to change the queue
717 system to know the ids
719 if(sender_peer_id != PEER_ID_SERVER)
721 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
722 "coming from server: peer_id="<<sender_peer_id
729 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
730 // All data is coming from the server
731 // PeerNotFoundException is handled by caller.
732 peer = m_con.GetPeer(PEER_ID_SERVER);
735 u8 ser_version = m_server_ser_ver;
737 //dstream<<"Client received command="<<(int)command<<std::endl;
739 if(command == TOCLIENT_INIT)
744 u8 deployed = data[2];
746 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
747 "deployed="<<((int)deployed&0xff)<<std::endl;
749 if(deployed < SER_FMT_VER_LOWEST
750 || deployed > SER_FMT_VER_HIGHEST)
752 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
753 <<"unsupported ser_fmt_ver"<<std::endl;
757 m_server_ser_ver = deployed;
759 // Get player position
760 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
761 if(datasize >= 2+1+6)
762 playerpos_s16 = readV3S16(&data[2+1]);
763 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
766 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
768 // Set player position
769 Player *player = m_env.getLocalPlayer();
770 assert(player != NULL);
771 player->setPosition(playerpos_f);
774 if(datasize >= 2+1+6+8)
777 m_map_seed = readU64(&data[2+1+6]);
778 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
783 SharedBuffer<u8> reply(replysize);
784 writeU16(&reply[0], TOSERVER_INIT2);
786 m_con.Send(PEER_ID_SERVER, 1, reply, true);
791 if(command == TOCLIENT_ACCESS_DENIED)
793 // The server didn't like our password. Note, this needs
794 // to be processed even if the serialisation format has
795 // not been agreed yet, the same as TOCLIENT_INIT.
796 m_access_denied = true;
797 m_access_denied_reason = L"Unknown";
800 std::string datastring((char*)&data[2], datasize-2);
801 std::istringstream is(datastring, std::ios_base::binary);
802 m_access_denied_reason = deSerializeWideString(is);
807 if(ser_version == SER_FMT_VER_INVALID)
809 dout_client<<DTIME<<"WARNING: Client: Server serialization"
810 " format invalid or not initialized."
811 " Skipping incoming command="<<command<<std::endl;
815 // Just here to avoid putting the two if's together when
816 // making some copypasta
819 if(command == TOCLIENT_REMOVENODE)
824 p.X = readS16(&data[2]);
825 p.Y = readS16(&data[4]);
826 p.Z = readS16(&data[6]);
828 //TimeTaker t1("TOCLIENT_REMOVENODE");
830 // This will clear the cracking animation after digging
831 ((ClientMap&)m_env.getMap()).clearTempMod(p);
835 else if(command == TOCLIENT_ADDNODE)
837 if(datasize < 8 + MapNode::serializedLength(ser_version))
841 p.X = readS16(&data[2]);
842 p.Y = readS16(&data[4]);
843 p.Z = readS16(&data[6]);
845 //TimeTaker t1("TOCLIENT_ADDNODE");
848 n.deSerialize(&data[8], ser_version);
852 else if(command == TOCLIENT_BLOCKDATA)
854 // Ignore too small packet
859 p.X = readS16(&data[2]);
860 p.Y = readS16(&data[4]);
861 p.Z = readS16(&data[6]);
863 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
864 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
865 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
866 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
868 std::string datastring((char*)&data[8], datasize-8);
869 std::istringstream istr(datastring, std::ios_base::binary);
875 sector = m_env.getMap().emergeSector(p2d);
877 assert(sector->getPos() == p2d);
879 //TimeTaker timer("MapBlock deSerialize");
882 block = sector->getBlockNoCreateNoEx(p.Y);
886 Update an existing block
888 //dstream<<"Updating"<<std::endl;
889 block->deSerialize(istr, ser_version);
896 //dstream<<"Creating new"<<std::endl;
897 block = new MapBlock(&m_env.getMap(), p);
898 block->deSerialize(istr, ser_version);
899 sector->insertBlock(block);
903 mod.type = NODEMOD_CHANGECONTENT;
904 mod.param = CONTENT_MESE;
905 block->setTempMod(v3s16(8,10,8), mod);
906 block->setTempMod(v3s16(8,9,8), mod);
907 block->setTempMod(v3s16(8,8,8), mod);
908 block->setTempMod(v3s16(8,7,8), mod);
909 block->setTempMod(v3s16(8,6,8), mod);*/
923 u32 replysize = 2+1+6;
924 SharedBuffer<u8> reply(replysize);
925 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
927 writeV3S16(&reply[3], p);
929 m_con.Send(PEER_ID_SERVER, 1, reply, true);
933 Update Mesh of this block and blocks at x-, y- and z-.
934 Environment should not be locked as it interlocks with the
935 main thread, from which is will want to retrieve textures.
938 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
940 Add it to mesh update queue and set it to be acknowledged after update.
942 //std::cerr<<"Adding mesh update task for received block"<<std::endl;
943 addUpdateMeshTaskWithEdge(p, true);
945 else if(command == TOCLIENT_PLAYERPOS)
947 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
951 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
952 our_peer_id = m_con.GetPeerID();
954 // Cancel if we don't have a peer id
955 if(our_peer_id == PEER_ID_INEXISTENT){
956 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
963 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
965 u32 player_size = 2+12+12+4+4;
967 u32 player_count = (datasize-2) / player_size;
969 for(u32 i=0; i<player_count; i++)
971 u16 peer_id = readU16(&data[start]);
973 Player *player = m_env.getPlayer(peer_id);
975 // Skip if player doesn't exist
978 start += player_size;
982 // Skip if player is local player
983 if(player->isLocal())
985 start += player_size;
989 v3s32 ps = readV3S32(&data[start+2]);
990 v3s32 ss = readV3S32(&data[start+2+12]);
991 s32 pitch_i = readS32(&data[start+2+12+12]);
992 s32 yaw_i = readS32(&data[start+2+12+12+4]);
993 /*dstream<<"Client: got "
994 <<"pitch_i="<<pitch_i
995 <<" yaw_i="<<yaw_i<<std::endl;*/
996 f32 pitch = (f32)pitch_i / 100.0;
997 f32 yaw = (f32)yaw_i / 100.0;
998 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
999 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1000 player->setPosition(position);
1001 player->setSpeed(speed);
1002 player->setPitch(pitch);
1003 player->setYaw(yaw);
1005 /*dstream<<"Client: player "<<peer_id
1007 <<" yaw="<<yaw<<std::endl;*/
1009 start += player_size;
1013 else if(command == TOCLIENT_PLAYERINFO)
1017 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1018 our_peer_id = m_con.GetPeerID();
1020 // Cancel if we don't have a peer id
1021 if(our_peer_id == PEER_ID_INEXISTENT){
1022 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
1023 "we have no peer id"
1028 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
1031 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1033 u32 item_size = 2+PLAYERNAME_SIZE;
1034 u32 player_count = (datasize-2) / item_size;
1037 core::list<u16> players_alive;
1038 for(u32 i=0; i<player_count; i++)
1040 // Make sure the name ends in '\0'
1041 data[start+2+20-1] = 0;
1043 u16 peer_id = readU16(&data[start]);
1045 players_alive.push_back(peer_id);
1047 /*dstream<<DTIME<<"peer_id="<<peer_id
1048 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1050 // Don't update the info of the local player
1051 if(peer_id == our_peer_id)
1057 Player *player = m_env.getPlayer(peer_id);
1059 // Create a player if it doesn't exist
1062 player = new RemotePlayer(
1063 m_device->getSceneManager()->getRootSceneNode(),
1066 player->peer_id = peer_id;
1067 m_env.addPlayer(player);
1068 dout_client<<DTIME<<"Client: Adding new player "
1069 <<peer_id<<std::endl;
1072 player->updateName((char*)&data[start+2]);
1078 Remove those players from the environment that
1079 weren't listed by the server.
1081 //dstream<<DTIME<<"Removing dead players"<<std::endl;
1082 core::list<Player*> players = m_env.getPlayers();
1083 core::list<Player*>::Iterator ip;
1084 for(ip=players.begin(); ip!=players.end(); ip++)
1086 // Ingore local player
1087 if((*ip)->isLocal())
1090 // Warn about a special case
1091 if((*ip)->peer_id == 0)
1093 dstream<<DTIME<<"WARNING: Client: Removing "
1094 "dead player with id=0"<<std::endl;
1097 bool is_alive = false;
1098 core::list<u16>::Iterator i;
1099 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1101 if((*ip)->peer_id == *i)
1107 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
1108 <<" is_alive="<<is_alive<<std::endl;*/
1111 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
1113 m_env.removePlayer((*ip)->peer_id);
1117 else if(command == TOCLIENT_SECTORMETA)
1119 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1124 [3...] v2s16 pos + sector metadata
1129 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1132 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1134 std::string datastring((char*)&data[2], datasize-2);
1135 std::istringstream is(datastring, std::ios_base::binary);
1139 is.read((char*)buf, 1);
1140 u16 sector_count = readU8(buf);
1142 //dstream<<"sector_count="<<sector_count<<std::endl;
1144 for(u16 i=0; i<sector_count; i++)
1147 is.read((char*)buf, 4);
1148 v2s16 pos = readV2S16(buf);
1149 /*dstream<<"Client: deserializing sector at "
1150 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1152 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1153 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1158 else if(command == TOCLIENT_INVENTORY)
1163 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1166 //TimeTaker t2("mutex locking", m_device);
1167 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1170 //TimeTaker t3("istringstream init", m_device);
1171 std::string datastring((char*)&data[2], datasize-2);
1172 std::istringstream is(datastring, std::ios_base::binary);
1175 //m_env.printPlayers(dstream);
1177 //TimeTaker t4("player get", m_device);
1178 Player *player = m_env.getLocalPlayer();
1179 assert(player != NULL);
1182 //TimeTaker t1("inventory.deSerialize()", m_device);
1183 player->inventory.deSerialize(is);
1186 m_inventory_updated = true;
1188 //dstream<<"Client got player inventory:"<<std::endl;
1189 //player->inventory.print(dstream);
1193 else if(command == TOCLIENT_OBJECTDATA)
1196 // Strip command word and create a stringstream
1197 std::string datastring((char*)&data[2], datasize-2);
1198 std::istringstream is(datastring, std::ios_base::binary);
1202 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1210 is.read((char*)buf, 2);
1211 u16 playercount = readU16(buf);
1213 for(u16 i=0; i<playercount; i++)
1215 is.read((char*)buf, 2);
1216 u16 peer_id = readU16(buf);
1217 is.read((char*)buf, 12);
1218 v3s32 p_i = readV3S32(buf);
1219 is.read((char*)buf, 12);
1220 v3s32 s_i = readV3S32(buf);
1221 is.read((char*)buf, 4);
1222 s32 pitch_i = readS32(buf);
1223 is.read((char*)buf, 4);
1224 s32 yaw_i = readS32(buf);
1226 Player *player = m_env.getPlayer(peer_id);
1228 // Skip if player doesn't exist
1234 // Skip if player is local player
1235 if(player->isLocal())
1240 f32 pitch = (f32)pitch_i / 100.0;
1241 f32 yaw = (f32)yaw_i / 100.0;
1242 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1243 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1245 player->setPosition(position);
1246 player->setSpeed(speed);
1247 player->setPitch(pitch);
1248 player->setYaw(yaw);
1253 NOTE: Deprecated stuff here, TODO: Remove
1256 // Read active block count
1257 is.read((char*)buf, 2);
1258 u16 blockcount = readU16(buf);
1260 // Initialize delete queue with all active blocks
1261 core::map<v3s16, bool> abs_to_delete;
1262 for(core::map<v3s16, bool>::Iterator
1263 i = m_active_blocks.getIterator();
1264 i.atEnd() == false; i++)
1266 v3s16 p = i.getNode()->getKey();
1267 /*dstream<<"adding "
1268 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1269 <<" to abs_to_delete"
1271 abs_to_delete.insert(p, true);
1274 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1277 for(u16 i=0; i<blockcount; i++)
1280 is.read((char*)buf, 6);
1281 v3s16 p = readV3S16(buf);
1282 // Get block from somewhere
1283 MapBlock *block = NULL;
1285 block = m_env.getMap().getBlockNoCreate(p);
1287 catch(InvalidPositionException &e)
1289 //TODO: Create a dummy block?
1293 dstream<<"WARNING: "
1294 <<"Could not get block at blockpos "
1295 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1296 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1297 <<"following block object data."
1302 /*dstream<<"Client updating objects for block "
1303 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1306 // Insert to active block list
1307 m_active_blocks.insert(p, true);
1309 // Remove from deletion queue
1310 if(abs_to_delete.find(p) != NULL)
1311 abs_to_delete.remove(p);
1314 Update objects of block
1316 NOTE: Be sure this is done in the main thread.
1318 block->updateObjects(is, m_server_ser_ver,
1319 m_device->getSceneManager(), m_env.getDayNightRatio());
1322 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1325 // Delete objects of blocks in delete queue
1326 for(core::map<v3s16, bool>::Iterator
1327 i = abs_to_delete.getIterator();
1328 i.atEnd() == false; i++)
1330 v3s16 p = i.getNode()->getKey();
1333 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1336 block->clearObjects();
1337 // Remove from active blocks list
1338 m_active_blocks.remove(p);
1340 catch(InvalidPositionException &e)
1342 dstream<<"WARNAING: Client: "
1343 <<"Couldn't clear objects of active->inactive"
1345 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1346 <<" because block was not found"
1354 else if(command == TOCLIENT_TIME_OF_DAY)
1359 u16 time_of_day = readU16(&data[2]);
1360 time_of_day = time_of_day % 24000;
1361 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1369 m_env.setTimeOfDay(time_of_day);
1371 u32 dr = m_env.getDayNightRatio();
1373 dstream<<"Client: time_of_day="<<time_of_day
1379 else if(command == TOCLIENT_CHAT_MESSAGE)
1387 std::string datastring((char*)&data[2], datasize-2);
1388 std::istringstream is(datastring, std::ios_base::binary);
1391 is.read((char*)buf, 2);
1392 u16 len = readU16(buf);
1394 std::wstring message;
1395 for(u16 i=0; i<len; i++)
1397 is.read((char*)buf, 2);
1398 message += (wchar_t)readU16(buf);
1401 /*dstream<<"Client received chat message: "
1402 <<wide_to_narrow(message)<<std::endl;*/
1404 m_chat_queue.push_back(message);
1406 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1408 //if(g_settings.getBool("enable_experimental"))
1412 u16 count of removed objects
1413 for all removed objects {
1416 u16 count of added objects
1417 for all added objects {
1420 u32 initialization data length
1421 string initialization data
1426 // Get all data except the command number
1427 std::string datastring((char*)&data[2], datasize-2);
1428 // Throw them in an istringstream
1429 std::istringstream is(datastring, std::ios_base::binary);
1433 // Read removed objects
1435 u16 removed_count = readU16((u8*)buf);
1436 for(u16 i=0; i<removed_count; i++)
1439 u16 id = readU16((u8*)buf);
1442 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1443 m_env.removeActiveObject(id);
1447 // Read added objects
1449 u16 added_count = readU16((u8*)buf);
1450 for(u16 i=0; i<added_count; i++)
1453 u16 id = readU16((u8*)buf);
1455 u8 type = readU8((u8*)buf);
1456 std::string data = deSerializeLongString(is);
1459 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1460 m_env.addActiveObject(id, type, data);
1465 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1467 //if(g_settings.getBool("enable_experimental"))
1479 // Get all data except the command number
1480 std::string datastring((char*)&data[2], datasize-2);
1481 // Throw them in an istringstream
1482 std::istringstream is(datastring, std::ios_base::binary);
1484 while(is.eof() == false)
1488 u16 id = readU16((u8*)buf);
1492 u16 message_size = readU16((u8*)buf);
1493 std::string message;
1494 message.reserve(message_size);
1495 for(u16 i=0; i<message_size; i++)
1498 message.append(buf, 1);
1500 // Pass on to the environment
1502 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1503 m_env.processActiveObjectMessage(id, message);
1508 else if(command == TOCLIENT_HP)
1510 std::string datastring((char*)&data[2], datasize-2);
1511 std::istringstream is(datastring, std::ios_base::binary);
1512 Player *player = m_env.getLocalPlayer();
1513 assert(player != NULL);
1517 else if(command == TOCLIENT_MOVE_PLAYER)
1519 std::string datastring((char*)&data[2], datasize-2);
1520 std::istringstream is(datastring, std::ios_base::binary);
1521 Player *player = m_env.getLocalPlayer();
1522 assert(player != NULL);
1523 v3f pos = readV3F1000(is);
1524 f32 pitch = readF1000(is);
1525 f32 yaw = readF1000(is);
1526 player->setPosition(pos);
1527 /*player->setPitch(pitch);
1528 player->setYaw(yaw);*/
1530 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1531 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1537 Add to ClientEvent queue.
1538 This has to be sent to the main program because otherwise
1539 it would just force the pitch and yaw values to whatever
1540 the camera points to.
1543 event.type = CE_PLAYER_FORCE_MOVE;
1544 event.player_force_move.pitch = pitch;
1545 event.player_force_move.yaw = yaw;
1546 m_client_event_queue.push_back(event);
1548 // Ignore damage for a few seconds, so that the player doesn't
1549 // get damage from falling on ground
1550 m_ignore_damage_timer = 3.0;
1552 else if(command == TOCLIENT_PLAYERITEM)
1554 std::string datastring((char*)&data[2], datasize-2);
1555 std::istringstream is(datastring, std::ios_base::binary);
1557 u16 count = readU16(is);
1559 for (u16 i = 0; i < count; ++i) {
1560 u16 peer_id = readU16(is);
1561 Player *player = m_env.getPlayer(peer_id);
1565 dout_client<<DTIME<<"Client: ignoring player item "
1566 << deSerializeString(is)
1567 << " for non-existing peer id " << peer_id
1570 } else if (player->isLocal()) {
1571 dout_client<<DTIME<<"Client: ignoring player item "
1572 << deSerializeString(is)
1573 << " for local player" << std::endl;
1576 InventoryList *inv = player->inventory.getList("main");
1577 std::string itemstring(deSerializeString(is));
1578 if (itemstring.empty()) {
1581 <<"Client: empty player item for peer "
1582 << peer_id << std::endl;
1584 std::istringstream iss(itemstring);
1585 delete inv->changeItem(0, InventoryItem::deSerialize(iss));
1586 dout_client<<DTIME<<"Client: player item for peer " << peer_id << ": ";
1587 player->getWieldItem()->serialize(dout_client);
1588 dout_client<<std::endl;
1595 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1596 <<command<<std::endl;
1600 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1602 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1603 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1606 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1607 v3s16 nodepos_oversurface, u16 item)
1609 if(connectedAndInitialized() == false){
1610 dout_client<<DTIME<<"Client::groundAction() "
1611 "cancelled (not connected)"
1620 [3] v3s16 nodepos_undersurface
1621 [9] v3s16 nodepos_abovesurface
1626 2: stop digging (all parameters ignored)
1627 3: digging completed
1629 u8 datasize = 2 + 1 + 6 + 6 + 2;
1630 SharedBuffer<u8> data(datasize);
1631 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1632 writeU8(&data[2], action);
1633 writeV3S16(&data[3], nodepos_undersurface);
1634 writeV3S16(&data[9], nodepos_oversurface);
1635 writeU16(&data[15], item);
1636 Send(0, data, true);
1639 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1641 if(connectedAndInitialized() == false){
1642 dout_client<<DTIME<<"Client::clickObject() "
1643 "cancelled (not connected)"
1649 [0] u16 command=TOSERVER_CLICK_OBJECT
1650 [2] u8 button (0=left, 1=right)
1655 u8 datasize = 2 + 1 + 6 + 2 + 2;
1656 SharedBuffer<u8> data(datasize);
1657 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1658 writeU8(&data[2], button);
1659 writeV3S16(&data[3], blockpos);
1660 writeS16(&data[9], id);
1661 writeU16(&data[11], item);
1662 Send(0, data, true);
1665 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1667 if(connectedAndInitialized() == false){
1668 dout_client<<DTIME<<"Client::clickActiveObject() "
1669 "cancelled (not connected)"
1677 [2] u8 button (0=left, 1=right)
1681 u8 datasize = 2 + 1 + 6 + 2 + 2;
1682 SharedBuffer<u8> data(datasize);
1683 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1684 writeU8(&data[2], button);
1685 writeU16(&data[3], id);
1686 writeU16(&data[5], item);
1687 Send(0, data, true);
1690 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1699 std::ostringstream os(std::ios_base::binary);
1703 writeU16(buf, TOSERVER_SIGNTEXT);
1704 os.write((char*)buf, 2);
1707 writeV3S16(buf, blockpos);
1708 os.write((char*)buf, 6);
1712 os.write((char*)buf, 2);
1714 u16 textlen = text.size();
1715 // Write text length
1716 writeS16(buf, textlen);
1717 os.write((char*)buf, 2);
1720 os.write((char*)text.c_str(), textlen);
1723 std::string s = os.str();
1724 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1726 Send(0, data, true);
1729 void Client::sendSignNodeText(v3s16 p, std::string text)
1737 std::ostringstream os(std::ios_base::binary);
1741 writeU16(buf, TOSERVER_SIGNNODETEXT);
1742 os.write((char*)buf, 2);
1746 os.write((char*)buf, 6);
1748 u16 textlen = text.size();
1749 // Write text length
1750 writeS16(buf, textlen);
1751 os.write((char*)buf, 2);
1754 os.write((char*)text.c_str(), textlen);
1757 std::string s = os.str();
1758 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1760 Send(0, data, true);
1763 void Client::sendInventoryAction(InventoryAction *a)
1765 std::ostringstream os(std::ios_base::binary);
1769 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1770 os.write((char*)buf, 2);
1775 std::string s = os.str();
1776 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1778 Send(0, data, true);
1781 void Client::sendChatMessage(const std::wstring &message)
1783 std::ostringstream os(std::ios_base::binary);
1787 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1788 os.write((char*)buf, 2);
1791 writeU16(buf, message.size());
1792 os.write((char*)buf, 2);
1795 for(u32 i=0; i<message.size(); i++)
1799 os.write((char*)buf, 2);
1803 std::string s = os.str();
1804 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1806 Send(0, data, true);
1809 void Client::sendChangePassword(const std::wstring oldpassword,
1810 const std::wstring newpassword)
1812 Player *player = m_env.getLocalPlayer();
1816 std::string playername = player->getName();
1817 std::string oldpwd = translatePassword(playername, oldpassword);
1818 std::string newpwd = translatePassword(playername, newpassword);
1820 std::ostringstream os(std::ios_base::binary);
1821 u8 buf[2+PASSWORD_SIZE*2];
1823 [0] u16 TOSERVER_PASSWORD
1824 [2] u8[28] old password
1825 [30] u8[28] new password
1828 writeU16(buf, TOSERVER_PASSWORD);
1829 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1831 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1832 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1834 buf[2+PASSWORD_SIZE-1] = 0;
1835 buf[30+PASSWORD_SIZE-1] = 0;
1836 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1839 std::string s = os.str();
1840 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1842 Send(0, data, true);
1846 void Client::sendDamage(u8 damage)
1848 DSTACK(__FUNCTION_NAME);
1849 std::ostringstream os(std::ios_base::binary);
1851 writeU16(os, TOSERVER_DAMAGE);
1852 writeU8(os, damage);
1855 std::string s = os.str();
1856 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1858 Send(0, data, true);
1861 void Client::sendPlayerPos()
1863 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1865 Player *myplayer = m_env.getLocalPlayer();
1866 if(myplayer == NULL)
1871 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1872 our_peer_id = m_con.GetPeerID();
1875 // Set peer id if not set already
1876 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1877 myplayer->peer_id = our_peer_id;
1878 // Check that an existing peer_id is the same as the connection's
1879 assert(myplayer->peer_id == our_peer_id);
1881 v3f pf = myplayer->getPosition();
1882 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1883 v3f sf = myplayer->getSpeed();
1884 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1885 s32 pitch = myplayer->getPitch() * 100;
1886 s32 yaw = myplayer->getYaw() * 100;
1891 [2] v3s32 position*100
1892 [2+12] v3s32 speed*100
1893 [2+12+12] s32 pitch*100
1894 [2+12+12+4] s32 yaw*100
1897 SharedBuffer<u8> data(2+12+12+4+4);
1898 writeU16(&data[0], TOSERVER_PLAYERPOS);
1899 writeV3S32(&data[2], position);
1900 writeV3S32(&data[2+12], speed);
1901 writeS32(&data[2+12+12], pitch);
1902 writeS32(&data[2+12+12+4], yaw);
1904 // Send as unreliable
1905 Send(0, data, false);
1908 void Client::sendPlayerItem(u16 item)
1910 Player *myplayer = m_env.getLocalPlayer();
1911 if(myplayer == NULL)
1914 u16 our_peer_id = m_con.GetPeerID();
1916 // Set peer id if not set already
1917 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1918 myplayer->peer_id = our_peer_id;
1919 // Check that an existing peer_id is the same as the connection's
1920 assert(myplayer->peer_id == our_peer_id);
1922 SharedBuffer<u8> data(2+2);
1923 writeU16(&data[0], TOSERVER_PLAYERITEM);
1924 writeU16(&data[2], item);
1927 Send(0, data, true);
1930 void Client::removeNode(v3s16 p)
1932 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1934 core::map<v3s16, MapBlock*> modified_blocks;
1938 //TimeTaker t("removeNodeAndUpdate", m_device);
1939 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1941 catch(InvalidPositionException &e)
1945 for(core::map<v3s16, MapBlock * >::Iterator
1946 i = modified_blocks.getIterator();
1947 i.atEnd() == false; i++)
1949 v3s16 p = i.getNode()->getKey();
1950 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1951 addUpdateMeshTaskWithEdge(p);
1955 void Client::addNode(v3s16 p, MapNode n)
1957 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1959 TimeTaker timer1("Client::addNode()");
1961 core::map<v3s16, MapBlock*> modified_blocks;
1965 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1966 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1968 catch(InvalidPositionException &e)
1971 //TimeTaker timer2("Client::addNode(): updateMeshes");
1973 for(core::map<v3s16, MapBlock * >::Iterator
1974 i = modified_blocks.getIterator();
1975 i.atEnd() == false; i++)
1977 v3s16 p = i.getNode()->getKey();
1978 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1979 addUpdateMeshTaskWithEdge(p);
1983 void Client::updateCamera(v3f pos, v3f dir)
1985 m_env.getClientMap().updateCamera(pos, dir);
1986 camera_position = pos;
1987 camera_direction = dir;
1990 MapNode Client::getNode(v3s16 p)
1992 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1993 return m_env.getMap().getNode(p);
1996 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1998 return m_env.getMap().getNodeMetadata(p);
2001 v3f Client::getPlayerPosition(v3f *eye_position)
2003 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2004 LocalPlayer *player = m_env.getLocalPlayer();
2005 assert(player != NULL);
2007 *eye_position = player->getEyePosition();
2008 return player->getPosition();
2011 void Client::setPlayerControl(PlayerControl &control)
2013 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2014 LocalPlayer *player = m_env.getLocalPlayer();
2015 assert(player != NULL);
2016 player->control = control;
2019 void Client::selectPlayerItem(u16 item)
2021 LocalPlayer *player = m_env.getLocalPlayer();
2022 assert(player != NULL);
2024 player->wieldItem(item);
2026 sendPlayerItem(item);
2029 // Returns true if the inventory of the local player has been
2030 // updated from the server. If it is true, it is set to false.
2031 bool Client::getLocalInventoryUpdated()
2033 // m_inventory_updated is behind envlock
2034 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2035 bool updated = m_inventory_updated;
2036 m_inventory_updated = false;
2040 // Copies the inventory of the local player to parameter
2041 void Client::getLocalInventory(Inventory &dst)
2043 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2044 Player *player = m_env.getLocalPlayer();
2045 assert(player != NULL);
2046 dst = player->inventory;
2049 InventoryContext *Client::getInventoryContext()
2051 return &m_inventory_context;
2054 Inventory* Client::getInventory(InventoryContext *c, std::string id)
2056 if(id == "current_player")
2058 assert(c->current_player);
2059 return &(c->current_player->inventory);
2063 std::string id0 = fn.next(":");
2065 if(id0 == "nodemeta")
2068 p.X = stoi(fn.next(","));
2069 p.Y = stoi(fn.next(","));
2070 p.Z = stoi(fn.next(","));
2071 NodeMetadata* meta = getNodeMetadata(p);
2073 return meta->getInventory();
2074 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2075 <<"no metadata found"<<std::endl;
2079 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2082 void Client::inventoryAction(InventoryAction *a)
2084 sendInventoryAction(a);
2087 MapBlockObject * Client::getSelectedObject(
2089 v3f from_pos_f_on_map,
2090 core::line3d<f32> shootline_on_map
2093 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2095 core::array<DistanceSortedObject> objects;
2097 for(core::map<v3s16, bool>::Iterator
2098 i = m_active_blocks.getIterator();
2099 i.atEnd() == false; i++)
2101 v3s16 p = i.getNode()->getKey();
2103 MapBlock *block = NULL;
2106 block = m_env.getMap().getBlockNoCreate(p);
2108 catch(InvalidPositionException &e)
2113 // Calculate from_pos relative to block
2114 v3s16 block_pos_i_on_map = block->getPosRelative();
2115 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2116 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
2118 block->getObjects(from_pos_f_on_block, max_d, objects);
2119 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
2122 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2125 // After this, the closest object is the first in the array.
2128 for(u32 i=0; i<objects.size(); i++)
2130 MapBlockObject *obj = objects[i].obj;
2131 MapBlock *block = obj->getBlock();
2133 // Calculate shootline relative to block
2134 v3s16 block_pos_i_on_map = block->getPosRelative();
2135 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2136 core::line3d<f32> shootline_on_block(
2137 shootline_on_map.start - block_pos_f_on_map,
2138 shootline_on_map.end - block_pos_f_on_map
2141 if(obj->isSelected(shootline_on_block))
2143 //dstream<<"Returning selected object"<<std::endl;
2148 //dstream<<"No object selected; returning NULL."<<std::endl;
2152 ClientActiveObject * Client::getSelectedActiveObject(
2154 v3f from_pos_f_on_map,
2155 core::line3d<f32> shootline_on_map
2158 core::array<DistanceSortedActiveObject> objects;
2160 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2162 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2165 // After this, the closest object is the first in the array.
2168 for(u32 i=0; i<objects.size(); i++)
2170 ClientActiveObject *obj = objects[i].obj;
2172 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2173 if(selection_box == NULL)
2176 v3f pos = obj->getPosition();
2178 core::aabbox3d<f32> offsetted_box(
2179 selection_box->MinEdge + pos,
2180 selection_box->MaxEdge + pos
2183 if(offsetted_box.intersectsWithLine(shootline_on_map))
2185 //dstream<<"Returning selected object"<<std::endl;
2190 //dstream<<"No object selected; returning NULL."<<std::endl;
2194 void Client::printDebugInfo(std::ostream &os)
2196 //JMutexAutoLock lock1(m_fetchblock_mutex);
2197 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2199 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2200 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2201 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2205 u32 Client::getDayNightRatio()
2207 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2208 return m_env.getDayNightRatio();
2213 Player *player = m_env.getLocalPlayer();
2214 assert(player != NULL);
2218 void Client::setTempMod(v3s16 p, NodeMod mod)
2220 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2221 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2223 core::map<v3s16, MapBlock*> affected_blocks;
2224 ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2227 for(core::map<v3s16, MapBlock*>::Iterator
2228 i = affected_blocks.getIterator();
2229 i.atEnd() == false; i++)
2231 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2235 void Client::clearTempMod(v3s16 p)
2237 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2238 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2240 core::map<v3s16, MapBlock*> affected_blocks;
2241 ((ClientMap&)m_env.getMap()).clearTempMod(p,
2244 for(core::map<v3s16, MapBlock*>::Iterator
2245 i = affected_blocks.getIterator();
2246 i.atEnd() == false; i++)
2248 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2252 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2254 /*dstream<<"Client::addUpdateMeshTask(): "
2255 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2258 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2263 Create a task to update the mesh of the block
2266 MeshMakeData *data = new MeshMakeData;
2269 //TimeTaker timer("data fill");
2271 // Debug: 1-6ms, avg=2ms
2272 data->fill(getDayNightRatio(), b);
2276 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2278 // Add task to queue
2279 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2281 /*dstream<<"Mesh update input queue size is "
2282 <<m_mesh_update_thread.m_queue_in.size()
2286 // Temporary test: make mesh directly in here
2288 //TimeTaker timer("make mesh");
2290 scene::SMesh *mesh_new = NULL;
2291 mesh_new = makeMapBlockMesh(data);
2292 b->replaceMesh(mesh_new);
2298 Mark mesh as non-expired at this point so that it can already
2299 be marked as expired again if the data changes
2301 b->setMeshExpired(false);
2304 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2308 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2309 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2314 v3s16 p = blockpos + v3s16(0,0,0);
2315 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2316 addUpdateMeshTask(p, ack_to_server);
2318 catch(InvalidPositionException &e){}
2321 v3s16 p = blockpos + v3s16(-1,0,0);
2322 addUpdateMeshTask(p);
2324 catch(InvalidPositionException &e){}
2326 v3s16 p = blockpos + v3s16(0,-1,0);
2327 addUpdateMeshTask(p);
2329 catch(InvalidPositionException &e){}
2331 v3s16 p = blockpos + v3s16(0,0,-1);
2332 addUpdateMeshTask(p);
2334 catch(InvalidPositionException &e){}
2337 ClientEvent Client::getClientEvent()
2339 if(m_client_event_queue.size() == 0)
2342 event.type = CE_NONE;
2345 return m_client_event_queue.pop_front();