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 //m_client->updateSomeExpiredMeshes();
52 bool was = m_client->AsyncProcessData();
57 #if CATCH_UNHANDLED_EXCEPTIONS
60 This is what has to be done in threads to get suitable debug info
62 catch(std::exception &e)
64 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
65 <<e.what()<<std::endl;
74 IrrlichtDevice *device,
75 const char *playername,
77 s16 &viewing_range_nodes,
78 bool &viewing_range_all):
80 m_env(new ClientMap(this,
84 device->getSceneManager()->getRootSceneNode(),
85 device->getSceneManager(), 666),
87 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
89 camera_position(0,0,0),
90 camera_direction(0,0,1),
91 m_server_ser_ver(SER_FMT_VER_INVALID),
93 m_inventory_updated(false),
96 m_packetcounter_timer = 0.0;
97 m_delete_unused_sectors_timer = 0.0;
98 m_connection_reinit_timer = 0.0;
99 m_avg_rtt_timer = 0.0;
100 m_playerpos_send_timer = 0.0;
102 //m_fetchblock_mutex.Init();
103 m_incoming_queue_mutex.Init();
106 m_step_dtime_mutex.Init();
111 JMutexAutoLock envlock(m_env_mutex);
112 //m_env.getMap().StartUpdater();
114 Player *player = new LocalPlayer();
116 player->updateName(playername);
118 /*f32 y = BS*2 + BS*20;
119 player->setPosition(v3f(0, y, 0));*/
120 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
121 m_env.addPlayer(player);
127 m_thread.setRun(false);
128 while(m_thread.IsRunning())
132 void Client::connect(Address address)
134 DSTACK(__FUNCTION_NAME);
135 JMutexAutoLock lock(m_con_mutex);
136 m_con.setTimeoutMs(0);
137 m_con.Connect(address);
140 bool Client::connectedAndInitialized()
142 JMutexAutoLock lock(m_con_mutex);
144 if(m_con.Connected() == false)
147 if(m_server_ser_ver == SER_FMT_VER_INVALID)
153 void Client::step(float dtime)
155 DSTACK(__FUNCTION_NAME);
166 s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
168 if(t == d/4 || t == (d-d/4))
170 else if(t < d/4 || t > (d-d/4))
183 if(dr != m_env.getDayNightRatio())
185 //dstream<<"dr="<<dr<<std::endl;
186 m_env.setDayNightRatio(dr);
187 m_env.expireMeshes(true);
192 //dstream<<"Client steps "<<dtime<<std::endl;
195 //TimeTaker timer("ReceiveAll()", m_device);
201 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
203 JMutexAutoLock lock(m_con_mutex);
204 m_con.RunTimeouts(dtime);
211 float &counter = m_packetcounter_timer;
217 dout_client<<"Client packetcounter (20s):"<<std::endl;
218 m_packetcounter.print(dout_client);
219 m_packetcounter.clear();
225 Delete unused sectors
227 NOTE: This jams the game for a while because deleting sectors
231 float &counter = m_delete_unused_sectors_timer;
239 JMutexAutoLock lock(m_env_mutex);
241 core::list<v3s16> deleted_blocks;
243 float delete_unused_sectors_timeout =
244 g_settings.getFloat("client_delete_unused_sectors_timeout");
246 // Delete sector blocks
247 /*u32 num = m_env.getMap().deleteUnusedSectors
248 (delete_unused_sectors_timeout,
249 true, &deleted_blocks);*/
251 // Delete whole sectors
252 u32 num = m_env.getMap().deleteUnusedSectors
253 (delete_unused_sectors_timeout,
254 false, &deleted_blocks);
258 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
259 <<" unused sectors"<<std::endl;*/
260 dstream<<DTIME<<"Client: Deleted "<<num
261 <<" unused sectors"<<std::endl;
267 // Env is locked so con can be locked.
268 JMutexAutoLock lock(m_con_mutex);
270 core::list<v3s16>::Iterator i = deleted_blocks.begin();
271 core::list<v3s16> sendlist;
274 if(sendlist.size() == 255 || i == deleted_blocks.end())
276 if(sendlist.size() == 0)
285 u32 replysize = 2+1+6*sendlist.size();
286 SharedBuffer<u8> reply(replysize);
287 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
288 reply[2] = sendlist.size();
290 for(core::list<v3s16>::Iterator
291 j = sendlist.begin();
292 j != sendlist.end(); j++)
294 writeV3S16(&reply[2+1+6*k], *j);
297 m_con.Send(PEER_ID_SERVER, 1, reply, true);
299 if(i == deleted_blocks.end())
305 sendlist.push_back(*i);
312 bool connected = connectedAndInitialized();
314 if(connected == false)
316 float &counter = m_connection_reinit_timer;
322 JMutexAutoLock envlock(m_env_mutex);
324 Player *myplayer = m_env.getLocalPlayer();
325 assert(myplayer != NULL);
327 // Send TOSERVER_INIT
328 // [0] u16 TOSERVER_INIT
329 // [2] u8 SER_FMT_VER_HIGHEST
330 // [3] u8[20] player_name
331 SharedBuffer<u8> data(2+1+20);
332 writeU16(&data[0], TOSERVER_INIT);
333 writeU8(&data[2], SER_FMT_VER_HIGHEST);
334 memcpy(&data[3], myplayer->getName(), 20);
335 // Send as unreliable
336 Send(0, data, false);
339 // Not connected, return
344 Do stuff if connected
349 JMutexAutoLock lock(m_env_mutex);
351 // Control local player (0ms)
352 LocalPlayer *player = m_env.getLocalPlayer();
353 assert(player != NULL);
354 player->applyControl(dtime);
356 //TimeTaker envtimer("env step", m_device);
360 // Step active blocks
361 for(core::map<v3s16, bool>::Iterator
362 i = m_active_blocks.getIterator();
363 i.atEnd() == false; i++)
365 v3s16 p = i.getNode()->getKey();
367 MapBlock *block = NULL;
370 block = m_env.getMap().getBlockNoCreate(p);
371 block->stepObjects(dtime, false);
373 catch(InvalidPositionException &e)
380 float &counter = m_avg_rtt_timer;
385 JMutexAutoLock lock(m_con_mutex);
386 // connectedAndInitialized() is true, peer exists.
387 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
388 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
392 float &counter = m_playerpos_send_timer;
402 JMutexAutoLock lock(m_step_dtime_mutex);
403 m_step_dtime += dtime;
407 float Client::asyncStep()
409 DSTACK(__FUNCTION_NAME);
410 //dstream<<"Client::asyncStep()"<<std::endl;
414 JMutexAutoLock lock1(m_step_dtime_mutex);
415 if(m_step_dtime < 0.001)
417 dtime = m_step_dtime;
425 // Virtual methods from con::PeerHandler
426 void Client::peerAdded(con::Peer *peer)
428 derr_client<<"Client::peerAdded(): peer->id="
429 <<peer->id<<std::endl;
431 void Client::deletingPeer(con::Peer *peer, bool timeout)
433 derr_client<<"Client::deletingPeer(): "
434 "Server Peer is getting deleted "
435 <<"(timeout="<<timeout<<")"<<std::endl;
438 void Client::ReceiveAll()
440 DSTACK(__FUNCTION_NAME);
446 catch(con::NoIncomingDataException &e)
450 catch(con::InvalidIncomingDataException &e)
452 dout_client<<DTIME<<"Client::ReceiveAll(): "
453 "InvalidIncomingDataException: what()="
454 <<e.what()<<std::endl;
461 void Client::Receive()
463 DSTACK(__FUNCTION_NAME);
464 u32 data_maxsize = 10000;
465 Buffer<u8> data(data_maxsize);
469 //TimeTaker t1("con mutex and receive", m_device);
470 JMutexAutoLock lock(m_con_mutex);
471 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
473 //TimeTaker t1("ProcessData", m_device);
474 ProcessData(*data, datasize, sender_peer_id);
478 sender_peer_id given to this shall be quaranteed to be a valid peer
480 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
482 DSTACK(__FUNCTION_NAME);
484 // Ignore packets that don't even fit a command
487 m_packetcounter.add(60000);
491 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
493 //dstream<<"Client: received command="<<command<<std::endl;
494 m_packetcounter.add((u16)command);
497 If this check is removed, be sure to change the queue
498 system to know the ids
500 if(sender_peer_id != PEER_ID_SERVER)
502 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
503 "coming from server: peer_id="<<sender_peer_id
510 JMutexAutoLock lock(m_con_mutex);
511 // All data is coming from the server
512 // PeerNotFoundException is handled by caller.
513 peer = m_con.GetPeer(PEER_ID_SERVER);
516 u8 ser_version = m_server_ser_ver;
518 //dstream<<"Client received command="<<(int)command<<std::endl;
520 // Execute fast commands straight away
522 if(command == TOCLIENT_INIT)
527 u8 deployed = data[2];
529 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
530 "deployed="<<((int)deployed&0xff)<<std::endl;
532 if(deployed < SER_FMT_VER_LOWEST
533 || deployed > SER_FMT_VER_HIGHEST)
535 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
536 <<"unsupported ser_fmt_ver"<<std::endl;
540 m_server_ser_ver = deployed;
542 // Get player position
543 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
544 if(datasize >= 2+1+6)
545 playerpos_s16 = readV3S16(&data[2+1]);
546 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
549 JMutexAutoLock envlock(m_env_mutex);
551 // Set player position
552 Player *player = m_env.getLocalPlayer();
553 assert(player != NULL);
554 player->setPosition(playerpos_f);
559 SharedBuffer<u8> reply(replysize);
560 writeU16(&reply[0], TOSERVER_INIT2);
562 m_con.Send(PEER_ID_SERVER, 1, reply, true);
567 if(ser_version == SER_FMT_VER_INVALID)
569 dout_client<<DTIME<<"WARNING: Client: Server serialization"
570 " format invalid or not initialized."
571 " Skipping incoming command="<<command<<std::endl;
575 // Just here to avoid putting the two if's together when
576 // making some copypasta
579 if(command == TOCLIENT_PLAYERPOS)
581 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
585 JMutexAutoLock lock(m_con_mutex);
586 our_peer_id = m_con.GetPeerID();
588 // Cancel if we don't have a peer id
589 if(our_peer_id == PEER_ID_NEW){
590 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
597 JMutexAutoLock envlock(m_env_mutex);
599 u32 player_size = 2+12+12+4+4;
601 u32 player_count = (datasize-2) / player_size;
603 for(u32 i=0; i<player_count; i++)
605 u16 peer_id = readU16(&data[start]);
607 Player *player = m_env.getPlayer(peer_id);
609 // Skip if player doesn't exist
612 start += player_size;
616 // Skip if player is local player
617 if(player->isLocal())
619 start += player_size;
623 v3s32 ps = readV3S32(&data[start+2]);
624 v3s32 ss = readV3S32(&data[start+2+12]);
625 s32 pitch_i = readS32(&data[start+2+12+12]);
626 s32 yaw_i = readS32(&data[start+2+12+12+4]);
627 /*dstream<<"Client: got "
628 <<"pitch_i="<<pitch_i
629 <<" yaw_i="<<yaw_i<<std::endl;*/
630 f32 pitch = (f32)pitch_i / 100.0;
631 f32 yaw = (f32)yaw_i / 100.0;
632 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
633 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
634 player->setPosition(position);
635 player->setSpeed(speed);
636 player->setPitch(pitch);
639 /*dstream<<"Client: player "<<peer_id
641 <<" yaw="<<yaw<<std::endl;*/
643 start += player_size;
647 else if(command == TOCLIENT_PLAYERINFO)
651 JMutexAutoLock lock(m_con_mutex);
652 our_peer_id = m_con.GetPeerID();
654 // Cancel if we don't have a peer id
655 if(our_peer_id == PEER_ID_NEW){
656 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
662 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
665 JMutexAutoLock envlock(m_env_mutex);
667 u32 item_size = 2+PLAYERNAME_SIZE;
668 u32 player_count = (datasize-2) / item_size;
671 core::list<u16> players_alive;
672 for(u32 i=0; i<player_count; i++)
674 // Make sure the name ends in '\0'
675 data[start+2+20-1] = 0;
677 u16 peer_id = readU16(&data[start]);
679 players_alive.push_back(peer_id);
681 /*dstream<<DTIME<<"peer_id="<<peer_id
682 <<" name="<<((char*)&data[start+2])<<std::endl;*/
684 // Don't update the info of the local player
685 if(peer_id == our_peer_id)
691 Player *player = m_env.getPlayer(peer_id);
693 // Create a player if it doesn't exist
696 player = new RemotePlayer(
697 m_device->getSceneManager()->getRootSceneNode(),
700 player->peer_id = peer_id;
701 m_env.addPlayer(player);
702 dout_client<<DTIME<<"Client: Adding new player "
703 <<peer_id<<std::endl;
706 player->updateName((char*)&data[start+2]);
712 Remove those players from the environment that
713 weren't listed by the server.
715 //dstream<<DTIME<<"Removing dead players"<<std::endl;
716 core::list<Player*> players = m_env.getPlayers();
717 core::list<Player*>::Iterator ip;
718 for(ip=players.begin(); ip!=players.end(); ip++)
720 // Ingore local player
724 // Warn about a special case
725 if((*ip)->peer_id == 0)
727 dstream<<DTIME<<"WARNING: Client: Removing "
728 "dead player with id=0"<<std::endl;
731 bool is_alive = false;
732 core::list<u16>::Iterator i;
733 for(i=players_alive.begin(); i!=players_alive.end(); i++)
735 if((*ip)->peer_id == *i)
741 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
742 <<" is_alive="<<is_alive<<std::endl;*/
745 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
747 m_env.removePlayer((*ip)->peer_id);
751 else if(command == TOCLIENT_SECTORMETA)
756 [3...] v2s16 pos + sector metadata
761 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
764 JMutexAutoLock envlock(m_env_mutex);
766 std::string datastring((char*)&data[2], datasize-2);
767 std::istringstream is(datastring, std::ios_base::binary);
771 is.read((char*)buf, 1);
772 u16 sector_count = readU8(buf);
774 //dstream<<"sector_count="<<sector_count<<std::endl;
776 for(u16 i=0; i<sector_count; i++)
779 is.read((char*)buf, 4);
780 v2s16 pos = readV2S16(buf);
781 /*dstream<<"Client: deserializing sector at "
782 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
784 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
785 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
789 else if(command == TOCLIENT_INVENTORY)
794 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
797 //TimeTaker t2("mutex locking", m_device);
798 JMutexAutoLock envlock(m_env_mutex);
801 //TimeTaker t3("istringstream init", m_device);
802 std::string datastring((char*)&data[2], datasize-2);
803 std::istringstream is(datastring, std::ios_base::binary);
806 //m_env.printPlayers(dstream);
808 //TimeTaker t4("player get", m_device);
809 Player *player = m_env.getLocalPlayer();
810 assert(player != NULL);
813 //TimeTaker t1("inventory.deSerialize()", m_device);
814 player->inventory.deSerialize(is);
817 m_inventory_updated = true;
819 //dstream<<"Client got player inventory:"<<std::endl;
820 //player->inventory.print(dstream);
824 else if(command == TOCLIENT_OBJECTDATA)
827 // Strip command word and create a stringstream
828 std::string datastring((char*)&data[2], datasize-2);
829 std::istringstream is(datastring, std::ios_base::binary);
833 JMutexAutoLock envlock(m_env_mutex);
841 is.read((char*)buf, 2);
842 u16 playercount = readU16(buf);
844 for(u16 i=0; i<playercount; i++)
846 is.read((char*)buf, 2);
847 u16 peer_id = readU16(buf);
848 is.read((char*)buf, 12);
849 v3s32 p_i = readV3S32(buf);
850 is.read((char*)buf, 12);
851 v3s32 s_i = readV3S32(buf);
852 is.read((char*)buf, 4);
853 s32 pitch_i = readS32(buf);
854 is.read((char*)buf, 4);
855 s32 yaw_i = readS32(buf);
857 Player *player = m_env.getPlayer(peer_id);
859 // Skip if player doesn't exist
865 // Skip if player is local player
866 if(player->isLocal())
871 f32 pitch = (f32)pitch_i / 100.0;
872 f32 yaw = (f32)yaw_i / 100.0;
873 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
874 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
876 player->setPosition(position);
877 player->setSpeed(speed);
878 player->setPitch(pitch);
886 // Read active block count
887 is.read((char*)buf, 2);
888 u16 blockcount = readU16(buf);
890 // Initialize delete queue with all active blocks
891 core::map<v3s16, bool> abs_to_delete;
892 for(core::map<v3s16, bool>::Iterator
893 i = m_active_blocks.getIterator();
894 i.atEnd() == false; i++)
896 v3s16 p = i.getNode()->getKey();
898 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
899 <<" to abs_to_delete"
901 abs_to_delete.insert(p, true);
904 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
907 for(u16 i=0; i<blockcount; i++)
910 is.read((char*)buf, 6);
911 v3s16 p = readV3S16(buf);
912 // Get block from somewhere
913 MapBlock *block = NULL;
915 block = m_env.getMap().getBlockNoCreate(p);
917 catch(InvalidPositionException &e)
919 //TODO: Create a dummy block?
924 <<"Could not get block at blockpos "
925 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
926 <<"in TOCLIENT_OBJECTDATA. Ignoring "
927 <<"following block object data."
932 /*dstream<<"Client updating objects for block "
933 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
936 // Insert to active block list
937 m_active_blocks.insert(p, true);
939 // Remove from deletion queue
940 if(abs_to_delete.find(p) != NULL)
941 abs_to_delete.remove(p);
944 Update objects of block
946 NOTE: Be sure this is done in the main thread.
948 block->updateObjects(is, m_server_ser_ver,
949 m_device->getSceneManager());
952 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
955 // Delete objects of blocks in delete queue
956 for(core::map<v3s16, bool>::Iterator
957 i = abs_to_delete.getIterator();
958 i.atEnd() == false; i++)
960 v3s16 p = i.getNode()->getKey();
963 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
966 block->clearObjects();
967 // Remove from active blocks list
968 m_active_blocks.remove(p);
970 catch(InvalidPositionException &e)
972 dstream<<"WARNAING: Client: "
973 <<"Couldn't clear objects of active->inactive"
975 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
976 <<" because block was not found"
984 else if(command == TOCLIENT_TIME_OF_DAY)
989 u16 time = readU16(&data[2]);
991 m_time_of_day.set(time);
992 //dstream<<"Client: time="<<time<<std::endl;
994 // Default to queueing it (for slow commands)
997 JMutexAutoLock lock(m_incoming_queue_mutex);
999 IncomingPacket packet(data, datasize);
1000 m_incoming_queue.push_back(packet);
1005 Returns true if there was something in queue
1007 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1009 DSTACK(__FUNCTION_NAME);
1011 try //for catching con::PeerNotFoundException
1016 JMutexAutoLock lock(m_con_mutex);
1017 // All data is coming from the server
1018 peer = m_con.GetPeer(PEER_ID_SERVER);
1021 u8 ser_version = m_server_ser_ver;
1023 IncomingPacket packet = getPacket();
1024 u8 *data = packet.m_data;
1025 u32 datasize = packet.m_datalen;
1027 // An empty packet means queue is empty
1035 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1037 if(command == TOCLIENT_REMOVENODE)
1042 p.X = readS16(&data[2]);
1043 p.Y = readS16(&data[4]);
1044 p.Z = readS16(&data[6]);
1046 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1048 core::map<v3s16, MapBlock*> modified_blocks;
1052 JMutexAutoLock envlock(m_env_mutex);
1053 //TimeTaker t("removeNodeAndUpdate", m_device);
1054 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1056 catch(InvalidPositionException &e)
1060 for(core::map<v3s16, MapBlock * >::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 v3s16 p = i.getNode()->getKey();
1065 //m_env.getMap().updateMeshes(p);
1066 mesh_updater.add(p);
1069 else if(command == TOCLIENT_ADDNODE)
1071 if(datasize < 8 + MapNode::serializedLength(ser_version))
1075 p.X = readS16(&data[2]);
1076 p.Y = readS16(&data[4]);
1077 p.Z = readS16(&data[6]);
1079 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1082 n.deSerialize(&data[8], ser_version);
1084 core::map<v3s16, MapBlock*> modified_blocks;
1088 JMutexAutoLock envlock(m_env_mutex);
1089 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1091 catch(InvalidPositionException &e)
1094 for(core::map<v3s16, MapBlock * >::Iterator
1095 i = modified_blocks.getIterator();
1096 i.atEnd() == false; i++)
1098 v3s16 p = i.getNode()->getKey();
1099 //m_env.getMap().updateMeshes(p);
1100 mesh_updater.add(p);
1103 else if(command == TOCLIENT_BLOCKDATA)
1105 // Ignore too small packet
1108 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1112 p.X = readS16(&data[2]);
1113 p.Y = readS16(&data[4]);
1114 p.Z = readS16(&data[6]);
1116 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1117 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1120 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1122 std::string datastring((char*)&data[8], datasize-8);
1123 std::istringstream istr(datastring, std::ios_base::binary);
1129 JMutexAutoLock envlock(m_env_mutex);
1131 v2s16 p2d(p.X, p.Z);
1132 sector = m_env.getMap().emergeSector(p2d);
1134 v2s16 sp = sector->getPos();
1137 dstream<<"ERROR: Got sector with getPos()="
1138 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1139 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1143 //assert(sector->getPos() == p2d);
1146 block = sector->getBlockNoCreate(p.Y);
1148 Update an existing block
1150 //dstream<<"Updating"<<std::endl;
1151 block->deSerialize(istr, ser_version);
1152 //block->setChangedFlag();
1154 catch(InvalidPositionException &e)
1159 //dstream<<"Creating new"<<std::endl;
1160 block = new MapBlock(&m_env.getMap(), p);
1161 block->deSerialize(istr, ser_version);
1162 sector->insertBlock(block);
1163 //block->setChangedFlag();
1167 mod.type = NODEMOD_CHANGECONTENT;
1168 mod.param = CONTENT_MESE;
1169 block->setTempMod(v3s16(8,10,8), mod);
1170 block->setTempMod(v3s16(8,9,8), mod);
1171 block->setTempMod(v3s16(8,8,8), mod);
1172 block->setTempMod(v3s16(8,7,8), mod);
1173 block->setTempMod(v3s16(8,6,8), mod);*/
1177 Well, this is a dumb way to do it, they should just
1178 be drawn as separate objects.
1183 mod.type = NODEMOD_CHANGECONTENT;
1184 mod.param = CONTENT_CLOUD;
1187 for(p2.X=3; p2.X<=13; p2.X++)
1188 for(p2.Z=3; p2.Z<=13; p2.Z++)
1190 block->setTempMod(p2, mod);
1197 // Old version has zero lighting, update it.
1198 if(ser_version == 0 || ser_version == 1)
1200 derr_client<<"Client: Block in old format: "
1201 "Calculating lighting"<<std::endl;
1202 core::map<v3s16, MapBlock*> blocks_changed;
1203 blocks_changed.insert(block->getPos(), block);
1204 core::map<v3s16, MapBlock*> modified_blocks;
1205 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1209 Update Mesh of this block and blocks at x-, y- and z-
1212 //m_env.getMap().updateMeshes(block->getPos());
1213 mesh_updater.add(block->getPos());
1225 u32 replysize = 2+1+6;
1226 SharedBuffer<u8> reply(replysize);
1227 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1229 writeV3S16(&reply[3], p);
1231 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1238 JMutexAutoLock lock(m_fetchblock_mutex);
1240 if(m_fetchblock_history.find(p) != NULL)
1242 m_fetchblock_history.remove(p);
1256 u32 replysize = 2+1+6;
1257 SharedBuffer<u8> reply(replysize);
1258 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1260 writeV3S16(&reply[3], p);
1262 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1269 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1270 <<command<<std::endl;
1276 catch(con::PeerNotFoundException &e)
1278 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1279 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1284 bool Client::AsyncProcessData()
1288 // We want to update the meshes as soon as a single packet has
1290 LazyMeshUpdater mesh_updater(&m_env);
1291 bool r = AsyncProcessPacket(mesh_updater);
1297 /*LazyMeshUpdater mesh_updater(&m_env);
1300 bool r = AsyncProcessPacket(mesh_updater);
1308 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1310 JMutexAutoLock lock(m_con_mutex);
1311 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1315 void Client::fetchBlock(v3s16 p, u8 flags)
1317 if(connectedAndInitialized() == false)
1318 throw ClientNotReadyException
1319 ("ClientNotReadyException: connectedAndInitialized() == false");
1321 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1322 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1324 JMutexAutoLock conlock(m_con_mutex);
1326 SharedBuffer<u8> data(9);
1327 writeU16(&data[0], TOSERVER_GETBLOCK);
1328 writeS16(&data[2], p.X);
1329 writeS16(&data[4], p.Y);
1330 writeS16(&data[6], p.Z);
1331 writeU8(&data[8], flags);
1332 m_con.Send(PEER_ID_SERVER, 1, data, true);
1336 Calls fetchBlock() on some nearby missing blocks.
1338 Returns when any of various network load indicators go over limit.
1340 Does nearly the same thing as the old updateChangedVisibleArea()
1342 void Client::fetchBlocks()
1344 if(connectedAndInitialized() == false)
1345 throw ClientNotReadyException
1346 ("ClientNotReadyException: connectedAndInitialized() == false");
1350 bool Client::isFetchingBlocks()
1352 JMutexAutoLock conlock(m_con_mutex);
1353 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1354 // Not really fetching but can't fetch more.
1355 if(peer == NULL) return true;
1357 con::Channel *channel = &(peer->channels[1]);
1359 NOTE: Channel 0 should always be used for fetching blocks,
1360 and for nothing else.
1362 if(channel->incoming_reliables.size() > 0)
1364 if(channel->outgoing_reliables.size() > 0)
1369 IncomingPacket Client::getPacket()
1371 JMutexAutoLock lock(m_incoming_queue_mutex);
1373 core::list<IncomingPacket>::Iterator i;
1374 // Refer to first one
1375 i = m_incoming_queue.begin();
1377 // If queue is empty, return empty packet
1378 if(i == m_incoming_queue.end()){
1379 IncomingPacket packet;
1383 // Pop out first packet and return it
1384 IncomingPacket packet = *i;
1385 m_incoming_queue.erase(i);
1390 void Client::removeNode(v3s16 nodepos)
1392 if(connectedAndInitialized() == false){
1393 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1398 // Test that the position exists
1400 JMutexAutoLock envlock(m_env_mutex);
1401 m_env.getMap().getNode(nodepos);
1403 catch(InvalidPositionException &e)
1405 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1410 SharedBuffer<u8> data(8);
1411 writeU16(&data[0], TOSERVER_REMOVENODE);
1412 writeS16(&data[2], nodepos.X);
1413 writeS16(&data[4], nodepos.Y);
1414 writeS16(&data[6], nodepos.Z);
1415 Send(0, data, true);
1418 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1420 if(connectedAndInitialized() == false){
1421 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1422 "cancelled (not connected)"
1427 // Test that the position exists
1429 JMutexAutoLock envlock(m_env_mutex);
1430 m_env.getMap().getNode(nodepos);
1432 catch(InvalidPositionException &e)
1434 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1439 //u8 ser_version = m_server_ser_ver;
1441 // SUGGESTION: The validity of the operation could be checked here too
1443 u8 datasize = 2 + 6 + 2;
1444 SharedBuffer<u8> data(datasize);
1445 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1446 writeS16(&data[2], nodepos.X);
1447 writeS16(&data[4], nodepos.Y);
1448 writeS16(&data[6], nodepos.Z);
1449 writeU16(&data[8], i);
1450 Send(0, data, true);
1454 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1455 v3s16 nodepos_oversurface, u16 item)
1457 if(connectedAndInitialized() == false){
1458 dout_client<<DTIME<<"Client::groundAction() "
1459 "cancelled (not connected)"
1468 [3] v3s16 nodepos_undersurface
1469 [9] v3s16 nodepos_abovesurface
1474 2: stop digging (all parameters ignored)
1476 u8 datasize = 2 + 1 + 6 + 6 + 2;
1477 SharedBuffer<u8> data(datasize);
1478 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1479 writeU8(&data[2], action);
1480 writeV3S16(&data[3], nodepos_undersurface);
1481 writeV3S16(&data[9], nodepos_oversurface);
1482 writeU16(&data[15], item);
1483 Send(0, data, true);
1486 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1488 if(connectedAndInitialized() == false){
1489 dout_client<<DTIME<<"Client::clickObject() "
1490 "cancelled (not connected)"
1497 [2] u8 button (0=left, 1=right)
1502 u8 datasize = 2 + 1 + 6 + 2 + 2;
1503 SharedBuffer<u8> data(datasize);
1504 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1505 writeU8(&data[2], button);
1506 writeV3S16(&data[3], blockpos);
1507 writeS16(&data[9], id);
1508 writeU16(&data[11], item);
1509 Send(0, data, true);
1512 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1521 std::ostringstream os(std::ios_base::binary);
1525 writeU16(buf, TOSERVER_SIGNTEXT);
1526 os.write((char*)buf, 2);
1529 writeV3S16(buf, blockpos);
1530 os.write((char*)buf, 6);
1534 os.write((char*)buf, 2);
1536 u16 textlen = text.size();
1537 // Write text length
1538 writeS16(buf, textlen);
1539 os.write((char*)buf, 2);
1542 os.write((char*)text.c_str(), textlen);
1545 std::string s = os.str();
1546 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1548 Send(0, data, true);
1551 void Client::sendPlayerPos()
1553 JMutexAutoLock envlock(m_env_mutex);
1555 Player *myplayer = m_env.getLocalPlayer();
1556 if(myplayer == NULL)
1561 JMutexAutoLock lock(m_con_mutex);
1562 our_peer_id = m_con.GetPeerID();
1565 // Set peer id if not set already
1566 if(myplayer->peer_id == PEER_ID_NEW)
1567 myplayer->peer_id = our_peer_id;
1568 // Check that an existing peer_id is the same as the connection's
1569 assert(myplayer->peer_id == our_peer_id);
1571 v3f pf = myplayer->getPosition();
1572 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1573 v3f sf = myplayer->getSpeed();
1574 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1575 s32 pitch = myplayer->getPitch() * 100;
1576 s32 yaw = myplayer->getYaw() * 100;
1581 [2] v3s32 position*100
1582 [2+12] v3s32 speed*100
1583 [2+12+12] s32 pitch*100
1584 [2+12+12+4] s32 yaw*100
1587 SharedBuffer<u8> data(2+12+12+4+4);
1588 writeU16(&data[0], TOSERVER_PLAYERPOS);
1589 writeV3S32(&data[2], position);
1590 writeV3S32(&data[2+12], speed);
1591 writeS32(&data[2+12+12], pitch);
1592 writeS32(&data[2+12+12+4], yaw);
1594 // Send as unreliable
1595 Send(0, data, false);
1599 void Client::updateCamera(v3f pos, v3f dir)
1601 m_env.getMap().updateCamera(pos, dir);
1602 camera_position = pos;
1603 camera_direction = dir;
1606 MapNode Client::getNode(v3s16 p)
1608 JMutexAutoLock envlock(m_env_mutex);
1609 return m_env.getMap().getNode(p);
1612 /*void Client::getNode(v3s16 p, MapNode n)
1614 JMutexAutoLock envlock(m_env_mutex);
1615 m_env.getMap().setNode(p, n);
1618 /*f32 Client::getGroundHeight(v2s16 p)
1620 JMutexAutoLock envlock(m_env_mutex);
1621 return m_env.getMap().getGroundHeight(p);
1624 /*bool Client::isNodeUnderground(v3s16 p)
1626 JMutexAutoLock envlock(m_env_mutex);
1627 return m_env.getMap().isNodeUnderground(p);
1630 /*Player * Client::getLocalPlayer()
1632 JMutexAutoLock envlock(m_env_mutex);
1633 return m_env.getLocalPlayer();
1636 /*core::list<Player*> Client::getPlayers()
1638 JMutexAutoLock envlock(m_env_mutex);
1639 return m_env.getPlayers();
1642 v3f Client::getPlayerPosition()
1644 JMutexAutoLock envlock(m_env_mutex);
1645 LocalPlayer *player = m_env.getLocalPlayer();
1646 assert(player != NULL);
1647 return player->getPosition();
1650 void Client::setPlayerControl(PlayerControl &control)
1652 JMutexAutoLock envlock(m_env_mutex);
1653 LocalPlayer *player = m_env.getLocalPlayer();
1654 assert(player != NULL);
1655 player->control = control;
1658 // Returns true if the inventory of the local player has been
1659 // updated from the server. If it is true, it is set to false.
1660 bool Client::getLocalInventoryUpdated()
1662 // m_inventory_updated is behind envlock
1663 JMutexAutoLock envlock(m_env_mutex);
1664 bool updated = m_inventory_updated;
1665 m_inventory_updated = false;
1669 // Copies the inventory of the local player to parameter
1670 void Client::getLocalInventory(Inventory &dst)
1672 JMutexAutoLock envlock(m_env_mutex);
1673 Player *player = m_env.getLocalPlayer();
1674 assert(player != NULL);
1675 dst = player->inventory;
1678 MapBlockObject * Client::getSelectedObject(
1680 v3f from_pos_f_on_map,
1681 core::line3d<f32> shootline_on_map
1684 JMutexAutoLock envlock(m_env_mutex);
1686 core::array<DistanceSortedObject> objects;
1688 for(core::map<v3s16, bool>::Iterator
1689 i = m_active_blocks.getIterator();
1690 i.atEnd() == false; i++)
1692 v3s16 p = i.getNode()->getKey();
1694 MapBlock *block = NULL;
1697 block = m_env.getMap().getBlockNoCreate(p);
1699 catch(InvalidPositionException &e)
1704 // Calculate from_pos relative to block
1705 v3s16 block_pos_i_on_map = block->getPosRelative();
1706 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1707 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1709 block->getObjects(from_pos_f_on_block, max_d, objects);
1710 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1713 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1716 // After this, the closest object is the first in the array.
1719 for(u32 i=0; i<objects.size(); i++)
1721 MapBlockObject *obj = objects[i].obj;
1722 MapBlock *block = obj->getBlock();
1724 // Calculate shootline relative to block
1725 v3s16 block_pos_i_on_map = block->getPosRelative();
1726 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1727 core::line3d<f32> shootline_on_block(
1728 shootline_on_map.start - block_pos_f_on_map,
1729 shootline_on_map.end - block_pos_f_on_map
1732 if(obj->isSelected(shootline_on_block))
1734 //dstream<<"Returning selected object"<<std::endl;
1739 //dstream<<"No object selected; returning NULL."<<std::endl;
1743 void Client::printDebugInfo(std::ostream &os)
1745 //JMutexAutoLock lock1(m_fetchblock_mutex);
1746 JMutexAutoLock lock2(m_incoming_queue_mutex);
1748 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1749 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1750 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1754 /*s32 Client::getDayNightIndex()
1756 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1757 return m_daynight_i;
1760 u32 Client::getDayNightRatio()
1762 JMutexAutoLock envlock(m_env_mutex);
1763 return m_env.getDayNightRatio();
1766 /*void Client::updateSomeExpiredMeshes()
1768 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1772 JMutexAutoLock envlock(m_env_mutex);
1773 player = m_env.getLocalPlayer();
1776 u32 daynight_ratio = getDayNightRatio();
1778 v3f playerpos = player->getPosition();
1779 v3f playerspeed = player->getSpeed();
1781 v3s16 center_nodepos = floatToInt(playerpos);
1782 v3s16 center = getNodeBlockPos(center_nodepos);
1788 for(s16 d = 0; d <= d_max; d++)
1790 core::list<v3s16> list;
1791 getFacePositions(list, d);
1793 core::list<v3s16>::Iterator li;
1794 for(li=list.begin(); li!=list.end(); li++)
1796 v3s16 p = *li + center;
1797 MapBlock *block = NULL;
1800 //JMutexAutoLock envlock(m_env_mutex);
1801 block = m_env.getMap().getBlockNoCreate(p);
1803 catch(InvalidPositionException &e)
1810 if(block->getMeshExpired() == false)
1813 block->updateMesh(daynight_ratio);