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,
72 const char *playername):
74 m_env(new ClientMap(this,
75 device->getSceneManager()->getRootSceneNode(),
76 device->getSceneManager(), 666),
78 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
80 camera_position(0,0,0),
81 camera_direction(0,0,1),
82 m_server_ser_ver(SER_FMT_VER_INVALID),
84 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);
151 m_time_counter += dtime;
152 int seconds = (int)m_time_counter;
153 m_time_counter -= (float)seconds;
157 dstream<<"m_time="<<m_time<<std::endl;
158 JMutexAutoLock envlock(m_env_mutex);
159 u32 dr = 500+500*sin((float)((m_time/10)%7)/7.*2.*PI);
160 if(dr != m_env.getDaylightRatio())
162 dstream<<"dr="<<dr<<std::endl;
163 m_env.setDaylightRatio(dr);
164 m_env.expireMeshes();
170 //dstream<<"Client steps "<<dtime<<std::endl;
173 //TimeTaker timer("ReceiveAll()", m_device);
179 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
181 JMutexAutoLock lock(m_con_mutex);
182 m_con.RunTimeouts(dtime);
189 static float counter = -0.001;
195 dout_client<<"Client packetcounter (20s):"<<std::endl;
196 m_packetcounter.print(dout_client);
197 m_packetcounter.clear();
203 Delete unused sectors
205 NOTE: This jams the game for a while because deleting sectors
209 static float counter = -0.001;
216 JMutexAutoLock lock(m_env_mutex);
218 core::list<v3s16> deleted_blocks;
220 float delete_unused_sectors_timeout =
221 g_settings.getFloat("client_delete_unused_sectors_timeout");
223 // Delete sector blocks
224 /*u32 num = m_env.getMap().deleteUnusedSectors
225 (delete_unused_sectors_timeout,
226 true, &deleted_blocks);*/
228 // Delete whole sectors
229 u32 num = m_env.getMap().deleteUnusedSectors
230 (delete_unused_sectors_timeout,
231 false, &deleted_blocks);
235 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
236 <<" unused sectors"<<std::endl;*/
237 dstream<<DTIME<<"Client: Deleted "<<num
238 <<" unused sectors"<<std::endl;
244 // Env is locked so con can be locked.
245 JMutexAutoLock lock(m_con_mutex);
247 core::list<v3s16>::Iterator i = deleted_blocks.begin();
248 core::list<v3s16> sendlist;
251 if(sendlist.size() == 255 || i == deleted_blocks.end())
253 if(sendlist.size() == 0)
262 u32 replysize = 2+1+6*sendlist.size();
263 SharedBuffer<u8> reply(replysize);
264 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
265 reply[2] = sendlist.size();
267 for(core::list<v3s16>::Iterator
268 j = sendlist.begin();
269 j != sendlist.end(); j++)
271 writeV3S16(&reply[2+1+6*k], *j);
274 m_con.Send(PEER_ID_SERVER, 1, reply, true);
276 if(i == deleted_blocks.end())
282 sendlist.push_back(*i);
289 bool connected = connectedAndInitialized();
291 if(connected == false)
293 static float counter = -0.001;
299 JMutexAutoLock envlock(m_env_mutex);
301 Player *myplayer = m_env.getLocalPlayer();
302 assert(myplayer != NULL);
304 // Send TOSERVER_INIT
305 // [0] u16 TOSERVER_INIT
306 // [2] u8 SER_FMT_VER_HIGHEST
307 // [3] u8[20] player_name
308 SharedBuffer<u8> data(2+1+20);
309 writeU16(&data[0], TOSERVER_INIT);
310 writeU8(&data[2], SER_FMT_VER_HIGHEST);
311 memcpy(&data[3], myplayer->getName(), 20);
312 // Send as unreliable
313 Send(0, data, false);
316 // Not connected, return
321 Do stuff if connected
326 JMutexAutoLock lock(m_env_mutex);
328 // Control local player (0ms)
329 LocalPlayer *player = m_env.getLocalPlayer();
330 assert(player != NULL);
331 player->applyControl(dtime);
333 //TimeTaker envtimer("env step", m_device);
337 // Step active blocks
338 for(core::map<v3s16, bool>::Iterator
339 i = m_active_blocks.getIterator();
340 i.atEnd() == false; i++)
342 v3s16 p = i.getNode()->getKey();
344 MapBlock *block = NULL;
347 block = m_env.getMap().getBlockNoCreate(p);
348 block->stepObjects(dtime, false);
350 catch(InvalidPositionException &e)
357 // Fetch some nearby blocks
362 static float counter = 0.0;
367 JMutexAutoLock lock(m_con_mutex);
368 // connectedAndInitialized() is true, peer exists.
369 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
370 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
374 // Update at reasonable intervals (0.2s)
375 static float counter = 0.0;
386 Clear old entries from fetchblock history
389 JMutexAutoLock lock(m_fetchblock_mutex);
391 core::list<v3s16> remove_queue;
392 core::map<v3s16, float>::Iterator i;
393 i = m_fetchblock_history.getIterator();
394 for(; i.atEnd() == false; i++)
396 float value = i.getNode()->getValue();
398 i.getNode()->setValue(value);
400 remove_queue.push_back(i.getNode()->getKey());
402 core::list<v3s16>::Iterator j;
403 j = remove_queue.begin();
404 for(; j != remove_queue.end(); j++)
406 m_fetchblock_history.remove(*j);
412 JMutexAutoLock lock(m_step_dtime_mutex);
413 m_step_dtime += dtime;
425 float Client::asyncStep()
427 DSTACK(__FUNCTION_NAME);
428 //dstream<<"Client::asyncStep()"<<std::endl;
432 JMutexAutoLock lock1(m_step_dtime_mutex);
433 if(m_step_dtime < 0.001)
435 dtime = m_step_dtime;
443 // Virtual methods from con::PeerHandler
444 void Client::peerAdded(con::Peer *peer)
446 derr_client<<"Client::peerAdded(): peer->id="
447 <<peer->id<<std::endl;
449 void Client::deletingPeer(con::Peer *peer, bool timeout)
451 derr_client<<"Client::deletingPeer(): "
452 "Server Peer is getting deleted "
453 <<"(timeout="<<timeout<<")"<<std::endl;
456 void Client::ReceiveAll()
458 DSTACK(__FUNCTION_NAME);
464 catch(con::NoIncomingDataException &e)
468 catch(con::InvalidIncomingDataException &e)
470 dout_client<<DTIME<<"Client::ReceiveAll(): "
471 "InvalidIncomingDataException: what()="
472 <<e.what()<<std::endl;
479 void Client::Receive()
481 DSTACK(__FUNCTION_NAME);
482 u32 data_maxsize = 10000;
483 Buffer<u8> data(data_maxsize);
487 //TimeTaker t1("con mutex and receive", m_device);
488 JMutexAutoLock lock(m_con_mutex);
489 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
491 //TimeTaker t1("ProcessData", m_device);
492 ProcessData(*data, datasize, sender_peer_id);
496 sender_peer_id given to this shall be quaranteed to be a valid peer
498 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
500 DSTACK(__FUNCTION_NAME);
502 // Ignore packets that don't even fit a command
505 m_packetcounter.add(60000);
509 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
511 //dstream<<"Client: received command="<<command<<std::endl;
512 m_packetcounter.add((u16)command);
515 If this check is removed, be sure to change the queue
516 system to know the ids
518 if(sender_peer_id != PEER_ID_SERVER)
520 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
521 "coming from server: peer_id="<<sender_peer_id
528 JMutexAutoLock lock(m_con_mutex);
529 // All data is coming from the server
530 // PeerNotFoundException is handled by caller.
531 peer = m_con.GetPeer(PEER_ID_SERVER);
534 u8 ser_version = m_server_ser_ver;
536 //dstream<<"Client received command="<<(int)command<<std::endl;
538 // Execute fast commands straight away
540 if(command == TOCLIENT_INIT)
545 u8 deployed = data[2];
547 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
548 "deployed="<<((int)deployed&0xff)<<std::endl;
550 if(deployed < SER_FMT_VER_LOWEST
551 || deployed > SER_FMT_VER_HIGHEST)
553 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
554 <<"unsupported ser_fmt_ver"<<std::endl;
558 m_server_ser_ver = deployed;
560 // Get player position
561 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
562 if(datasize >= 2+1+6)
563 playerpos_s16 = readV3S16(&data[2+1]);
564 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
567 JMutexAutoLock envlock(m_env_mutex);
569 // Set player position
570 Player *player = m_env.getLocalPlayer();
571 assert(player != NULL);
572 player->setPosition(playerpos_f);
577 SharedBuffer<u8> reply(replysize);
578 writeU16(&reply[0], TOSERVER_INIT2);
580 m_con.Send(PEER_ID_SERVER, 1, reply, true);
585 if(ser_version == SER_FMT_VER_INVALID)
587 dout_client<<DTIME<<"WARNING: Client: Server serialization"
588 " format invalid or not initialized."
589 " Skipping incoming command="<<command<<std::endl;
593 // Just here to avoid putting the two if's together when
594 // making some copypasta
597 if(command == TOCLIENT_PLAYERPOS)
599 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
603 JMutexAutoLock lock(m_con_mutex);
604 our_peer_id = m_con.GetPeerID();
606 // Cancel if we don't have a peer id
607 if(our_peer_id == PEER_ID_NEW){
608 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
615 JMutexAutoLock envlock(m_env_mutex);
617 u32 player_size = 2+12+12+4+4;
619 u32 player_count = (datasize-2) / player_size;
621 for(u32 i=0; i<player_count; i++)
623 u16 peer_id = readU16(&data[start]);
625 Player *player = m_env.getPlayer(peer_id);
627 // Skip if player doesn't exist
630 start += player_size;
634 // Skip if player is local player
635 if(player->isLocal())
637 start += player_size;
641 v3s32 ps = readV3S32(&data[start+2]);
642 v3s32 ss = readV3S32(&data[start+2+12]);
643 s32 pitch_i = readS32(&data[start+2+12+12]);
644 s32 yaw_i = readS32(&data[start+2+12+12+4]);
645 /*dstream<<"Client: got "
646 <<"pitch_i="<<pitch_i
647 <<" yaw_i="<<yaw_i<<std::endl;*/
648 f32 pitch = (f32)pitch_i / 100.0;
649 f32 yaw = (f32)yaw_i / 100.0;
650 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
651 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
652 player->setPosition(position);
653 player->setSpeed(speed);
654 player->setPitch(pitch);
657 /*dstream<<"Client: player "<<peer_id
659 <<" yaw="<<yaw<<std::endl;*/
661 start += player_size;
665 else if(command == TOCLIENT_PLAYERINFO)
669 JMutexAutoLock lock(m_con_mutex);
670 our_peer_id = m_con.GetPeerID();
672 // Cancel if we don't have a peer id
673 if(our_peer_id == PEER_ID_NEW){
674 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
680 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
683 JMutexAutoLock envlock(m_env_mutex);
685 u32 item_size = 2+PLAYERNAME_SIZE;
686 u32 player_count = (datasize-2) / item_size;
689 core::list<u16> players_alive;
690 for(u32 i=0; i<player_count; i++)
692 // Make sure the name ends in '\0'
693 data[start+2+20-1] = 0;
695 u16 peer_id = readU16(&data[start]);
697 players_alive.push_back(peer_id);
699 /*dstream<<DTIME<<"peer_id="<<peer_id
700 <<" name="<<((char*)&data[start+2])<<std::endl;*/
702 // Don't update the info of the local player
703 if(peer_id == our_peer_id)
709 Player *player = m_env.getPlayer(peer_id);
711 // Create a player if it doesn't exist
714 player = new RemotePlayer(
715 m_device->getSceneManager()->getRootSceneNode(),
718 player->peer_id = peer_id;
719 m_env.addPlayer(player);
720 dout_client<<DTIME<<"Client: Adding new player "
721 <<peer_id<<std::endl;
724 player->updateName((char*)&data[start+2]);
730 Remove those players from the environment that
731 weren't listed by the server.
733 //dstream<<DTIME<<"Removing dead players"<<std::endl;
734 core::list<Player*> players = m_env.getPlayers();
735 core::list<Player*>::Iterator ip;
736 for(ip=players.begin(); ip!=players.end(); ip++)
738 // Ingore local player
742 // Warn about a special case
743 if((*ip)->peer_id == 0)
745 dstream<<DTIME<<"WARNING: Client: Removing "
746 "dead player with id=0"<<std::endl;
749 bool is_alive = false;
750 core::list<u16>::Iterator i;
751 for(i=players_alive.begin(); i!=players_alive.end(); i++)
753 if((*ip)->peer_id == *i)
759 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
760 <<" is_alive="<<is_alive<<std::endl;*/
763 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
765 m_env.removePlayer((*ip)->peer_id);
769 else if(command == TOCLIENT_SECTORMETA)
774 [3...] v2s16 pos + sector metadata
779 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
782 JMutexAutoLock envlock(m_env_mutex);
784 std::string datastring((char*)&data[2], datasize-2);
785 std::istringstream is(datastring, std::ios_base::binary);
789 is.read((char*)buf, 1);
790 u16 sector_count = readU8(buf);
792 //dstream<<"sector_count="<<sector_count<<std::endl;
794 for(u16 i=0; i<sector_count; i++)
797 is.read((char*)buf, 4);
798 v2s16 pos = readV2S16(buf);
799 /*dstream<<"Client: deserializing sector at "
800 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
802 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
803 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
807 else if(command == TOCLIENT_INVENTORY)
812 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
815 //TimeTaker t2("mutex locking", m_device);
816 JMutexAutoLock envlock(m_env_mutex);
819 //TimeTaker t3("istringstream init", m_device);
820 std::string datastring((char*)&data[2], datasize-2);
821 std::istringstream is(datastring, std::ios_base::binary);
824 //m_env.printPlayers(dstream);
826 //TimeTaker t4("player get", m_device);
827 Player *player = m_env.getLocalPlayer();
828 assert(player != NULL);
831 //TimeTaker t1("inventory.deSerialize()", m_device);
832 player->inventory.deSerialize(is);
835 m_inventory_updated = true;
837 //dstream<<"Client got player inventory:"<<std::endl;
838 //player->inventory.print(dstream);
842 else if(command == TOCLIENT_OBJECTDATA)
845 // Strip command word and create a stringstream
846 std::string datastring((char*)&data[2], datasize-2);
847 std::istringstream is(datastring, std::ios_base::binary);
851 JMutexAutoLock envlock(m_env_mutex);
859 is.read((char*)buf, 2);
860 u16 playercount = readU16(buf);
862 for(u16 i=0; i<playercount; i++)
864 is.read((char*)buf, 2);
865 u16 peer_id = readU16(buf);
866 is.read((char*)buf, 12);
867 v3s32 p_i = readV3S32(buf);
868 is.read((char*)buf, 12);
869 v3s32 s_i = readV3S32(buf);
870 is.read((char*)buf, 4);
871 s32 pitch_i = readS32(buf);
872 is.read((char*)buf, 4);
873 s32 yaw_i = readS32(buf);
875 Player *player = m_env.getPlayer(peer_id);
877 // Skip if player doesn't exist
883 // Skip if player is local player
884 if(player->isLocal())
889 f32 pitch = (f32)pitch_i / 100.0;
890 f32 yaw = (f32)yaw_i / 100.0;
891 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
892 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
894 player->setPosition(position);
895 player->setSpeed(speed);
896 player->setPitch(pitch);
904 // Read active block count
905 is.read((char*)buf, 2);
906 u16 blockcount = readU16(buf);
908 // Initialize delete queue with all active blocks
909 core::map<v3s16, bool> abs_to_delete;
910 for(core::map<v3s16, bool>::Iterator
911 i = m_active_blocks.getIterator();
912 i.atEnd() == false; i++)
914 v3s16 p = i.getNode()->getKey();
916 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
917 <<" to abs_to_delete"
919 abs_to_delete.insert(p, true);
922 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
925 for(u16 i=0; i<blockcount; i++)
928 is.read((char*)buf, 6);
929 v3s16 p = readV3S16(buf);
930 // Get block from somewhere
931 MapBlock *block = NULL;
933 block = m_env.getMap().getBlockNoCreate(p);
935 catch(InvalidPositionException &e)
937 //TODO: Create a dummy block?
942 <<"Could not get block at blockpos "
943 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
944 <<"in TOCLIENT_OBJECTDATA. Ignoring "
945 <<"following block object data."
950 /*dstream<<"Client updating objects for block "
951 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
954 // Insert to active block list
955 m_active_blocks.insert(p, true);
957 // Remove from deletion queue
958 if(abs_to_delete.find(p) != NULL)
959 abs_to_delete.remove(p);
961 // Update objects of block
962 block->updateObjects(is, m_server_ser_ver,
963 m_device->getSceneManager());
966 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
969 // Delete objects of blocks in delete queue
970 for(core::map<v3s16, bool>::Iterator
971 i = abs_to_delete.getIterator();
972 i.atEnd() == false; i++)
974 v3s16 p = i.getNode()->getKey();
977 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
980 block->clearObjects();
981 // Remove from active blocks list
982 m_active_blocks.remove(p);
984 catch(InvalidPositionException &e)
986 dstream<<"WARNAING: Client: "
987 <<"Couldn't clear objects of active->inactive"
989 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
990 <<" because block was not found"
998 // Default to queueing it (for slow commands)
1001 JMutexAutoLock lock(m_incoming_queue_mutex);
1003 IncomingPacket packet(data, datasize);
1004 m_incoming_queue.push_back(packet);
1009 Returns true if there was something in queue
1011 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1013 DSTACK(__FUNCTION_NAME);
1015 try //for catching con::PeerNotFoundException
1020 JMutexAutoLock lock(m_con_mutex);
1021 // All data is coming from the server
1022 peer = m_con.GetPeer(PEER_ID_SERVER);
1025 u8 ser_version = m_server_ser_ver;
1027 IncomingPacket packet = getPacket();
1028 u8 *data = packet.m_data;
1029 u32 datasize = packet.m_datalen;
1031 // An empty packet means queue is empty
1039 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1041 if(command == TOCLIENT_REMOVENODE)
1046 p.X = readS16(&data[2]);
1047 p.Y = readS16(&data[4]);
1048 p.Z = readS16(&data[6]);
1050 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1052 core::map<v3s16, MapBlock*> modified_blocks;
1056 JMutexAutoLock envlock(m_env_mutex);
1057 //TimeTaker t("removeNodeAndUpdate", m_device);
1058 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1060 catch(InvalidPositionException &e)
1064 for(core::map<v3s16, MapBlock * >::Iterator
1065 i = modified_blocks.getIterator();
1066 i.atEnd() == false; i++)
1068 v3s16 p = i.getNode()->getKey();
1069 //m_env.getMap().updateMeshes(p);
1070 mesh_updater.add(p);
1073 else if(command == TOCLIENT_ADDNODE)
1075 if(datasize < 8 + MapNode::serializedLength(ser_version))
1079 p.X = readS16(&data[2]);
1080 p.Y = readS16(&data[4]);
1081 p.Z = readS16(&data[6]);
1083 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1086 n.deSerialize(&data[8], ser_version);
1088 core::map<v3s16, MapBlock*> modified_blocks;
1092 JMutexAutoLock envlock(m_env_mutex);
1093 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1095 catch(InvalidPositionException &e)
1098 for(core::map<v3s16, MapBlock * >::Iterator
1099 i = modified_blocks.getIterator();
1100 i.atEnd() == false; i++)
1102 v3s16 p = i.getNode()->getKey();
1103 //m_env.getMap().updateMeshes(p);
1104 mesh_updater.add(p);
1107 else if(command == TOCLIENT_BLOCKDATA)
1109 // Ignore too small packet
1112 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1116 p.X = readS16(&data[2]);
1117 p.Y = readS16(&data[4]);
1118 p.Z = readS16(&data[6]);
1120 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1121 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1123 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1124 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1126 std::string datastring((char*)&data[8], datasize-8);
1127 std::istringstream istr(datastring, std::ios_base::binary);
1133 JMutexAutoLock envlock(m_env_mutex);
1135 v2s16 p2d(p.X, p.Z);
1136 sector = m_env.getMap().emergeSector(p2d);
1138 v2s16 sp = sector->getPos();
1141 dstream<<"ERROR: Got sector with getPos()="
1142 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1143 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1147 //assert(sector->getPos() == p2d);
1150 block = sector->getBlockNoCreate(p.Y);
1152 Update an existing block
1154 //dstream<<"Updating"<<std::endl;
1155 block->deSerialize(istr, ser_version);
1156 //block->setChangedFlag();
1158 catch(InvalidPositionException &e)
1163 //dstream<<"Creating new"<<std::endl;
1164 block = new MapBlock(&m_env.getMap(), p);
1165 block->deSerialize(istr, ser_version);
1166 sector->insertBlock(block);
1167 //block->setChangedFlag();
1171 mod.type = NODEMOD_CHANGECONTENT;
1172 mod.param = CONTENT_MESE;
1173 block->setTempMod(v3s16(8,10,8), mod);
1174 block->setTempMod(v3s16(8,9,8), mod);
1175 block->setTempMod(v3s16(8,8,8), mod);
1176 block->setTempMod(v3s16(8,7,8), mod);
1177 block->setTempMod(v3s16(8,6,8), mod);*/
1181 Well, this is a dumb way to do it, they should just
1182 be drawn as separate objects.
1187 mod.type = NODEMOD_CHANGECONTENT;
1188 mod.param = CONTENT_CLOUD;
1191 for(p2.X=3; p2.X<=13; p2.X++)
1192 for(p2.Z=3; p2.Z<=13; p2.Z++)
1194 block->setTempMod(p2, mod);
1201 // Old version has zero lighting, update it.
1202 if(ser_version == 0 || ser_version == 1)
1204 derr_client<<"Client: Block in old format: "
1205 "Calculating lighting"<<std::endl;
1206 core::map<v3s16, MapBlock*> blocks_changed;
1207 blocks_changed.insert(block->getPos(), block);
1208 core::map<v3s16, MapBlock*> modified_blocks;
1209 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1213 Update Mesh of this block and blocks at x-, y- and z-
1216 //m_env.getMap().updateMeshes(block->getPos());
1217 mesh_updater.add(block->getPos());
1229 u32 replysize = 2+1+6;
1230 SharedBuffer<u8> reply(replysize);
1231 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1233 writeV3S16(&reply[3], p);
1235 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1242 JMutexAutoLock lock(m_fetchblock_mutex);
1244 if(m_fetchblock_history.find(p) != NULL)
1246 m_fetchblock_history.remove(p);
1260 u32 replysize = 2+1+6;
1261 SharedBuffer<u8> reply(replysize);
1262 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1264 writeV3S16(&reply[3], p);
1266 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1273 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1274 <<command<<std::endl;
1280 catch(con::PeerNotFoundException &e)
1282 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1283 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1288 bool Client::AsyncProcessData()
1292 // We want to update the meshes as soon as a single packet has
1294 LazyMeshUpdater mesh_updater(&m_env);
1295 bool r = AsyncProcessPacket(mesh_updater);
1301 /*LazyMeshUpdater mesh_updater(&m_env);
1304 bool r = AsyncProcessPacket(mesh_updater);
1312 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1314 JMutexAutoLock lock(m_con_mutex);
1315 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1319 void Client::fetchBlock(v3s16 p, u8 flags)
1321 if(connectedAndInitialized() == false)
1322 throw ClientNotReadyException
1323 ("ClientNotReadyException: connectedAndInitialized() == false");
1325 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1326 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1328 JMutexAutoLock conlock(m_con_mutex);
1330 SharedBuffer<u8> data(9);
1331 writeU16(&data[0], TOSERVER_GETBLOCK);
1332 writeS16(&data[2], p.X);
1333 writeS16(&data[4], p.Y);
1334 writeS16(&data[6], p.Z);
1335 writeU8(&data[8], flags);
1336 m_con.Send(PEER_ID_SERVER, 1, data, true);
1340 Calls fetchBlock() on some nearby missing blocks.
1342 Returns when any of various network load indicators go over limit.
1344 Does nearly the same thing as the old updateChangedVisibleArea()
1346 void Client::fetchBlocks()
1348 if(connectedAndInitialized() == false)
1349 throw ClientNotReadyException
1350 ("ClientNotReadyException: connectedAndInitialized() == false");
1354 bool Client::isFetchingBlocks()
1356 JMutexAutoLock conlock(m_con_mutex);
1357 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1358 // Not really fetching but can't fetch more.
1359 if(peer == NULL) return true;
1361 con::Channel *channel = &(peer->channels[1]);
1363 NOTE: Channel 0 should always be used for fetching blocks,
1364 and for nothing else.
1366 if(channel->incoming_reliables.size() > 0)
1368 if(channel->outgoing_reliables.size() > 0)
1373 IncomingPacket Client::getPacket()
1375 JMutexAutoLock lock(m_incoming_queue_mutex);
1377 core::list<IncomingPacket>::Iterator i;
1378 // Refer to first one
1379 i = m_incoming_queue.begin();
1381 // If queue is empty, return empty packet
1382 if(i == m_incoming_queue.end()){
1383 IncomingPacket packet;
1387 // Pop out first packet and return it
1388 IncomingPacket packet = *i;
1389 m_incoming_queue.erase(i);
1394 void Client::removeNode(v3s16 nodepos)
1396 if(connectedAndInitialized() == false){
1397 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1402 // Test that the position exists
1404 JMutexAutoLock envlock(m_env_mutex);
1405 m_env.getMap().getNode(nodepos);
1407 catch(InvalidPositionException &e)
1409 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1414 SharedBuffer<u8> data(8);
1415 writeU16(&data[0], TOSERVER_REMOVENODE);
1416 writeS16(&data[2], nodepos.X);
1417 writeS16(&data[4], nodepos.Y);
1418 writeS16(&data[6], nodepos.Z);
1419 Send(0, data, true);
1422 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1424 if(connectedAndInitialized() == false){
1425 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1426 "cancelled (not connected)"
1431 // Test that the position exists
1433 JMutexAutoLock envlock(m_env_mutex);
1434 m_env.getMap().getNode(nodepos);
1436 catch(InvalidPositionException &e)
1438 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1443 //u8 ser_version = m_server_ser_ver;
1445 // SUGGESTION: The validity of the operation could be checked here too
1447 u8 datasize = 2 + 6 + 2;
1448 SharedBuffer<u8> data(datasize);
1449 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1450 writeS16(&data[2], nodepos.X);
1451 writeS16(&data[4], nodepos.Y);
1452 writeS16(&data[6], nodepos.Z);
1453 writeU16(&data[8], i);
1454 Send(0, data, true);
1458 void Client::pressGround(u8 button, v3s16 nodepos_undersurface,
1459 v3s16 nodepos_oversurface, u16 item)
1461 if(connectedAndInitialized() == false){
1462 dout_client<<DTIME<<"Client::pressGround() "
1463 "cancelled (not connected)"
1472 [3] v3s16 nodepos_undersurface
1473 [9] v3s16 nodepos_abovesurface
1478 2: stop digging (all parameters ignored)
1480 u8 datasize = 2 + 1 + 6 + 6 + 2;
1481 SharedBuffer<u8> data(datasize);
1482 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1483 writeU8(&data[2], button);
1484 writeV3S16(&data[3], nodepos_undersurface);
1485 writeV3S16(&data[9], nodepos_oversurface);
1486 writeU16(&data[15], item);
1487 Send(0, data, true);
1490 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1492 if(connectedAndInitialized() == false){
1493 dout_client<<DTIME<<"Client::clickObject() "
1494 "cancelled (not connected)"
1501 [2] u8 button (0=left, 1=right)
1506 u8 datasize = 2 + 1 + 6 + 2 + 2;
1507 SharedBuffer<u8> data(datasize);
1508 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1509 writeU8(&data[2], button);
1510 writeV3S16(&data[3], blockpos);
1511 writeS16(&data[9], id);
1512 writeU16(&data[11], item);
1513 Send(0, data, true);
1516 void Client::stopDigging()
1518 if(connectedAndInitialized() == false){
1519 dout_client<<DTIME<<"Client::release() "
1520 "cancelled (not connected)"
1529 [3] v3s16 nodepos_undersurface
1530 [9] v3s16 nodepos_abovesurface
1535 2: stop digging (all parameters ignored)
1537 u8 datasize = 2 + 1 + 6 + 6 + 2;
1538 SharedBuffer<u8> data(datasize);
1539 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1540 writeU8(&data[2], 2);
1541 writeV3S16(&data[3], v3s16(0,0,0));
1542 writeV3S16(&data[9], v3s16(0,0,0));
1543 writeU16(&data[15], 0);
1544 Send(0, data, true);
1547 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1556 std::ostringstream os(std::ios_base::binary);
1560 writeU16(buf, TOSERVER_SIGNTEXT);
1561 os.write((char*)buf, 2);
1564 writeV3S16(buf, blockpos);
1565 os.write((char*)buf, 6);
1569 os.write((char*)buf, 2);
1571 u16 textlen = text.size();
1572 // Write text length
1573 writeS16(buf, textlen);
1574 os.write((char*)buf, 2);
1577 os.write((char*)text.c_str(), textlen);
1580 std::string s = os.str();
1581 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1583 Send(0, data, true);
1586 void Client::sendPlayerPos()
1588 JMutexAutoLock envlock(m_env_mutex);
1590 Player *myplayer = m_env.getLocalPlayer();
1591 if(myplayer == NULL)
1596 JMutexAutoLock lock(m_con_mutex);
1597 our_peer_id = m_con.GetPeerID();
1600 // Set peer id if not set already
1601 if(myplayer->peer_id == PEER_ID_NEW)
1602 myplayer->peer_id = our_peer_id;
1603 // Check that an existing peer_id is the same as the connection's
1604 assert(myplayer->peer_id == our_peer_id);
1606 v3f pf = myplayer->getPosition();
1607 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1608 v3f sf = myplayer->getSpeed();
1609 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1610 s32 pitch = myplayer->getPitch() * 100;
1611 s32 yaw = myplayer->getYaw() * 100;
1616 [2] v3s32 position*100
1617 [2+12] v3s32 speed*100
1618 [2+12+12] s32 pitch*100
1619 [2+12+12+4] s32 yaw*100
1622 SharedBuffer<u8> data(2+12+12+4+4);
1623 writeU16(&data[0], TOSERVER_PLAYERPOS);
1624 writeV3S32(&data[2], position);
1625 writeV3S32(&data[2+12], speed);
1626 writeS32(&data[2+12+12], pitch);
1627 writeS32(&data[2+12+12+4], yaw);
1629 // Send as unreliable
1630 Send(0, data, false);
1634 void Client::updateCamera(v3f pos, v3f dir)
1636 m_env.getMap().updateCamera(pos, dir);
1637 camera_position = pos;
1638 camera_direction = dir;
1641 MapNode Client::getNode(v3s16 p)
1643 JMutexAutoLock envlock(m_env_mutex);
1644 return m_env.getMap().getNode(p);
1647 /*f32 Client::getGroundHeight(v2s16 p)
1649 JMutexAutoLock envlock(m_env_mutex);
1650 return m_env.getMap().getGroundHeight(p);
1653 bool Client::isNodeUnderground(v3s16 p)
1655 JMutexAutoLock envlock(m_env_mutex);
1656 return m_env.getMap().isNodeUnderground(p);
1659 /*Player * Client::getLocalPlayer()
1661 JMutexAutoLock envlock(m_env_mutex);
1662 return m_env.getLocalPlayer();
1665 /*core::list<Player*> Client::getPlayers()
1667 JMutexAutoLock envlock(m_env_mutex);
1668 return m_env.getPlayers();
1671 v3f Client::getPlayerPosition()
1673 JMutexAutoLock envlock(m_env_mutex);
1674 LocalPlayer *player = m_env.getLocalPlayer();
1675 assert(player != NULL);
1676 return player->getPosition();
1679 void Client::setPlayerControl(PlayerControl &control)
1681 JMutexAutoLock envlock(m_env_mutex);
1682 LocalPlayer *player = m_env.getLocalPlayer();
1683 assert(player != NULL);
1684 player->control = control;
1687 // Returns true if the inventory of the local player has been
1688 // updated from the server. If it is true, it is set to false.
1689 bool Client::getLocalInventoryUpdated()
1691 // m_inventory_updated is behind envlock
1692 JMutexAutoLock envlock(m_env_mutex);
1693 bool updated = m_inventory_updated;
1694 m_inventory_updated = false;
1698 // Copies the inventory of the local player to parameter
1699 void Client::getLocalInventory(Inventory &dst)
1701 JMutexAutoLock envlock(m_env_mutex);
1702 Player *player = m_env.getLocalPlayer();
1703 assert(player != NULL);
1704 dst = player->inventory;
1707 MapBlockObject * Client::getSelectedObject(
1709 v3f from_pos_f_on_map,
1710 core::line3d<f32> shootline_on_map
1713 JMutexAutoLock envlock(m_env_mutex);
1715 core::array<DistanceSortedObject> objects;
1717 for(core::map<v3s16, bool>::Iterator
1718 i = m_active_blocks.getIterator();
1719 i.atEnd() == false; i++)
1721 v3s16 p = i.getNode()->getKey();
1723 MapBlock *block = NULL;
1726 block = m_env.getMap().getBlockNoCreate(p);
1728 catch(InvalidPositionException &e)
1733 // Calculate from_pos relative to block
1734 v3s16 block_pos_i_on_map = block->getPosRelative();
1735 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1736 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1738 block->getObjects(from_pos_f_on_block, max_d, objects);
1739 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1742 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1745 // After this, the closest object is the first in the array.
1748 for(u32 i=0; i<objects.size(); i++)
1750 MapBlockObject *obj = objects[i].obj;
1751 MapBlock *block = obj->getBlock();
1753 // Calculate shootline relative to block
1754 v3s16 block_pos_i_on_map = block->getPosRelative();
1755 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1756 core::line3d<f32> shootline_on_block(
1757 shootline_on_map.start - block_pos_f_on_map,
1758 shootline_on_map.end - block_pos_f_on_map
1761 if(obj->isSelected(shootline_on_block))
1763 //dstream<<"Returning selected object"<<std::endl;
1768 //dstream<<"No object selected; returning NULL."<<std::endl;
1772 void Client::printDebugInfo(std::ostream &os)
1774 //JMutexAutoLock lock1(m_fetchblock_mutex);
1775 JMutexAutoLock lock2(m_incoming_queue_mutex);
1777 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1778 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1779 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1783 float Client::getDaylightRatio()
1785 JMutexAutoLock envlock(m_env_mutex);
1786 return m_env.getDaylightRatio();