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),
97 //m_daynight_ratio(1000)
99 m_packetcounter_timer = 0.0;
100 m_delete_unused_sectors_timer = 0.0;
101 m_connection_reinit_timer = 0.0;
102 m_avg_rtt_timer = 0.0;
103 m_playerpos_send_timer = 0.0;
105 //m_fetchblock_mutex.Init();
106 m_incoming_queue_mutex.Init();
109 m_step_dtime_mutex.Init();
114 JMutexAutoLock envlock(m_env_mutex);
115 //m_env.getMap().StartUpdater();
117 Player *player = new LocalPlayer();
119 player->updateName(playername);
121 /*f32 y = BS*2 + BS*20;
122 player->setPosition(v3f(0, y, 0));*/
123 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
124 m_env.addPlayer(player);
130 m_thread.setRun(false);
131 while(m_thread.IsRunning())
135 void Client::connect(Address address)
137 DSTACK(__FUNCTION_NAME);
138 JMutexAutoLock lock(m_con_mutex);
139 m_con.setTimeoutMs(0);
140 m_con.Connect(address);
143 bool Client::connectedAndInitialized()
145 JMutexAutoLock lock(m_con_mutex);
147 if(m_con.Connected() == false)
150 if(m_server_ser_ver == SER_FMT_VER_INVALID)
156 void Client::step(float dtime)
158 DSTACK(__FUNCTION_NAME);
168 m_time_counter += dtime;
169 int seconds = (int)m_time_counter;
170 m_time_counter -= (float)seconds;
174 //dstream<<"m_time="<<m_time<<std::endl;
175 /*JMutexAutoLock envlock(m_env_mutex);
176 u32 dr = 500+500*sin((float)((m_time/10)%7)/7.*2.*PI);
177 if(dr != m_env.getDayNightRatio())
179 dstream<<"dr="<<dr<<std::endl;
180 m_env.setDayNightRatio(dr);
181 m_env.expireMeshes();
185 s32 t = (m_time/10)%d;
187 if(t == d/2-1 || t == d-1)
204 if(dr != m_env.getDayNightRatio())
206 dstream<<"dr="<<dr<<std::endl;
207 m_env.setDayNightRatio(dr);
208 m_env.expireMeshes(true);
214 //dstream<<"Client steps "<<dtime<<std::endl;
217 //TimeTaker timer("ReceiveAll()", m_device);
223 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
225 JMutexAutoLock lock(m_con_mutex);
226 m_con.RunTimeouts(dtime);
233 float &counter = m_packetcounter_timer;
239 dout_client<<"Client packetcounter (20s):"<<std::endl;
240 m_packetcounter.print(dout_client);
241 m_packetcounter.clear();
247 Delete unused sectors
249 NOTE: This jams the game for a while because deleting sectors
253 float &counter = m_delete_unused_sectors_timer;
261 JMutexAutoLock lock(m_env_mutex);
263 core::list<v3s16> deleted_blocks;
265 float delete_unused_sectors_timeout =
266 g_settings.getFloat("client_delete_unused_sectors_timeout");
268 // Delete sector blocks
269 /*u32 num = m_env.getMap().deleteUnusedSectors
270 (delete_unused_sectors_timeout,
271 true, &deleted_blocks);*/
273 // Delete whole sectors
274 u32 num = m_env.getMap().deleteUnusedSectors
275 (delete_unused_sectors_timeout,
276 false, &deleted_blocks);
280 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
281 <<" unused sectors"<<std::endl;*/
282 dstream<<DTIME<<"Client: Deleted "<<num
283 <<" unused sectors"<<std::endl;
289 // Env is locked so con can be locked.
290 JMutexAutoLock lock(m_con_mutex);
292 core::list<v3s16>::Iterator i = deleted_blocks.begin();
293 core::list<v3s16> sendlist;
296 if(sendlist.size() == 255 || i == deleted_blocks.end())
298 if(sendlist.size() == 0)
307 u32 replysize = 2+1+6*sendlist.size();
308 SharedBuffer<u8> reply(replysize);
309 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
310 reply[2] = sendlist.size();
312 for(core::list<v3s16>::Iterator
313 j = sendlist.begin();
314 j != sendlist.end(); j++)
316 writeV3S16(&reply[2+1+6*k], *j);
319 m_con.Send(PEER_ID_SERVER, 1, reply, true);
321 if(i == deleted_blocks.end())
327 sendlist.push_back(*i);
334 bool connected = connectedAndInitialized();
336 if(connected == false)
338 float &counter = m_connection_reinit_timer;
344 JMutexAutoLock envlock(m_env_mutex);
346 Player *myplayer = m_env.getLocalPlayer();
347 assert(myplayer != NULL);
349 // Send TOSERVER_INIT
350 // [0] u16 TOSERVER_INIT
351 // [2] u8 SER_FMT_VER_HIGHEST
352 // [3] u8[20] player_name
353 SharedBuffer<u8> data(2+1+20);
354 writeU16(&data[0], TOSERVER_INIT);
355 writeU8(&data[2], SER_FMT_VER_HIGHEST);
356 memcpy(&data[3], myplayer->getName(), 20);
357 // Send as unreliable
358 Send(0, data, false);
361 // Not connected, return
366 Do stuff if connected
371 JMutexAutoLock lock(m_env_mutex);
373 // Control local player (0ms)
374 LocalPlayer *player = m_env.getLocalPlayer();
375 assert(player != NULL);
376 player->applyControl(dtime);
378 //TimeTaker envtimer("env step", m_device);
382 // Step active blocks
383 for(core::map<v3s16, bool>::Iterator
384 i = m_active_blocks.getIterator();
385 i.atEnd() == false; i++)
387 v3s16 p = i.getNode()->getKey();
389 MapBlock *block = NULL;
392 block = m_env.getMap().getBlockNoCreate(p);
393 block->stepObjects(dtime, false);
395 catch(InvalidPositionException &e)
402 float &counter = m_avg_rtt_timer;
407 JMutexAutoLock lock(m_con_mutex);
408 // connectedAndInitialized() is true, peer exists.
409 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
410 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
414 float &counter = m_playerpos_send_timer;
425 Clear old entries from fetchblock history
428 JMutexAutoLock lock(m_fetchblock_mutex);
430 core::list<v3s16> remove_queue;
431 core::map<v3s16, float>::Iterator i;
432 i = m_fetchblock_history.getIterator();
433 for(; i.atEnd() == false; i++)
435 float value = i.getNode()->getValue();
437 i.getNode()->setValue(value);
439 remove_queue.push_back(i.getNode()->getKey());
441 core::list<v3s16>::Iterator j;
442 j = remove_queue.begin();
443 for(; j != remove_queue.end(); j++)
445 m_fetchblock_history.remove(*j);
451 JMutexAutoLock lock(m_step_dtime_mutex);
452 m_step_dtime += dtime;
464 float Client::asyncStep()
466 DSTACK(__FUNCTION_NAME);
467 //dstream<<"Client::asyncStep()"<<std::endl;
471 JMutexAutoLock lock1(m_step_dtime_mutex);
472 if(m_step_dtime < 0.001)
474 dtime = m_step_dtime;
482 // Virtual methods from con::PeerHandler
483 void Client::peerAdded(con::Peer *peer)
485 derr_client<<"Client::peerAdded(): peer->id="
486 <<peer->id<<std::endl;
488 void Client::deletingPeer(con::Peer *peer, bool timeout)
490 derr_client<<"Client::deletingPeer(): "
491 "Server Peer is getting deleted "
492 <<"(timeout="<<timeout<<")"<<std::endl;
495 void Client::ReceiveAll()
497 DSTACK(__FUNCTION_NAME);
503 catch(con::NoIncomingDataException &e)
507 catch(con::InvalidIncomingDataException &e)
509 dout_client<<DTIME<<"Client::ReceiveAll(): "
510 "InvalidIncomingDataException: what()="
511 <<e.what()<<std::endl;
518 void Client::Receive()
520 DSTACK(__FUNCTION_NAME);
521 u32 data_maxsize = 10000;
522 Buffer<u8> data(data_maxsize);
526 //TimeTaker t1("con mutex and receive", m_device);
527 JMutexAutoLock lock(m_con_mutex);
528 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
530 //TimeTaker t1("ProcessData", m_device);
531 ProcessData(*data, datasize, sender_peer_id);
535 sender_peer_id given to this shall be quaranteed to be a valid peer
537 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
539 DSTACK(__FUNCTION_NAME);
541 // Ignore packets that don't even fit a command
544 m_packetcounter.add(60000);
548 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
550 //dstream<<"Client: received command="<<command<<std::endl;
551 m_packetcounter.add((u16)command);
554 If this check is removed, be sure to change the queue
555 system to know the ids
557 if(sender_peer_id != PEER_ID_SERVER)
559 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
560 "coming from server: peer_id="<<sender_peer_id
567 JMutexAutoLock lock(m_con_mutex);
568 // All data is coming from the server
569 // PeerNotFoundException is handled by caller.
570 peer = m_con.GetPeer(PEER_ID_SERVER);
573 u8 ser_version = m_server_ser_ver;
575 //dstream<<"Client received command="<<(int)command<<std::endl;
577 // Execute fast commands straight away
579 if(command == TOCLIENT_INIT)
584 u8 deployed = data[2];
586 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
587 "deployed="<<((int)deployed&0xff)<<std::endl;
589 if(deployed < SER_FMT_VER_LOWEST
590 || deployed > SER_FMT_VER_HIGHEST)
592 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
593 <<"unsupported ser_fmt_ver"<<std::endl;
597 m_server_ser_ver = deployed;
599 // Get player position
600 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
601 if(datasize >= 2+1+6)
602 playerpos_s16 = readV3S16(&data[2+1]);
603 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
606 JMutexAutoLock envlock(m_env_mutex);
608 // Set player position
609 Player *player = m_env.getLocalPlayer();
610 assert(player != NULL);
611 player->setPosition(playerpos_f);
616 SharedBuffer<u8> reply(replysize);
617 writeU16(&reply[0], TOSERVER_INIT2);
619 m_con.Send(PEER_ID_SERVER, 1, reply, true);
624 if(ser_version == SER_FMT_VER_INVALID)
626 dout_client<<DTIME<<"WARNING: Client: Server serialization"
627 " format invalid or not initialized."
628 " Skipping incoming command="<<command<<std::endl;
632 // Just here to avoid putting the two if's together when
633 // making some copypasta
636 if(command == TOCLIENT_PLAYERPOS)
638 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
642 JMutexAutoLock lock(m_con_mutex);
643 our_peer_id = m_con.GetPeerID();
645 // Cancel if we don't have a peer id
646 if(our_peer_id == PEER_ID_NEW){
647 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
654 JMutexAutoLock envlock(m_env_mutex);
656 u32 player_size = 2+12+12+4+4;
658 u32 player_count = (datasize-2) / player_size;
660 for(u32 i=0; i<player_count; i++)
662 u16 peer_id = readU16(&data[start]);
664 Player *player = m_env.getPlayer(peer_id);
666 // Skip if player doesn't exist
669 start += player_size;
673 // Skip if player is local player
674 if(player->isLocal())
676 start += player_size;
680 v3s32 ps = readV3S32(&data[start+2]);
681 v3s32 ss = readV3S32(&data[start+2+12]);
682 s32 pitch_i = readS32(&data[start+2+12+12]);
683 s32 yaw_i = readS32(&data[start+2+12+12+4]);
684 /*dstream<<"Client: got "
685 <<"pitch_i="<<pitch_i
686 <<" yaw_i="<<yaw_i<<std::endl;*/
687 f32 pitch = (f32)pitch_i / 100.0;
688 f32 yaw = (f32)yaw_i / 100.0;
689 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
690 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
691 player->setPosition(position);
692 player->setSpeed(speed);
693 player->setPitch(pitch);
696 /*dstream<<"Client: player "<<peer_id
698 <<" yaw="<<yaw<<std::endl;*/
700 start += player_size;
704 else if(command == TOCLIENT_PLAYERINFO)
708 JMutexAutoLock lock(m_con_mutex);
709 our_peer_id = m_con.GetPeerID();
711 // Cancel if we don't have a peer id
712 if(our_peer_id == PEER_ID_NEW){
713 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
719 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
722 JMutexAutoLock envlock(m_env_mutex);
724 u32 item_size = 2+PLAYERNAME_SIZE;
725 u32 player_count = (datasize-2) / item_size;
728 core::list<u16> players_alive;
729 for(u32 i=0; i<player_count; i++)
731 // Make sure the name ends in '\0'
732 data[start+2+20-1] = 0;
734 u16 peer_id = readU16(&data[start]);
736 players_alive.push_back(peer_id);
738 /*dstream<<DTIME<<"peer_id="<<peer_id
739 <<" name="<<((char*)&data[start+2])<<std::endl;*/
741 // Don't update the info of the local player
742 if(peer_id == our_peer_id)
748 Player *player = m_env.getPlayer(peer_id);
750 // Create a player if it doesn't exist
753 player = new RemotePlayer(
754 m_device->getSceneManager()->getRootSceneNode(),
757 player->peer_id = peer_id;
758 m_env.addPlayer(player);
759 dout_client<<DTIME<<"Client: Adding new player "
760 <<peer_id<<std::endl;
763 player->updateName((char*)&data[start+2]);
769 Remove those players from the environment that
770 weren't listed by the server.
772 //dstream<<DTIME<<"Removing dead players"<<std::endl;
773 core::list<Player*> players = m_env.getPlayers();
774 core::list<Player*>::Iterator ip;
775 for(ip=players.begin(); ip!=players.end(); ip++)
777 // Ingore local player
781 // Warn about a special case
782 if((*ip)->peer_id == 0)
784 dstream<<DTIME<<"WARNING: Client: Removing "
785 "dead player with id=0"<<std::endl;
788 bool is_alive = false;
789 core::list<u16>::Iterator i;
790 for(i=players_alive.begin(); i!=players_alive.end(); i++)
792 if((*ip)->peer_id == *i)
798 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
799 <<" is_alive="<<is_alive<<std::endl;*/
802 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
804 m_env.removePlayer((*ip)->peer_id);
808 else if(command == TOCLIENT_SECTORMETA)
813 [3...] v2s16 pos + sector metadata
818 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
821 JMutexAutoLock envlock(m_env_mutex);
823 std::string datastring((char*)&data[2], datasize-2);
824 std::istringstream is(datastring, std::ios_base::binary);
828 is.read((char*)buf, 1);
829 u16 sector_count = readU8(buf);
831 //dstream<<"sector_count="<<sector_count<<std::endl;
833 for(u16 i=0; i<sector_count; i++)
836 is.read((char*)buf, 4);
837 v2s16 pos = readV2S16(buf);
838 /*dstream<<"Client: deserializing sector at "
839 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
841 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
842 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
846 else if(command == TOCLIENT_INVENTORY)
851 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
854 //TimeTaker t2("mutex locking", m_device);
855 JMutexAutoLock envlock(m_env_mutex);
858 //TimeTaker t3("istringstream init", m_device);
859 std::string datastring((char*)&data[2], datasize-2);
860 std::istringstream is(datastring, std::ios_base::binary);
863 //m_env.printPlayers(dstream);
865 //TimeTaker t4("player get", m_device);
866 Player *player = m_env.getLocalPlayer();
867 assert(player != NULL);
870 //TimeTaker t1("inventory.deSerialize()", m_device);
871 player->inventory.deSerialize(is);
874 m_inventory_updated = true;
876 //dstream<<"Client got player inventory:"<<std::endl;
877 //player->inventory.print(dstream);
881 else if(command == TOCLIENT_OBJECTDATA)
884 // Strip command word and create a stringstream
885 std::string datastring((char*)&data[2], datasize-2);
886 std::istringstream is(datastring, std::ios_base::binary);
890 JMutexAutoLock envlock(m_env_mutex);
898 is.read((char*)buf, 2);
899 u16 playercount = readU16(buf);
901 for(u16 i=0; i<playercount; i++)
903 is.read((char*)buf, 2);
904 u16 peer_id = readU16(buf);
905 is.read((char*)buf, 12);
906 v3s32 p_i = readV3S32(buf);
907 is.read((char*)buf, 12);
908 v3s32 s_i = readV3S32(buf);
909 is.read((char*)buf, 4);
910 s32 pitch_i = readS32(buf);
911 is.read((char*)buf, 4);
912 s32 yaw_i = readS32(buf);
914 Player *player = m_env.getPlayer(peer_id);
916 // Skip if player doesn't exist
922 // Skip if player is local player
923 if(player->isLocal())
928 f32 pitch = (f32)pitch_i / 100.0;
929 f32 yaw = (f32)yaw_i / 100.0;
930 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
931 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
933 player->setPosition(position);
934 player->setSpeed(speed);
935 player->setPitch(pitch);
943 // Read active block count
944 is.read((char*)buf, 2);
945 u16 blockcount = readU16(buf);
947 // Initialize delete queue with all active blocks
948 core::map<v3s16, bool> abs_to_delete;
949 for(core::map<v3s16, bool>::Iterator
950 i = m_active_blocks.getIterator();
951 i.atEnd() == false; i++)
953 v3s16 p = i.getNode()->getKey();
955 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
956 <<" to abs_to_delete"
958 abs_to_delete.insert(p, true);
961 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
964 for(u16 i=0; i<blockcount; i++)
967 is.read((char*)buf, 6);
968 v3s16 p = readV3S16(buf);
969 // Get block from somewhere
970 MapBlock *block = NULL;
972 block = m_env.getMap().getBlockNoCreate(p);
974 catch(InvalidPositionException &e)
976 //TODO: Create a dummy block?
981 <<"Could not get block at blockpos "
982 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
983 <<"in TOCLIENT_OBJECTDATA. Ignoring "
984 <<"following block object data."
989 /*dstream<<"Client updating objects for block "
990 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
993 // Insert to active block list
994 m_active_blocks.insert(p, true);
996 // Remove from deletion queue
997 if(abs_to_delete.find(p) != NULL)
998 abs_to_delete.remove(p);
1000 // Update objects of block
1001 block->updateObjects(is, m_server_ser_ver,
1002 m_device->getSceneManager());
1005 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1008 // Delete objects of blocks in delete queue
1009 for(core::map<v3s16, bool>::Iterator
1010 i = abs_to_delete.getIterator();
1011 i.atEnd() == false; i++)
1013 v3s16 p = i.getNode()->getKey();
1016 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1019 block->clearObjects();
1020 // Remove from active blocks list
1021 m_active_blocks.remove(p);
1023 catch(InvalidPositionException &e)
1025 dstream<<"WARNAING: Client: "
1026 <<"Couldn't clear objects of active->inactive"
1028 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1029 <<" because block was not found"
1037 // Default to queueing it (for slow commands)
1040 JMutexAutoLock lock(m_incoming_queue_mutex);
1042 IncomingPacket packet(data, datasize);
1043 m_incoming_queue.push_back(packet);
1048 Returns true if there was something in queue
1050 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1052 DSTACK(__FUNCTION_NAME);
1054 try //for catching con::PeerNotFoundException
1059 JMutexAutoLock lock(m_con_mutex);
1060 // All data is coming from the server
1061 peer = m_con.GetPeer(PEER_ID_SERVER);
1064 u8 ser_version = m_server_ser_ver;
1066 IncomingPacket packet = getPacket();
1067 u8 *data = packet.m_data;
1068 u32 datasize = packet.m_datalen;
1070 // An empty packet means queue is empty
1078 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1080 if(command == TOCLIENT_REMOVENODE)
1085 p.X = readS16(&data[2]);
1086 p.Y = readS16(&data[4]);
1087 p.Z = readS16(&data[6]);
1089 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1091 core::map<v3s16, MapBlock*> modified_blocks;
1095 JMutexAutoLock envlock(m_env_mutex);
1096 //TimeTaker t("removeNodeAndUpdate", m_device);
1097 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1099 catch(InvalidPositionException &e)
1103 for(core::map<v3s16, MapBlock * >::Iterator
1104 i = modified_blocks.getIterator();
1105 i.atEnd() == false; i++)
1107 v3s16 p = i.getNode()->getKey();
1108 //m_env.getMap().updateMeshes(p);
1109 mesh_updater.add(p);
1112 else if(command == TOCLIENT_ADDNODE)
1114 if(datasize < 8 + MapNode::serializedLength(ser_version))
1118 p.X = readS16(&data[2]);
1119 p.Y = readS16(&data[4]);
1120 p.Z = readS16(&data[6]);
1122 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1125 n.deSerialize(&data[8], ser_version);
1127 core::map<v3s16, MapBlock*> modified_blocks;
1131 JMutexAutoLock envlock(m_env_mutex);
1132 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1134 catch(InvalidPositionException &e)
1137 for(core::map<v3s16, MapBlock * >::Iterator
1138 i = modified_blocks.getIterator();
1139 i.atEnd() == false; i++)
1141 v3s16 p = i.getNode()->getKey();
1142 //m_env.getMap().updateMeshes(p);
1143 mesh_updater.add(p);
1146 else if(command == TOCLIENT_BLOCKDATA)
1148 // Ignore too small packet
1151 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1155 p.X = readS16(&data[2]);
1156 p.Y = readS16(&data[4]);
1157 p.Z = readS16(&data[6]);
1159 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1160 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1162 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1163 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1165 std::string datastring((char*)&data[8], datasize-8);
1166 std::istringstream istr(datastring, std::ios_base::binary);
1172 JMutexAutoLock envlock(m_env_mutex);
1174 v2s16 p2d(p.X, p.Z);
1175 sector = m_env.getMap().emergeSector(p2d);
1177 v2s16 sp = sector->getPos();
1180 dstream<<"ERROR: Got sector with getPos()="
1181 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1182 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1186 //assert(sector->getPos() == p2d);
1189 block = sector->getBlockNoCreate(p.Y);
1191 Update an existing block
1193 //dstream<<"Updating"<<std::endl;
1194 block->deSerialize(istr, ser_version);
1195 //block->setChangedFlag();
1197 catch(InvalidPositionException &e)
1202 //dstream<<"Creating new"<<std::endl;
1203 block = new MapBlock(&m_env.getMap(), p);
1204 block->deSerialize(istr, ser_version);
1205 sector->insertBlock(block);
1206 //block->setChangedFlag();
1210 mod.type = NODEMOD_CHANGECONTENT;
1211 mod.param = CONTENT_MESE;
1212 block->setTempMod(v3s16(8,10,8), mod);
1213 block->setTempMod(v3s16(8,9,8), mod);
1214 block->setTempMod(v3s16(8,8,8), mod);
1215 block->setTempMod(v3s16(8,7,8), mod);
1216 block->setTempMod(v3s16(8,6,8), mod);*/
1220 Well, this is a dumb way to do it, they should just
1221 be drawn as separate objects.
1226 mod.type = NODEMOD_CHANGECONTENT;
1227 mod.param = CONTENT_CLOUD;
1230 for(p2.X=3; p2.X<=13; p2.X++)
1231 for(p2.Z=3; p2.Z<=13; p2.Z++)
1233 block->setTempMod(p2, mod);
1240 // Old version has zero lighting, update it.
1241 if(ser_version == 0 || ser_version == 1)
1243 derr_client<<"Client: Block in old format: "
1244 "Calculating lighting"<<std::endl;
1245 core::map<v3s16, MapBlock*> blocks_changed;
1246 blocks_changed.insert(block->getPos(), block);
1247 core::map<v3s16, MapBlock*> modified_blocks;
1248 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1252 Update Mesh of this block and blocks at x-, y- and z-
1255 //m_env.getMap().updateMeshes(block->getPos());
1256 mesh_updater.add(block->getPos());
1268 u32 replysize = 2+1+6;
1269 SharedBuffer<u8> reply(replysize);
1270 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1272 writeV3S16(&reply[3], p);
1274 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1281 JMutexAutoLock lock(m_fetchblock_mutex);
1283 if(m_fetchblock_history.find(p) != NULL)
1285 m_fetchblock_history.remove(p);
1299 u32 replysize = 2+1+6;
1300 SharedBuffer<u8> reply(replysize);
1301 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1303 writeV3S16(&reply[3], p);
1305 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1312 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1313 <<command<<std::endl;
1319 catch(con::PeerNotFoundException &e)
1321 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1322 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1327 bool Client::AsyncProcessData()
1331 // We want to update the meshes as soon as a single packet has
1333 LazyMeshUpdater mesh_updater(&m_env);
1334 bool r = AsyncProcessPacket(mesh_updater);
1340 /*LazyMeshUpdater mesh_updater(&m_env);
1343 bool r = AsyncProcessPacket(mesh_updater);
1351 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1353 JMutexAutoLock lock(m_con_mutex);
1354 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1358 void Client::fetchBlock(v3s16 p, u8 flags)
1360 if(connectedAndInitialized() == false)
1361 throw ClientNotReadyException
1362 ("ClientNotReadyException: connectedAndInitialized() == false");
1364 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1365 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1367 JMutexAutoLock conlock(m_con_mutex);
1369 SharedBuffer<u8> data(9);
1370 writeU16(&data[0], TOSERVER_GETBLOCK);
1371 writeS16(&data[2], p.X);
1372 writeS16(&data[4], p.Y);
1373 writeS16(&data[6], p.Z);
1374 writeU8(&data[8], flags);
1375 m_con.Send(PEER_ID_SERVER, 1, data, true);
1379 Calls fetchBlock() on some nearby missing blocks.
1381 Returns when any of various network load indicators go over limit.
1383 Does nearly the same thing as the old updateChangedVisibleArea()
1385 void Client::fetchBlocks()
1387 if(connectedAndInitialized() == false)
1388 throw ClientNotReadyException
1389 ("ClientNotReadyException: connectedAndInitialized() == false");
1393 bool Client::isFetchingBlocks()
1395 JMutexAutoLock conlock(m_con_mutex);
1396 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1397 // Not really fetching but can't fetch more.
1398 if(peer == NULL) return true;
1400 con::Channel *channel = &(peer->channels[1]);
1402 NOTE: Channel 0 should always be used for fetching blocks,
1403 and for nothing else.
1405 if(channel->incoming_reliables.size() > 0)
1407 if(channel->outgoing_reliables.size() > 0)
1412 IncomingPacket Client::getPacket()
1414 JMutexAutoLock lock(m_incoming_queue_mutex);
1416 core::list<IncomingPacket>::Iterator i;
1417 // Refer to first one
1418 i = m_incoming_queue.begin();
1420 // If queue is empty, return empty packet
1421 if(i == m_incoming_queue.end()){
1422 IncomingPacket packet;
1426 // Pop out first packet and return it
1427 IncomingPacket packet = *i;
1428 m_incoming_queue.erase(i);
1433 void Client::removeNode(v3s16 nodepos)
1435 if(connectedAndInitialized() == false){
1436 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1441 // Test that the position exists
1443 JMutexAutoLock envlock(m_env_mutex);
1444 m_env.getMap().getNode(nodepos);
1446 catch(InvalidPositionException &e)
1448 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1453 SharedBuffer<u8> data(8);
1454 writeU16(&data[0], TOSERVER_REMOVENODE);
1455 writeS16(&data[2], nodepos.X);
1456 writeS16(&data[4], nodepos.Y);
1457 writeS16(&data[6], nodepos.Z);
1458 Send(0, data, true);
1461 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1463 if(connectedAndInitialized() == false){
1464 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1465 "cancelled (not connected)"
1470 // Test that the position exists
1472 JMutexAutoLock envlock(m_env_mutex);
1473 m_env.getMap().getNode(nodepos);
1475 catch(InvalidPositionException &e)
1477 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1482 //u8 ser_version = m_server_ser_ver;
1484 // SUGGESTION: The validity of the operation could be checked here too
1486 u8 datasize = 2 + 6 + 2;
1487 SharedBuffer<u8> data(datasize);
1488 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1489 writeS16(&data[2], nodepos.X);
1490 writeS16(&data[4], nodepos.Y);
1491 writeS16(&data[6], nodepos.Z);
1492 writeU16(&data[8], i);
1493 Send(0, data, true);
1497 void Client::pressGround(u8 button, v3s16 nodepos_undersurface,
1498 v3s16 nodepos_oversurface, u16 item)
1500 if(connectedAndInitialized() == false){
1501 dout_client<<DTIME<<"Client::pressGround() "
1502 "cancelled (not connected)"
1511 [3] v3s16 nodepos_undersurface
1512 [9] v3s16 nodepos_abovesurface
1517 2: stop digging (all parameters ignored)
1519 u8 datasize = 2 + 1 + 6 + 6 + 2;
1520 SharedBuffer<u8> data(datasize);
1521 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1522 writeU8(&data[2], button);
1523 writeV3S16(&data[3], nodepos_undersurface);
1524 writeV3S16(&data[9], nodepos_oversurface);
1525 writeU16(&data[15], item);
1526 Send(0, data, true);
1529 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1531 if(connectedAndInitialized() == false){
1532 dout_client<<DTIME<<"Client::clickObject() "
1533 "cancelled (not connected)"
1540 [2] u8 button (0=left, 1=right)
1545 u8 datasize = 2 + 1 + 6 + 2 + 2;
1546 SharedBuffer<u8> data(datasize);
1547 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1548 writeU8(&data[2], button);
1549 writeV3S16(&data[3], blockpos);
1550 writeS16(&data[9], id);
1551 writeU16(&data[11], item);
1552 Send(0, data, true);
1555 void Client::stopDigging()
1557 if(connectedAndInitialized() == false){
1558 dout_client<<DTIME<<"Client::release() "
1559 "cancelled (not connected)"
1568 [3] v3s16 nodepos_undersurface
1569 [9] v3s16 nodepos_abovesurface
1574 2: stop digging (all parameters ignored)
1576 u8 datasize = 2 + 1 + 6 + 6 + 2;
1577 SharedBuffer<u8> data(datasize);
1578 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1579 writeU8(&data[2], 2);
1580 writeV3S16(&data[3], v3s16(0,0,0));
1581 writeV3S16(&data[9], v3s16(0,0,0));
1582 writeU16(&data[15], 0);
1583 Send(0, data, true);
1586 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1595 std::ostringstream os(std::ios_base::binary);
1599 writeU16(buf, TOSERVER_SIGNTEXT);
1600 os.write((char*)buf, 2);
1603 writeV3S16(buf, blockpos);
1604 os.write((char*)buf, 6);
1608 os.write((char*)buf, 2);
1610 u16 textlen = text.size();
1611 // Write text length
1612 writeS16(buf, textlen);
1613 os.write((char*)buf, 2);
1616 os.write((char*)text.c_str(), textlen);
1619 std::string s = os.str();
1620 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1622 Send(0, data, true);
1625 void Client::sendPlayerPos()
1627 JMutexAutoLock envlock(m_env_mutex);
1629 Player *myplayer = m_env.getLocalPlayer();
1630 if(myplayer == NULL)
1635 JMutexAutoLock lock(m_con_mutex);
1636 our_peer_id = m_con.GetPeerID();
1639 // Set peer id if not set already
1640 if(myplayer->peer_id == PEER_ID_NEW)
1641 myplayer->peer_id = our_peer_id;
1642 // Check that an existing peer_id is the same as the connection's
1643 assert(myplayer->peer_id == our_peer_id);
1645 v3f pf = myplayer->getPosition();
1646 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1647 v3f sf = myplayer->getSpeed();
1648 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1649 s32 pitch = myplayer->getPitch() * 100;
1650 s32 yaw = myplayer->getYaw() * 100;
1655 [2] v3s32 position*100
1656 [2+12] v3s32 speed*100
1657 [2+12+12] s32 pitch*100
1658 [2+12+12+4] s32 yaw*100
1661 SharedBuffer<u8> data(2+12+12+4+4);
1662 writeU16(&data[0], TOSERVER_PLAYERPOS);
1663 writeV3S32(&data[2], position);
1664 writeV3S32(&data[2+12], speed);
1665 writeS32(&data[2+12+12], pitch);
1666 writeS32(&data[2+12+12+4], yaw);
1668 // Send as unreliable
1669 Send(0, data, false);
1673 void Client::updateCamera(v3f pos, v3f dir)
1675 m_env.getMap().updateCamera(pos, dir);
1676 camera_position = pos;
1677 camera_direction = dir;
1680 MapNode Client::getNode(v3s16 p)
1682 JMutexAutoLock envlock(m_env_mutex);
1683 return m_env.getMap().getNode(p);
1686 /*f32 Client::getGroundHeight(v2s16 p)
1688 JMutexAutoLock envlock(m_env_mutex);
1689 return m_env.getMap().getGroundHeight(p);
1692 bool Client::isNodeUnderground(v3s16 p)
1694 JMutexAutoLock envlock(m_env_mutex);
1695 return m_env.getMap().isNodeUnderground(p);
1698 /*Player * Client::getLocalPlayer()
1700 JMutexAutoLock envlock(m_env_mutex);
1701 return m_env.getLocalPlayer();
1704 /*core::list<Player*> Client::getPlayers()
1706 JMutexAutoLock envlock(m_env_mutex);
1707 return m_env.getPlayers();
1710 v3f Client::getPlayerPosition()
1712 JMutexAutoLock envlock(m_env_mutex);
1713 LocalPlayer *player = m_env.getLocalPlayer();
1714 assert(player != NULL);
1715 return player->getPosition();
1718 void Client::setPlayerControl(PlayerControl &control)
1720 JMutexAutoLock envlock(m_env_mutex);
1721 LocalPlayer *player = m_env.getLocalPlayer();
1722 assert(player != NULL);
1723 player->control = control;
1726 // Returns true if the inventory of the local player has been
1727 // updated from the server. If it is true, it is set to false.
1728 bool Client::getLocalInventoryUpdated()
1730 // m_inventory_updated is behind envlock
1731 JMutexAutoLock envlock(m_env_mutex);
1732 bool updated = m_inventory_updated;
1733 m_inventory_updated = false;
1737 // Copies the inventory of the local player to parameter
1738 void Client::getLocalInventory(Inventory &dst)
1740 JMutexAutoLock envlock(m_env_mutex);
1741 Player *player = m_env.getLocalPlayer();
1742 assert(player != NULL);
1743 dst = player->inventory;
1746 MapBlockObject * Client::getSelectedObject(
1748 v3f from_pos_f_on_map,
1749 core::line3d<f32> shootline_on_map
1752 JMutexAutoLock envlock(m_env_mutex);
1754 core::array<DistanceSortedObject> objects;
1756 for(core::map<v3s16, bool>::Iterator
1757 i = m_active_blocks.getIterator();
1758 i.atEnd() == false; i++)
1760 v3s16 p = i.getNode()->getKey();
1762 MapBlock *block = NULL;
1765 block = m_env.getMap().getBlockNoCreate(p);
1767 catch(InvalidPositionException &e)
1772 // Calculate from_pos relative to block
1773 v3s16 block_pos_i_on_map = block->getPosRelative();
1774 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1775 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1777 block->getObjects(from_pos_f_on_block, max_d, objects);
1778 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1781 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1784 // After this, the closest object is the first in the array.
1787 for(u32 i=0; i<objects.size(); i++)
1789 MapBlockObject *obj = objects[i].obj;
1790 MapBlock *block = obj->getBlock();
1792 // Calculate shootline relative to block
1793 v3s16 block_pos_i_on_map = block->getPosRelative();
1794 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1795 core::line3d<f32> shootline_on_block(
1796 shootline_on_map.start - block_pos_f_on_map,
1797 shootline_on_map.end - block_pos_f_on_map
1800 if(obj->isSelected(shootline_on_block))
1802 //dstream<<"Returning selected object"<<std::endl;
1807 //dstream<<"No object selected; returning NULL."<<std::endl;
1811 void Client::printDebugInfo(std::ostream &os)
1813 //JMutexAutoLock lock1(m_fetchblock_mutex);
1814 JMutexAutoLock lock2(m_incoming_queue_mutex);
1816 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1817 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1818 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1822 /*s32 Client::getDayNightIndex()
1824 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1825 return m_daynight_i;
1828 u32 Client::getDayNightRatio()
1830 JMutexAutoLock envlock(m_env_mutex);
1831 return m_env.getDayNightRatio();
1834 /*void Client::updateSomeExpiredMeshes()
1836 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1840 JMutexAutoLock envlock(m_env_mutex);
1841 player = m_env.getLocalPlayer();
1844 u32 daynight_ratio = getDayNightRatio();
1846 v3f playerpos = player->getPosition();
1847 v3f playerspeed = player->getSpeed();
1849 v3s16 center_nodepos = floatToInt(playerpos);
1850 v3s16 center = getNodeBlockPos(center_nodepos);
1856 for(s16 d = 0; d <= d_max; d++)
1858 core::list<v3s16> list;
1859 getFacePositions(list, d);
1861 core::list<v3s16>::Iterator li;
1862 for(li=list.begin(); li!=list.end(); li++)
1864 v3s16 p = *li + center;
1865 MapBlock *block = NULL;
1868 //JMutexAutoLock envlock(m_env_mutex);
1869 block = m_env.getMap().getBlockNoCreate(p);
1871 catch(InvalidPositionException &e)
1878 if(block->getMeshExpired() == false)
1881 block->updateMesh(daynight_ratio);