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"
29 void * ClientUpdateThread::Thread()
33 DSTACK(__FUNCTION_NAME);
35 #if CATCH_UNHANDLED_EXCEPTIONS
41 m_client->asyncStep();
43 //m_client->updateSomeExpiredMeshes();
45 bool was = m_client->AsyncProcessData();
50 #if CATCH_UNHANDLED_EXCEPTIONS
53 This is what has to be done in threads to get suitable debug info
55 catch(std::exception &e)
57 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
58 <<e.what()<<std::endl;
67 IrrlichtDevice *device,
68 const char *playername,
70 s16 &viewing_range_nodes,
71 bool &viewing_range_all):
73 m_env(new ClientMap(this,
77 device->getSceneManager()->getRootSceneNode(),
78 device->getSceneManager(), 666),
80 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
82 camera_position(0,0,0),
83 camera_direction(0,0,1),
84 m_server_ser_ver(SER_FMT_VER_INVALID),
86 m_inventory_updated(false),
89 m_packetcounter_timer = 0.0;
90 m_delete_unused_sectors_timer = 0.0;
91 m_connection_reinit_timer = 0.0;
92 m_avg_rtt_timer = 0.0;
93 m_playerpos_send_timer = 0.0;
95 //m_fetchblock_mutex.Init();
96 m_incoming_queue_mutex.Init();
99 m_step_dtime_mutex.Init();
104 JMutexAutoLock envlock(m_env_mutex);
105 //m_env.getMap().StartUpdater();
107 Player *player = new LocalPlayer();
109 player->updateName(playername);
111 /*f32 y = BS*2 + BS*20;
112 player->setPosition(v3f(0, y, 0));*/
113 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
114 m_env.addPlayer(player);
120 m_thread.setRun(false);
121 while(m_thread.IsRunning())
125 void Client::connect(Address address)
127 DSTACK(__FUNCTION_NAME);
128 JMutexAutoLock lock(m_con_mutex);
129 m_con.setTimeoutMs(0);
130 m_con.Connect(address);
133 bool Client::connectedAndInitialized()
135 JMutexAutoLock lock(m_con_mutex);
137 if(m_con.Connected() == false)
140 if(m_server_ser_ver == SER_FMT_VER_INVALID)
146 void Client::step(float dtime)
148 DSTACK(__FUNCTION_NAME);
159 s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
161 if(t == d/4 || t == (d-d/4))
163 else if(t < d/4 || t > (d-d/4))
176 if(dr != m_env.getDayNightRatio())
178 //dstream<<"dr="<<dr<<std::endl;
179 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
180 m_env.setDayNightRatio(dr);
181 m_env.expireMeshes(true);
186 //dstream<<"Client steps "<<dtime<<std::endl;
189 //TimeTaker timer("ReceiveAll()", m_device);
195 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
197 JMutexAutoLock lock(m_con_mutex);
198 m_con.RunTimeouts(dtime);
205 float &counter = m_packetcounter_timer;
211 dout_client<<"Client packetcounter (20s):"<<std::endl;
212 m_packetcounter.print(dout_client);
213 m_packetcounter.clear();
219 Delete unused sectors
221 NOTE: This jams the game for a while because deleting sectors
225 float &counter = m_delete_unused_sectors_timer;
233 JMutexAutoLock lock(m_env_mutex);
235 core::list<v3s16> deleted_blocks;
237 float delete_unused_sectors_timeout =
238 g_settings.getFloat("client_delete_unused_sectors_timeout");
240 // Delete sector blocks
241 /*u32 num = m_env.getMap().deleteUnusedSectors
242 (delete_unused_sectors_timeout,
243 true, &deleted_blocks);*/
245 // Delete whole sectors
246 u32 num = m_env.getMap().deleteUnusedSectors
247 (delete_unused_sectors_timeout,
248 false, &deleted_blocks);
252 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
253 <<" unused sectors"<<std::endl;*/
254 dstream<<DTIME<<"Client: Deleted "<<num
255 <<" unused sectors"<<std::endl;
261 // Env is locked so con can be locked.
262 JMutexAutoLock lock(m_con_mutex);
264 core::list<v3s16>::Iterator i = deleted_blocks.begin();
265 core::list<v3s16> sendlist;
268 if(sendlist.size() == 255 || i == deleted_blocks.end())
270 if(sendlist.size() == 0)
279 u32 replysize = 2+1+6*sendlist.size();
280 SharedBuffer<u8> reply(replysize);
281 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
282 reply[2] = sendlist.size();
284 for(core::list<v3s16>::Iterator
285 j = sendlist.begin();
286 j != sendlist.end(); j++)
288 writeV3S16(&reply[2+1+6*k], *j);
291 m_con.Send(PEER_ID_SERVER, 1, reply, true);
293 if(i == deleted_blocks.end())
299 sendlist.push_back(*i);
306 bool connected = connectedAndInitialized();
308 if(connected == false)
310 float &counter = m_connection_reinit_timer;
316 JMutexAutoLock envlock(m_env_mutex);
318 Player *myplayer = m_env.getLocalPlayer();
319 assert(myplayer != NULL);
321 // Send TOSERVER_INIT
322 // [0] u16 TOSERVER_INIT
323 // [2] u8 SER_FMT_VER_HIGHEST
324 // [3] u8[20] player_name
325 SharedBuffer<u8> data(2+1+20);
326 writeU16(&data[0], TOSERVER_INIT);
327 writeU8(&data[2], SER_FMT_VER_HIGHEST);
328 memcpy(&data[3], myplayer->getName(), 20);
329 // Send as unreliable
330 Send(0, data, false);
333 // Not connected, return
338 Do stuff if connected
343 JMutexAutoLock lock(m_env_mutex);
345 // Control local player (0ms)
346 LocalPlayer *player = m_env.getLocalPlayer();
347 assert(player != NULL);
348 player->applyControl(dtime);
350 //TimeTaker envtimer("env step", m_device);
354 // Step active blocks
355 for(core::map<v3s16, bool>::Iterator
356 i = m_active_blocks.getIterator();
357 i.atEnd() == false; i++)
359 v3s16 p = i.getNode()->getKey();
361 MapBlock *block = NULL;
364 block = m_env.getMap().getBlockNoCreate(p);
365 block->stepObjects(dtime, false, m_env.getDayNightRatio());
367 catch(InvalidPositionException &e)
374 float &counter = m_avg_rtt_timer;
379 JMutexAutoLock lock(m_con_mutex);
380 // connectedAndInitialized() is true, peer exists.
381 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
382 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
386 float &counter = m_playerpos_send_timer;
396 JMutexAutoLock lock(m_step_dtime_mutex);
397 m_step_dtime += dtime;
401 float Client::asyncStep()
403 DSTACK(__FUNCTION_NAME);
404 //dstream<<"Client::asyncStep()"<<std::endl;
408 JMutexAutoLock lock1(m_step_dtime_mutex);
409 if(m_step_dtime < 0.001)
411 dtime = m_step_dtime;
419 // Virtual methods from con::PeerHandler
420 void Client::peerAdded(con::Peer *peer)
422 derr_client<<"Client::peerAdded(): peer->id="
423 <<peer->id<<std::endl;
425 void Client::deletingPeer(con::Peer *peer, bool timeout)
427 derr_client<<"Client::deletingPeer(): "
428 "Server Peer is getting deleted "
429 <<"(timeout="<<timeout<<")"<<std::endl;
432 void Client::ReceiveAll()
434 DSTACK(__FUNCTION_NAME);
440 catch(con::NoIncomingDataException &e)
444 catch(con::InvalidIncomingDataException &e)
446 dout_client<<DTIME<<"Client::ReceiveAll(): "
447 "InvalidIncomingDataException: what()="
448 <<e.what()<<std::endl;
455 void Client::Receive()
457 DSTACK(__FUNCTION_NAME);
458 u32 data_maxsize = 10000;
459 Buffer<u8> data(data_maxsize);
463 //TimeTaker t1("con mutex and receive", m_device);
464 JMutexAutoLock lock(m_con_mutex);
465 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
467 //TimeTaker t1("ProcessData", m_device);
468 ProcessData(*data, datasize, sender_peer_id);
472 sender_peer_id given to this shall be quaranteed to be a valid peer
474 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
476 DSTACK(__FUNCTION_NAME);
478 // Ignore packets that don't even fit a command
481 m_packetcounter.add(60000);
485 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
487 //dstream<<"Client: received command="<<command<<std::endl;
488 m_packetcounter.add((u16)command);
491 If this check is removed, be sure to change the queue
492 system to know the ids
494 if(sender_peer_id != PEER_ID_SERVER)
496 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
497 "coming from server: peer_id="<<sender_peer_id
504 JMutexAutoLock lock(m_con_mutex);
505 // All data is coming from the server
506 // PeerNotFoundException is handled by caller.
507 peer = m_con.GetPeer(PEER_ID_SERVER);
510 u8 ser_version = m_server_ser_ver;
512 //dstream<<"Client received command="<<(int)command<<std::endl;
514 // Execute fast commands straight away
516 if(command == TOCLIENT_INIT)
521 u8 deployed = data[2];
523 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
524 "deployed="<<((int)deployed&0xff)<<std::endl;
526 if(deployed < SER_FMT_VER_LOWEST
527 || deployed > SER_FMT_VER_HIGHEST)
529 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
530 <<"unsupported ser_fmt_ver"<<std::endl;
534 m_server_ser_ver = deployed;
536 // Get player position
537 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
538 if(datasize >= 2+1+6)
539 playerpos_s16 = readV3S16(&data[2+1]);
540 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
543 JMutexAutoLock envlock(m_env_mutex);
545 // Set player position
546 Player *player = m_env.getLocalPlayer();
547 assert(player != NULL);
548 player->setPosition(playerpos_f);
553 SharedBuffer<u8> reply(replysize);
554 writeU16(&reply[0], TOSERVER_INIT2);
556 m_con.Send(PEER_ID_SERVER, 1, reply, true);
561 if(ser_version == SER_FMT_VER_INVALID)
563 dout_client<<DTIME<<"WARNING: Client: Server serialization"
564 " format invalid or not initialized."
565 " Skipping incoming command="<<command<<std::endl;
569 // Just here to avoid putting the two if's together when
570 // making some copypasta
573 if(command == TOCLIENT_PLAYERPOS)
575 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
579 JMutexAutoLock lock(m_con_mutex);
580 our_peer_id = m_con.GetPeerID();
582 // Cancel if we don't have a peer id
583 if(our_peer_id == PEER_ID_NEW){
584 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
591 JMutexAutoLock envlock(m_env_mutex);
593 u32 player_size = 2+12+12+4+4;
595 u32 player_count = (datasize-2) / player_size;
597 for(u32 i=0; i<player_count; i++)
599 u16 peer_id = readU16(&data[start]);
601 Player *player = m_env.getPlayer(peer_id);
603 // Skip if player doesn't exist
606 start += player_size;
610 // Skip if player is local player
611 if(player->isLocal())
613 start += player_size;
617 v3s32 ps = readV3S32(&data[start+2]);
618 v3s32 ss = readV3S32(&data[start+2+12]);
619 s32 pitch_i = readS32(&data[start+2+12+12]);
620 s32 yaw_i = readS32(&data[start+2+12+12+4]);
621 /*dstream<<"Client: got "
622 <<"pitch_i="<<pitch_i
623 <<" yaw_i="<<yaw_i<<std::endl;*/
624 f32 pitch = (f32)pitch_i / 100.0;
625 f32 yaw = (f32)yaw_i / 100.0;
626 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
627 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
628 player->setPosition(position);
629 player->setSpeed(speed);
630 player->setPitch(pitch);
633 /*dstream<<"Client: player "<<peer_id
635 <<" yaw="<<yaw<<std::endl;*/
637 start += player_size;
641 else if(command == TOCLIENT_PLAYERINFO)
645 JMutexAutoLock lock(m_con_mutex);
646 our_peer_id = m_con.GetPeerID();
648 // Cancel if we don't have a peer id
649 if(our_peer_id == PEER_ID_NEW){
650 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
656 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
659 JMutexAutoLock envlock(m_env_mutex);
661 u32 item_size = 2+PLAYERNAME_SIZE;
662 u32 player_count = (datasize-2) / item_size;
665 core::list<u16> players_alive;
666 for(u32 i=0; i<player_count; i++)
668 // Make sure the name ends in '\0'
669 data[start+2+20-1] = 0;
671 u16 peer_id = readU16(&data[start]);
673 players_alive.push_back(peer_id);
675 /*dstream<<DTIME<<"peer_id="<<peer_id
676 <<" name="<<((char*)&data[start+2])<<std::endl;*/
678 // Don't update the info of the local player
679 if(peer_id == our_peer_id)
685 Player *player = m_env.getPlayer(peer_id);
687 // Create a player if it doesn't exist
690 player = new RemotePlayer(
691 m_device->getSceneManager()->getRootSceneNode(),
694 player->peer_id = peer_id;
695 m_env.addPlayer(player);
696 dout_client<<DTIME<<"Client: Adding new player "
697 <<peer_id<<std::endl;
700 player->updateName((char*)&data[start+2]);
706 Remove those players from the environment that
707 weren't listed by the server.
709 //dstream<<DTIME<<"Removing dead players"<<std::endl;
710 core::list<Player*> players = m_env.getPlayers();
711 core::list<Player*>::Iterator ip;
712 for(ip=players.begin(); ip!=players.end(); ip++)
714 // Ingore local player
718 // Warn about a special case
719 if((*ip)->peer_id == 0)
721 dstream<<DTIME<<"WARNING: Client: Removing "
722 "dead player with id=0"<<std::endl;
725 bool is_alive = false;
726 core::list<u16>::Iterator i;
727 for(i=players_alive.begin(); i!=players_alive.end(); i++)
729 if((*ip)->peer_id == *i)
735 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
736 <<" is_alive="<<is_alive<<std::endl;*/
739 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
741 m_env.removePlayer((*ip)->peer_id);
745 else if(command == TOCLIENT_SECTORMETA)
750 [3...] v2s16 pos + sector metadata
755 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
758 JMutexAutoLock envlock(m_env_mutex);
760 std::string datastring((char*)&data[2], datasize-2);
761 std::istringstream is(datastring, std::ios_base::binary);
765 is.read((char*)buf, 1);
766 u16 sector_count = readU8(buf);
768 //dstream<<"sector_count="<<sector_count<<std::endl;
770 for(u16 i=0; i<sector_count; i++)
773 is.read((char*)buf, 4);
774 v2s16 pos = readV2S16(buf);
775 /*dstream<<"Client: deserializing sector at "
776 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
778 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
779 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
783 else if(command == TOCLIENT_INVENTORY)
788 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
791 //TimeTaker t2("mutex locking", m_device);
792 JMutexAutoLock envlock(m_env_mutex);
795 //TimeTaker t3("istringstream init", m_device);
796 std::string datastring((char*)&data[2], datasize-2);
797 std::istringstream is(datastring, std::ios_base::binary);
800 //m_env.printPlayers(dstream);
802 //TimeTaker t4("player get", m_device);
803 Player *player = m_env.getLocalPlayer();
804 assert(player != NULL);
807 //TimeTaker t1("inventory.deSerialize()", m_device);
808 player->inventory.deSerialize(is);
811 m_inventory_updated = true;
813 //dstream<<"Client got player inventory:"<<std::endl;
814 //player->inventory.print(dstream);
818 else if(command == TOCLIENT_OBJECTDATA)
821 // Strip command word and create a stringstream
822 std::string datastring((char*)&data[2], datasize-2);
823 std::istringstream is(datastring, std::ios_base::binary);
827 JMutexAutoLock envlock(m_env_mutex);
835 is.read((char*)buf, 2);
836 u16 playercount = readU16(buf);
838 for(u16 i=0; i<playercount; i++)
840 is.read((char*)buf, 2);
841 u16 peer_id = readU16(buf);
842 is.read((char*)buf, 12);
843 v3s32 p_i = readV3S32(buf);
844 is.read((char*)buf, 12);
845 v3s32 s_i = readV3S32(buf);
846 is.read((char*)buf, 4);
847 s32 pitch_i = readS32(buf);
848 is.read((char*)buf, 4);
849 s32 yaw_i = readS32(buf);
851 Player *player = m_env.getPlayer(peer_id);
853 // Skip if player doesn't exist
859 // Skip if player is local player
860 if(player->isLocal())
865 f32 pitch = (f32)pitch_i / 100.0;
866 f32 yaw = (f32)yaw_i / 100.0;
867 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
868 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
870 player->setPosition(position);
871 player->setSpeed(speed);
872 player->setPitch(pitch);
880 // Read active block count
881 is.read((char*)buf, 2);
882 u16 blockcount = readU16(buf);
884 // Initialize delete queue with all active blocks
885 core::map<v3s16, bool> abs_to_delete;
886 for(core::map<v3s16, bool>::Iterator
887 i = m_active_blocks.getIterator();
888 i.atEnd() == false; i++)
890 v3s16 p = i.getNode()->getKey();
892 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
893 <<" to abs_to_delete"
895 abs_to_delete.insert(p, true);
898 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
901 for(u16 i=0; i<blockcount; i++)
904 is.read((char*)buf, 6);
905 v3s16 p = readV3S16(buf);
906 // Get block from somewhere
907 MapBlock *block = NULL;
909 block = m_env.getMap().getBlockNoCreate(p);
911 catch(InvalidPositionException &e)
913 //TODO: Create a dummy block?
918 <<"Could not get block at blockpos "
919 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
920 <<"in TOCLIENT_OBJECTDATA. Ignoring "
921 <<"following block object data."
926 /*dstream<<"Client updating objects for block "
927 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
930 // Insert to active block list
931 m_active_blocks.insert(p, true);
933 // Remove from deletion queue
934 if(abs_to_delete.find(p) != NULL)
935 abs_to_delete.remove(p);
938 Update objects of block
940 NOTE: Be sure this is done in the main thread.
942 block->updateObjects(is, m_server_ser_ver,
943 m_device->getSceneManager(), m_env.getDayNightRatio());
946 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
949 // Delete objects of blocks in delete queue
950 for(core::map<v3s16, bool>::Iterator
951 i = abs_to_delete.getIterator();
952 i.atEnd() == false; i++)
954 v3s16 p = i.getNode()->getKey();
957 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
960 block->clearObjects();
961 // Remove from active blocks list
962 m_active_blocks.remove(p);
964 catch(InvalidPositionException &e)
966 dstream<<"WARNAING: Client: "
967 <<"Couldn't clear objects of active->inactive"
969 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
970 <<" because block was not found"
978 else if(command == TOCLIENT_TIME_OF_DAY)
983 u16 time = readU16(&data[2]);
985 m_time_of_day.set(time);
986 //dstream<<"Client: time="<<time<<std::endl;
988 // Default to queueing it (for slow commands)
991 JMutexAutoLock lock(m_incoming_queue_mutex);
993 IncomingPacket packet(data, datasize);
994 m_incoming_queue.push_back(packet);
999 Returns true if there was something in queue
1001 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1003 DSTACK(__FUNCTION_NAME);
1005 try //for catching con::PeerNotFoundException
1010 JMutexAutoLock lock(m_con_mutex);
1011 // All data is coming from the server
1012 peer = m_con.GetPeer(PEER_ID_SERVER);
1015 u8 ser_version = m_server_ser_ver;
1017 IncomingPacket packet = getPacket();
1018 u8 *data = packet.m_data;
1019 u32 datasize = packet.m_datalen;
1021 // An empty packet means queue is empty
1029 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1031 if(command == TOCLIENT_REMOVENODE)
1036 p.X = readS16(&data[2]);
1037 p.Y = readS16(&data[4]);
1038 p.Z = readS16(&data[6]);
1040 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1042 // This will clear the cracking animation after digging
1043 ((ClientMap&)m_env.getMap()).clearTempMod(p);
1045 core::map<v3s16, MapBlock*> modified_blocks;
1049 JMutexAutoLock envlock(m_env_mutex);
1050 //TimeTaker t("removeNodeAndUpdate", m_device);
1051 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1053 catch(InvalidPositionException &e)
1057 for(core::map<v3s16, MapBlock * >::Iterator
1058 i = modified_blocks.getIterator();
1059 i.atEnd() == false; i++)
1061 v3s16 p = i.getNode()->getKey();
1062 //m_env.getMap().updateMeshes(p);
1063 mesh_updater.add(p);
1066 else if(command == TOCLIENT_ADDNODE)
1068 if(datasize < 8 + MapNode::serializedLength(ser_version))
1072 p.X = readS16(&data[2]);
1073 p.Y = readS16(&data[4]);
1074 p.Z = readS16(&data[6]);
1076 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1079 n.deSerialize(&data[8], ser_version);
1081 core::map<v3s16, MapBlock*> modified_blocks;
1085 JMutexAutoLock envlock(m_env_mutex);
1086 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1088 catch(InvalidPositionException &e)
1091 for(core::map<v3s16, MapBlock * >::Iterator
1092 i = modified_blocks.getIterator();
1093 i.atEnd() == false; i++)
1095 v3s16 p = i.getNode()->getKey();
1096 //m_env.getMap().updateMeshes(p);
1097 mesh_updater.add(p);
1100 else if(command == TOCLIENT_BLOCKDATA)
1102 // Ignore too small packet
1105 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1109 p.X = readS16(&data[2]);
1110 p.Y = readS16(&data[4]);
1111 p.Z = readS16(&data[6]);
1113 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1114 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1116 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1117 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 std::string datastring((char*)&data[8], datasize-8);
1120 std::istringstream istr(datastring, std::ios_base::binary);
1126 JMutexAutoLock envlock(m_env_mutex);
1128 v2s16 p2d(p.X, p.Z);
1129 sector = m_env.getMap().emergeSector(p2d);
1131 v2s16 sp = sector->getPos();
1134 dstream<<"ERROR: Got sector with getPos()="
1135 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1136 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1140 //assert(sector->getPos() == p2d);
1143 block = sector->getBlockNoCreate(p.Y);
1145 Update an existing block
1147 //dstream<<"Updating"<<std::endl;
1148 block->deSerialize(istr, ser_version);
1149 //block->setChangedFlag();
1151 catch(InvalidPositionException &e)
1156 //dstream<<"Creating new"<<std::endl;
1157 block = new MapBlock(&m_env.getMap(), p);
1158 block->deSerialize(istr, ser_version);
1159 sector->insertBlock(block);
1160 //block->setChangedFlag();
1164 mod.type = NODEMOD_CHANGECONTENT;
1165 mod.param = CONTENT_MESE;
1166 block->setTempMod(v3s16(8,10,8), mod);
1167 block->setTempMod(v3s16(8,9,8), mod);
1168 block->setTempMod(v3s16(8,8,8), mod);
1169 block->setTempMod(v3s16(8,7,8), mod);
1170 block->setTempMod(v3s16(8,6,8), mod);*/
1174 Well, this is a dumb way to do it, they should just
1175 be drawn as separate objects.
1180 mod.type = NODEMOD_CHANGECONTENT;
1181 mod.param = CONTENT_CLOUD;
1184 for(p2.X=3; p2.X<=13; p2.X++)
1185 for(p2.Z=3; p2.Z<=13; p2.Z++)
1187 block->setTempMod(p2, mod);
1194 // Old version has zero lighting, update it.
1195 if(ser_version == 0 || ser_version == 1)
1197 derr_client<<"Client: Block in old format: "
1198 "Calculating lighting"<<std::endl;
1199 core::map<v3s16, MapBlock*> blocks_changed;
1200 blocks_changed.insert(block->getPos(), block);
1201 core::map<v3s16, MapBlock*> modified_blocks;
1202 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1206 Update Mesh of this block and blocks at x-, y- and z-
1209 //m_env.getMap().updateMeshes(block->getPos());
1210 mesh_updater.add(block->getPos());
1222 u32 replysize = 2+1+6;
1223 SharedBuffer<u8> reply(replysize);
1224 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1226 writeV3S16(&reply[3], p);
1228 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1235 JMutexAutoLock lock(m_fetchblock_mutex);
1237 if(m_fetchblock_history.find(p) != NULL)
1239 m_fetchblock_history.remove(p);
1253 u32 replysize = 2+1+6;
1254 SharedBuffer<u8> reply(replysize);
1255 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1257 writeV3S16(&reply[3], p);
1259 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1266 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1267 <<command<<std::endl;
1273 catch(con::PeerNotFoundException &e)
1275 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1276 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1281 bool Client::AsyncProcessData()
1285 // We want to update the meshes as soon as a single packet has
1287 LazyMeshUpdater mesh_updater(&m_env);
1288 bool r = AsyncProcessPacket(mesh_updater);
1294 /*LazyMeshUpdater mesh_updater(&m_env);
1297 bool r = AsyncProcessPacket(mesh_updater);
1305 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1307 JMutexAutoLock lock(m_con_mutex);
1308 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1312 void Client::fetchBlock(v3s16 p, u8 flags)
1314 if(connectedAndInitialized() == false)
1315 throw ClientNotReadyException
1316 ("ClientNotReadyException: connectedAndInitialized() == false");
1318 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1319 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1321 JMutexAutoLock conlock(m_con_mutex);
1323 SharedBuffer<u8> data(9);
1324 writeU16(&data[0], TOSERVER_GETBLOCK);
1325 writeS16(&data[2], p.X);
1326 writeS16(&data[4], p.Y);
1327 writeS16(&data[6], p.Z);
1328 writeU8(&data[8], flags);
1329 m_con.Send(PEER_ID_SERVER, 1, data, true);
1333 Calls fetchBlock() on some nearby missing blocks.
1335 Returns when any of various network load indicators go over limit.
1337 Does nearly the same thing as the old updateChangedVisibleArea()
1339 void Client::fetchBlocks()
1341 if(connectedAndInitialized() == false)
1342 throw ClientNotReadyException
1343 ("ClientNotReadyException: connectedAndInitialized() == false");
1347 bool Client::isFetchingBlocks()
1349 JMutexAutoLock conlock(m_con_mutex);
1350 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1351 // Not really fetching but can't fetch more.
1352 if(peer == NULL) return true;
1354 con::Channel *channel = &(peer->channels[1]);
1356 NOTE: Channel 0 should always be used for fetching blocks,
1357 and for nothing else.
1359 if(channel->incoming_reliables.size() > 0)
1361 if(channel->outgoing_reliables.size() > 0)
1366 IncomingPacket Client::getPacket()
1368 JMutexAutoLock lock(m_incoming_queue_mutex);
1370 core::list<IncomingPacket>::Iterator i;
1371 // Refer to first one
1372 i = m_incoming_queue.begin();
1374 // If queue is empty, return empty packet
1375 if(i == m_incoming_queue.end()){
1376 IncomingPacket packet;
1380 // Pop out first packet and return it
1381 IncomingPacket packet = *i;
1382 m_incoming_queue.erase(i);
1387 void Client::removeNode(v3s16 nodepos)
1389 if(connectedAndInitialized() == false){
1390 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1395 // Test that the position exists
1397 JMutexAutoLock envlock(m_env_mutex);
1398 m_env.getMap().getNode(nodepos);
1400 catch(InvalidPositionException &e)
1402 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1407 SharedBuffer<u8> data(8);
1408 writeU16(&data[0], TOSERVER_REMOVENODE);
1409 writeS16(&data[2], nodepos.X);
1410 writeS16(&data[4], nodepos.Y);
1411 writeS16(&data[6], nodepos.Z);
1412 Send(0, data, true);
1415 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1417 if(connectedAndInitialized() == false){
1418 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1419 "cancelled (not connected)"
1424 // Test that the position exists
1426 JMutexAutoLock envlock(m_env_mutex);
1427 m_env.getMap().getNode(nodepos);
1429 catch(InvalidPositionException &e)
1431 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1436 //u8 ser_version = m_server_ser_ver;
1438 // SUGGESTION: The validity of the operation could be checked here too
1440 u8 datasize = 2 + 6 + 2;
1441 SharedBuffer<u8> data(datasize);
1442 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1443 writeS16(&data[2], nodepos.X);
1444 writeS16(&data[4], nodepos.Y);
1445 writeS16(&data[6], nodepos.Z);
1446 writeU16(&data[8], i);
1447 Send(0, data, true);
1451 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1452 v3s16 nodepos_oversurface, u16 item)
1454 if(connectedAndInitialized() == false){
1455 dout_client<<DTIME<<"Client::groundAction() "
1456 "cancelled (not connected)"
1465 [3] v3s16 nodepos_undersurface
1466 [9] v3s16 nodepos_abovesurface
1471 2: stop digging (all parameters ignored)
1473 u8 datasize = 2 + 1 + 6 + 6 + 2;
1474 SharedBuffer<u8> data(datasize);
1475 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1476 writeU8(&data[2], action);
1477 writeV3S16(&data[3], nodepos_undersurface);
1478 writeV3S16(&data[9], nodepos_oversurface);
1479 writeU16(&data[15], item);
1480 Send(0, data, true);
1483 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1485 if(connectedAndInitialized() == false){
1486 dout_client<<DTIME<<"Client::clickObject() "
1487 "cancelled (not connected)"
1494 [2] u8 button (0=left, 1=right)
1499 u8 datasize = 2 + 1 + 6 + 2 + 2;
1500 SharedBuffer<u8> data(datasize);
1501 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1502 writeU8(&data[2], button);
1503 writeV3S16(&data[3], blockpos);
1504 writeS16(&data[9], id);
1505 writeU16(&data[11], item);
1506 Send(0, data, true);
1509 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1518 std::ostringstream os(std::ios_base::binary);
1522 writeU16(buf, TOSERVER_SIGNTEXT);
1523 os.write((char*)buf, 2);
1526 writeV3S16(buf, blockpos);
1527 os.write((char*)buf, 6);
1531 os.write((char*)buf, 2);
1533 u16 textlen = text.size();
1534 // Write text length
1535 writeS16(buf, textlen);
1536 os.write((char*)buf, 2);
1539 os.write((char*)text.c_str(), textlen);
1542 std::string s = os.str();
1543 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1545 Send(0, data, true);
1548 void Client::sendInventoryAction(InventoryAction *a)
1550 std::ostringstream os(std::ios_base::binary);
1554 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1555 os.write((char*)buf, 2);
1560 std::string s = os.str();
1561 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1563 Send(0, data, true);
1566 void Client::sendPlayerPos()
1568 JMutexAutoLock envlock(m_env_mutex);
1570 Player *myplayer = m_env.getLocalPlayer();
1571 if(myplayer == NULL)
1576 JMutexAutoLock lock(m_con_mutex);
1577 our_peer_id = m_con.GetPeerID();
1580 // Set peer id if not set already
1581 if(myplayer->peer_id == PEER_ID_NEW)
1582 myplayer->peer_id = our_peer_id;
1583 // Check that an existing peer_id is the same as the connection's
1584 assert(myplayer->peer_id == our_peer_id);
1586 v3f pf = myplayer->getPosition();
1587 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1588 v3f sf = myplayer->getSpeed();
1589 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1590 s32 pitch = myplayer->getPitch() * 100;
1591 s32 yaw = myplayer->getYaw() * 100;
1596 [2] v3s32 position*100
1597 [2+12] v3s32 speed*100
1598 [2+12+12] s32 pitch*100
1599 [2+12+12+4] s32 yaw*100
1602 SharedBuffer<u8> data(2+12+12+4+4);
1603 writeU16(&data[0], TOSERVER_PLAYERPOS);
1604 writeV3S32(&data[2], position);
1605 writeV3S32(&data[2+12], speed);
1606 writeS32(&data[2+12+12], pitch);
1607 writeS32(&data[2+12+12+4], yaw);
1609 // Send as unreliable
1610 Send(0, data, false);
1614 void Client::updateCamera(v3f pos, v3f dir)
1616 m_env.getMap().updateCamera(pos, dir);
1617 camera_position = pos;
1618 camera_direction = dir;
1621 MapNode Client::getNode(v3s16 p)
1623 JMutexAutoLock envlock(m_env_mutex);
1624 return m_env.getMap().getNode(p);
1627 /*void Client::getNode(v3s16 p, MapNode n)
1629 JMutexAutoLock envlock(m_env_mutex);
1630 m_env.getMap().setNode(p, n);
1633 /*f32 Client::getGroundHeight(v2s16 p)
1635 JMutexAutoLock envlock(m_env_mutex);
1636 return m_env.getMap().getGroundHeight(p);
1639 /*bool Client::isNodeUnderground(v3s16 p)
1641 JMutexAutoLock envlock(m_env_mutex);
1642 return m_env.getMap().isNodeUnderground(p);
1645 /*Player * Client::getLocalPlayer()
1647 JMutexAutoLock envlock(m_env_mutex);
1648 return m_env.getLocalPlayer();
1651 /*core::list<Player*> Client::getPlayers()
1653 JMutexAutoLock envlock(m_env_mutex);
1654 return m_env.getPlayers();
1657 v3f Client::getPlayerPosition()
1659 JMutexAutoLock envlock(m_env_mutex);
1660 LocalPlayer *player = m_env.getLocalPlayer();
1661 assert(player != NULL);
1662 return player->getPosition();
1665 void Client::setPlayerControl(PlayerControl &control)
1667 JMutexAutoLock envlock(m_env_mutex);
1668 LocalPlayer *player = m_env.getLocalPlayer();
1669 assert(player != NULL);
1670 player->control = control;
1673 // Returns true if the inventory of the local player has been
1674 // updated from the server. If it is true, it is set to false.
1675 bool Client::getLocalInventoryUpdated()
1677 // m_inventory_updated is behind envlock
1678 JMutexAutoLock envlock(m_env_mutex);
1679 bool updated = m_inventory_updated;
1680 m_inventory_updated = false;
1684 // Copies the inventory of the local player to parameter
1685 void Client::getLocalInventory(Inventory &dst)
1687 JMutexAutoLock envlock(m_env_mutex);
1688 Player *player = m_env.getLocalPlayer();
1689 assert(player != NULL);
1690 dst = player->inventory;
1693 MapBlockObject * Client::getSelectedObject(
1695 v3f from_pos_f_on_map,
1696 core::line3d<f32> shootline_on_map
1699 JMutexAutoLock envlock(m_env_mutex);
1701 core::array<DistanceSortedObject> objects;
1703 for(core::map<v3s16, bool>::Iterator
1704 i = m_active_blocks.getIterator();
1705 i.atEnd() == false; i++)
1707 v3s16 p = i.getNode()->getKey();
1709 MapBlock *block = NULL;
1712 block = m_env.getMap().getBlockNoCreate(p);
1714 catch(InvalidPositionException &e)
1719 // Calculate from_pos relative to block
1720 v3s16 block_pos_i_on_map = block->getPosRelative();
1721 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1722 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1724 block->getObjects(from_pos_f_on_block, max_d, objects);
1725 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1728 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1731 // After this, the closest object is the first in the array.
1734 for(u32 i=0; i<objects.size(); i++)
1736 MapBlockObject *obj = objects[i].obj;
1737 MapBlock *block = obj->getBlock();
1739 // Calculate shootline relative to block
1740 v3s16 block_pos_i_on_map = block->getPosRelative();
1741 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1742 core::line3d<f32> shootline_on_block(
1743 shootline_on_map.start - block_pos_f_on_map,
1744 shootline_on_map.end - block_pos_f_on_map
1747 if(obj->isSelected(shootline_on_block))
1749 //dstream<<"Returning selected object"<<std::endl;
1754 //dstream<<"No object selected; returning NULL."<<std::endl;
1758 void Client::printDebugInfo(std::ostream &os)
1760 //JMutexAutoLock lock1(m_fetchblock_mutex);
1761 JMutexAutoLock lock2(m_incoming_queue_mutex);
1763 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1764 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1765 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1769 /*s32 Client::getDayNightIndex()
1771 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1772 return m_daynight_i;
1775 u32 Client::getDayNightRatio()
1777 JMutexAutoLock envlock(m_env_mutex);
1778 return m_env.getDayNightRatio();
1781 /*void Client::updateSomeExpiredMeshes()
1783 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1787 JMutexAutoLock envlock(m_env_mutex);
1788 player = m_env.getLocalPlayer();
1791 u32 daynight_ratio = getDayNightRatio();
1793 v3f playerpos = player->getPosition();
1794 v3f playerspeed = player->getSpeed();
1796 v3s16 center_nodepos = floatToInt(playerpos);
1797 v3s16 center = getNodeBlockPos(center_nodepos);
1803 for(s16 d = 0; d <= d_max; d++)
1805 core::list<v3s16> list;
1806 getFacePositions(list, d);
1808 core::list<v3s16>::Iterator li;
1809 for(li=list.begin(); li!=list.end(); li++)
1811 v3s16 p = *li + center;
1812 MapBlock *block = NULL;
1815 //JMutexAutoLock envlock(m_env_mutex);
1816 block = m_env.getMap().getBlockNoCreate(p);
1818 catch(InvalidPositionException &e)
1825 if(block->getMeshExpired() == false)
1828 block->updateMesh(daynight_ratio);