3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
29 void * ClientUpdateThread::Thread()
33 DSTACK(__FUNCTION_NAME);
35 #if CATCH_UNHANDLED_EXCEPTIONS
41 m_client->asyncStep();
43 //m_client->updateSomeExpiredMeshes();
45 bool was = m_client->AsyncProcessData();
50 #if CATCH_UNHANDLED_EXCEPTIONS
53 This is what has to be done in threads to get suitable debug info
55 catch(std::exception &e)
57 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
58 <<e.what()<<std::endl;
67 IrrlichtDevice *device,
68 const char *playername,
70 s16 &viewing_range_nodes,
71 bool &viewing_range_all):
73 m_env(new ClientMap(this,
77 device->getSceneManager()->getRootSceneNode(),
78 device->getSceneManager(), 666),
80 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
82 camera_position(0,0,0),
83 camera_direction(0,0,1),
84 m_server_ser_ver(SER_FMT_VER_INVALID),
86 m_inventory_updated(false),
89 m_packetcounter_timer = 0.0;
90 m_delete_unused_sectors_timer = 0.0;
91 m_connection_reinit_timer = 0.0;
92 m_avg_rtt_timer = 0.0;
93 m_playerpos_send_timer = 0.0;
95 //m_fetchblock_mutex.Init();
96 m_incoming_queue_mutex.Init();
99 m_step_dtime_mutex.Init();
104 JMutexAutoLock envlock(m_env_mutex);
105 //m_env.getMap().StartUpdater();
107 Player *player = new LocalPlayer();
109 player->updateName(playername);
111 /*f32 y = BS*2 + BS*20;
112 player->setPosition(v3f(0, y, 0));*/
113 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
114 m_env.addPlayer(player);
121 JMutexAutoLock conlock(m_con_mutex);
125 m_thread.setRun(false);
126 while(m_thread.IsRunning())
130 void Client::connect(Address address)
132 DSTACK(__FUNCTION_NAME);
133 JMutexAutoLock lock(m_con_mutex);
134 m_con.setTimeoutMs(0);
135 m_con.Connect(address);
138 bool Client::connectedAndInitialized()
140 JMutexAutoLock lock(m_con_mutex);
142 if(m_con.Connected() == false)
145 if(m_server_ser_ver == SER_FMT_VER_INVALID)
151 void Client::step(float dtime)
153 DSTACK(__FUNCTION_NAME);
164 s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
166 if(t == d/4 || t == (d-d/4))
168 else if(t < d/4 || t > (d-d/4))
181 if(dr != m_env.getDayNightRatio())
183 //dstream<<"dr="<<dr<<std::endl;
184 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
185 m_env.setDayNightRatio(dr);
186 m_env.expireMeshes(true);
191 //dstream<<"Client steps "<<dtime<<std::endl;
194 //TimeTaker timer("ReceiveAll()", m_device);
200 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
202 JMutexAutoLock lock(m_con_mutex);
203 m_con.RunTimeouts(dtime);
210 float &counter = m_packetcounter_timer;
216 dout_client<<"Client packetcounter (20s):"<<std::endl;
217 m_packetcounter.print(dout_client);
218 m_packetcounter.clear();
224 Delete unused sectors
226 NOTE: This jams the game for a while because deleting sectors
230 float &counter = m_delete_unused_sectors_timer;
238 JMutexAutoLock lock(m_env_mutex);
240 core::list<v3s16> deleted_blocks;
242 float delete_unused_sectors_timeout =
243 g_settings.getFloat("client_delete_unused_sectors_timeout");
245 // Delete sector blocks
246 /*u32 num = m_env.getMap().deleteUnusedSectors
247 (delete_unused_sectors_timeout,
248 true, &deleted_blocks);*/
250 // Delete whole sectors
251 u32 num = m_env.getMap().deleteUnusedSectors
252 (delete_unused_sectors_timeout,
253 false, &deleted_blocks);
257 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
258 <<" unused sectors"<<std::endl;*/
259 dstream<<DTIME<<"Client: Deleted "<<num
260 <<" unused sectors"<<std::endl;
266 // Env is locked so con can be locked.
267 JMutexAutoLock lock(m_con_mutex);
269 core::list<v3s16>::Iterator i = deleted_blocks.begin();
270 core::list<v3s16> sendlist;
273 if(sendlist.size() == 255 || i == deleted_blocks.end())
275 if(sendlist.size() == 0)
284 u32 replysize = 2+1+6*sendlist.size();
285 SharedBuffer<u8> reply(replysize);
286 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
287 reply[2] = sendlist.size();
289 for(core::list<v3s16>::Iterator
290 j = sendlist.begin();
291 j != sendlist.end(); j++)
293 writeV3S16(&reply[2+1+6*k], *j);
296 m_con.Send(PEER_ID_SERVER, 1, reply, true);
298 if(i == deleted_blocks.end())
304 sendlist.push_back(*i);
311 bool connected = connectedAndInitialized();
313 if(connected == false)
315 float &counter = m_connection_reinit_timer;
321 JMutexAutoLock envlock(m_env_mutex);
323 Player *myplayer = m_env.getLocalPlayer();
324 assert(myplayer != NULL);
326 // Send TOSERVER_INIT
327 // [0] u16 TOSERVER_INIT
328 // [2] u8 SER_FMT_VER_HIGHEST
329 // [3] u8[20] player_name
330 SharedBuffer<u8> data(2+1+20);
331 writeU16(&data[0], TOSERVER_INIT);
332 writeU8(&data[2], SER_FMT_VER_HIGHEST);
333 memcpy(&data[3], myplayer->getName(), 20);
334 // Send as unreliable
335 Send(0, data, false);
338 // Not connected, return
343 Do stuff if connected
348 JMutexAutoLock lock(m_env_mutex);
350 // Control local player (0ms)
351 LocalPlayer *player = m_env.getLocalPlayer();
352 assert(player != NULL);
353 player->applyControl(dtime);
355 //TimeTaker envtimer("env step", m_device);
359 // Step active blocks
360 for(core::map<v3s16, bool>::Iterator
361 i = m_active_blocks.getIterator();
362 i.atEnd() == false; i++)
364 v3s16 p = i.getNode()->getKey();
366 MapBlock *block = NULL;
369 block = m_env.getMap().getBlockNoCreate(p);
370 block->stepObjects(dtime, false, m_env.getDayNightRatio());
372 catch(InvalidPositionException &e)
379 float &counter = m_avg_rtt_timer;
384 JMutexAutoLock lock(m_con_mutex);
385 // connectedAndInitialized() is true, peer exists.
386 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
387 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
391 float &counter = m_playerpos_send_timer;
401 JMutexAutoLock lock(m_step_dtime_mutex);
402 m_step_dtime += dtime;
406 float Client::asyncStep()
408 DSTACK(__FUNCTION_NAME);
409 //dstream<<"Client::asyncStep()"<<std::endl;
413 JMutexAutoLock lock1(m_step_dtime_mutex);
414 if(m_step_dtime < 0.001)
416 dtime = m_step_dtime;
424 // Virtual methods from con::PeerHandler
425 void Client::peerAdded(con::Peer *peer)
427 derr_client<<"Client::peerAdded(): peer->id="
428 <<peer->id<<std::endl;
430 void Client::deletingPeer(con::Peer *peer, bool timeout)
432 derr_client<<"Client::deletingPeer(): "
433 "Server Peer is getting deleted "
434 <<"(timeout="<<timeout<<")"<<std::endl;
437 void Client::ReceiveAll()
439 DSTACK(__FUNCTION_NAME);
445 catch(con::NoIncomingDataException &e)
449 catch(con::InvalidIncomingDataException &e)
451 dout_client<<DTIME<<"Client::ReceiveAll(): "
452 "InvalidIncomingDataException: what()="
453 <<e.what()<<std::endl;
458 void Client::Receive()
460 DSTACK(__FUNCTION_NAME);
461 u32 data_maxsize = 10000;
462 Buffer<u8> data(data_maxsize);
466 //TimeTaker t1("con mutex and receive", m_device);
467 JMutexAutoLock lock(m_con_mutex);
468 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
470 //TimeTaker t1("ProcessData", m_device);
471 ProcessData(*data, datasize, sender_peer_id);
475 sender_peer_id given to this shall be quaranteed to be a valid peer
477 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
479 DSTACK(__FUNCTION_NAME);
481 // Ignore packets that don't even fit a command
484 m_packetcounter.add(60000);
488 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
490 //dstream<<"Client: received command="<<command<<std::endl;
491 m_packetcounter.add((u16)command);
494 If this check is removed, be sure to change the queue
495 system to know the ids
497 if(sender_peer_id != PEER_ID_SERVER)
499 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
500 "coming from server: peer_id="<<sender_peer_id
507 JMutexAutoLock lock(m_con_mutex);
508 // All data is coming from the server
509 // PeerNotFoundException is handled by caller.
510 peer = m_con.GetPeer(PEER_ID_SERVER);
513 u8 ser_version = m_server_ser_ver;
515 //dstream<<"Client received command="<<(int)command<<std::endl;
517 // Execute fast commands straight away
519 if(command == TOCLIENT_INIT)
524 u8 deployed = data[2];
526 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
527 "deployed="<<((int)deployed&0xff)<<std::endl;
529 if(deployed < SER_FMT_VER_LOWEST
530 || deployed > SER_FMT_VER_HIGHEST)
532 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
533 <<"unsupported ser_fmt_ver"<<std::endl;
537 m_server_ser_ver = deployed;
539 // Get player position
540 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
541 if(datasize >= 2+1+6)
542 playerpos_s16 = readV3S16(&data[2+1]);
543 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
546 JMutexAutoLock envlock(m_env_mutex);
548 // Set player position
549 Player *player = m_env.getLocalPlayer();
550 assert(player != NULL);
551 player->setPosition(playerpos_f);
556 SharedBuffer<u8> reply(replysize);
557 writeU16(&reply[0], TOSERVER_INIT2);
559 m_con.Send(PEER_ID_SERVER, 1, reply, true);
564 if(ser_version == SER_FMT_VER_INVALID)
566 dout_client<<DTIME<<"WARNING: Client: Server serialization"
567 " format invalid or not initialized."
568 " Skipping incoming command="<<command<<std::endl;
572 // Just here to avoid putting the two if's together when
573 // making some copypasta
576 if(command == TOCLIENT_REMOVENODE)
581 p.X = readS16(&data[2]);
582 p.Y = readS16(&data[4]);
583 p.Z = readS16(&data[6]);
585 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
587 // This will clear the cracking animation after digging
588 ((ClientMap&)m_env.getMap()).clearTempMod(p);
592 else if(command == TOCLIENT_ADDNODE)
594 if(datasize < 8 + MapNode::serializedLength(ser_version))
598 p.X = readS16(&data[2]);
599 p.Y = readS16(&data[4]);
600 p.Z = readS16(&data[6]);
602 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
605 n.deSerialize(&data[8], ser_version);
609 else if(command == TOCLIENT_PLAYERPOS)
611 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
615 JMutexAutoLock lock(m_con_mutex);
616 our_peer_id = m_con.GetPeerID();
618 // Cancel if we don't have a peer id
619 if(our_peer_id == PEER_ID_NEW){
620 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
627 JMutexAutoLock envlock(m_env_mutex);
629 u32 player_size = 2+12+12+4+4;
631 u32 player_count = (datasize-2) / player_size;
633 for(u32 i=0; i<player_count; i++)
635 u16 peer_id = readU16(&data[start]);
637 Player *player = m_env.getPlayer(peer_id);
639 // Skip if player doesn't exist
642 start += player_size;
646 // Skip if player is local player
647 if(player->isLocal())
649 start += player_size;
653 v3s32 ps = readV3S32(&data[start+2]);
654 v3s32 ss = readV3S32(&data[start+2+12]);
655 s32 pitch_i = readS32(&data[start+2+12+12]);
656 s32 yaw_i = readS32(&data[start+2+12+12+4]);
657 /*dstream<<"Client: got "
658 <<"pitch_i="<<pitch_i
659 <<" yaw_i="<<yaw_i<<std::endl;*/
660 f32 pitch = (f32)pitch_i / 100.0;
661 f32 yaw = (f32)yaw_i / 100.0;
662 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
663 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
664 player->setPosition(position);
665 player->setSpeed(speed);
666 player->setPitch(pitch);
669 /*dstream<<"Client: player "<<peer_id
671 <<" yaw="<<yaw<<std::endl;*/
673 start += player_size;
677 else if(command == TOCLIENT_PLAYERINFO)
681 JMutexAutoLock lock(m_con_mutex);
682 our_peer_id = m_con.GetPeerID();
684 // Cancel if we don't have a peer id
685 if(our_peer_id == PEER_ID_NEW){
686 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
692 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
695 JMutexAutoLock envlock(m_env_mutex);
697 u32 item_size = 2+PLAYERNAME_SIZE;
698 u32 player_count = (datasize-2) / item_size;
701 core::list<u16> players_alive;
702 for(u32 i=0; i<player_count; i++)
704 // Make sure the name ends in '\0'
705 data[start+2+20-1] = 0;
707 u16 peer_id = readU16(&data[start]);
709 players_alive.push_back(peer_id);
711 /*dstream<<DTIME<<"peer_id="<<peer_id
712 <<" name="<<((char*)&data[start+2])<<std::endl;*/
714 // Don't update the info of the local player
715 if(peer_id == our_peer_id)
721 Player *player = m_env.getPlayer(peer_id);
723 // Create a player if it doesn't exist
726 player = new RemotePlayer(
727 m_device->getSceneManager()->getRootSceneNode(),
730 player->peer_id = peer_id;
731 m_env.addPlayer(player);
732 dout_client<<DTIME<<"Client: Adding new player "
733 <<peer_id<<std::endl;
736 player->updateName((char*)&data[start+2]);
742 Remove those players from the environment that
743 weren't listed by the server.
745 //dstream<<DTIME<<"Removing dead players"<<std::endl;
746 core::list<Player*> players = m_env.getPlayers();
747 core::list<Player*>::Iterator ip;
748 for(ip=players.begin(); ip!=players.end(); ip++)
750 // Ingore local player
754 // Warn about a special case
755 if((*ip)->peer_id == 0)
757 dstream<<DTIME<<"WARNING: Client: Removing "
758 "dead player with id=0"<<std::endl;
761 bool is_alive = false;
762 core::list<u16>::Iterator i;
763 for(i=players_alive.begin(); i!=players_alive.end(); i++)
765 if((*ip)->peer_id == *i)
771 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
772 <<" is_alive="<<is_alive<<std::endl;*/
775 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
777 m_env.removePlayer((*ip)->peer_id);
781 else if(command == TOCLIENT_SECTORMETA)
786 [3...] v2s16 pos + sector metadata
791 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
794 JMutexAutoLock envlock(m_env_mutex);
796 std::string datastring((char*)&data[2], datasize-2);
797 std::istringstream is(datastring, std::ios_base::binary);
801 is.read((char*)buf, 1);
802 u16 sector_count = readU8(buf);
804 //dstream<<"sector_count="<<sector_count<<std::endl;
806 for(u16 i=0; i<sector_count; i++)
809 is.read((char*)buf, 4);
810 v2s16 pos = readV2S16(buf);
811 /*dstream<<"Client: deserializing sector at "
812 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
814 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
815 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
819 else if(command == TOCLIENT_INVENTORY)
824 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
827 //TimeTaker t2("mutex locking", m_device);
828 JMutexAutoLock envlock(m_env_mutex);
831 //TimeTaker t3("istringstream init", m_device);
832 std::string datastring((char*)&data[2], datasize-2);
833 std::istringstream is(datastring, std::ios_base::binary);
836 //m_env.printPlayers(dstream);
838 //TimeTaker t4("player get", m_device);
839 Player *player = m_env.getLocalPlayer();
840 assert(player != NULL);
843 //TimeTaker t1("inventory.deSerialize()", m_device);
844 player->inventory.deSerialize(is);
847 m_inventory_updated = true;
849 //dstream<<"Client got player inventory:"<<std::endl;
850 //player->inventory.print(dstream);
854 else if(command == TOCLIENT_OBJECTDATA)
857 // Strip command word and create a stringstream
858 std::string datastring((char*)&data[2], datasize-2);
859 std::istringstream is(datastring, std::ios_base::binary);
863 JMutexAutoLock envlock(m_env_mutex);
871 is.read((char*)buf, 2);
872 u16 playercount = readU16(buf);
874 for(u16 i=0; i<playercount; i++)
876 is.read((char*)buf, 2);
877 u16 peer_id = readU16(buf);
878 is.read((char*)buf, 12);
879 v3s32 p_i = readV3S32(buf);
880 is.read((char*)buf, 12);
881 v3s32 s_i = readV3S32(buf);
882 is.read((char*)buf, 4);
883 s32 pitch_i = readS32(buf);
884 is.read((char*)buf, 4);
885 s32 yaw_i = readS32(buf);
887 Player *player = m_env.getPlayer(peer_id);
889 // Skip if player doesn't exist
895 // Skip if player is local player
896 if(player->isLocal())
901 f32 pitch = (f32)pitch_i / 100.0;
902 f32 yaw = (f32)yaw_i / 100.0;
903 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
904 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
906 player->setPosition(position);
907 player->setSpeed(speed);
908 player->setPitch(pitch);
916 // Read active block count
917 is.read((char*)buf, 2);
918 u16 blockcount = readU16(buf);
920 // Initialize delete queue with all active blocks
921 core::map<v3s16, bool> abs_to_delete;
922 for(core::map<v3s16, bool>::Iterator
923 i = m_active_blocks.getIterator();
924 i.atEnd() == false; i++)
926 v3s16 p = i.getNode()->getKey();
928 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
929 <<" to abs_to_delete"
931 abs_to_delete.insert(p, true);
934 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
937 for(u16 i=0; i<blockcount; i++)
940 is.read((char*)buf, 6);
941 v3s16 p = readV3S16(buf);
942 // Get block from somewhere
943 MapBlock *block = NULL;
945 block = m_env.getMap().getBlockNoCreate(p);
947 catch(InvalidPositionException &e)
949 //TODO: Create a dummy block?
954 <<"Could not get block at blockpos "
955 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
956 <<"in TOCLIENT_OBJECTDATA. Ignoring "
957 <<"following block object data."
962 /*dstream<<"Client updating objects for block "
963 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
966 // Insert to active block list
967 m_active_blocks.insert(p, true);
969 // Remove from deletion queue
970 if(abs_to_delete.find(p) != NULL)
971 abs_to_delete.remove(p);
974 Update objects of block
976 NOTE: Be sure this is done in the main thread.
978 block->updateObjects(is, m_server_ser_ver,
979 m_device->getSceneManager(), m_env.getDayNightRatio());
982 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
985 // Delete objects of blocks in delete queue
986 for(core::map<v3s16, bool>::Iterator
987 i = abs_to_delete.getIterator();
988 i.atEnd() == false; i++)
990 v3s16 p = i.getNode()->getKey();
993 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
996 block->clearObjects();
997 // Remove from active blocks list
998 m_active_blocks.remove(p);
1000 catch(InvalidPositionException &e)
1002 dstream<<"WARNAING: Client: "
1003 <<"Couldn't clear objects of active->inactive"
1005 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1006 <<" because block was not found"
1014 else if(command == TOCLIENT_TIME_OF_DAY)
1019 u16 time = readU16(&data[2]);
1020 time = time % 24000;
1021 m_time_of_day.set(time);
1022 //dstream<<"Client: time="<<time<<std::endl;
1024 else if(command == TOCLIENT_CHAT_MESSAGE)
1032 std::string datastring((char*)&data[2], datasize-2);
1033 std::istringstream is(datastring, std::ios_base::binary);
1036 is.read((char*)buf, 2);
1037 u16 len = readU16(buf);
1039 std::wstring message;
1040 for(u16 i=0; i<len; i++)
1042 is.read((char*)buf, 2);
1043 message += (wchar_t)readU16(buf);
1046 /*dstream<<"Client received chat message: "
1047 <<wide_to_narrow(message)<<std::endl;*/
1049 m_chat_queue.push_back(message);
1051 // Default to queueing it (for slow commands)
1054 JMutexAutoLock lock(m_incoming_queue_mutex);
1056 IncomingPacket packet(data, datasize);
1057 m_incoming_queue.push_back(packet);
1062 Returns true if there was something in queue
1064 bool Client::AsyncProcessPacket()
1066 DSTACK(__FUNCTION_NAME);
1068 try //for catching con::PeerNotFoundException
1073 JMutexAutoLock lock(m_con_mutex);
1074 // All data is coming from the server
1075 peer = m_con.GetPeer(PEER_ID_SERVER);
1078 u8 ser_version = m_server_ser_ver;
1080 IncomingPacket packet = getPacket();
1081 u8 *data = packet.m_data;
1082 u32 datasize = packet.m_datalen;
1084 // An empty packet means queue is empty
1092 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1094 if(command == TOCLIENT_BLOCKDATA)
1096 // Ignore too small packet
1099 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1103 p.X = readS16(&data[2]);
1104 p.Y = readS16(&data[4]);
1105 p.Z = readS16(&data[6]);
1107 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1108 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1110 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1111 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1113 std::string datastring((char*)&data[8], datasize-8);
1114 std::istringstream istr(datastring, std::ios_base::binary);
1120 JMutexAutoLock envlock(m_env_mutex);
1122 v2s16 p2d(p.X, p.Z);
1123 sector = m_env.getMap().emergeSector(p2d);
1125 v2s16 sp = sector->getPos();
1128 dstream<<"ERROR: Got sector with getPos()="
1129 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1130 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1134 //assert(sector->getPos() == p2d);
1137 block = sector->getBlockNoCreate(p.Y);
1139 Update an existing block
1141 //dstream<<"Updating"<<std::endl;
1142 block->deSerialize(istr, ser_version);
1143 //block->setChangedFlag();
1145 catch(InvalidPositionException &e)
1150 //dstream<<"Creating new"<<std::endl;
1151 block = new MapBlock(&m_env.getMap(), p);
1152 block->deSerialize(istr, ser_version);
1153 sector->insertBlock(block);
1154 //block->setChangedFlag();
1158 mod.type = NODEMOD_CHANGECONTENT;
1159 mod.param = CONTENT_MESE;
1160 block->setTempMod(v3s16(8,10,8), mod);
1161 block->setTempMod(v3s16(8,9,8), mod);
1162 block->setTempMod(v3s16(8,8,8), mod);
1163 block->setTempMod(v3s16(8,7,8), mod);
1164 block->setTempMod(v3s16(8,6,8), mod);*/
1168 Well, this is a dumb way to do it, they should just
1169 be drawn as separate objects.
1174 mod.type = NODEMOD_CHANGECONTENT;
1175 mod.param = CONTENT_CLOUD;
1178 for(p2.X=3; p2.X<=13; p2.X++)
1179 for(p2.Z=3; p2.Z<=13; p2.Z++)
1181 block->setTempMod(p2, mod);
1197 u32 replysize = 2+1+6;
1198 SharedBuffer<u8> reply(replysize);
1199 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1201 writeV3S16(&reply[3], p);
1203 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1206 Update Mesh of this block and blocks at x-, y- and z-.
1207 Environment should not be locked as it interlocks with the
1208 main thread, from which is will want to retrieve textures.
1211 m_env.getMap().updateMeshes(block->getPos(), getDayNightRatio());
1215 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1216 <<command<<std::endl;
1222 catch(con::PeerNotFoundException &e)
1224 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1225 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1230 bool Client::AsyncProcessData()
1234 bool r = AsyncProcessPacket();
1241 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1243 JMutexAutoLock lock(m_con_mutex);
1244 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1247 bool Client::isFetchingBlocks()
1249 JMutexAutoLock conlock(m_con_mutex);
1250 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1251 // Not really fetching but can't fetch more.
1252 if(peer == NULL) return true;
1254 con::Channel *channel = &(peer->channels[1]);
1256 NOTE: Channel 0 should always be used for fetching blocks,
1257 and for nothing else.
1259 if(channel->incoming_reliables.size() > 0)
1261 if(channel->outgoing_reliables.size() > 0)
1266 IncomingPacket Client::getPacket()
1268 JMutexAutoLock lock(m_incoming_queue_mutex);
1270 core::list<IncomingPacket>::Iterator i;
1271 // Refer to first one
1272 i = m_incoming_queue.begin();
1274 // If queue is empty, return empty packet
1275 if(i == m_incoming_queue.end()){
1276 IncomingPacket packet;
1280 // Pop out first packet and return it
1281 IncomingPacket packet = *i;
1282 m_incoming_queue.erase(i);
1286 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1287 v3s16 nodepos_oversurface, u16 item)
1289 if(connectedAndInitialized() == false){
1290 dout_client<<DTIME<<"Client::groundAction() "
1291 "cancelled (not connected)"
1300 [3] v3s16 nodepos_undersurface
1301 [9] v3s16 nodepos_abovesurface
1306 2: stop digging (all parameters ignored)
1308 u8 datasize = 2 + 1 + 6 + 6 + 2;
1309 SharedBuffer<u8> data(datasize);
1310 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1311 writeU8(&data[2], action);
1312 writeV3S16(&data[3], nodepos_undersurface);
1313 writeV3S16(&data[9], nodepos_oversurface);
1314 writeU16(&data[15], item);
1315 Send(0, data, true);
1318 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1320 if(connectedAndInitialized() == false){
1321 dout_client<<DTIME<<"Client::clickObject() "
1322 "cancelled (not connected)"
1328 [0] u16 command=TOSERVER_CLICK_OBJECT
1329 [2] u8 button (0=left, 1=right)
1334 u8 datasize = 2 + 1 + 6 + 2 + 2;
1335 SharedBuffer<u8> data(datasize);
1336 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1337 writeU8(&data[2], button);
1338 writeV3S16(&data[3], blockpos);
1339 writeS16(&data[9], id);
1340 writeU16(&data[11], item);
1341 Send(0, data, true);
1344 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1353 std::ostringstream os(std::ios_base::binary);
1357 writeU16(buf, TOSERVER_SIGNTEXT);
1358 os.write((char*)buf, 2);
1361 writeV3S16(buf, blockpos);
1362 os.write((char*)buf, 6);
1366 os.write((char*)buf, 2);
1368 u16 textlen = text.size();
1369 // Write text length
1370 writeS16(buf, textlen);
1371 os.write((char*)buf, 2);
1374 os.write((char*)text.c_str(), textlen);
1377 std::string s = os.str();
1378 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1380 Send(0, data, true);
1383 void Client::sendInventoryAction(InventoryAction *a)
1385 std::ostringstream os(std::ios_base::binary);
1389 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1390 os.write((char*)buf, 2);
1395 std::string s = os.str();
1396 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1398 Send(0, data, true);
1401 void Client::sendChatMessage(const std::wstring &message)
1403 std::ostringstream os(std::ios_base::binary);
1407 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1408 os.write((char*)buf, 2);
1411 writeU16(buf, message.size());
1412 os.write((char*)buf, 2);
1415 for(u32 i=0; i<message.size(); i++)
1419 os.write((char*)buf, 2);
1423 std::string s = os.str();
1424 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1426 Send(0, data, true);
1429 void Client::sendPlayerPos()
1431 JMutexAutoLock envlock(m_env_mutex);
1433 Player *myplayer = m_env.getLocalPlayer();
1434 if(myplayer == NULL)
1439 JMutexAutoLock lock(m_con_mutex);
1440 our_peer_id = m_con.GetPeerID();
1443 // Set peer id if not set already
1444 if(myplayer->peer_id == PEER_ID_NEW)
1445 myplayer->peer_id = our_peer_id;
1446 // Check that an existing peer_id is the same as the connection's
1447 assert(myplayer->peer_id == our_peer_id);
1449 v3f pf = myplayer->getPosition();
1450 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1451 v3f sf = myplayer->getSpeed();
1452 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1453 s32 pitch = myplayer->getPitch() * 100;
1454 s32 yaw = myplayer->getYaw() * 100;
1459 [2] v3s32 position*100
1460 [2+12] v3s32 speed*100
1461 [2+12+12] s32 pitch*100
1462 [2+12+12+4] s32 yaw*100
1465 SharedBuffer<u8> data(2+12+12+4+4);
1466 writeU16(&data[0], TOSERVER_PLAYERPOS);
1467 writeV3S32(&data[2], position);
1468 writeV3S32(&data[2+12], speed);
1469 writeS32(&data[2+12+12], pitch);
1470 writeS32(&data[2+12+12+4], yaw);
1472 // Send as unreliable
1473 Send(0, data, false);
1476 void Client::removeNode(v3s16 p)
1478 JMutexAutoLock envlock(m_env_mutex);
1480 core::map<v3s16, MapBlock*> modified_blocks;
1484 //TimeTaker t("removeNodeAndUpdate", m_device);
1485 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1487 catch(InvalidPositionException &e)
1491 for(core::map<v3s16, MapBlock * >::Iterator
1492 i = modified_blocks.getIterator();
1493 i.atEnd() == false; i++)
1495 v3s16 p = i.getNode()->getKey();
1496 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1500 void Client::addNode(v3s16 p, MapNode n)
1502 JMutexAutoLock envlock(m_env_mutex);
1504 core::map<v3s16, MapBlock*> modified_blocks;
1508 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1510 catch(InvalidPositionException &e)
1513 for(core::map<v3s16, MapBlock * >::Iterator
1514 i = modified_blocks.getIterator();
1515 i.atEnd() == false; i++)
1517 v3s16 p = i.getNode()->getKey();
1518 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1522 void Client::updateCamera(v3f pos, v3f dir)
1524 m_env.getMap().updateCamera(pos, dir);
1525 camera_position = pos;
1526 camera_direction = dir;
1529 MapNode Client::getNode(v3s16 p)
1531 JMutexAutoLock envlock(m_env_mutex);
1532 return m_env.getMap().getNode(p);
1535 /*void Client::getNode(v3s16 p, MapNode n)
1537 JMutexAutoLock envlock(m_env_mutex);
1538 m_env.getMap().setNode(p, n);
1541 /*f32 Client::getGroundHeight(v2s16 p)
1543 JMutexAutoLock envlock(m_env_mutex);
1544 return m_env.getMap().getGroundHeight(p);
1547 /*bool Client::isNodeUnderground(v3s16 p)
1549 JMutexAutoLock envlock(m_env_mutex);
1550 return m_env.getMap().isNodeUnderground(p);
1553 /*Player * Client::getLocalPlayer()
1555 JMutexAutoLock envlock(m_env_mutex);
1556 return m_env.getLocalPlayer();
1559 /*core::list<Player*> Client::getPlayers()
1561 JMutexAutoLock envlock(m_env_mutex);
1562 return m_env.getPlayers();
1565 v3f Client::getPlayerPosition()
1567 JMutexAutoLock envlock(m_env_mutex);
1568 LocalPlayer *player = m_env.getLocalPlayer();
1569 assert(player != NULL);
1570 return player->getPosition();
1573 void Client::setPlayerControl(PlayerControl &control)
1575 JMutexAutoLock envlock(m_env_mutex);
1576 LocalPlayer *player = m_env.getLocalPlayer();
1577 assert(player != NULL);
1578 player->control = control;
1581 // Returns true if the inventory of the local player has been
1582 // updated from the server. If it is true, it is set to false.
1583 bool Client::getLocalInventoryUpdated()
1585 // m_inventory_updated is behind envlock
1586 JMutexAutoLock envlock(m_env_mutex);
1587 bool updated = m_inventory_updated;
1588 m_inventory_updated = false;
1592 // Copies the inventory of the local player to parameter
1593 void Client::getLocalInventory(Inventory &dst)
1595 JMutexAutoLock envlock(m_env_mutex);
1596 Player *player = m_env.getLocalPlayer();
1597 assert(player != NULL);
1598 dst = player->inventory;
1601 MapBlockObject * Client::getSelectedObject(
1603 v3f from_pos_f_on_map,
1604 core::line3d<f32> shootline_on_map
1607 JMutexAutoLock envlock(m_env_mutex);
1609 core::array<DistanceSortedObject> objects;
1611 for(core::map<v3s16, bool>::Iterator
1612 i = m_active_blocks.getIterator();
1613 i.atEnd() == false; i++)
1615 v3s16 p = i.getNode()->getKey();
1617 MapBlock *block = NULL;
1620 block = m_env.getMap().getBlockNoCreate(p);
1622 catch(InvalidPositionException &e)
1627 // Calculate from_pos relative to block
1628 v3s16 block_pos_i_on_map = block->getPosRelative();
1629 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1630 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1632 block->getObjects(from_pos_f_on_block, max_d, objects);
1633 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1636 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1639 // After this, the closest object is the first in the array.
1642 for(u32 i=0; i<objects.size(); i++)
1644 MapBlockObject *obj = objects[i].obj;
1645 MapBlock *block = obj->getBlock();
1647 // Calculate shootline relative to block
1648 v3s16 block_pos_i_on_map = block->getPosRelative();
1649 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1650 core::line3d<f32> shootline_on_block(
1651 shootline_on_map.start - block_pos_f_on_map,
1652 shootline_on_map.end - block_pos_f_on_map
1655 if(obj->isSelected(shootline_on_block))
1657 //dstream<<"Returning selected object"<<std::endl;
1662 //dstream<<"No object selected; returning NULL."<<std::endl;
1666 void Client::printDebugInfo(std::ostream &os)
1668 //JMutexAutoLock lock1(m_fetchblock_mutex);
1669 JMutexAutoLock lock2(m_incoming_queue_mutex);
1671 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1672 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1673 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1677 /*s32 Client::getDayNightIndex()
1679 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1680 return m_daynight_i;
1683 u32 Client::getDayNightRatio()
1685 JMutexAutoLock envlock(m_env_mutex);
1686 return m_env.getDayNightRatio();
1689 /*void Client::updateSomeExpiredMeshes()
1691 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1695 JMutexAutoLock envlock(m_env_mutex);
1696 player = m_env.getLocalPlayer();
1699 u32 daynight_ratio = getDayNightRatio();
1701 v3f playerpos = player->getPosition();
1702 v3f playerspeed = player->getSpeed();
1704 v3s16 center_nodepos = floatToInt(playerpos);
1705 v3s16 center = getNodeBlockPos(center_nodepos);
1711 for(s16 d = 0; d <= d_max; d++)
1713 core::list<v3s16> list;
1714 getFacePositions(list, d);
1716 core::list<v3s16>::Iterator li;
1717 for(li=list.begin(); li!=list.end(); li++)
1719 v3s16 p = *li + center;
1720 MapBlock *block = NULL;
1723 //JMutexAutoLock envlock(m_env_mutex);
1724 block = m_env.getMap().getBlockNoCreate(p);
1726 catch(InvalidPositionException &e)
1733 if(block->getMeshExpired() == false)
1736 block->updateMesh(daynight_ratio);