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;
403 Clear old entries from fetchblock history
406 JMutexAutoLock lock(m_fetchblock_mutex);
408 core::list<v3s16> remove_queue;
409 core::map<v3s16, float>::Iterator i;
410 i = m_fetchblock_history.getIterator();
411 for(; i.atEnd() == false; i++)
413 float value = i.getNode()->getValue();
415 i.getNode()->setValue(value);
417 remove_queue.push_back(i.getNode()->getKey());
419 core::list<v3s16>::Iterator j;
420 j = remove_queue.begin();
421 for(; j != remove_queue.end(); j++)
423 m_fetchblock_history.remove(*j);
429 JMutexAutoLock lock(m_step_dtime_mutex);
430 m_step_dtime += dtime;
442 float Client::asyncStep()
444 DSTACK(__FUNCTION_NAME);
445 //dstream<<"Client::asyncStep()"<<std::endl;
449 JMutexAutoLock lock1(m_step_dtime_mutex);
450 if(m_step_dtime < 0.001)
452 dtime = m_step_dtime;
460 // Virtual methods from con::PeerHandler
461 void Client::peerAdded(con::Peer *peer)
463 derr_client<<"Client::peerAdded(): peer->id="
464 <<peer->id<<std::endl;
466 void Client::deletingPeer(con::Peer *peer, bool timeout)
468 derr_client<<"Client::deletingPeer(): "
469 "Server Peer is getting deleted "
470 <<"(timeout="<<timeout<<")"<<std::endl;
473 void Client::ReceiveAll()
475 DSTACK(__FUNCTION_NAME);
481 catch(con::NoIncomingDataException &e)
485 catch(con::InvalidIncomingDataException &e)
487 dout_client<<DTIME<<"Client::ReceiveAll(): "
488 "InvalidIncomingDataException: what()="
489 <<e.what()<<std::endl;
496 void Client::Receive()
498 DSTACK(__FUNCTION_NAME);
499 u32 data_maxsize = 10000;
500 Buffer<u8> data(data_maxsize);
504 //TimeTaker t1("con mutex and receive", m_device);
505 JMutexAutoLock lock(m_con_mutex);
506 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
508 //TimeTaker t1("ProcessData", m_device);
509 ProcessData(*data, datasize, sender_peer_id);
513 sender_peer_id given to this shall be quaranteed to be a valid peer
515 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
517 DSTACK(__FUNCTION_NAME);
519 // Ignore packets that don't even fit a command
522 m_packetcounter.add(60000);
526 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
528 //dstream<<"Client: received command="<<command<<std::endl;
529 m_packetcounter.add((u16)command);
532 If this check is removed, be sure to change the queue
533 system to know the ids
535 if(sender_peer_id != PEER_ID_SERVER)
537 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
538 "coming from server: peer_id="<<sender_peer_id
545 JMutexAutoLock lock(m_con_mutex);
546 // All data is coming from the server
547 // PeerNotFoundException is handled by caller.
548 peer = m_con.GetPeer(PEER_ID_SERVER);
551 u8 ser_version = m_server_ser_ver;
553 //dstream<<"Client received command="<<(int)command<<std::endl;
555 // Execute fast commands straight away
557 if(command == TOCLIENT_INIT)
562 u8 deployed = data[2];
564 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
565 "deployed="<<((int)deployed&0xff)<<std::endl;
567 if(deployed < SER_FMT_VER_LOWEST
568 || deployed > SER_FMT_VER_HIGHEST)
570 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
571 <<"unsupported ser_fmt_ver"<<std::endl;
575 m_server_ser_ver = deployed;
577 // Get player position
578 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
579 if(datasize >= 2+1+6)
580 playerpos_s16 = readV3S16(&data[2+1]);
581 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
584 JMutexAutoLock envlock(m_env_mutex);
586 // Set player position
587 Player *player = m_env.getLocalPlayer();
588 assert(player != NULL);
589 player->setPosition(playerpos_f);
594 SharedBuffer<u8> reply(replysize);
595 writeU16(&reply[0], TOSERVER_INIT2);
597 m_con.Send(PEER_ID_SERVER, 1, reply, true);
602 if(ser_version == SER_FMT_VER_INVALID)
604 dout_client<<DTIME<<"WARNING: Client: Server serialization"
605 " format invalid or not initialized."
606 " Skipping incoming command="<<command<<std::endl;
610 // Just here to avoid putting the two if's together when
611 // making some copypasta
614 if(command == TOCLIENT_PLAYERPOS)
616 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
620 JMutexAutoLock lock(m_con_mutex);
621 our_peer_id = m_con.GetPeerID();
623 // Cancel if we don't have a peer id
624 if(our_peer_id == PEER_ID_NEW){
625 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
632 JMutexAutoLock envlock(m_env_mutex);
634 u32 player_size = 2+12+12+4+4;
636 u32 player_count = (datasize-2) / player_size;
638 for(u32 i=0; i<player_count; i++)
640 u16 peer_id = readU16(&data[start]);
642 Player *player = m_env.getPlayer(peer_id);
644 // Skip if player doesn't exist
647 start += player_size;
651 // Skip if player is local player
652 if(player->isLocal())
654 start += player_size;
658 v3s32 ps = readV3S32(&data[start+2]);
659 v3s32 ss = readV3S32(&data[start+2+12]);
660 s32 pitch_i = readS32(&data[start+2+12+12]);
661 s32 yaw_i = readS32(&data[start+2+12+12+4]);
662 /*dstream<<"Client: got "
663 <<"pitch_i="<<pitch_i
664 <<" yaw_i="<<yaw_i<<std::endl;*/
665 f32 pitch = (f32)pitch_i / 100.0;
666 f32 yaw = (f32)yaw_i / 100.0;
667 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
668 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
669 player->setPosition(position);
670 player->setSpeed(speed);
671 player->setPitch(pitch);
674 /*dstream<<"Client: player "<<peer_id
676 <<" yaw="<<yaw<<std::endl;*/
678 start += player_size;
682 else if(command == TOCLIENT_PLAYERINFO)
686 JMutexAutoLock lock(m_con_mutex);
687 our_peer_id = m_con.GetPeerID();
689 // Cancel if we don't have a peer id
690 if(our_peer_id == PEER_ID_NEW){
691 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
697 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
700 JMutexAutoLock envlock(m_env_mutex);
702 u32 item_size = 2+PLAYERNAME_SIZE;
703 u32 player_count = (datasize-2) / item_size;
706 core::list<u16> players_alive;
707 for(u32 i=0; i<player_count; i++)
709 // Make sure the name ends in '\0'
710 data[start+2+20-1] = 0;
712 u16 peer_id = readU16(&data[start]);
714 players_alive.push_back(peer_id);
716 /*dstream<<DTIME<<"peer_id="<<peer_id
717 <<" name="<<((char*)&data[start+2])<<std::endl;*/
719 // Don't update the info of the local player
720 if(peer_id == our_peer_id)
726 Player *player = m_env.getPlayer(peer_id);
728 // Create a player if it doesn't exist
731 player = new RemotePlayer(
732 m_device->getSceneManager()->getRootSceneNode(),
735 player->peer_id = peer_id;
736 m_env.addPlayer(player);
737 dout_client<<DTIME<<"Client: Adding new player "
738 <<peer_id<<std::endl;
741 player->updateName((char*)&data[start+2]);
747 Remove those players from the environment that
748 weren't listed by the server.
750 //dstream<<DTIME<<"Removing dead players"<<std::endl;
751 core::list<Player*> players = m_env.getPlayers();
752 core::list<Player*>::Iterator ip;
753 for(ip=players.begin(); ip!=players.end(); ip++)
755 // Ingore local player
759 // Warn about a special case
760 if((*ip)->peer_id == 0)
762 dstream<<DTIME<<"WARNING: Client: Removing "
763 "dead player with id=0"<<std::endl;
766 bool is_alive = false;
767 core::list<u16>::Iterator i;
768 for(i=players_alive.begin(); i!=players_alive.end(); i++)
770 if((*ip)->peer_id == *i)
776 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
777 <<" is_alive="<<is_alive<<std::endl;*/
780 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
782 m_env.removePlayer((*ip)->peer_id);
786 else if(command == TOCLIENT_SECTORMETA)
791 [3...] v2s16 pos + sector metadata
796 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
799 JMutexAutoLock envlock(m_env_mutex);
801 std::string datastring((char*)&data[2], datasize-2);
802 std::istringstream is(datastring, std::ios_base::binary);
806 is.read((char*)buf, 1);
807 u16 sector_count = readU8(buf);
809 //dstream<<"sector_count="<<sector_count<<std::endl;
811 for(u16 i=0; i<sector_count; i++)
814 is.read((char*)buf, 4);
815 v2s16 pos = readV2S16(buf);
816 /*dstream<<"Client: deserializing sector at "
817 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
819 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
820 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
824 else if(command == TOCLIENT_INVENTORY)
829 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
832 //TimeTaker t2("mutex locking", m_device);
833 JMutexAutoLock envlock(m_env_mutex);
836 //TimeTaker t3("istringstream init", m_device);
837 std::string datastring((char*)&data[2], datasize-2);
838 std::istringstream is(datastring, std::ios_base::binary);
841 //m_env.printPlayers(dstream);
843 //TimeTaker t4("player get", m_device);
844 Player *player = m_env.getLocalPlayer();
845 assert(player != NULL);
848 //TimeTaker t1("inventory.deSerialize()", m_device);
849 player->inventory.deSerialize(is);
852 m_inventory_updated = true;
854 //dstream<<"Client got player inventory:"<<std::endl;
855 //player->inventory.print(dstream);
859 else if(command == TOCLIENT_OBJECTDATA)
862 // Strip command word and create a stringstream
863 std::string datastring((char*)&data[2], datasize-2);
864 std::istringstream is(datastring, std::ios_base::binary);
868 JMutexAutoLock envlock(m_env_mutex);
876 is.read((char*)buf, 2);
877 u16 playercount = readU16(buf);
879 for(u16 i=0; i<playercount; i++)
881 is.read((char*)buf, 2);
882 u16 peer_id = readU16(buf);
883 is.read((char*)buf, 12);
884 v3s32 p_i = readV3S32(buf);
885 is.read((char*)buf, 12);
886 v3s32 s_i = readV3S32(buf);
887 is.read((char*)buf, 4);
888 s32 pitch_i = readS32(buf);
889 is.read((char*)buf, 4);
890 s32 yaw_i = readS32(buf);
892 Player *player = m_env.getPlayer(peer_id);
894 // Skip if player doesn't exist
900 // Skip if player is local player
901 if(player->isLocal())
906 f32 pitch = (f32)pitch_i / 100.0;
907 f32 yaw = (f32)yaw_i / 100.0;
908 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
909 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
911 player->setPosition(position);
912 player->setSpeed(speed);
913 player->setPitch(pitch);
921 // Read active block count
922 is.read((char*)buf, 2);
923 u16 blockcount = readU16(buf);
925 // Initialize delete queue with all active blocks
926 core::map<v3s16, bool> abs_to_delete;
927 for(core::map<v3s16, bool>::Iterator
928 i = m_active_blocks.getIterator();
929 i.atEnd() == false; i++)
931 v3s16 p = i.getNode()->getKey();
933 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
934 <<" to abs_to_delete"
936 abs_to_delete.insert(p, true);
939 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
942 for(u16 i=0; i<blockcount; i++)
945 is.read((char*)buf, 6);
946 v3s16 p = readV3S16(buf);
947 // Get block from somewhere
948 MapBlock *block = NULL;
950 block = m_env.getMap().getBlockNoCreate(p);
952 catch(InvalidPositionException &e)
954 //TODO: Create a dummy block?
959 <<"Could not get block at blockpos "
960 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
961 <<"in TOCLIENT_OBJECTDATA. Ignoring "
962 <<"following block object data."
967 /*dstream<<"Client updating objects for block "
968 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
971 // Insert to active block list
972 m_active_blocks.insert(p, true);
974 // Remove from deletion queue
975 if(abs_to_delete.find(p) != NULL)
976 abs_to_delete.remove(p);
978 // Update objects of block
979 block->updateObjects(is, m_server_ser_ver,
980 m_device->getSceneManager());
983 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
986 // Delete objects of blocks in delete queue
987 for(core::map<v3s16, bool>::Iterator
988 i = abs_to_delete.getIterator();
989 i.atEnd() == false; i++)
991 v3s16 p = i.getNode()->getKey();
994 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
997 block->clearObjects();
998 // Remove from active blocks list
999 m_active_blocks.remove(p);
1001 catch(InvalidPositionException &e)
1003 dstream<<"WARNAING: Client: "
1004 <<"Couldn't clear objects of active->inactive"
1006 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1007 <<" because block was not found"
1015 else if(command == TOCLIENT_TIME_OF_DAY)
1020 u16 time = readU16(&data[2]);
1021 time = time % 24000;
1022 m_time_of_day.set(time);
1023 //dstream<<"Client: time="<<time<<std::endl;
1025 // Default to queueing it (for slow commands)
1028 JMutexAutoLock lock(m_incoming_queue_mutex);
1030 IncomingPacket packet(data, datasize);
1031 m_incoming_queue.push_back(packet);
1036 Returns true if there was something in queue
1038 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1040 DSTACK(__FUNCTION_NAME);
1042 try //for catching con::PeerNotFoundException
1047 JMutexAutoLock lock(m_con_mutex);
1048 // All data is coming from the server
1049 peer = m_con.GetPeer(PEER_ID_SERVER);
1052 u8 ser_version = m_server_ser_ver;
1054 IncomingPacket packet = getPacket();
1055 u8 *data = packet.m_data;
1056 u32 datasize = packet.m_datalen;
1058 // An empty packet means queue is empty
1066 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1068 if(command == TOCLIENT_REMOVENODE)
1073 p.X = readS16(&data[2]);
1074 p.Y = readS16(&data[4]);
1075 p.Z = readS16(&data[6]);
1077 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1079 core::map<v3s16, MapBlock*> modified_blocks;
1083 JMutexAutoLock envlock(m_env_mutex);
1084 //TimeTaker t("removeNodeAndUpdate", m_device);
1085 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1087 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_ADDNODE)
1102 if(datasize < 8 + MapNode::serializedLength(ser_version))
1106 p.X = readS16(&data[2]);
1107 p.Y = readS16(&data[4]);
1108 p.Z = readS16(&data[6]);
1110 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1113 n.deSerialize(&data[8], ser_version);
1115 core::map<v3s16, MapBlock*> modified_blocks;
1119 JMutexAutoLock envlock(m_env_mutex);
1120 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1122 catch(InvalidPositionException &e)
1125 for(core::map<v3s16, MapBlock * >::Iterator
1126 i = modified_blocks.getIterator();
1127 i.atEnd() == false; i++)
1129 v3s16 p = i.getNode()->getKey();
1130 //m_env.getMap().updateMeshes(p);
1131 mesh_updater.add(p);
1134 else if(command == TOCLIENT_BLOCKDATA)
1136 // Ignore too small packet
1139 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1143 p.X = readS16(&data[2]);
1144 p.Y = readS16(&data[4]);
1145 p.Z = readS16(&data[6]);
1147 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1148 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1150 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1151 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1153 std::string datastring((char*)&data[8], datasize-8);
1154 std::istringstream istr(datastring, std::ios_base::binary);
1160 JMutexAutoLock envlock(m_env_mutex);
1162 v2s16 p2d(p.X, p.Z);
1163 sector = m_env.getMap().emergeSector(p2d);
1165 v2s16 sp = sector->getPos();
1168 dstream<<"ERROR: Got sector with getPos()="
1169 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1170 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1174 //assert(sector->getPos() == p2d);
1177 block = sector->getBlockNoCreate(p.Y);
1179 Update an existing block
1181 //dstream<<"Updating"<<std::endl;
1182 block->deSerialize(istr, ser_version);
1183 //block->setChangedFlag();
1185 catch(InvalidPositionException &e)
1190 //dstream<<"Creating new"<<std::endl;
1191 block = new MapBlock(&m_env.getMap(), p);
1192 block->deSerialize(istr, ser_version);
1193 sector->insertBlock(block);
1194 //block->setChangedFlag();
1198 mod.type = NODEMOD_CHANGECONTENT;
1199 mod.param = CONTENT_MESE;
1200 block->setTempMod(v3s16(8,10,8), mod);
1201 block->setTempMod(v3s16(8,9,8), mod);
1202 block->setTempMod(v3s16(8,8,8), mod);
1203 block->setTempMod(v3s16(8,7,8), mod);
1204 block->setTempMod(v3s16(8,6,8), mod);*/
1208 Well, this is a dumb way to do it, they should just
1209 be drawn as separate objects.
1214 mod.type = NODEMOD_CHANGECONTENT;
1215 mod.param = CONTENT_CLOUD;
1218 for(p2.X=3; p2.X<=13; p2.X++)
1219 for(p2.Z=3; p2.Z<=13; p2.Z++)
1221 block->setTempMod(p2, mod);
1228 // Old version has zero lighting, update it.
1229 if(ser_version == 0 || ser_version == 1)
1231 derr_client<<"Client: Block in old format: "
1232 "Calculating lighting"<<std::endl;
1233 core::map<v3s16, MapBlock*> blocks_changed;
1234 blocks_changed.insert(block->getPos(), block);
1235 core::map<v3s16, MapBlock*> modified_blocks;
1236 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1240 Update Mesh of this block and blocks at x-, y- and z-
1243 //m_env.getMap().updateMeshes(block->getPos());
1244 mesh_updater.add(block->getPos());
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 JMutexAutoLock lock(m_fetchblock_mutex);
1271 if(m_fetchblock_history.find(p) != NULL)
1273 m_fetchblock_history.remove(p);
1287 u32 replysize = 2+1+6;
1288 SharedBuffer<u8> reply(replysize);
1289 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1291 writeV3S16(&reply[3], p);
1293 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1300 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1301 <<command<<std::endl;
1307 catch(con::PeerNotFoundException &e)
1309 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1310 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1315 bool Client::AsyncProcessData()
1319 // We want to update the meshes as soon as a single packet has
1321 LazyMeshUpdater mesh_updater(&m_env);
1322 bool r = AsyncProcessPacket(mesh_updater);
1328 /*LazyMeshUpdater mesh_updater(&m_env);
1331 bool r = AsyncProcessPacket(mesh_updater);
1339 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1341 JMutexAutoLock lock(m_con_mutex);
1342 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1346 void Client::fetchBlock(v3s16 p, u8 flags)
1348 if(connectedAndInitialized() == false)
1349 throw ClientNotReadyException
1350 ("ClientNotReadyException: connectedAndInitialized() == false");
1352 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1353 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1355 JMutexAutoLock conlock(m_con_mutex);
1357 SharedBuffer<u8> data(9);
1358 writeU16(&data[0], TOSERVER_GETBLOCK);
1359 writeS16(&data[2], p.X);
1360 writeS16(&data[4], p.Y);
1361 writeS16(&data[6], p.Z);
1362 writeU8(&data[8], flags);
1363 m_con.Send(PEER_ID_SERVER, 1, data, true);
1367 Calls fetchBlock() on some nearby missing blocks.
1369 Returns when any of various network load indicators go over limit.
1371 Does nearly the same thing as the old updateChangedVisibleArea()
1373 void Client::fetchBlocks()
1375 if(connectedAndInitialized() == false)
1376 throw ClientNotReadyException
1377 ("ClientNotReadyException: connectedAndInitialized() == false");
1381 bool Client::isFetchingBlocks()
1383 JMutexAutoLock conlock(m_con_mutex);
1384 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1385 // Not really fetching but can't fetch more.
1386 if(peer == NULL) return true;
1388 con::Channel *channel = &(peer->channels[1]);
1390 NOTE: Channel 0 should always be used for fetching blocks,
1391 and for nothing else.
1393 if(channel->incoming_reliables.size() > 0)
1395 if(channel->outgoing_reliables.size() > 0)
1400 IncomingPacket Client::getPacket()
1402 JMutexAutoLock lock(m_incoming_queue_mutex);
1404 core::list<IncomingPacket>::Iterator i;
1405 // Refer to first one
1406 i = m_incoming_queue.begin();
1408 // If queue is empty, return empty packet
1409 if(i == m_incoming_queue.end()){
1410 IncomingPacket packet;
1414 // Pop out first packet and return it
1415 IncomingPacket packet = *i;
1416 m_incoming_queue.erase(i);
1421 void Client::removeNode(v3s16 nodepos)
1423 if(connectedAndInitialized() == false){
1424 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1429 // Test that the position exists
1431 JMutexAutoLock envlock(m_env_mutex);
1432 m_env.getMap().getNode(nodepos);
1434 catch(InvalidPositionException &e)
1436 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1441 SharedBuffer<u8> data(8);
1442 writeU16(&data[0], TOSERVER_REMOVENODE);
1443 writeS16(&data[2], nodepos.X);
1444 writeS16(&data[4], nodepos.Y);
1445 writeS16(&data[6], nodepos.Z);
1446 Send(0, data, true);
1449 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1451 if(connectedAndInitialized() == false){
1452 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1453 "cancelled (not connected)"
1458 // Test that the position exists
1460 JMutexAutoLock envlock(m_env_mutex);
1461 m_env.getMap().getNode(nodepos);
1463 catch(InvalidPositionException &e)
1465 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1470 //u8 ser_version = m_server_ser_ver;
1472 // SUGGESTION: The validity of the operation could be checked here too
1474 u8 datasize = 2 + 6 + 2;
1475 SharedBuffer<u8> data(datasize);
1476 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1477 writeS16(&data[2], nodepos.X);
1478 writeS16(&data[4], nodepos.Y);
1479 writeS16(&data[6], nodepos.Z);
1480 writeU16(&data[8], i);
1481 Send(0, data, true);
1485 void Client::pressGround(u8 button, v3s16 nodepos_undersurface,
1486 v3s16 nodepos_oversurface, u16 item)
1488 if(connectedAndInitialized() == false){
1489 dout_client<<DTIME<<"Client::pressGround() "
1490 "cancelled (not connected)"
1499 [3] v3s16 nodepos_undersurface
1500 [9] v3s16 nodepos_abovesurface
1505 2: stop digging (all parameters ignored)
1507 u8 datasize = 2 + 1 + 6 + 6 + 2;
1508 SharedBuffer<u8> data(datasize);
1509 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1510 writeU8(&data[2], button);
1511 writeV3S16(&data[3], nodepos_undersurface);
1512 writeV3S16(&data[9], nodepos_oversurface);
1513 writeU16(&data[15], item);
1514 Send(0, data, true);
1517 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1519 if(connectedAndInitialized() == false){
1520 dout_client<<DTIME<<"Client::clickObject() "
1521 "cancelled (not connected)"
1528 [2] u8 button (0=left, 1=right)
1533 u8 datasize = 2 + 1 + 6 + 2 + 2;
1534 SharedBuffer<u8> data(datasize);
1535 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1536 writeU8(&data[2], button);
1537 writeV3S16(&data[3], blockpos);
1538 writeS16(&data[9], id);
1539 writeU16(&data[11], item);
1540 Send(0, data, true);
1543 void Client::stopDigging()
1545 if(connectedAndInitialized() == false){
1546 dout_client<<DTIME<<"Client::release() "
1547 "cancelled (not connected)"
1556 [3] v3s16 nodepos_undersurface
1557 [9] v3s16 nodepos_abovesurface
1562 2: stop digging (all parameters ignored)
1564 u8 datasize = 2 + 1 + 6 + 6 + 2;
1565 SharedBuffer<u8> data(datasize);
1566 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1567 writeU8(&data[2], 2);
1568 writeV3S16(&data[3], v3s16(0,0,0));
1569 writeV3S16(&data[9], v3s16(0,0,0));
1570 writeU16(&data[15], 0);
1571 Send(0, data, true);
1574 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1583 std::ostringstream os(std::ios_base::binary);
1587 writeU16(buf, TOSERVER_SIGNTEXT);
1588 os.write((char*)buf, 2);
1591 writeV3S16(buf, blockpos);
1592 os.write((char*)buf, 6);
1596 os.write((char*)buf, 2);
1598 u16 textlen = text.size();
1599 // Write text length
1600 writeS16(buf, textlen);
1601 os.write((char*)buf, 2);
1604 os.write((char*)text.c_str(), textlen);
1607 std::string s = os.str();
1608 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1610 Send(0, data, true);
1613 void Client::sendPlayerPos()
1615 JMutexAutoLock envlock(m_env_mutex);
1617 Player *myplayer = m_env.getLocalPlayer();
1618 if(myplayer == NULL)
1623 JMutexAutoLock lock(m_con_mutex);
1624 our_peer_id = m_con.GetPeerID();
1627 // Set peer id if not set already
1628 if(myplayer->peer_id == PEER_ID_NEW)
1629 myplayer->peer_id = our_peer_id;
1630 // Check that an existing peer_id is the same as the connection's
1631 assert(myplayer->peer_id == our_peer_id);
1633 v3f pf = myplayer->getPosition();
1634 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1635 v3f sf = myplayer->getSpeed();
1636 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1637 s32 pitch = myplayer->getPitch() * 100;
1638 s32 yaw = myplayer->getYaw() * 100;
1643 [2] v3s32 position*100
1644 [2+12] v3s32 speed*100
1645 [2+12+12] s32 pitch*100
1646 [2+12+12+4] s32 yaw*100
1649 SharedBuffer<u8> data(2+12+12+4+4);
1650 writeU16(&data[0], TOSERVER_PLAYERPOS);
1651 writeV3S32(&data[2], position);
1652 writeV3S32(&data[2+12], speed);
1653 writeS32(&data[2+12+12], pitch);
1654 writeS32(&data[2+12+12+4], yaw);
1656 // Send as unreliable
1657 Send(0, data, false);
1661 void Client::updateCamera(v3f pos, v3f dir)
1663 m_env.getMap().updateCamera(pos, dir);
1664 camera_position = pos;
1665 camera_direction = dir;
1668 MapNode Client::getNode(v3s16 p)
1670 JMutexAutoLock envlock(m_env_mutex);
1671 return m_env.getMap().getNode(p);
1674 /*f32 Client::getGroundHeight(v2s16 p)
1676 JMutexAutoLock envlock(m_env_mutex);
1677 return m_env.getMap().getGroundHeight(p);
1680 bool Client::isNodeUnderground(v3s16 p)
1682 JMutexAutoLock envlock(m_env_mutex);
1683 return m_env.getMap().isNodeUnderground(p);
1686 /*Player * Client::getLocalPlayer()
1688 JMutexAutoLock envlock(m_env_mutex);
1689 return m_env.getLocalPlayer();
1692 /*core::list<Player*> Client::getPlayers()
1694 JMutexAutoLock envlock(m_env_mutex);
1695 return m_env.getPlayers();
1698 v3f Client::getPlayerPosition()
1700 JMutexAutoLock envlock(m_env_mutex);
1701 LocalPlayer *player = m_env.getLocalPlayer();
1702 assert(player != NULL);
1703 return player->getPosition();
1706 void Client::setPlayerControl(PlayerControl &control)
1708 JMutexAutoLock envlock(m_env_mutex);
1709 LocalPlayer *player = m_env.getLocalPlayer();
1710 assert(player != NULL);
1711 player->control = control;
1714 // Returns true if the inventory of the local player has been
1715 // updated from the server. If it is true, it is set to false.
1716 bool Client::getLocalInventoryUpdated()
1718 // m_inventory_updated is behind envlock
1719 JMutexAutoLock envlock(m_env_mutex);
1720 bool updated = m_inventory_updated;
1721 m_inventory_updated = false;
1725 // Copies the inventory of the local player to parameter
1726 void Client::getLocalInventory(Inventory &dst)
1728 JMutexAutoLock envlock(m_env_mutex);
1729 Player *player = m_env.getLocalPlayer();
1730 assert(player != NULL);
1731 dst = player->inventory;
1734 MapBlockObject * Client::getSelectedObject(
1736 v3f from_pos_f_on_map,
1737 core::line3d<f32> shootline_on_map
1740 JMutexAutoLock envlock(m_env_mutex);
1742 core::array<DistanceSortedObject> objects;
1744 for(core::map<v3s16, bool>::Iterator
1745 i = m_active_blocks.getIterator();
1746 i.atEnd() == false; i++)
1748 v3s16 p = i.getNode()->getKey();
1750 MapBlock *block = NULL;
1753 block = m_env.getMap().getBlockNoCreate(p);
1755 catch(InvalidPositionException &e)
1760 // Calculate from_pos relative to block
1761 v3s16 block_pos_i_on_map = block->getPosRelative();
1762 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1763 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1765 block->getObjects(from_pos_f_on_block, max_d, objects);
1766 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1769 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1772 // After this, the closest object is the first in the array.
1775 for(u32 i=0; i<objects.size(); i++)
1777 MapBlockObject *obj = objects[i].obj;
1778 MapBlock *block = obj->getBlock();
1780 // Calculate shootline relative to block
1781 v3s16 block_pos_i_on_map = block->getPosRelative();
1782 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1783 core::line3d<f32> shootline_on_block(
1784 shootline_on_map.start - block_pos_f_on_map,
1785 shootline_on_map.end - block_pos_f_on_map
1788 if(obj->isSelected(shootline_on_block))
1790 //dstream<<"Returning selected object"<<std::endl;
1795 //dstream<<"No object selected; returning NULL."<<std::endl;
1799 void Client::printDebugInfo(std::ostream &os)
1801 //JMutexAutoLock lock1(m_fetchblock_mutex);
1802 JMutexAutoLock lock2(m_incoming_queue_mutex);
1804 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1805 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1806 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1810 /*s32 Client::getDayNightIndex()
1812 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1813 return m_daynight_i;
1816 u32 Client::getDayNightRatio()
1818 JMutexAutoLock envlock(m_env_mutex);
1819 return m_env.getDayNightRatio();
1822 /*void Client::updateSomeExpiredMeshes()
1824 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1828 JMutexAutoLock envlock(m_env_mutex);
1829 player = m_env.getLocalPlayer();
1832 u32 daynight_ratio = getDayNightRatio();
1834 v3f playerpos = player->getPosition();
1835 v3f playerspeed = player->getSpeed();
1837 v3s16 center_nodepos = floatToInt(playerpos);
1838 v3s16 center = getNodeBlockPos(center_nodepos);
1844 for(s16 d = 0; d <= d_max; d++)
1846 core::list<v3s16> list;
1847 getFacePositions(list, d);
1849 core::list<v3s16>::Iterator li;
1850 for(li=list.begin(); li!=list.end(); li++)
1852 v3s16 p = *li + center;
1853 MapBlock *block = NULL;
1856 //JMutexAutoLock envlock(m_env_mutex);
1857 block = m_env.getMap().getBlockNoCreate(p);
1859 catch(InvalidPositionException &e)
1866 if(block->getMeshExpired() == false)
1869 block->updateMesh(daynight_ratio);