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"
30 #define sleep_ms(x) Sleep(x)
33 #define sleep_ms(x) usleep(x*1000)
36 void * ClientUpdateThread::Thread()
40 DSTACK(__FUNCTION_NAME);
42 #if CATCH_UNHANDLED_EXCEPTIONS
48 m_client->asyncStep();
50 bool was = m_client->AsyncProcessData();
55 #if CATCH_UNHANDLED_EXCEPTIONS
58 This is what has to be done in threads to get suitable debug info
60 catch(std::exception &e)
62 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
63 <<e.what()<<std::endl;
71 Client::Client(IrrlichtDevice *device, video::SMaterial *materials,
72 float delete_unused_sectors_timeout,
73 const char *playername):
75 m_env(new ClientMap(this, materials,
76 device->getSceneManager()->getRootSceneNode(),
77 device->getSceneManager(), 666),
79 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
81 camera_position(0,0,0),
82 camera_direction(0,0,1),
83 m_server_ser_ver(SER_FMT_VER_INVALID),
85 m_delete_unused_sectors_timeout(delete_unused_sectors_timeout),
86 m_inventory_updated(false)
88 //m_fetchblock_mutex.Init();
89 m_incoming_queue_mutex.Init();
92 m_step_dtime_mutex.Init();
97 JMutexAutoLock envlock(m_env_mutex);
98 //m_env.getMap().StartUpdater();
100 Player *player = new LocalPlayer();
102 player->updateName(playername);
104 /*f32 y = BS*2 + BS*20;
105 player->setPosition(v3f(0, y, 0));*/
106 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
107 m_env.addPlayer(player);
113 m_thread.setRun(false);
114 while(m_thread.IsRunning())
118 void Client::connect(Address address)
120 DSTACK(__FUNCTION_NAME);
121 JMutexAutoLock lock(m_con_mutex);
122 m_con.setTimeoutMs(0);
123 m_con.Connect(address);
126 bool Client::connectedAndInitialized()
128 JMutexAutoLock lock(m_con_mutex);
130 if(m_con.Connected() == false)
133 if(m_server_ser_ver == SER_FMT_VER_INVALID)
139 void Client::step(float dtime)
141 DSTACK(__FUNCTION_NAME);
147 //dstream<<"Client steps "<<dtime<<std::endl;
150 //TimeTaker timer("ReceiveAll()", m_device);
156 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
158 JMutexAutoLock lock(m_con_mutex);
159 m_con.RunTimeouts(dtime);
166 static float counter = -0.001;
172 dout_client<<"Client packetcounter (20s):"<<std::endl;
173 m_packetcounter.print(dout_client);
174 m_packetcounter.clear();
180 Delete unused sectors
182 NOTE: This jams the game for a while because deleting sectors
186 static float counter = -0.001;
193 JMutexAutoLock lock(m_env_mutex);
195 core::list<v3s16> deleted_blocks;
197 // Delete sector blocks
198 /*u32 num = m_env.getMap().deleteUnusedSectors
199 (m_delete_unused_sectors_timeout,
200 true, &deleted_blocks);*/
202 // Delete whole sectors
203 u32 num = m_env.getMap().deleteUnusedSectors
204 (m_delete_unused_sectors_timeout,
205 false, &deleted_blocks);
209 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
210 <<" unused sectors"<<std::endl;*/
211 dstream<<DTIME<<"Client: Deleted "<<num
212 <<" unused sectors"<<std::endl;
218 // Env is locked so con can be locked.
219 JMutexAutoLock lock(m_con_mutex);
221 core::list<v3s16>::Iterator i = deleted_blocks.begin();
222 core::list<v3s16> sendlist;
225 if(sendlist.size() == 255 || i == deleted_blocks.end())
227 if(sendlist.size() == 0)
236 u32 replysize = 2+1+6*sendlist.size();
237 SharedBuffer<u8> reply(replysize);
238 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
239 reply[2] = sendlist.size();
241 for(core::list<v3s16>::Iterator
242 j = sendlist.begin();
243 j != sendlist.end(); j++)
245 writeV3S16(&reply[2+1+6*k], *j);
248 m_con.Send(PEER_ID_SERVER, 1, reply, true);
250 if(i == deleted_blocks.end())
256 sendlist.push_back(*i);
263 bool connected = connectedAndInitialized();
265 if(connected == false)
267 static float counter = -0.001;
273 JMutexAutoLock envlock(m_env_mutex);
275 Player *myplayer = m_env.getLocalPlayer();
276 assert(myplayer != NULL);
278 // Send TOSERVER_INIT
279 // [0] u16 TOSERVER_INIT
280 // [2] u8 SER_FMT_VER_HIGHEST
281 // [3] u8[20] player_name
282 SharedBuffer<u8> data(2+1+20);
283 writeU16(&data[0], TOSERVER_INIT);
284 writeU8(&data[2], SER_FMT_VER_HIGHEST);
285 memcpy(&data[3], myplayer->getName(), 20);
286 // Send as unreliable
287 Send(0, data, false);
290 // Not connected, return
295 Do stuff if connected
300 JMutexAutoLock lock(m_env_mutex);
302 // Control local player (0ms)
303 LocalPlayer *player = m_env.getLocalPlayer();
304 assert(player != NULL);
305 player->applyControl(dtime);
307 //TimeTaker envtimer("env step", m_device);
311 // Step active blocks
312 for(core::map<v3s16, bool>::Iterator
313 i = m_active_blocks.getIterator();
314 i.atEnd() == false; i++)
316 v3s16 p = i.getNode()->getKey();
318 MapBlock *block = NULL;
321 block = m_env.getMap().getBlockNoCreate(p);
322 block->stepObjects(dtime, false);
324 catch(InvalidPositionException &e)
331 // Fetch some nearby blocks
336 static float counter = 0.0;
341 JMutexAutoLock lock(m_con_mutex);
342 // connectedAndInitialized() is true, peer exists.
343 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
344 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
348 // Update at reasonable intervals (0.2s)
349 static float counter = 0.0;
360 Clear old entries from fetchblock history
363 JMutexAutoLock lock(m_fetchblock_mutex);
365 core::list<v3s16> remove_queue;
366 core::map<v3s16, float>::Iterator i;
367 i = m_fetchblock_history.getIterator();
368 for(; i.atEnd() == false; i++)
370 float value = i.getNode()->getValue();
372 i.getNode()->setValue(value);
374 remove_queue.push_back(i.getNode()->getKey());
376 core::list<v3s16>::Iterator j;
377 j = remove_queue.begin();
378 for(; j != remove_queue.end(); j++)
380 m_fetchblock_history.remove(*j);
386 JMutexAutoLock lock(m_step_dtime_mutex);
387 m_step_dtime += dtime;
399 float Client::asyncStep()
401 DSTACK(__FUNCTION_NAME);
402 //dstream<<"Client::asyncStep()"<<std::endl;
406 JMutexAutoLock lock1(m_step_dtime_mutex);
407 if(m_step_dtime < 0.001)
409 dtime = m_step_dtime;
417 // Virtual methods from con::PeerHandler
418 void Client::peerAdded(con::Peer *peer)
420 derr_client<<"Client::peerAdded(): peer->id="
421 <<peer->id<<std::endl;
423 void Client::deletingPeer(con::Peer *peer, bool timeout)
425 derr_client<<"Client::deletingPeer(): "
426 "Server Peer is getting deleted "
427 <<"(timeout="<<timeout<<")"<<std::endl;
430 void Client::ReceiveAll()
432 DSTACK(__FUNCTION_NAME);
438 catch(con::NoIncomingDataException &e)
442 catch(con::InvalidIncomingDataException &e)
444 dout_client<<DTIME<<"Client::ReceiveAll(): "
445 "InvalidIncomingDataException: what()="
446 <<e.what()<<std::endl;
453 void Client::Receive()
455 DSTACK(__FUNCTION_NAME);
456 u32 data_maxsize = 10000;
457 Buffer<u8> data(data_maxsize);
461 //TimeTaker t1("con mutex and receive", m_device);
462 JMutexAutoLock lock(m_con_mutex);
463 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
465 //TimeTaker t1("ProcessData", m_device);
466 ProcessData(*data, datasize, sender_peer_id);
470 sender_peer_id given to this shall be quaranteed to be a valid peer
472 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
474 DSTACK(__FUNCTION_NAME);
476 // Ignore packets that don't even fit a command
479 m_packetcounter.add(60000);
483 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
485 //dstream<<"Client: received command="<<command<<std::endl;
486 m_packetcounter.add((u16)command);
489 If this check is removed, be sure to change the queue
490 system to know the ids
492 if(sender_peer_id != PEER_ID_SERVER)
494 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
495 "coming from server: peer_id="<<sender_peer_id
502 JMutexAutoLock lock(m_con_mutex);
503 // All data is coming from the server
504 // PeerNotFoundException is handled by caller.
505 peer = m_con.GetPeer(PEER_ID_SERVER);
508 u8 ser_version = m_server_ser_ver;
510 //dstream<<"Client received command="<<(int)command<<std::endl;
512 // Execute fast commands straight away
514 if(command == TOCLIENT_INIT)
519 u8 deployed = data[2];
521 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
522 "deployed="<<((int)deployed&0xff)<<std::endl;
524 if(deployed < SER_FMT_VER_LOWEST
525 || deployed > SER_FMT_VER_HIGHEST)
527 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
528 <<"unsupported ser_fmt_ver"<<std::endl;
532 m_server_ser_ver = deployed;
534 // Get player position
535 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
536 if(datasize >= 2+1+6)
537 playerpos_s16 = readV3S16(&data[2+1]);
538 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
541 JMutexAutoLock envlock(m_env_mutex);
543 // Set player position
544 Player *player = m_env.getLocalPlayer();
545 assert(player != NULL);
546 player->setPosition(playerpos_f);
551 SharedBuffer<u8> reply(replysize);
552 writeU16(&reply[0], TOSERVER_INIT2);
554 m_con.Send(PEER_ID_SERVER, 1, reply, true);
559 if(ser_version == SER_FMT_VER_INVALID)
561 dout_client<<DTIME<<"WARNING: Client: Server serialization"
562 " format invalid or not initialized."
563 " Skipping incoming command="<<command<<std::endl;
567 // Just here to avoid putting the two if's together when
568 // making some copypasta
571 if(command == TOCLIENT_PLAYERPOS)
573 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
577 JMutexAutoLock lock(m_con_mutex);
578 our_peer_id = m_con.GetPeerID();
580 // Cancel if we don't have a peer id
581 if(our_peer_id == PEER_ID_NEW){
582 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
589 JMutexAutoLock envlock(m_env_mutex);
591 u32 player_size = 2+12+12+4+4;
593 u32 player_count = (datasize-2) / player_size;
595 for(u32 i=0; i<player_count; i++)
597 u16 peer_id = readU16(&data[start]);
599 Player *player = m_env.getPlayer(peer_id);
601 // Skip if player doesn't exist
604 start += player_size;
608 // Skip if player is local player
609 if(player->isLocal())
611 start += player_size;
615 v3s32 ps = readV3S32(&data[start+2]);
616 v3s32 ss = readV3S32(&data[start+2+12]);
617 s32 pitch_i = readS32(&data[start+2+12+12]);
618 s32 yaw_i = readS32(&data[start+2+12+12+4]);
619 /*dstream<<"Client: got "
620 <<"pitch_i="<<pitch_i
621 <<" yaw_i="<<yaw_i<<std::endl;*/
622 f32 pitch = (f32)pitch_i / 100.0;
623 f32 yaw = (f32)yaw_i / 100.0;
624 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
625 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
626 player->setPosition(position);
627 player->setSpeed(speed);
628 player->setPitch(pitch);
631 /*dstream<<"Client: player "<<peer_id
633 <<" yaw="<<yaw<<std::endl;*/
635 start += player_size;
639 else if(command == TOCLIENT_PLAYERINFO)
643 JMutexAutoLock lock(m_con_mutex);
644 our_peer_id = m_con.GetPeerID();
646 // Cancel if we don't have a peer id
647 if(our_peer_id == PEER_ID_NEW){
648 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
654 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
657 JMutexAutoLock envlock(m_env_mutex);
659 u32 item_size = 2+PLAYERNAME_SIZE;
660 u32 player_count = (datasize-2) / item_size;
663 core::list<u16> players_alive;
664 for(u32 i=0; i<player_count; i++)
666 // Make sure the name ends in '\0'
667 data[start+2+20-1] = 0;
669 u16 peer_id = readU16(&data[start]);
671 players_alive.push_back(peer_id);
673 /*dstream<<DTIME<<"peer_id="<<peer_id
674 <<" name="<<((char*)&data[start+2])<<std::endl;*/
676 // Don't update the info of the local player
677 if(peer_id == our_peer_id)
683 Player *player = m_env.getPlayer(peer_id);
685 // Create a player if it doesn't exist
688 player = new RemotePlayer(
689 m_device->getSceneManager()->getRootSceneNode(),
692 player->peer_id = peer_id;
693 m_env.addPlayer(player);
694 dout_client<<DTIME<<"Client: Adding new player "
695 <<peer_id<<std::endl;
698 player->updateName((char*)&data[start+2]);
704 Remove those players from the environment that
705 weren't listed by the server.
707 //dstream<<DTIME<<"Removing dead players"<<std::endl;
708 core::list<Player*> players = m_env.getPlayers();
709 core::list<Player*>::Iterator ip;
710 for(ip=players.begin(); ip!=players.end(); ip++)
712 // Ingore local player
716 // Warn about a special case
717 if((*ip)->peer_id == 0)
719 dstream<<DTIME<<"WARNING: Client: Removing "
720 "dead player with id=0"<<std::endl;
723 bool is_alive = false;
724 core::list<u16>::Iterator i;
725 for(i=players_alive.begin(); i!=players_alive.end(); i++)
727 if((*ip)->peer_id == *i)
733 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
734 <<" is_alive="<<is_alive<<std::endl;*/
737 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
739 m_env.removePlayer((*ip)->peer_id);
743 else if(command == TOCLIENT_SECTORMETA)
748 [3...] v2s16 pos + sector metadata
753 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
756 JMutexAutoLock envlock(m_env_mutex);
758 std::string datastring((char*)&data[2], datasize-2);
759 std::istringstream is(datastring, std::ios_base::binary);
763 is.read((char*)buf, 1);
764 u16 sector_count = readU8(buf);
766 //dstream<<"sector_count="<<sector_count<<std::endl;
768 for(u16 i=0; i<sector_count; i++)
771 is.read((char*)buf, 4);
772 v2s16 pos = readV2S16(buf);
773 /*dstream<<"Client: deserializing sector at "
774 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
776 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
777 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
781 else if(command == TOCLIENT_INVENTORY)
786 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
789 //TimeTaker t2("mutex locking", m_device);
790 JMutexAutoLock envlock(m_env_mutex);
793 //TimeTaker t3("istringstream init", m_device);
794 std::string datastring((char*)&data[2], datasize-2);
795 std::istringstream is(datastring, std::ios_base::binary);
798 //m_env.printPlayers(dstream);
800 //TimeTaker t4("player get", m_device);
801 Player *player = m_env.getLocalPlayer();
802 assert(player != NULL);
805 //TimeTaker t1("inventory.deSerialize()", m_device);
806 player->inventory.deSerialize(is);
809 m_inventory_updated = true;
811 //dstream<<"Client got player inventory:"<<std::endl;
812 //player->inventory.print(dstream);
816 else if(command == TOCLIENT_OBJECTDATA)
819 // Strip command word and create a stringstream
820 std::string datastring((char*)&data[2], datasize-2);
821 std::istringstream is(datastring, std::ios_base::binary);
825 JMutexAutoLock envlock(m_env_mutex);
833 is.read((char*)buf, 2);
834 u16 playercount = readU16(buf);
836 for(u16 i=0; i<playercount; i++)
838 is.read((char*)buf, 2);
839 u16 peer_id = readU16(buf);
840 is.read((char*)buf, 12);
841 v3s32 p_i = readV3S32(buf);
842 is.read((char*)buf, 12);
843 v3s32 s_i = readV3S32(buf);
844 is.read((char*)buf, 4);
845 s32 pitch_i = readS32(buf);
846 is.read((char*)buf, 4);
847 s32 yaw_i = readS32(buf);
849 Player *player = m_env.getPlayer(peer_id);
851 // Skip if player doesn't exist
857 // Skip if player is local player
858 if(player->isLocal())
863 f32 pitch = (f32)pitch_i / 100.0;
864 f32 yaw = (f32)yaw_i / 100.0;
865 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
866 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
868 player->setPosition(position);
869 player->setSpeed(speed);
870 player->setPitch(pitch);
878 // Read active block count
879 is.read((char*)buf, 2);
880 u16 blockcount = readU16(buf);
882 // Initialize delete queue with all active blocks
883 core::map<v3s16, bool> abs_to_delete;
884 for(core::map<v3s16, bool>::Iterator
885 i = m_active_blocks.getIterator();
886 i.atEnd() == false; i++)
888 v3s16 p = i.getNode()->getKey();
890 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
891 <<" to abs_to_delete"
893 abs_to_delete.insert(p, true);
896 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
899 for(u16 i=0; i<blockcount; i++)
902 is.read((char*)buf, 6);
903 v3s16 p = readV3S16(buf);
904 // Get block from somewhere
905 MapBlock *block = NULL;
907 block = m_env.getMap().getBlockNoCreate(p);
909 catch(InvalidPositionException &e)
911 //TODO: Create a dummy block?
916 <<"Could not get block at blockpos "
917 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
918 <<"in TOCLIENT_OBJECTDATA. Ignoring "
919 <<"following block object data."
924 /*dstream<<"Client updating objects for block "
925 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
928 // Insert to active block list
929 m_active_blocks.insert(p, true);
931 // Remove from deletion queue
932 if(abs_to_delete.find(p) != NULL)
933 abs_to_delete.remove(p);
935 // Update objects of block
936 block->updateObjects(is, m_server_ser_ver,
937 m_device->getSceneManager());
940 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
943 // Delete objects of blocks in delete queue
944 for(core::map<v3s16, bool>::Iterator
945 i = abs_to_delete.getIterator();
946 i.atEnd() == false; i++)
948 v3s16 p = i.getNode()->getKey();
951 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
954 block->clearObjects();
955 // Remove from active blocks list
956 m_active_blocks.remove(p);
958 catch(InvalidPositionException &e)
960 dstream<<"WARNAING: Client: "
961 <<"Couldn't clear objects of active->inactive"
963 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
964 <<" because block was not found"
972 // Default to queueing it (for slow commands)
975 JMutexAutoLock lock(m_incoming_queue_mutex);
977 IncomingPacket packet(data, datasize);
978 m_incoming_queue.push_back(packet);
983 Returns true if there was something in queue
985 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
987 DSTACK(__FUNCTION_NAME);
989 try //for catching con::PeerNotFoundException
994 JMutexAutoLock lock(m_con_mutex);
995 // All data is coming from the server
996 peer = m_con.GetPeer(PEER_ID_SERVER);
999 u8 ser_version = m_server_ser_ver;
1001 IncomingPacket packet = getPacket();
1002 u8 *data = packet.m_data;
1003 u32 datasize = packet.m_datalen;
1005 // An empty packet means queue is empty
1013 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1015 if(command == TOCLIENT_REMOVENODE)
1020 p.X = readS16(&data[2]);
1021 p.Y = readS16(&data[4]);
1022 p.Z = readS16(&data[6]);
1024 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1026 core::map<v3s16, MapBlock*> modified_blocks;
1030 JMutexAutoLock envlock(m_env_mutex);
1031 //TimeTaker t("removeNodeAndUpdate", m_device);
1032 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1034 catch(InvalidPositionException &e)
1038 for(core::map<v3s16, MapBlock * >::Iterator
1039 i = modified_blocks.getIterator();
1040 i.atEnd() == false; i++)
1042 v3s16 p = i.getNode()->getKey();
1043 //m_env.getMap().updateMeshes(p);
1044 mesh_updater.add(p);
1047 else if(command == TOCLIENT_ADDNODE)
1049 if(datasize < 8 + MapNode::serializedLength(ser_version))
1053 p.X = readS16(&data[2]);
1054 p.Y = readS16(&data[4]);
1055 p.Z = readS16(&data[6]);
1057 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1060 n.deSerialize(&data[8], ser_version);
1062 core::map<v3s16, MapBlock*> modified_blocks;
1066 JMutexAutoLock envlock(m_env_mutex);
1067 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1069 catch(InvalidPositionException &e)
1072 for(core::map<v3s16, MapBlock * >::Iterator
1073 i = modified_blocks.getIterator();
1074 i.atEnd() == false; i++)
1076 v3s16 p = i.getNode()->getKey();
1077 //m_env.getMap().updateMeshes(p);
1078 mesh_updater.add(p);
1081 else if(command == TOCLIENT_BLOCKDATA)
1083 // Ignore too small packet
1086 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1090 p.X = readS16(&data[2]);
1091 p.Y = readS16(&data[4]);
1092 p.Z = readS16(&data[6]);
1094 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1095 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1097 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1098 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1100 std::string datastring((char*)&data[8], datasize-8);
1101 std::istringstream istr(datastring, std::ios_base::binary);
1107 JMutexAutoLock envlock(m_env_mutex);
1109 v2s16 p2d(p.X, p.Z);
1110 sector = m_env.getMap().emergeSector(p2d);
1112 v2s16 sp = sector->getPos();
1115 dstream<<"ERROR: Got sector with getPos()="
1116 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1117 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1121 //assert(sector->getPos() == p2d);
1124 block = sector->getBlockNoCreate(p.Y);
1126 Update an existing block
1128 //dstream<<"Updating"<<std::endl;
1129 block->deSerialize(istr, ser_version);
1130 //block->setChangedFlag();
1132 catch(InvalidPositionException &e)
1137 //dstream<<"Creating new"<<std::endl;
1138 block = new MapBlock(&m_env.getMap(), p);
1139 block->deSerialize(istr, ser_version);
1140 sector->insertBlock(block);
1141 //block->setChangedFlag();
1146 // Old version has zero lighting, update it.
1147 if(ser_version == 0 || ser_version == 1)
1149 derr_client<<"Client: Block in old format: "
1150 "Calculating lighting"<<std::endl;
1151 core::map<v3s16, MapBlock*> blocks_changed;
1152 blocks_changed.insert(block->getPos(), block);
1153 core::map<v3s16, MapBlock*> modified_blocks;
1154 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1158 Update Mesh of this block and blocks at x-, y- and z-
1161 //m_env.getMap().updateMeshes(block->getPos());
1162 mesh_updater.add(block->getPos());
1174 u32 replysize = 2+1+6;
1175 SharedBuffer<u8> reply(replysize);
1176 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1178 writeV3S16(&reply[3], p);
1180 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1187 JMutexAutoLock lock(m_fetchblock_mutex);
1189 if(m_fetchblock_history.find(p) != NULL)
1191 m_fetchblock_history.remove(p);
1205 u32 replysize = 2+1+6;
1206 SharedBuffer<u8> reply(replysize);
1207 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1209 writeV3S16(&reply[3], p);
1211 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1218 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1219 <<command<<std::endl;
1225 catch(con::PeerNotFoundException &e)
1227 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1228 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1233 bool Client::AsyncProcessData()
1237 // We want to update the meshes as soon as a single packet has
1239 LazyMeshUpdater mesh_updater(&m_env);
1240 bool r = AsyncProcessPacket(mesh_updater);
1246 /*LazyMeshUpdater mesh_updater(&m_env);
1249 bool r = AsyncProcessPacket(mesh_updater);
1257 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1259 JMutexAutoLock lock(m_con_mutex);
1260 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1264 void Client::fetchBlock(v3s16 p, u8 flags)
1266 if(connectedAndInitialized() == false)
1267 throw ClientNotReadyException
1268 ("ClientNotReadyException: connectedAndInitialized() == false");
1270 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1271 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1273 JMutexAutoLock conlock(m_con_mutex);
1275 SharedBuffer<u8> data(9);
1276 writeU16(&data[0], TOSERVER_GETBLOCK);
1277 writeS16(&data[2], p.X);
1278 writeS16(&data[4], p.Y);
1279 writeS16(&data[6], p.Z);
1280 writeU8(&data[8], flags);
1281 m_con.Send(PEER_ID_SERVER, 1, data, true);
1285 Calls fetchBlock() on some nearby missing blocks.
1287 Returns when any of various network load indicators go over limit.
1289 Does nearly the same thing as the old updateChangedVisibleArea()
1291 void Client::fetchBlocks()
1293 if(connectedAndInitialized() == false)
1294 throw ClientNotReadyException
1295 ("ClientNotReadyException: connectedAndInitialized() == false");
1299 bool Client::isFetchingBlocks()
1301 JMutexAutoLock conlock(m_con_mutex);
1302 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1303 // Not really fetching but can't fetch more.
1304 if(peer == NULL) return true;
1306 con::Channel *channel = &(peer->channels[1]);
1308 NOTE: Channel 0 should always be used for fetching blocks,
1309 and for nothing else.
1311 if(channel->incoming_reliables.size() > 0)
1313 if(channel->outgoing_reliables.size() > 0)
1318 IncomingPacket Client::getPacket()
1320 JMutexAutoLock lock(m_incoming_queue_mutex);
1322 core::list<IncomingPacket>::Iterator i;
1323 // Refer to first one
1324 i = m_incoming_queue.begin();
1326 // If queue is empty, return empty packet
1327 if(i == m_incoming_queue.end()){
1328 IncomingPacket packet;
1332 // Pop out first packet and return it
1333 IncomingPacket packet = *i;
1334 m_incoming_queue.erase(i);
1339 void Client::removeNode(v3s16 nodepos)
1341 if(connectedAndInitialized() == false){
1342 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1347 // Test that the position exists
1349 JMutexAutoLock envlock(m_env_mutex);
1350 m_env.getMap().getNode(nodepos);
1352 catch(InvalidPositionException &e)
1354 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1359 SharedBuffer<u8> data(8);
1360 writeU16(&data[0], TOSERVER_REMOVENODE);
1361 writeS16(&data[2], nodepos.X);
1362 writeS16(&data[4], nodepos.Y);
1363 writeS16(&data[6], nodepos.Z);
1364 Send(0, data, true);
1367 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1369 if(connectedAndInitialized() == false){
1370 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1371 "cancelled (not connected)"
1376 // Test that the position exists
1378 JMutexAutoLock envlock(m_env_mutex);
1379 m_env.getMap().getNode(nodepos);
1381 catch(InvalidPositionException &e)
1383 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1388 //u8 ser_version = m_server_ser_ver;
1390 // SUGGESTION: The validity of the operation could be checked here too
1392 u8 datasize = 2 + 6 + 2;
1393 SharedBuffer<u8> data(datasize);
1394 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1395 writeS16(&data[2], nodepos.X);
1396 writeS16(&data[4], nodepos.Y);
1397 writeS16(&data[6], nodepos.Z);
1398 writeU16(&data[8], i);
1399 Send(0, data, true);
1403 void Client::clickGround(u8 button, v3s16 nodepos_undersurface,
1404 v3s16 nodepos_oversurface, u16 item)
1406 if(connectedAndInitialized() == false){
1407 dout_client<<DTIME<<"Client::clickGround() "
1408 "cancelled (not connected)"
1416 [2] u8 button (0=left, 1=right)
1417 [3] v3s16 nodepos_undersurface
1418 [9] v3s16 nodepos_abovesurface
1421 u8 datasize = 2 + 1 + 6 + 6 + 2;
1422 SharedBuffer<u8> data(datasize);
1423 writeU16(&data[0], TOSERVER_CLICK_GROUND);
1424 writeU8(&data[2], button);
1425 writeV3S16(&data[3], nodepos_undersurface);
1426 writeV3S16(&data[9], nodepos_oversurface);
1427 writeU16(&data[15], item);
1428 Send(0, data, true);
1431 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1433 if(connectedAndInitialized() == false){
1434 dout_client<<DTIME<<"Client::clickObject() "
1435 "cancelled (not connected)"
1442 [2] u8 button (0=left, 1=right)
1447 u8 datasize = 2 + 1 + 6 + 2 + 2;
1448 SharedBuffer<u8> data(datasize);
1449 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1450 writeU8(&data[2], button);
1451 writeV3S16(&data[3], blockpos);
1452 writeS16(&data[9], id);
1453 writeU16(&data[11], item);
1454 Send(0, data, true);
1457 void Client::release(u8 button)
1462 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1471 std::ostringstream os(std::ios_base::binary);
1475 writeU16(buf, TOSERVER_SIGNTEXT);
1476 os.write((char*)buf, 2);
1479 writeV3S16(buf, blockpos);
1480 os.write((char*)buf, 6);
1484 os.write((char*)buf, 2);
1486 u16 textlen = text.size();
1487 // Write text length
1488 writeS16(buf, textlen);
1489 os.write((char*)buf, 2);
1492 os.write((char*)text.c_str(), textlen);
1495 std::string s = os.str();
1496 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1498 Send(0, data, true);
1501 void Client::sendPlayerPos()
1503 JMutexAutoLock envlock(m_env_mutex);
1505 Player *myplayer = m_env.getLocalPlayer();
1506 if(myplayer == NULL)
1511 JMutexAutoLock lock(m_con_mutex);
1512 our_peer_id = m_con.GetPeerID();
1515 // Set peer id if not set already
1516 if(myplayer->peer_id == PEER_ID_NEW)
1517 myplayer->peer_id = our_peer_id;
1518 // Check that an existing peer_id is the same as the connection's
1519 assert(myplayer->peer_id == our_peer_id);
1521 v3f pf = myplayer->getPosition();
1522 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1523 v3f sf = myplayer->getSpeed();
1524 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1525 s32 pitch = myplayer->getPitch() * 100;
1526 s32 yaw = myplayer->getYaw() * 100;
1531 [2] v3s32 position*100
1532 [2+12] v3s32 speed*100
1533 [2+12+12] s32 pitch*100
1534 [2+12+12+4] s32 yaw*100
1537 SharedBuffer<u8> data(2+12+12+4+4);
1538 writeU16(&data[0], TOSERVER_PLAYERPOS);
1539 writeV3S32(&data[2], position);
1540 writeV3S32(&data[2+12], speed);
1541 writeS32(&data[2+12+12], pitch);
1542 writeS32(&data[2+12+12+4], yaw);
1544 // Send as unreliable
1545 Send(0, data, false);
1549 void Client::updateCamera(v3f pos, v3f dir)
1551 m_env.getMap().updateCamera(pos, dir);
1552 camera_position = pos;
1553 camera_direction = dir;
1556 MapNode Client::getNode(v3s16 p)
1558 JMutexAutoLock envlock(m_env_mutex);
1559 return m_env.getMap().getNode(p);
1562 /*f32 Client::getGroundHeight(v2s16 p)
1564 JMutexAutoLock envlock(m_env_mutex);
1565 return m_env.getMap().getGroundHeight(p);
1568 bool Client::isNodeUnderground(v3s16 p)
1570 JMutexAutoLock envlock(m_env_mutex);
1571 return m_env.getMap().isNodeUnderground(p);
1574 /*Player * Client::getLocalPlayer()
1576 JMutexAutoLock envlock(m_env_mutex);
1577 return m_env.getLocalPlayer();
1580 /*core::list<Player*> Client::getPlayers()
1582 JMutexAutoLock envlock(m_env_mutex);
1583 return m_env.getPlayers();
1586 v3f Client::getPlayerPosition()
1588 JMutexAutoLock envlock(m_env_mutex);
1589 LocalPlayer *player = m_env.getLocalPlayer();
1590 assert(player != NULL);
1591 return player->getPosition();
1594 void Client::setPlayerControl(PlayerControl &control)
1596 JMutexAutoLock envlock(m_env_mutex);
1597 LocalPlayer *player = m_env.getLocalPlayer();
1598 assert(player != NULL);
1599 player->control = control;
1602 // Returns true if the inventory of the local player has been
1603 // updated from the server. If it is true, it is set to false.
1604 bool Client::getLocalInventoryUpdated()
1606 // m_inventory_updated is behind envlock
1607 JMutexAutoLock envlock(m_env_mutex);
1608 bool updated = m_inventory_updated;
1609 m_inventory_updated = false;
1613 // Copies the inventory of the local player to parameter
1614 void Client::getLocalInventory(Inventory &dst)
1616 JMutexAutoLock envlock(m_env_mutex);
1617 Player *player = m_env.getLocalPlayer();
1618 assert(player != NULL);
1619 dst = player->inventory;
1622 MapBlockObject * Client::getSelectedObject(
1624 v3f from_pos_f_on_map,
1625 core::line3d<f32> shootline_on_map
1628 JMutexAutoLock envlock(m_env_mutex);
1630 core::array<DistanceSortedObject> objects;
1632 for(core::map<v3s16, bool>::Iterator
1633 i = m_active_blocks.getIterator();
1634 i.atEnd() == false; i++)
1636 v3s16 p = i.getNode()->getKey();
1638 MapBlock *block = NULL;
1641 block = m_env.getMap().getBlockNoCreate(p);
1643 catch(InvalidPositionException &e)
1648 // Calculate from_pos relative to block
1649 v3s16 block_pos_i_on_map = block->getPosRelative();
1650 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1651 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1653 block->getObjects(from_pos_f_on_block, max_d, objects);
1656 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1659 // After this, the closest object is the first in the array.
1662 for(u32 i=0; i<objects.size(); i++)
1664 MapBlockObject *obj = objects[i].obj;
1665 MapBlock *block = obj->getBlock();
1667 // Calculate shootline relative to block
1668 v3s16 block_pos_i_on_map = block->getPosRelative();
1669 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1670 core::line3d<f32> shootline_on_block(
1671 shootline_on_map.start - block_pos_f_on_map,
1672 shootline_on_map.end - block_pos_f_on_map
1675 if(obj->isSelected(shootline_on_block))
1677 //dstream<<"Returning selected object"<<std::endl;
1682 //dstream<<"No object selected; returning NULL."<<std::endl;
1686 void Client::printDebugInfo(std::ostream &os)
1688 //JMutexAutoLock lock1(m_fetchblock_mutex);
1689 JMutexAutoLock lock2(m_incoming_queue_mutex);
1691 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1692 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1693 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()