3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
29 void * ClientUpdateThread::Thread()
33 DSTACK(__FUNCTION_NAME);
35 #if CATCH_UNHANDLED_EXCEPTIONS
41 m_client->asyncStep();
43 //m_client->updateSomeExpiredMeshes();
45 bool was = m_client->AsyncProcessData();
50 #if CATCH_UNHANDLED_EXCEPTIONS
53 This is what has to be done in threads to get suitable debug info
55 catch(std::exception &e)
57 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
58 <<e.what()<<std::endl;
67 IrrlichtDevice *device,
68 const char *playername,
70 s16 &viewing_range_nodes,
71 bool &viewing_range_all):
73 m_env(new ClientMap(this,
77 device->getSceneManager()->getRootSceneNode(),
78 device->getSceneManager(), 666),
80 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
82 camera_position(0,0,0),
83 camera_direction(0,0,1),
84 m_server_ser_ver(SER_FMT_VER_INVALID),
86 m_inventory_updated(false),
89 m_packetcounter_timer = 0.0;
90 m_delete_unused_sectors_timer = 0.0;
91 m_connection_reinit_timer = 0.0;
92 m_avg_rtt_timer = 0.0;
93 m_playerpos_send_timer = 0.0;
95 //m_fetchblock_mutex.Init();
96 m_incoming_queue_mutex.Init();
99 m_step_dtime_mutex.Init();
104 JMutexAutoLock envlock(m_env_mutex);
105 //m_env.getMap().StartUpdater();
107 Player *player = new LocalPlayer();
109 player->updateName(playername);
111 /*f32 y = BS*2 + BS*20;
112 player->setPosition(v3f(0, y, 0));*/
113 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
114 m_env.addPlayer(player);
120 m_thread.setRun(false);
121 while(m_thread.IsRunning())
125 void Client::connect(Address address)
127 DSTACK(__FUNCTION_NAME);
128 JMutexAutoLock lock(m_con_mutex);
129 m_con.setTimeoutMs(0);
130 m_con.Connect(address);
133 bool Client::connectedAndInitialized()
135 JMutexAutoLock lock(m_con_mutex);
137 if(m_con.Connected() == false)
140 if(m_server_ser_ver == SER_FMT_VER_INVALID)
146 void Client::step(float dtime)
148 DSTACK(__FUNCTION_NAME);
159 s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
161 if(t == d/4 || t == (d-d/4))
163 else if(t < d/4 || t > (d-d/4))
176 if(dr != m_env.getDayNightRatio())
178 //dstream<<"dr="<<dr<<std::endl;
179 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
180 m_env.setDayNightRatio(dr);
181 m_env.expireMeshes(true);
186 //dstream<<"Client steps "<<dtime<<std::endl;
189 //TimeTaker timer("ReceiveAll()", m_device);
195 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
197 JMutexAutoLock lock(m_con_mutex);
198 m_con.RunTimeouts(dtime);
205 float &counter = m_packetcounter_timer;
211 dout_client<<"Client packetcounter (20s):"<<std::endl;
212 m_packetcounter.print(dout_client);
213 m_packetcounter.clear();
219 Delete unused sectors
221 NOTE: This jams the game for a while because deleting sectors
225 float &counter = m_delete_unused_sectors_timer;
233 JMutexAutoLock lock(m_env_mutex);
235 core::list<v3s16> deleted_blocks;
237 float delete_unused_sectors_timeout =
238 g_settings.getFloat("client_delete_unused_sectors_timeout");
240 // Delete sector blocks
241 /*u32 num = m_env.getMap().deleteUnusedSectors
242 (delete_unused_sectors_timeout,
243 true, &deleted_blocks);*/
245 // Delete whole sectors
246 u32 num = m_env.getMap().deleteUnusedSectors
247 (delete_unused_sectors_timeout,
248 false, &deleted_blocks);
252 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
253 <<" unused sectors"<<std::endl;*/
254 dstream<<DTIME<<"Client: Deleted "<<num
255 <<" unused sectors"<<std::endl;
261 // Env is locked so con can be locked.
262 JMutexAutoLock lock(m_con_mutex);
264 core::list<v3s16>::Iterator i = deleted_blocks.begin();
265 core::list<v3s16> sendlist;
268 if(sendlist.size() == 255 || i == deleted_blocks.end())
270 if(sendlist.size() == 0)
279 u32 replysize = 2+1+6*sendlist.size();
280 SharedBuffer<u8> reply(replysize);
281 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
282 reply[2] = sendlist.size();
284 for(core::list<v3s16>::Iterator
285 j = sendlist.begin();
286 j != sendlist.end(); j++)
288 writeV3S16(&reply[2+1+6*k], *j);
291 m_con.Send(PEER_ID_SERVER, 1, reply, true);
293 if(i == deleted_blocks.end())
299 sendlist.push_back(*i);
306 bool connected = connectedAndInitialized();
308 if(connected == false)
310 float &counter = m_connection_reinit_timer;
316 JMutexAutoLock envlock(m_env_mutex);
318 Player *myplayer = m_env.getLocalPlayer();
319 assert(myplayer != NULL);
321 // Send TOSERVER_INIT
322 // [0] u16 TOSERVER_INIT
323 // [2] u8 SER_FMT_VER_HIGHEST
324 // [3] u8[20] player_name
325 SharedBuffer<u8> data(2+1+20);
326 writeU16(&data[0], TOSERVER_INIT);
327 writeU8(&data[2], SER_FMT_VER_HIGHEST);
328 memcpy(&data[3], myplayer->getName(), 20);
329 // Send as unreliable
330 Send(0, data, false);
333 // Not connected, return
338 Do stuff if connected
343 JMutexAutoLock lock(m_env_mutex);
345 // Control local player (0ms)
346 LocalPlayer *player = m_env.getLocalPlayer();
347 assert(player != NULL);
348 player->applyControl(dtime);
350 //TimeTaker envtimer("env step", m_device);
354 // Step active blocks
355 for(core::map<v3s16, bool>::Iterator
356 i = m_active_blocks.getIterator();
357 i.atEnd() == false; i++)
359 v3s16 p = i.getNode()->getKey();
361 MapBlock *block = NULL;
364 block = m_env.getMap().getBlockNoCreate(p);
365 block->stepObjects(dtime, false, m_env.getDayNightRatio());
367 catch(InvalidPositionException &e)
374 float &counter = m_avg_rtt_timer;
379 JMutexAutoLock lock(m_con_mutex);
380 // connectedAndInitialized() is true, peer exists.
381 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
382 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
386 float &counter = m_playerpos_send_timer;
396 JMutexAutoLock lock(m_step_dtime_mutex);
397 m_step_dtime += dtime;
401 float Client::asyncStep()
403 DSTACK(__FUNCTION_NAME);
404 //dstream<<"Client::asyncStep()"<<std::endl;
408 JMutexAutoLock lock1(m_step_dtime_mutex);
409 if(m_step_dtime < 0.001)
411 dtime = m_step_dtime;
419 // Virtual methods from con::PeerHandler
420 void Client::peerAdded(con::Peer *peer)
422 derr_client<<"Client::peerAdded(): peer->id="
423 <<peer->id<<std::endl;
425 void Client::deletingPeer(con::Peer *peer, bool timeout)
427 derr_client<<"Client::deletingPeer(): "
428 "Server Peer is getting deleted "
429 <<"(timeout="<<timeout<<")"<<std::endl;
432 void Client::ReceiveAll()
434 DSTACK(__FUNCTION_NAME);
440 catch(con::NoIncomingDataException &e)
444 catch(con::InvalidIncomingDataException &e)
446 dout_client<<DTIME<<"Client::ReceiveAll(): "
447 "InvalidIncomingDataException: what()="
448 <<e.what()<<std::endl;
453 void Client::Receive()
455 DSTACK(__FUNCTION_NAME);
456 u32 data_maxsize = 10000;
457 Buffer<u8> data(data_maxsize);
461 //TimeTaker t1("con mutex and receive", m_device);
462 JMutexAutoLock lock(m_con_mutex);
463 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
465 //TimeTaker t1("ProcessData", m_device);
466 ProcessData(*data, datasize, sender_peer_id);
470 sender_peer_id given to this shall be quaranteed to be a valid peer
472 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
474 DSTACK(__FUNCTION_NAME);
476 // Ignore packets that don't even fit a command
479 m_packetcounter.add(60000);
483 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
485 //dstream<<"Client: received command="<<command<<std::endl;
486 m_packetcounter.add((u16)command);
489 If this check is removed, be sure to change the queue
490 system to know the ids
492 if(sender_peer_id != PEER_ID_SERVER)
494 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
495 "coming from server: peer_id="<<sender_peer_id
502 JMutexAutoLock lock(m_con_mutex);
503 // All data is coming from the server
504 // PeerNotFoundException is handled by caller.
505 peer = m_con.GetPeer(PEER_ID_SERVER);
508 u8 ser_version = m_server_ser_ver;
510 //dstream<<"Client received command="<<(int)command<<std::endl;
512 // Execute fast commands straight away
514 if(command == TOCLIENT_INIT)
519 u8 deployed = data[2];
521 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
522 "deployed="<<((int)deployed&0xff)<<std::endl;
524 if(deployed < SER_FMT_VER_LOWEST
525 || deployed > SER_FMT_VER_HIGHEST)
527 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
528 <<"unsupported ser_fmt_ver"<<std::endl;
532 m_server_ser_ver = deployed;
534 // Get player position
535 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
536 if(datasize >= 2+1+6)
537 playerpos_s16 = readV3S16(&data[2+1]);
538 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
541 JMutexAutoLock envlock(m_env_mutex);
543 // Set player position
544 Player *player = m_env.getLocalPlayer();
545 assert(player != NULL);
546 player->setPosition(playerpos_f);
551 SharedBuffer<u8> reply(replysize);
552 writeU16(&reply[0], TOSERVER_INIT2);
554 m_con.Send(PEER_ID_SERVER, 1, reply, true);
559 if(ser_version == SER_FMT_VER_INVALID)
561 dout_client<<DTIME<<"WARNING: Client: Server serialization"
562 " format invalid or not initialized."
563 " Skipping incoming command="<<command<<std::endl;
567 // Just here to avoid putting the two if's together when
568 // making some copypasta
571 if(command == TOCLIENT_PLAYERPOS)
573 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
577 JMutexAutoLock lock(m_con_mutex);
578 our_peer_id = m_con.GetPeerID();
580 // Cancel if we don't have a peer id
581 if(our_peer_id == PEER_ID_NEW){
582 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
589 JMutexAutoLock envlock(m_env_mutex);
591 u32 player_size = 2+12+12+4+4;
593 u32 player_count = (datasize-2) / player_size;
595 for(u32 i=0; i<player_count; i++)
597 u16 peer_id = readU16(&data[start]);
599 Player *player = m_env.getPlayer(peer_id);
601 // Skip if player doesn't exist
604 start += player_size;
608 // Skip if player is local player
609 if(player->isLocal())
611 start += player_size;
615 v3s32 ps = readV3S32(&data[start+2]);
616 v3s32 ss = readV3S32(&data[start+2+12]);
617 s32 pitch_i = readS32(&data[start+2+12+12]);
618 s32 yaw_i = readS32(&data[start+2+12+12+4]);
619 /*dstream<<"Client: got "
620 <<"pitch_i="<<pitch_i
621 <<" yaw_i="<<yaw_i<<std::endl;*/
622 f32 pitch = (f32)pitch_i / 100.0;
623 f32 yaw = (f32)yaw_i / 100.0;
624 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
625 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
626 player->setPosition(position);
627 player->setSpeed(speed);
628 player->setPitch(pitch);
631 /*dstream<<"Client: player "<<peer_id
633 <<" yaw="<<yaw<<std::endl;*/
635 start += player_size;
639 else if(command == TOCLIENT_PLAYERINFO)
643 JMutexAutoLock lock(m_con_mutex);
644 our_peer_id = m_con.GetPeerID();
646 // Cancel if we don't have a peer id
647 if(our_peer_id == PEER_ID_NEW){
648 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
654 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
657 JMutexAutoLock envlock(m_env_mutex);
659 u32 item_size = 2+PLAYERNAME_SIZE;
660 u32 player_count = (datasize-2) / item_size;
663 core::list<u16> players_alive;
664 for(u32 i=0; i<player_count; i++)
666 // Make sure the name ends in '\0'
667 data[start+2+20-1] = 0;
669 u16 peer_id = readU16(&data[start]);
671 players_alive.push_back(peer_id);
673 /*dstream<<DTIME<<"peer_id="<<peer_id
674 <<" name="<<((char*)&data[start+2])<<std::endl;*/
676 // Don't update the info of the local player
677 if(peer_id == our_peer_id)
683 Player *player = m_env.getPlayer(peer_id);
685 // Create a player if it doesn't exist
688 player = new RemotePlayer(
689 m_device->getSceneManager()->getRootSceneNode(),
692 player->peer_id = peer_id;
693 m_env.addPlayer(player);
694 dout_client<<DTIME<<"Client: Adding new player "
695 <<peer_id<<std::endl;
698 player->updateName((char*)&data[start+2]);
704 Remove those players from the environment that
705 weren't listed by the server.
707 //dstream<<DTIME<<"Removing dead players"<<std::endl;
708 core::list<Player*> players = m_env.getPlayers();
709 core::list<Player*>::Iterator ip;
710 for(ip=players.begin(); ip!=players.end(); ip++)
712 // Ingore local player
716 // Warn about a special case
717 if((*ip)->peer_id == 0)
719 dstream<<DTIME<<"WARNING: Client: Removing "
720 "dead player with id=0"<<std::endl;
723 bool is_alive = false;
724 core::list<u16>::Iterator i;
725 for(i=players_alive.begin(); i!=players_alive.end(); i++)
727 if((*ip)->peer_id == *i)
733 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
734 <<" is_alive="<<is_alive<<std::endl;*/
737 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
739 m_env.removePlayer((*ip)->peer_id);
743 else if(command == TOCLIENT_SECTORMETA)
748 [3...] v2s16 pos + sector metadata
753 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
756 JMutexAutoLock envlock(m_env_mutex);
758 std::string datastring((char*)&data[2], datasize-2);
759 std::istringstream is(datastring, std::ios_base::binary);
763 is.read((char*)buf, 1);
764 u16 sector_count = readU8(buf);
766 //dstream<<"sector_count="<<sector_count<<std::endl;
768 for(u16 i=0; i<sector_count; i++)
771 is.read((char*)buf, 4);
772 v2s16 pos = readV2S16(buf);
773 /*dstream<<"Client: deserializing sector at "
774 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
776 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
777 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
781 else if(command == TOCLIENT_INVENTORY)
786 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
789 //TimeTaker t2("mutex locking", m_device);
790 JMutexAutoLock envlock(m_env_mutex);
793 //TimeTaker t3("istringstream init", m_device);
794 std::string datastring((char*)&data[2], datasize-2);
795 std::istringstream is(datastring, std::ios_base::binary);
798 //m_env.printPlayers(dstream);
800 //TimeTaker t4("player get", m_device);
801 Player *player = m_env.getLocalPlayer();
802 assert(player != NULL);
805 //TimeTaker t1("inventory.deSerialize()", m_device);
806 player->inventory.deSerialize(is);
809 m_inventory_updated = true;
811 //dstream<<"Client got player inventory:"<<std::endl;
812 //player->inventory.print(dstream);
816 else if(command == TOCLIENT_OBJECTDATA)
819 // Strip command word and create a stringstream
820 std::string datastring((char*)&data[2], datasize-2);
821 std::istringstream is(datastring, std::ios_base::binary);
825 JMutexAutoLock envlock(m_env_mutex);
833 is.read((char*)buf, 2);
834 u16 playercount = readU16(buf);
836 for(u16 i=0; i<playercount; i++)
838 is.read((char*)buf, 2);
839 u16 peer_id = readU16(buf);
840 is.read((char*)buf, 12);
841 v3s32 p_i = readV3S32(buf);
842 is.read((char*)buf, 12);
843 v3s32 s_i = readV3S32(buf);
844 is.read((char*)buf, 4);
845 s32 pitch_i = readS32(buf);
846 is.read((char*)buf, 4);
847 s32 yaw_i = readS32(buf);
849 Player *player = m_env.getPlayer(peer_id);
851 // Skip if player doesn't exist
857 // Skip if player is local player
858 if(player->isLocal())
863 f32 pitch = (f32)pitch_i / 100.0;
864 f32 yaw = (f32)yaw_i / 100.0;
865 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
866 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
868 player->setPosition(position);
869 player->setSpeed(speed);
870 player->setPitch(pitch);
878 // Read active block count
879 is.read((char*)buf, 2);
880 u16 blockcount = readU16(buf);
882 // Initialize delete queue with all active blocks
883 core::map<v3s16, bool> abs_to_delete;
884 for(core::map<v3s16, bool>::Iterator
885 i = m_active_blocks.getIterator();
886 i.atEnd() == false; i++)
888 v3s16 p = i.getNode()->getKey();
890 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
891 <<" to abs_to_delete"
893 abs_to_delete.insert(p, true);
896 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
899 for(u16 i=0; i<blockcount; i++)
902 is.read((char*)buf, 6);
903 v3s16 p = readV3S16(buf);
904 // Get block from somewhere
905 MapBlock *block = NULL;
907 block = m_env.getMap().getBlockNoCreate(p);
909 catch(InvalidPositionException &e)
911 //TODO: Create a dummy block?
916 <<"Could not get block at blockpos "
917 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
918 <<"in TOCLIENT_OBJECTDATA. Ignoring "
919 <<"following block object data."
924 /*dstream<<"Client updating objects for block "
925 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
928 // Insert to active block list
929 m_active_blocks.insert(p, true);
931 // Remove from deletion queue
932 if(abs_to_delete.find(p) != NULL)
933 abs_to_delete.remove(p);
936 Update objects of block
938 NOTE: Be sure this is done in the main thread.
940 block->updateObjects(is, m_server_ser_ver,
941 m_device->getSceneManager(), m_env.getDayNightRatio());
944 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
947 // Delete objects of blocks in delete queue
948 for(core::map<v3s16, bool>::Iterator
949 i = abs_to_delete.getIterator();
950 i.atEnd() == false; i++)
952 v3s16 p = i.getNode()->getKey();
955 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
958 block->clearObjects();
959 // Remove from active blocks list
960 m_active_blocks.remove(p);
962 catch(InvalidPositionException &e)
964 dstream<<"WARNAING: Client: "
965 <<"Couldn't clear objects of active->inactive"
967 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
968 <<" because block was not found"
976 else if(command == TOCLIENT_TIME_OF_DAY)
981 u16 time = readU16(&data[2]);
983 m_time_of_day.set(time);
984 //dstream<<"Client: time="<<time<<std::endl;
986 else if(command == TOCLIENT_CHAT_MESSAGE)
994 std::string datastring((char*)&data[2], datasize-2);
995 std::istringstream is(datastring, std::ios_base::binary);
998 is.read((char*)buf, 2);
999 u16 len = readU16(buf);
1001 std::wstring message;
1002 for(u16 i=0; i<len; i++)
1004 is.read((char*)buf, 2);
1005 message += (wchar_t)readU16(buf);
1008 /*dstream<<"Client received chat message: "
1009 <<wide_to_narrow(message)<<std::endl;*/
1011 m_chat_queue.push_back(message);
1013 // Default to queueing it (for slow commands)
1016 JMutexAutoLock lock(m_incoming_queue_mutex);
1018 IncomingPacket packet(data, datasize);
1019 m_incoming_queue.push_back(packet);
1024 Returns true if there was something in queue
1026 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1028 DSTACK(__FUNCTION_NAME);
1030 try //for catching con::PeerNotFoundException
1035 JMutexAutoLock lock(m_con_mutex);
1036 // All data is coming from the server
1037 peer = m_con.GetPeer(PEER_ID_SERVER);
1040 u8 ser_version = m_server_ser_ver;
1042 IncomingPacket packet = getPacket();
1043 u8 *data = packet.m_data;
1044 u32 datasize = packet.m_datalen;
1046 // An empty packet means queue is empty
1054 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1056 if(command == TOCLIENT_REMOVENODE)
1061 p.X = readS16(&data[2]);
1062 p.Y = readS16(&data[4]);
1063 p.Z = readS16(&data[6]);
1065 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1067 // This will clear the cracking animation after digging
1068 ((ClientMap&)m_env.getMap()).clearTempMod(p);
1072 else if(command == TOCLIENT_ADDNODE)
1074 if(datasize < 8 + MapNode::serializedLength(ser_version))
1078 p.X = readS16(&data[2]);
1079 p.Y = readS16(&data[4]);
1080 p.Z = readS16(&data[6]);
1082 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1085 n.deSerialize(&data[8], ser_version);
1089 else if(command == TOCLIENT_BLOCKDATA)
1091 // Ignore too small packet
1094 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1098 p.X = readS16(&data[2]);
1099 p.Y = readS16(&data[4]);
1100 p.Z = readS16(&data[6]);
1102 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1103 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1105 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1106 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1108 std::string datastring((char*)&data[8], datasize-8);
1109 std::istringstream istr(datastring, std::ios_base::binary);
1115 JMutexAutoLock envlock(m_env_mutex);
1117 v2s16 p2d(p.X, p.Z);
1118 sector = m_env.getMap().emergeSector(p2d);
1120 v2s16 sp = sector->getPos();
1123 dstream<<"ERROR: Got sector with getPos()="
1124 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1125 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1129 //assert(sector->getPos() == p2d);
1132 block = sector->getBlockNoCreate(p.Y);
1134 Update an existing block
1136 //dstream<<"Updating"<<std::endl;
1137 block->deSerialize(istr, ser_version);
1138 //block->setChangedFlag();
1140 catch(InvalidPositionException &e)
1145 //dstream<<"Creating new"<<std::endl;
1146 block = new MapBlock(&m_env.getMap(), p);
1147 block->deSerialize(istr, ser_version);
1148 sector->insertBlock(block);
1149 //block->setChangedFlag();
1153 mod.type = NODEMOD_CHANGECONTENT;
1154 mod.param = CONTENT_MESE;
1155 block->setTempMod(v3s16(8,10,8), mod);
1156 block->setTempMod(v3s16(8,9,8), mod);
1157 block->setTempMod(v3s16(8,8,8), mod);
1158 block->setTempMod(v3s16(8,7,8), mod);
1159 block->setTempMod(v3s16(8,6,8), mod);*/
1163 Well, this is a dumb way to do it, they should just
1164 be drawn as separate objects.
1169 mod.type = NODEMOD_CHANGECONTENT;
1170 mod.param = CONTENT_CLOUD;
1173 for(p2.X=3; p2.X<=13; p2.X++)
1174 for(p2.Z=3; p2.Z<=13; p2.Z++)
1176 block->setTempMod(p2, mod);
1192 u32 replysize = 2+1+6;
1193 SharedBuffer<u8> reply(replysize);
1194 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1196 writeV3S16(&reply[3], p);
1198 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1201 Update Mesh of this block and blocks at x-, y- and z-.
1202 Environment should not be locked as it interlocks with the
1203 main thread, from which is will want to retrieve textures.
1206 m_env.getMap().updateMeshes(block->getPos(), getDayNightRatio());
1210 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1211 <<command<<std::endl;
1217 catch(con::PeerNotFoundException &e)
1219 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1220 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1225 bool Client::AsyncProcessData()
1229 // We want to update the meshes as soon as a single packet has
1231 LazyMeshUpdater mesh_updater(&m_env);
1232 bool r = AsyncProcessPacket(mesh_updater);
1238 /*LazyMeshUpdater mesh_updater(&m_env);
1241 bool r = AsyncProcessPacket(mesh_updater);
1249 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1251 JMutexAutoLock lock(m_con_mutex);
1252 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1256 void Client::fetchBlock(v3s16 p, u8 flags)
1258 if(connectedAndInitialized() == false)
1259 throw ClientNotReadyException
1260 ("ClientNotReadyException: connectedAndInitialized() == false");
1262 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1263 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1265 JMutexAutoLock conlock(m_con_mutex);
1267 SharedBuffer<u8> data(9);
1268 writeU16(&data[0], TOSERVER_GETBLOCK);
1269 writeS16(&data[2], p.X);
1270 writeS16(&data[4], p.Y);
1271 writeS16(&data[6], p.Z);
1272 writeU8(&data[8], flags);
1273 m_con.Send(PEER_ID_SERVER, 1, data, true);
1277 Calls fetchBlock() on some nearby missing blocks.
1279 Returns when any of various network load indicators go over limit.
1281 Does nearly the same thing as the old updateChangedVisibleArea()
1283 void Client::fetchBlocks()
1285 if(connectedAndInitialized() == false)
1286 throw ClientNotReadyException
1287 ("ClientNotReadyException: connectedAndInitialized() == false");
1291 bool Client::isFetchingBlocks()
1293 JMutexAutoLock conlock(m_con_mutex);
1294 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1295 // Not really fetching but can't fetch more.
1296 if(peer == NULL) return true;
1298 con::Channel *channel = &(peer->channels[1]);
1300 NOTE: Channel 0 should always be used for fetching blocks,
1301 and for nothing else.
1303 if(channel->incoming_reliables.size() > 0)
1305 if(channel->outgoing_reliables.size() > 0)
1310 IncomingPacket Client::getPacket()
1312 JMutexAutoLock lock(m_incoming_queue_mutex);
1314 core::list<IncomingPacket>::Iterator i;
1315 // Refer to first one
1316 i = m_incoming_queue.begin();
1318 // If queue is empty, return empty packet
1319 if(i == m_incoming_queue.end()){
1320 IncomingPacket packet;
1324 // Pop out first packet and return it
1325 IncomingPacket packet = *i;
1326 m_incoming_queue.erase(i);
1330 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1331 v3s16 nodepos_oversurface, u16 item)
1333 if(connectedAndInitialized() == false){
1334 dout_client<<DTIME<<"Client::groundAction() "
1335 "cancelled (not connected)"
1344 [3] v3s16 nodepos_undersurface
1345 [9] v3s16 nodepos_abovesurface
1350 2: stop digging (all parameters ignored)
1352 u8 datasize = 2 + 1 + 6 + 6 + 2;
1353 SharedBuffer<u8> data(datasize);
1354 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1355 writeU8(&data[2], action);
1356 writeV3S16(&data[3], nodepos_undersurface);
1357 writeV3S16(&data[9], nodepos_oversurface);
1358 writeU16(&data[15], item);
1359 Send(0, data, true);
1362 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1364 if(connectedAndInitialized() == false){
1365 dout_client<<DTIME<<"Client::clickObject() "
1366 "cancelled (not connected)"
1373 [2] u8 button (0=left, 1=right)
1378 u8 datasize = 2 + 1 + 6 + 2 + 2;
1379 SharedBuffer<u8> data(datasize);
1380 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1381 writeU8(&data[2], button);
1382 writeV3S16(&data[3], blockpos);
1383 writeS16(&data[9], id);
1384 writeU16(&data[11], item);
1385 Send(0, data, true);
1388 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1397 std::ostringstream os(std::ios_base::binary);
1401 writeU16(buf, TOSERVER_SIGNTEXT);
1402 os.write((char*)buf, 2);
1405 writeV3S16(buf, blockpos);
1406 os.write((char*)buf, 6);
1410 os.write((char*)buf, 2);
1412 u16 textlen = text.size();
1413 // Write text length
1414 writeS16(buf, textlen);
1415 os.write((char*)buf, 2);
1418 os.write((char*)text.c_str(), textlen);
1421 std::string s = os.str();
1422 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1424 Send(0, data, true);
1427 void Client::sendInventoryAction(InventoryAction *a)
1429 std::ostringstream os(std::ios_base::binary);
1433 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1434 os.write((char*)buf, 2);
1439 std::string s = os.str();
1440 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1442 Send(0, data, true);
1445 void Client::sendChatMessage(const std::wstring &message)
1447 std::ostringstream os(std::ios_base::binary);
1451 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1452 os.write((char*)buf, 2);
1455 writeU16(buf, message.size());
1456 os.write((char*)buf, 2);
1459 for(u32 i=0; i<message.size(); i++)
1463 os.write((char*)buf, 2);
1467 std::string s = os.str();
1468 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1470 Send(0, data, true);
1473 void Client::sendPlayerPos()
1475 JMutexAutoLock envlock(m_env_mutex);
1477 Player *myplayer = m_env.getLocalPlayer();
1478 if(myplayer == NULL)
1483 JMutexAutoLock lock(m_con_mutex);
1484 our_peer_id = m_con.GetPeerID();
1487 // Set peer id if not set already
1488 if(myplayer->peer_id == PEER_ID_NEW)
1489 myplayer->peer_id = our_peer_id;
1490 // Check that an existing peer_id is the same as the connection's
1491 assert(myplayer->peer_id == our_peer_id);
1493 v3f pf = myplayer->getPosition();
1494 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1495 v3f sf = myplayer->getSpeed();
1496 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1497 s32 pitch = myplayer->getPitch() * 100;
1498 s32 yaw = myplayer->getYaw() * 100;
1503 [2] v3s32 position*100
1504 [2+12] v3s32 speed*100
1505 [2+12+12] s32 pitch*100
1506 [2+12+12+4] s32 yaw*100
1509 SharedBuffer<u8> data(2+12+12+4+4);
1510 writeU16(&data[0], TOSERVER_PLAYERPOS);
1511 writeV3S32(&data[2], position);
1512 writeV3S32(&data[2+12], speed);
1513 writeS32(&data[2+12+12], pitch);
1514 writeS32(&data[2+12+12+4], yaw);
1516 // Send as unreliable
1517 Send(0, data, false);
1520 void Client::removeNode(v3s16 p)
1522 JMutexAutoLock envlock(m_env_mutex);
1524 core::map<v3s16, MapBlock*> modified_blocks;
1528 //TimeTaker t("removeNodeAndUpdate", m_device);
1529 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1531 catch(InvalidPositionException &e)
1535 for(core::map<v3s16, MapBlock * >::Iterator
1536 i = modified_blocks.getIterator();
1537 i.atEnd() == false; i++)
1539 v3s16 p = i.getNode()->getKey();
1540 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1544 void Client::addNode(v3s16 p, MapNode n)
1546 JMutexAutoLock envlock(m_env_mutex);
1548 core::map<v3s16, MapBlock*> modified_blocks;
1552 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1554 catch(InvalidPositionException &e)
1557 for(core::map<v3s16, MapBlock * >::Iterator
1558 i = modified_blocks.getIterator();
1559 i.atEnd() == false; i++)
1561 v3s16 p = i.getNode()->getKey();
1562 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1566 void Client::updateCamera(v3f pos, v3f dir)
1568 m_env.getMap().updateCamera(pos, dir);
1569 camera_position = pos;
1570 camera_direction = dir;
1573 MapNode Client::getNode(v3s16 p)
1575 JMutexAutoLock envlock(m_env_mutex);
1576 return m_env.getMap().getNode(p);
1579 /*void Client::getNode(v3s16 p, MapNode n)
1581 JMutexAutoLock envlock(m_env_mutex);
1582 m_env.getMap().setNode(p, n);
1585 /*f32 Client::getGroundHeight(v2s16 p)
1587 JMutexAutoLock envlock(m_env_mutex);
1588 return m_env.getMap().getGroundHeight(p);
1591 /*bool Client::isNodeUnderground(v3s16 p)
1593 JMutexAutoLock envlock(m_env_mutex);
1594 return m_env.getMap().isNodeUnderground(p);
1597 /*Player * Client::getLocalPlayer()
1599 JMutexAutoLock envlock(m_env_mutex);
1600 return m_env.getLocalPlayer();
1603 /*core::list<Player*> Client::getPlayers()
1605 JMutexAutoLock envlock(m_env_mutex);
1606 return m_env.getPlayers();
1609 v3f Client::getPlayerPosition()
1611 JMutexAutoLock envlock(m_env_mutex);
1612 LocalPlayer *player = m_env.getLocalPlayer();
1613 assert(player != NULL);
1614 return player->getPosition();
1617 void Client::setPlayerControl(PlayerControl &control)
1619 JMutexAutoLock envlock(m_env_mutex);
1620 LocalPlayer *player = m_env.getLocalPlayer();
1621 assert(player != NULL);
1622 player->control = control;
1625 // Returns true if the inventory of the local player has been
1626 // updated from the server. If it is true, it is set to false.
1627 bool Client::getLocalInventoryUpdated()
1629 // m_inventory_updated is behind envlock
1630 JMutexAutoLock envlock(m_env_mutex);
1631 bool updated = m_inventory_updated;
1632 m_inventory_updated = false;
1636 // Copies the inventory of the local player to parameter
1637 void Client::getLocalInventory(Inventory &dst)
1639 JMutexAutoLock envlock(m_env_mutex);
1640 Player *player = m_env.getLocalPlayer();
1641 assert(player != NULL);
1642 dst = player->inventory;
1645 MapBlockObject * Client::getSelectedObject(
1647 v3f from_pos_f_on_map,
1648 core::line3d<f32> shootline_on_map
1651 JMutexAutoLock envlock(m_env_mutex);
1653 core::array<DistanceSortedObject> objects;
1655 for(core::map<v3s16, bool>::Iterator
1656 i = m_active_blocks.getIterator();
1657 i.atEnd() == false; i++)
1659 v3s16 p = i.getNode()->getKey();
1661 MapBlock *block = NULL;
1664 block = m_env.getMap().getBlockNoCreate(p);
1666 catch(InvalidPositionException &e)
1671 // Calculate from_pos relative to block
1672 v3s16 block_pos_i_on_map = block->getPosRelative();
1673 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1674 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1676 block->getObjects(from_pos_f_on_block, max_d, objects);
1677 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1680 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1683 // After this, the closest object is the first in the array.
1686 for(u32 i=0; i<objects.size(); i++)
1688 MapBlockObject *obj = objects[i].obj;
1689 MapBlock *block = obj->getBlock();
1691 // Calculate shootline relative to block
1692 v3s16 block_pos_i_on_map = block->getPosRelative();
1693 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1694 core::line3d<f32> shootline_on_block(
1695 shootline_on_map.start - block_pos_f_on_map,
1696 shootline_on_map.end - block_pos_f_on_map
1699 if(obj->isSelected(shootline_on_block))
1701 //dstream<<"Returning selected object"<<std::endl;
1706 //dstream<<"No object selected; returning NULL."<<std::endl;
1710 void Client::printDebugInfo(std::ostream &os)
1712 //JMutexAutoLock lock1(m_fetchblock_mutex);
1713 JMutexAutoLock lock2(m_incoming_queue_mutex);
1715 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1716 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1717 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1721 /*s32 Client::getDayNightIndex()
1723 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1724 return m_daynight_i;
1727 u32 Client::getDayNightRatio()
1729 JMutexAutoLock envlock(m_env_mutex);
1730 return m_env.getDayNightRatio();
1733 /*void Client::updateSomeExpiredMeshes()
1735 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1739 JMutexAutoLock envlock(m_env_mutex);
1740 player = m_env.getLocalPlayer();
1743 u32 daynight_ratio = getDayNightRatio();
1745 v3f playerpos = player->getPosition();
1746 v3f playerspeed = player->getSpeed();
1748 v3s16 center_nodepos = floatToInt(playerpos);
1749 v3s16 center = getNodeBlockPos(center_nodepos);
1755 for(s16 d = 0; d <= d_max; d++)
1757 core::list<v3s16> list;
1758 getFacePositions(list, d);
1760 core::list<v3s16>::Iterator li;
1761 for(li=list.begin(); li!=list.end(); li++)
1763 v3s16 p = *li + center;
1764 MapBlock *block = NULL;
1767 //JMutexAutoLock envlock(m_env_mutex);
1768 block = m_env.getMap().getBlockNoCreate(p);
1770 catch(InvalidPositionException &e)
1777 if(block->getMeshExpired() == false)
1780 block->updateMesh(daynight_ratio);