4 #include "clientserver.h"
5 #include "jmutexautolock.h"
11 #define sleep_ms(x) Sleep(x)
14 #define sleep_ms(x) usleep(x*1000)
17 void * ClientUpdateThread::Thread()
21 DSTACK(__FUNCTION_NAME);
23 #if CATCH_UNHANDLED_EXCEPTIONS
29 m_client->asyncStep();
31 bool was = m_client->AsyncProcessData();
36 #if CATCH_UNHANDLED_EXCEPTIONS
39 This is what has to be done in threads to get suitable debug info
41 catch(std::exception &e)
43 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
44 <<e.what()<<std::endl;
52 Client::Client(IrrlichtDevice *device, video::SMaterial *materials,
53 float delete_unused_sectors_timeout,
54 const char *playername):
56 m_env(new ClientMap(this, materials,
57 device->getSceneManager()->getRootSceneNode(),
58 device->getSceneManager(), 666),
60 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
62 camera_position(0,0,0),
63 camera_direction(0,0,1),
64 m_server_ser_ver(SER_FMT_VER_INVALID),
66 m_delete_unused_sectors_timeout(delete_unused_sectors_timeout),
67 m_inventory_updated(false)
69 //m_fetchblock_mutex.Init();
70 m_incoming_queue_mutex.Init();
73 m_step_dtime_mutex.Init();
78 JMutexAutoLock envlock(m_env_mutex);
79 //m_env.getMap().StartUpdater();
81 Player *player = new LocalPlayer();
83 player->updateName(playername);
85 /*f32 y = BS*2 + BS*20;
86 player->setPosition(v3f(0, y, 0));*/
87 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
88 m_env.addPlayer(player);
94 m_thread.setRun(false);
95 while(m_thread.IsRunning())
99 void Client::connect(Address address)
101 DSTACK(__FUNCTION_NAME);
102 JMutexAutoLock lock(m_con_mutex);
103 m_con.setTimeoutMs(0);
104 m_con.Connect(address);
107 bool Client::connectedAndInitialized()
109 JMutexAutoLock lock(m_con_mutex);
111 if(m_con.Connected() == false)
114 if(m_server_ser_ver == SER_FMT_VER_INVALID)
120 void Client::step(float dtime)
122 DSTACK(__FUNCTION_NAME);
128 //dstream<<"Client steps "<<dtime<<std::endl;
131 //TimeTaker timer("ReceiveAll()", m_device);
137 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
139 JMutexAutoLock lock(m_con_mutex);
140 m_con.RunTimeouts(dtime);
147 static float counter = -0.001;
153 dout_client<<"Client packetcounter:"<<std::endl;
154 m_packetcounter.print(dout_client);
155 m_packetcounter.clear();
161 Delete unused sectors
164 static float counter = -0.001;
170 JMutexAutoLock lock(m_env_mutex);
172 core::list<v3s16> deleted_blocks;
174 // Delete sector blocks
175 /*u32 num = m_env.getMap().deleteUnusedSectors
176 (m_delete_unused_sectors_timeout,
177 true, &deleted_blocks);*/
179 // Delete whole sectors
180 u32 num = m_env.getMap().deleteUnusedSectors
181 (m_delete_unused_sectors_timeout,
182 false, &deleted_blocks);
186 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
187 <<" unused sectors"<<std::endl;*/
188 dstream<<DTIME<<"Client: Deleted "<<num
189 <<" unused sectors"<<std::endl;
195 // Env is locked so con can be locked.
196 JMutexAutoLock lock(m_con_mutex);
198 core::list<v3s16>::Iterator i = deleted_blocks.begin();
199 core::list<v3s16> sendlist;
202 if(sendlist.size() == 255 || i == deleted_blocks.end())
204 if(sendlist.size() == 0)
213 u32 replysize = 2+1+6*sendlist.size();
214 SharedBuffer<u8> reply(replysize);
215 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
216 reply[2] = sendlist.size();
218 for(core::list<v3s16>::Iterator
219 j = sendlist.begin();
220 j != sendlist.end(); j++)
222 writeV3S16(&reply[2+1+6*k], *j);
225 m_con.Send(PEER_ID_SERVER, 1, reply, true);
227 if(i == deleted_blocks.end())
233 sendlist.push_back(*i);
240 bool connected = connectedAndInitialized();
242 if(connected == false)
244 static float counter = -0.001;
250 JMutexAutoLock envlock(m_env_mutex);
252 Player *myplayer = m_env.getLocalPlayer();
253 assert(myplayer != NULL);
255 // Send TOSERVER_INIT
256 // [0] u16 TOSERVER_INIT
257 // [2] u8 SER_FMT_VER_HIGHEST
258 // [3] u8[20] player_name
259 SharedBuffer<u8> data(2+1+20);
260 writeU16(&data[0], TOSERVER_INIT);
261 writeU8(&data[2], SER_FMT_VER_HIGHEST);
262 memcpy(&data[3], myplayer->getName(), 20);
263 // Send as unreliable
264 Send(0, data, false);
267 // Not connected, return
272 Do stuff if connected
277 JMutexAutoLock lock(m_env_mutex);
279 // Control local player (0ms)
280 LocalPlayer *player = m_env.getLocalPlayer();
281 assert(player != NULL);
282 player->applyControl(dtime);
284 //TimeTaker envtimer("env step", m_device);
288 // Step active blocks
289 for(core::map<v3s16, bool>::Iterator
290 i = m_active_blocks.getIterator();
291 i.atEnd() == false; i++)
293 v3s16 p = i.getNode()->getKey();
295 MapBlock *block = NULL;
298 block = m_env.getMap().getBlockNoCreate(p);
299 block->stepObjects(dtime, false);
301 catch(InvalidPositionException &e)
308 // Fetch some nearby blocks
313 static float counter = 0.0;
318 JMutexAutoLock lock(m_con_mutex);
319 // connectedAndInitialized() is true, peer exists.
320 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
321 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
325 // Update at reasonable intervals (0.2s)
326 static float counter = 0.0;
337 Clear old entries from fetchblock history
340 JMutexAutoLock lock(m_fetchblock_mutex);
342 core::list<v3s16> remove_queue;
343 core::map<v3s16, float>::Iterator i;
344 i = m_fetchblock_history.getIterator();
345 for(; i.atEnd() == false; i++)
347 float value = i.getNode()->getValue();
349 i.getNode()->setValue(value);
351 remove_queue.push_back(i.getNode()->getKey());
353 core::list<v3s16>::Iterator j;
354 j = remove_queue.begin();
355 for(; j != remove_queue.end(); j++)
357 m_fetchblock_history.remove(*j);
363 JMutexAutoLock lock(m_step_dtime_mutex);
364 m_step_dtime += dtime;
376 float Client::asyncStep()
378 DSTACK(__FUNCTION_NAME);
379 //dstream<<"Client::asyncStep()"<<std::endl;
383 JMutexAutoLock lock1(m_step_dtime_mutex);
384 dtime = m_step_dtime;
392 // Virtual methods from con::PeerHandler
393 void Client::peerAdded(con::Peer *peer)
395 derr_client<<"Client::peerAdded(): peer->id="
396 <<peer->id<<std::endl;
398 void Client::deletingPeer(con::Peer *peer, bool timeout)
400 derr_client<<"Client::deletingPeer(): "
401 "Server Peer is getting deleted "
402 <<"(timeout="<<timeout<<")"<<std::endl;
405 void Client::ReceiveAll()
407 DSTACK(__FUNCTION_NAME);
413 catch(con::NoIncomingDataException &e)
417 catch(con::InvalidIncomingDataException &e)
419 dout_client<<DTIME<<"Client::ReceiveAll(): "
420 "InvalidIncomingDataException: what()="
421 <<e.what()<<std::endl;
428 void Client::Receive()
430 DSTACK(__FUNCTION_NAME);
431 u32 data_maxsize = 10000;
432 Buffer<u8> data(data_maxsize);
436 //TimeTaker t1("con mutex and receive", m_device);
437 JMutexAutoLock lock(m_con_mutex);
438 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
440 //TimeTaker t1("ProcessData", m_device);
441 ProcessData(*data, datasize, sender_peer_id);
445 sender_peer_id given to this shall be quaranteed to be a valid peer
447 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
449 DSTACK(__FUNCTION_NAME);
451 // Ignore packets that don't even fit a command
454 m_packetcounter.add(60000);
458 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
460 //dstream<<"Client: received command="<<command<<std::endl;
461 m_packetcounter.add((u16)command);
464 If this check is removed, be sure to change the queue
465 system to know the ids
467 if(sender_peer_id != PEER_ID_SERVER)
469 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
470 "coming from server: peer_id="<<sender_peer_id
477 JMutexAutoLock lock(m_con_mutex);
478 // All data is coming from the server
479 // PeerNotFoundException is handled by caller.
480 peer = m_con.GetPeer(PEER_ID_SERVER);
483 u8 ser_version = m_server_ser_ver;
485 //dstream<<"Client received command="<<(int)command<<std::endl;
487 // Execute fast commands straight away
489 if(command == TOCLIENT_INIT)
494 u8 deployed = data[2];
496 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
497 "deployed="<<((int)deployed&0xff)<<std::endl;
499 if(deployed < SER_FMT_VER_LOWEST
500 || deployed > SER_FMT_VER_HIGHEST)
502 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
503 <<"unsupported ser_fmt_ver"<<std::endl;
507 m_server_ser_ver = deployed;
509 // Get player position
510 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
511 if(datasize >= 2+1+6)
512 playerpos_s16 = readV3S16(&data[2+1]);
513 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
516 JMutexAutoLock envlock(m_env_mutex);
518 // Set player position
519 Player *player = m_env.getLocalPlayer();
520 assert(player != NULL);
521 player->setPosition(playerpos_f);
526 SharedBuffer<u8> reply(replysize);
527 writeU16(&reply[0], TOSERVER_INIT2);
529 m_con.Send(PEER_ID_SERVER, 1, reply, true);
534 if(ser_version == SER_FMT_VER_INVALID)
536 dout_client<<DTIME<<"WARNING: Client: Server serialization"
537 " format invalid or not initialized."
538 " Skipping incoming command="<<command<<std::endl;
542 // Just here to avoid putting the two if's together when
543 // making some copypasta
546 if(command == TOCLIENT_PLAYERPOS)
548 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
552 JMutexAutoLock lock(m_con_mutex);
553 our_peer_id = m_con.GetPeerID();
555 // Cancel if we don't have a peer id
556 if(our_peer_id == PEER_ID_NEW){
557 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
564 JMutexAutoLock envlock(m_env_mutex);
566 u32 player_size = 2+12+12+4+4;
568 u32 player_count = (datasize-2) / player_size;
570 for(u32 i=0; i<player_count; i++)
572 u16 peer_id = readU16(&data[start]);
574 Player *player = m_env.getPlayer(peer_id);
576 // Skip if player doesn't exist
579 start += player_size;
583 // Skip if player is local player
584 if(player->isLocal())
586 start += player_size;
590 v3s32 ps = readV3S32(&data[start+2]);
591 v3s32 ss = readV3S32(&data[start+2+12]);
592 s32 pitch_i = readS32(&data[start+2+12+12]);
593 s32 yaw_i = readS32(&data[start+2+12+12+4]);
594 /*dstream<<"Client: got "
595 <<"pitch_i="<<pitch_i
596 <<" yaw_i="<<yaw_i<<std::endl;*/
597 f32 pitch = (f32)pitch_i / 100.0;
598 f32 yaw = (f32)yaw_i / 100.0;
599 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
600 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
601 player->setPosition(position);
602 player->setSpeed(speed);
603 player->setPitch(pitch);
606 /*dstream<<"Client: player "<<peer_id
608 <<" yaw="<<yaw<<std::endl;*/
610 start += player_size;
614 else if(command == TOCLIENT_PLAYERINFO)
618 JMutexAutoLock lock(m_con_mutex);
619 our_peer_id = m_con.GetPeerID();
621 // Cancel if we don't have a peer id
622 if(our_peer_id == PEER_ID_NEW){
623 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
629 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
632 JMutexAutoLock envlock(m_env_mutex);
634 u32 item_size = 2+PLAYERNAME_SIZE;
635 u32 player_count = (datasize-2) / item_size;
638 core::list<u16> players_alive;
639 for(u32 i=0; i<player_count; i++)
641 // Make sure the name ends in '\0'
642 data[start+2+20-1] = 0;
644 u16 peer_id = readU16(&data[start]);
646 players_alive.push_back(peer_id);
648 /*dstream<<DTIME<<"peer_id="<<peer_id
649 <<" name="<<((char*)&data[start+2])<<std::endl;*/
651 // Don't update the info of the local player
652 if(peer_id == our_peer_id)
658 Player *player = m_env.getPlayer(peer_id);
660 // Create a player if it doesn't exist
663 player = new RemotePlayer(
664 m_device->getSceneManager()->getRootSceneNode(),
667 player->peer_id = peer_id;
668 m_env.addPlayer(player);
669 dout_client<<DTIME<<"Client: Adding new player "
670 <<peer_id<<std::endl;
673 player->updateName((char*)&data[start+2]);
679 Remove those players from the environment that
680 weren't listed by the server.
682 //dstream<<DTIME<<"Removing dead players"<<std::endl;
683 core::list<Player*> players = m_env.getPlayers();
684 core::list<Player*>::Iterator ip;
685 for(ip=players.begin(); ip!=players.end(); ip++)
687 // Ingore local player
691 // Warn about a special case
692 if((*ip)->peer_id == 0)
694 dstream<<DTIME<<"WARNING: Client: Removing "
695 "dead player with id=0"<<std::endl;
698 bool is_alive = false;
699 core::list<u16>::Iterator i;
700 for(i=players_alive.begin(); i!=players_alive.end(); i++)
702 if((*ip)->peer_id == *i)
708 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
709 <<" is_alive="<<is_alive<<std::endl;*/
712 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
714 m_env.removePlayer((*ip)->peer_id);
718 else if(command == TOCLIENT_SECTORMETA)
723 [3...] v2s16 pos + sector metadata
728 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
731 JMutexAutoLock envlock(m_env_mutex);
733 std::string datastring((char*)&data[2], datasize-2);
734 std::istringstream is(datastring, std::ios_base::binary);
738 is.read((char*)buf, 1);
739 u16 sector_count = readU8(buf);
741 //dstream<<"sector_count="<<sector_count<<std::endl;
743 for(u16 i=0; i<sector_count; i++)
746 is.read((char*)buf, 4);
747 v2s16 pos = readV2S16(buf);
748 /*dstream<<"Client: deserializing sector at "
749 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
751 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
752 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
756 else if(command == TOCLIENT_INVENTORY)
761 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
764 //TimeTaker t2("mutex locking", m_device);
765 JMutexAutoLock envlock(m_env_mutex);
768 //TimeTaker t3("istringstream init", m_device);
769 std::string datastring((char*)&data[2], datasize-2);
770 std::istringstream is(datastring, std::ios_base::binary);
773 //m_env.printPlayers(dstream);
775 //TimeTaker t4("player get", m_device);
776 Player *player = m_env.getLocalPlayer();
777 assert(player != NULL);
780 //TimeTaker t1("inventory.deSerialize()", m_device);
781 player->inventory.deSerialize(is);
784 m_inventory_updated = true;
786 //dstream<<"Client got player inventory:"<<std::endl;
787 //player->inventory.print(dstream);
791 else if(command == TOCLIENT_OBJECTDATA)
794 // Strip command word and create a stringstream
795 std::string datastring((char*)&data[2], datasize-2);
796 std::istringstream is(datastring, std::ios_base::binary);
800 JMutexAutoLock envlock(m_env_mutex);
808 is.read((char*)buf, 2);
809 u16 playercount = readU16(buf);
811 for(u16 i=0; i<playercount; i++)
813 is.read((char*)buf, 2);
814 u16 peer_id = readU16(buf);
815 is.read((char*)buf, 12);
816 v3s32 p_i = readV3S32(buf);
817 is.read((char*)buf, 12);
818 v3s32 s_i = readV3S32(buf);
819 is.read((char*)buf, 4);
820 s32 pitch_i = readS32(buf);
821 is.read((char*)buf, 4);
822 s32 yaw_i = readS32(buf);
824 Player *player = m_env.getPlayer(peer_id);
826 // Skip if player doesn't exist
832 // Skip if player is local player
833 if(player->isLocal())
838 f32 pitch = (f32)pitch_i / 100.0;
839 f32 yaw = (f32)yaw_i / 100.0;
840 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
841 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
843 player->setPosition(position);
844 player->setSpeed(speed);
845 player->setPitch(pitch);
853 // Read active block count
854 is.read((char*)buf, 2);
855 u16 blockcount = readU16(buf);
857 // Initialize delete queue with all active blocks
858 core::map<v3s16, bool> abs_to_delete;
859 for(core::map<v3s16, bool>::Iterator
860 i = m_active_blocks.getIterator();
861 i.atEnd() == false; i++)
863 v3s16 p = i.getNode()->getKey();
865 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
866 <<" to abs_to_delete"
868 abs_to_delete.insert(p, true);
871 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
874 for(u16 i=0; i<blockcount; i++)
877 is.read((char*)buf, 6);
878 v3s16 p = readV3S16(buf);
879 // Get block from somewhere
880 MapBlock *block = NULL;
882 block = m_env.getMap().getBlockNoCreate(p);
884 catch(InvalidPositionException &e)
886 //TODO: Create a dummy block?
891 <<"Could not get block at blockpos "
892 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
893 <<"in TOCLIENT_OBJECTDATA. Ignoring "
894 <<"following block object data."
899 /*dstream<<"Client updating objects for block "
900 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
903 // Insert to active block list
904 m_active_blocks.insert(p, true);
906 // Remove from deletion queue
907 if(abs_to_delete.find(p) != NULL)
908 abs_to_delete.remove(p);
910 // Update objects of block
911 block->updateObjects(is, m_server_ser_ver,
912 m_device->getSceneManager());
915 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
918 // Delete objects of blocks in delete queue
919 for(core::map<v3s16, bool>::Iterator
920 i = abs_to_delete.getIterator();
921 i.atEnd() == false; i++)
923 v3s16 p = i.getNode()->getKey();
926 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
929 block->clearObjects();
930 // Remove from active blocks list
931 m_active_blocks.remove(p);
933 catch(InvalidPositionException &e)
935 dstream<<"WARNAING: Client: "
936 <<"Couldn't clear objects of active->inactive"
938 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
939 <<" because block was not found"
947 // Default to queueing it (for slow commands)
950 JMutexAutoLock lock(m_incoming_queue_mutex);
952 IncomingPacket packet(data, datasize);
953 m_incoming_queue.push_back(packet);
958 Returns true if there was something in queue
960 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
962 DSTACK(__FUNCTION_NAME);
964 try //for catching con::PeerNotFoundException
969 JMutexAutoLock lock(m_con_mutex);
970 // All data is coming from the server
971 peer = m_con.GetPeer(PEER_ID_SERVER);
974 u8 ser_version = m_server_ser_ver;
976 IncomingPacket packet = getPacket();
977 u8 *data = packet.m_data;
978 u32 datasize = packet.m_datalen;
980 // An empty packet means queue is empty
988 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
990 if(command == TOCLIENT_REMOVENODE)
995 p.X = readS16(&data[2]);
996 p.Y = readS16(&data[4]);
997 p.Z = readS16(&data[6]);
999 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1001 core::map<v3s16, MapBlock*> modified_blocks;
1005 JMutexAutoLock envlock(m_env_mutex);
1006 //TimeTaker t("removeNodeAndUpdate", m_device);
1007 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1009 catch(InvalidPositionException &e)
1013 for(core::map<v3s16, MapBlock * >::Iterator
1014 i = modified_blocks.getIterator();
1015 i.atEnd() == false; i++)
1017 v3s16 p = i.getNode()->getKey();
1018 //m_env.getMap().updateMeshes(p);
1019 mesh_updater.add(p);
1022 else if(command == TOCLIENT_ADDNODE)
1024 if(datasize < 8 + MapNode::serializedLength(ser_version))
1028 p.X = readS16(&data[2]);
1029 p.Y = readS16(&data[4]);
1030 p.Z = readS16(&data[6]);
1032 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1035 n.deSerialize(&data[8], ser_version);
1037 core::map<v3s16, MapBlock*> modified_blocks;
1041 JMutexAutoLock envlock(m_env_mutex);
1042 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1044 catch(InvalidPositionException &e)
1047 for(core::map<v3s16, MapBlock * >::Iterator
1048 i = modified_blocks.getIterator();
1049 i.atEnd() == false; i++)
1051 v3s16 p = i.getNode()->getKey();
1052 //m_env.getMap().updateMeshes(p);
1053 mesh_updater.add(p);
1056 else if(command == TOCLIENT_BLOCKDATA)
1058 // Ignore too small packet
1061 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1065 p.X = readS16(&data[2]);
1066 p.Y = readS16(&data[4]);
1067 p.Z = readS16(&data[6]);
1069 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1070 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1072 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1073 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1075 std::string datastring((char*)&data[8], datasize-8);
1076 std::istringstream istr(datastring, std::ios_base::binary);
1082 JMutexAutoLock envlock(m_env_mutex);
1084 v2s16 p2d(p.X, p.Z);
1085 sector = m_env.getMap().emergeSector(p2d);
1087 v2s16 sp = sector->getPos();
1090 dstream<<"ERROR: Got sector with getPos()="
1091 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1092 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1096 //assert(sector->getPos() == p2d);
1099 block = sector->getBlockNoCreate(p.Y);
1101 Update an existing block
1103 //dstream<<"Updating"<<std::endl;
1104 block->deSerialize(istr, ser_version);
1105 //block->setChangedFlag();
1107 catch(InvalidPositionException &e)
1112 //dstream<<"Creating new"<<std::endl;
1113 block = new MapBlock(&m_env.getMap(), p);
1114 block->deSerialize(istr, ser_version);
1115 sector->insertBlock(block);
1116 //block->setChangedFlag();
1121 // Old version has zero lighting, update it.
1122 if(ser_version == 0 || ser_version == 1)
1124 derr_client<<"Client: Block in old format: "
1125 "Calculating lighting"<<std::endl;
1126 core::map<v3s16, MapBlock*> blocks_changed;
1127 blocks_changed.insert(block->getPos(), block);
1128 core::map<v3s16, MapBlock*> modified_blocks;
1129 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1133 Update Mesh of this block and blocks at x-, y- and z-
1136 //m_env.getMap().updateMeshes(block->getPos());
1137 mesh_updater.add(block->getPos());
1149 u32 replysize = 2+1+6;
1150 SharedBuffer<u8> reply(replysize);
1151 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1153 writeV3S16(&reply[3], p);
1155 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1162 JMutexAutoLock lock(m_fetchblock_mutex);
1164 if(m_fetchblock_history.find(p) != NULL)
1166 m_fetchblock_history.remove(p);
1180 u32 replysize = 2+1+6;
1181 SharedBuffer<u8> reply(replysize);
1182 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1184 writeV3S16(&reply[3], p);
1186 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1193 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1194 <<command<<std::endl;
1200 catch(con::PeerNotFoundException &e)
1202 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1203 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1208 bool Client::AsyncProcessData()
1210 LazyMeshUpdater mesh_updater(&m_env);
1213 bool r = AsyncProcessPacket(mesh_updater);
1220 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1222 JMutexAutoLock lock(m_con_mutex);
1223 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1227 void Client::fetchBlock(v3s16 p, u8 flags)
1229 if(connectedAndInitialized() == false)
1230 throw ClientNotReadyException
1231 ("ClientNotReadyException: connectedAndInitialized() == false");
1233 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1234 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1236 JMutexAutoLock conlock(m_con_mutex);
1238 SharedBuffer<u8> data(9);
1239 writeU16(&data[0], TOSERVER_GETBLOCK);
1240 writeS16(&data[2], p.X);
1241 writeS16(&data[4], p.Y);
1242 writeS16(&data[6], p.Z);
1243 writeU8(&data[8], flags);
1244 m_con.Send(PEER_ID_SERVER, 1, data, true);
1248 Calls fetchBlock() on some nearby missing blocks.
1250 Returns when any of various network load indicators go over limit.
1252 Does nearly the same thing as the old updateChangedVisibleArea()
1254 void Client::fetchBlocks()
1256 if(connectedAndInitialized() == false)
1257 throw ClientNotReadyException
1258 ("ClientNotReadyException: connectedAndInitialized() == false");
1262 bool Client::isFetchingBlocks()
1264 JMutexAutoLock conlock(m_con_mutex);
1265 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1266 // Not really fetching but can't fetch more.
1267 if(peer == NULL) return true;
1269 con::Channel *channel = &(peer->channels[1]);
1271 NOTE: Channel 0 should always be used for fetching blocks,
1272 and for nothing else.
1274 if(channel->incoming_reliables.size() > 0)
1276 if(channel->outgoing_reliables.size() > 0)
1281 IncomingPacket Client::getPacket()
1283 JMutexAutoLock lock(m_incoming_queue_mutex);
1285 core::list<IncomingPacket>::Iterator i;
1286 // Refer to first one
1287 i = m_incoming_queue.begin();
1289 // If queue is empty, return empty packet
1290 if(i == m_incoming_queue.end()){
1291 IncomingPacket packet;
1295 // Pop out first packet and return it
1296 IncomingPacket packet = *i;
1297 m_incoming_queue.erase(i);
1302 void Client::removeNode(v3s16 nodepos)
1304 if(connectedAndInitialized() == false){
1305 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1310 // Test that the position exists
1312 JMutexAutoLock envlock(m_env_mutex);
1313 m_env.getMap().getNode(nodepos);
1315 catch(InvalidPositionException &e)
1317 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1322 SharedBuffer<u8> data(8);
1323 writeU16(&data[0], TOSERVER_REMOVENODE);
1324 writeS16(&data[2], nodepos.X);
1325 writeS16(&data[4], nodepos.Y);
1326 writeS16(&data[6], nodepos.Z);
1327 Send(0, data, true);
1330 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1332 if(connectedAndInitialized() == false){
1333 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1334 "cancelled (not connected)"
1339 // Test that the position exists
1341 JMutexAutoLock envlock(m_env_mutex);
1342 m_env.getMap().getNode(nodepos);
1344 catch(InvalidPositionException &e)
1346 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1351 //u8 ser_version = m_server_ser_ver;
1353 // SUGGESTION: The validity of the operation could be checked here too
1355 u8 datasize = 2 + 6 + 2;
1356 SharedBuffer<u8> data(datasize);
1357 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1358 writeS16(&data[2], nodepos.X);
1359 writeS16(&data[4], nodepos.Y);
1360 writeS16(&data[6], nodepos.Z);
1361 writeU16(&data[8], i);
1362 Send(0, data, true);
1366 void Client::clickGround(u8 button, v3s16 nodepos_undersurface,
1367 v3s16 nodepos_oversurface, u16 item)
1369 if(connectedAndInitialized() == false){
1370 dout_client<<DTIME<<"Client::clickGround() "
1371 "cancelled (not connected)"
1379 [2] u8 button (0=left, 1=right)
1380 [3] v3s16 nodepos_undersurface
1381 [9] v3s16 nodepos_abovesurface
1384 u8 datasize = 2 + 1 + 6 + 6 + 2;
1385 SharedBuffer<u8> data(datasize);
1386 writeU16(&data[0], TOSERVER_CLICK_GROUND);
1387 writeU8(&data[2], button);
1388 writeV3S16(&data[3], nodepos_undersurface);
1389 writeV3S16(&data[9], nodepos_oversurface);
1390 writeU16(&data[15], item);
1391 Send(0, data, true);
1394 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1396 if(connectedAndInitialized() == false){
1397 dout_client<<DTIME<<"Client::clickObject() "
1398 "cancelled (not connected)"
1405 [2] u8 button (0=left, 1=right)
1410 u8 datasize = 2 + 1 + 6 + 2 + 2;
1411 SharedBuffer<u8> data(datasize);
1412 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1413 writeU8(&data[2], button);
1414 writeV3S16(&data[3], blockpos);
1415 writeS16(&data[9], id);
1416 writeU16(&data[11], item);
1417 Send(0, data, true);
1420 void Client::release(u8 button)
1425 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1434 std::ostringstream os(std::ios_base::binary);
1438 writeU16(buf, TOSERVER_SIGNTEXT);
1439 os.write((char*)buf, 2);
1442 writeV3S16(buf, blockpos);
1443 os.write((char*)buf, 6);
1447 os.write((char*)buf, 2);
1449 u16 textlen = text.size();
1450 // Write text length
1451 writeS16(buf, textlen);
1452 os.write((char*)buf, 2);
1455 os.write((char*)text.c_str(), textlen);
1458 std::string s = os.str();
1459 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1461 Send(0, data, true);
1464 void Client::sendPlayerPos()
1466 JMutexAutoLock envlock(m_env_mutex);
1468 Player *myplayer = m_env.getLocalPlayer();
1469 if(myplayer == NULL)
1474 JMutexAutoLock lock(m_con_mutex);
1475 our_peer_id = m_con.GetPeerID();
1478 // Set peer id if not set already
1479 if(myplayer->peer_id == PEER_ID_NEW)
1480 myplayer->peer_id = our_peer_id;
1481 // Check that an existing peer_id is the same as the connection's
1482 assert(myplayer->peer_id == our_peer_id);
1484 v3f pf = myplayer->getPosition();
1485 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1486 v3f sf = myplayer->getSpeed();
1487 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1488 s32 pitch = myplayer->getPitch() * 100;
1489 s32 yaw = myplayer->getYaw() * 100;
1494 [2] v3s32 position*100
1495 [2+12] v3s32 speed*100
1496 [2+12+12] s32 pitch*100
1497 [2+12+12+4] s32 yaw*100
1500 SharedBuffer<u8> data(2+12+12+4+4);
1501 writeU16(&data[0], TOSERVER_PLAYERPOS);
1502 writeV3S32(&data[2], position);
1503 writeV3S32(&data[2+12], speed);
1504 writeS32(&data[2+12+12], pitch);
1505 writeS32(&data[2+12+12+4], yaw);
1507 // Send as unreliable
1508 Send(0, data, false);
1512 void Client::updateCamera(v3f pos, v3f dir)
1514 m_env.getMap().updateCamera(pos, dir);
1515 camera_position = pos;
1516 camera_direction = dir;
1519 MapNode Client::getNode(v3s16 p)
1521 JMutexAutoLock envlock(m_env_mutex);
1522 return m_env.getMap().getNode(p);
1525 /*f32 Client::getGroundHeight(v2s16 p)
1527 JMutexAutoLock envlock(m_env_mutex);
1528 return m_env.getMap().getGroundHeight(p);
1531 bool Client::isNodeUnderground(v3s16 p)
1533 JMutexAutoLock envlock(m_env_mutex);
1534 return m_env.getMap().isNodeUnderground(p);
1537 /*Player * Client::getLocalPlayer()
1539 JMutexAutoLock envlock(m_env_mutex);
1540 return m_env.getLocalPlayer();
1543 /*core::list<Player*> Client::getPlayers()
1545 JMutexAutoLock envlock(m_env_mutex);
1546 return m_env.getPlayers();
1549 v3f Client::getPlayerPosition()
1551 JMutexAutoLock envlock(m_env_mutex);
1552 LocalPlayer *player = m_env.getLocalPlayer();
1553 assert(player != NULL);
1554 return player->getPosition();
1557 void Client::setPlayerControl(PlayerControl &control)
1559 JMutexAutoLock envlock(m_env_mutex);
1560 LocalPlayer *player = m_env.getLocalPlayer();
1561 assert(player != NULL);
1562 player->control = control;
1565 // Returns true if the inventory of the local player has been
1566 // updated from the server. If it is true, it is set to false.
1567 bool Client::getLocalInventoryUpdated()
1569 // m_inventory_updated is behind envlock
1570 JMutexAutoLock envlock(m_env_mutex);
1571 bool updated = m_inventory_updated;
1572 m_inventory_updated = false;
1576 // Copies the inventory of the local player to parameter
1577 void Client::getLocalInventory(Inventory &dst)
1579 JMutexAutoLock envlock(m_env_mutex);
1580 Player *player = m_env.getLocalPlayer();
1581 assert(player != NULL);
1582 dst = player->inventory;
1585 MapBlockObject * Client::getSelectedObject(
1587 v3f from_pos_f_on_map,
1588 core::line3d<f32> shootline_on_map
1591 JMutexAutoLock envlock(m_env_mutex);
1593 core::array<DistanceSortedObject> objects;
1595 for(core::map<v3s16, bool>::Iterator
1596 i = m_active_blocks.getIterator();
1597 i.atEnd() == false; i++)
1599 v3s16 p = i.getNode()->getKey();
1601 MapBlock *block = NULL;
1604 block = m_env.getMap().getBlockNoCreate(p);
1606 catch(InvalidPositionException &e)
1611 // Calculate from_pos relative to block
1612 v3s16 block_pos_i_on_map = block->getPosRelative();
1613 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1614 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1616 block->getObjects(from_pos_f_on_block, max_d, objects);
1619 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1622 // After this, the closest object is the first in the array.
1625 for(u32 i=0; i<objects.size(); i++)
1627 MapBlockObject *obj = objects[i].obj;
1628 MapBlock *block = obj->getBlock();
1630 // Calculate shootline relative to block
1631 v3s16 block_pos_i_on_map = block->getPosRelative();
1632 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1633 core::line3d<f32> shootline_on_block(
1634 shootline_on_map.start - block_pos_f_on_map,
1635 shootline_on_map.end - block_pos_f_on_map
1638 if(obj->isSelected(shootline_on_block))
1640 //dstream<<"Returning selected object"<<std::endl;
1645 //dstream<<"No object selected; returning NULL."<<std::endl;
1649 void Client::printDebugInfo(std::ostream &os)
1651 //JMutexAutoLock lock1(m_fetchblock_mutex);
1652 JMutexAutoLock lock2(m_incoming_queue_mutex);
1654 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1655 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1656 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()