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_REMOVENODE)
576 p.X = readS16(&data[2]);
577 p.Y = readS16(&data[4]);
578 p.Z = readS16(&data[6]);
580 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
582 // This will clear the cracking animation after digging
583 ((ClientMap&)m_env.getMap()).clearTempMod(p);
587 else if(command == TOCLIENT_ADDNODE)
589 if(datasize < 8 + MapNode::serializedLength(ser_version))
593 p.X = readS16(&data[2]);
594 p.Y = readS16(&data[4]);
595 p.Z = readS16(&data[6]);
597 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
600 n.deSerialize(&data[8], ser_version);
604 if(command == TOCLIENT_PLAYERPOS)
606 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
610 JMutexAutoLock lock(m_con_mutex);
611 our_peer_id = m_con.GetPeerID();
613 // Cancel if we don't have a peer id
614 if(our_peer_id == PEER_ID_NEW){
615 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
622 JMutexAutoLock envlock(m_env_mutex);
624 u32 player_size = 2+12+12+4+4;
626 u32 player_count = (datasize-2) / player_size;
628 for(u32 i=0; i<player_count; i++)
630 u16 peer_id = readU16(&data[start]);
632 Player *player = m_env.getPlayer(peer_id);
634 // Skip if player doesn't exist
637 start += player_size;
641 // Skip if player is local player
642 if(player->isLocal())
644 start += player_size;
648 v3s32 ps = readV3S32(&data[start+2]);
649 v3s32 ss = readV3S32(&data[start+2+12]);
650 s32 pitch_i = readS32(&data[start+2+12+12]);
651 s32 yaw_i = readS32(&data[start+2+12+12+4]);
652 /*dstream<<"Client: got "
653 <<"pitch_i="<<pitch_i
654 <<" yaw_i="<<yaw_i<<std::endl;*/
655 f32 pitch = (f32)pitch_i / 100.0;
656 f32 yaw = (f32)yaw_i / 100.0;
657 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
658 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
659 player->setPosition(position);
660 player->setSpeed(speed);
661 player->setPitch(pitch);
664 /*dstream<<"Client: player "<<peer_id
666 <<" yaw="<<yaw<<std::endl;*/
668 start += player_size;
672 else if(command == TOCLIENT_PLAYERINFO)
676 JMutexAutoLock lock(m_con_mutex);
677 our_peer_id = m_con.GetPeerID();
679 // Cancel if we don't have a peer id
680 if(our_peer_id == PEER_ID_NEW){
681 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
687 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
690 JMutexAutoLock envlock(m_env_mutex);
692 u32 item_size = 2+PLAYERNAME_SIZE;
693 u32 player_count = (datasize-2) / item_size;
696 core::list<u16> players_alive;
697 for(u32 i=0; i<player_count; i++)
699 // Make sure the name ends in '\0'
700 data[start+2+20-1] = 0;
702 u16 peer_id = readU16(&data[start]);
704 players_alive.push_back(peer_id);
706 /*dstream<<DTIME<<"peer_id="<<peer_id
707 <<" name="<<((char*)&data[start+2])<<std::endl;*/
709 // Don't update the info of the local player
710 if(peer_id == our_peer_id)
716 Player *player = m_env.getPlayer(peer_id);
718 // Create a player if it doesn't exist
721 player = new RemotePlayer(
722 m_device->getSceneManager()->getRootSceneNode(),
725 player->peer_id = peer_id;
726 m_env.addPlayer(player);
727 dout_client<<DTIME<<"Client: Adding new player "
728 <<peer_id<<std::endl;
731 player->updateName((char*)&data[start+2]);
737 Remove those players from the environment that
738 weren't listed by the server.
740 //dstream<<DTIME<<"Removing dead players"<<std::endl;
741 core::list<Player*> players = m_env.getPlayers();
742 core::list<Player*>::Iterator ip;
743 for(ip=players.begin(); ip!=players.end(); ip++)
745 // Ingore local player
749 // Warn about a special case
750 if((*ip)->peer_id == 0)
752 dstream<<DTIME<<"WARNING: Client: Removing "
753 "dead player with id=0"<<std::endl;
756 bool is_alive = false;
757 core::list<u16>::Iterator i;
758 for(i=players_alive.begin(); i!=players_alive.end(); i++)
760 if((*ip)->peer_id == *i)
766 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
767 <<" is_alive="<<is_alive<<std::endl;*/
770 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
772 m_env.removePlayer((*ip)->peer_id);
776 else if(command == TOCLIENT_SECTORMETA)
781 [3...] v2s16 pos + sector metadata
786 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
789 JMutexAutoLock envlock(m_env_mutex);
791 std::string datastring((char*)&data[2], datasize-2);
792 std::istringstream is(datastring, std::ios_base::binary);
796 is.read((char*)buf, 1);
797 u16 sector_count = readU8(buf);
799 //dstream<<"sector_count="<<sector_count<<std::endl;
801 for(u16 i=0; i<sector_count; i++)
804 is.read((char*)buf, 4);
805 v2s16 pos = readV2S16(buf);
806 /*dstream<<"Client: deserializing sector at "
807 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
809 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
810 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
814 else if(command == TOCLIENT_INVENTORY)
819 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
822 //TimeTaker t2("mutex locking", m_device);
823 JMutexAutoLock envlock(m_env_mutex);
826 //TimeTaker t3("istringstream init", m_device);
827 std::string datastring((char*)&data[2], datasize-2);
828 std::istringstream is(datastring, std::ios_base::binary);
831 //m_env.printPlayers(dstream);
833 //TimeTaker t4("player get", m_device);
834 Player *player = m_env.getLocalPlayer();
835 assert(player != NULL);
838 //TimeTaker t1("inventory.deSerialize()", m_device);
839 player->inventory.deSerialize(is);
842 m_inventory_updated = true;
844 //dstream<<"Client got player inventory:"<<std::endl;
845 //player->inventory.print(dstream);
849 else if(command == TOCLIENT_OBJECTDATA)
852 // Strip command word and create a stringstream
853 std::string datastring((char*)&data[2], datasize-2);
854 std::istringstream is(datastring, std::ios_base::binary);
858 JMutexAutoLock envlock(m_env_mutex);
866 is.read((char*)buf, 2);
867 u16 playercount = readU16(buf);
869 for(u16 i=0; i<playercount; i++)
871 is.read((char*)buf, 2);
872 u16 peer_id = readU16(buf);
873 is.read((char*)buf, 12);
874 v3s32 p_i = readV3S32(buf);
875 is.read((char*)buf, 12);
876 v3s32 s_i = readV3S32(buf);
877 is.read((char*)buf, 4);
878 s32 pitch_i = readS32(buf);
879 is.read((char*)buf, 4);
880 s32 yaw_i = readS32(buf);
882 Player *player = m_env.getPlayer(peer_id);
884 // Skip if player doesn't exist
890 // Skip if player is local player
891 if(player->isLocal())
896 f32 pitch = (f32)pitch_i / 100.0;
897 f32 yaw = (f32)yaw_i / 100.0;
898 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
899 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
901 player->setPosition(position);
902 player->setSpeed(speed);
903 player->setPitch(pitch);
911 // Read active block count
912 is.read((char*)buf, 2);
913 u16 blockcount = readU16(buf);
915 // Initialize delete queue with all active blocks
916 core::map<v3s16, bool> abs_to_delete;
917 for(core::map<v3s16, bool>::Iterator
918 i = m_active_blocks.getIterator();
919 i.atEnd() == false; i++)
921 v3s16 p = i.getNode()->getKey();
923 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
924 <<" to abs_to_delete"
926 abs_to_delete.insert(p, true);
929 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
932 for(u16 i=0; i<blockcount; i++)
935 is.read((char*)buf, 6);
936 v3s16 p = readV3S16(buf);
937 // Get block from somewhere
938 MapBlock *block = NULL;
940 block = m_env.getMap().getBlockNoCreate(p);
942 catch(InvalidPositionException &e)
944 //TODO: Create a dummy block?
949 <<"Could not get block at blockpos "
950 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
951 <<"in TOCLIENT_OBJECTDATA. Ignoring "
952 <<"following block object data."
957 /*dstream<<"Client updating objects for block "
958 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
961 // Insert to active block list
962 m_active_blocks.insert(p, true);
964 // Remove from deletion queue
965 if(abs_to_delete.find(p) != NULL)
966 abs_to_delete.remove(p);
969 Update objects of block
971 NOTE: Be sure this is done in the main thread.
973 block->updateObjects(is, m_server_ser_ver,
974 m_device->getSceneManager(), m_env.getDayNightRatio());
977 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
980 // Delete objects of blocks in delete queue
981 for(core::map<v3s16, bool>::Iterator
982 i = abs_to_delete.getIterator();
983 i.atEnd() == false; i++)
985 v3s16 p = i.getNode()->getKey();
988 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
991 block->clearObjects();
992 // Remove from active blocks list
993 m_active_blocks.remove(p);
995 catch(InvalidPositionException &e)
997 dstream<<"WARNAING: Client: "
998 <<"Couldn't clear objects of active->inactive"
1000 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1001 <<" because block was not found"
1009 else if(command == TOCLIENT_TIME_OF_DAY)
1014 u16 time = readU16(&data[2]);
1015 time = time % 24000;
1016 m_time_of_day.set(time);
1017 //dstream<<"Client: time="<<time<<std::endl;
1019 else if(command == TOCLIENT_CHAT_MESSAGE)
1027 std::string datastring((char*)&data[2], datasize-2);
1028 std::istringstream is(datastring, std::ios_base::binary);
1031 is.read((char*)buf, 2);
1032 u16 len = readU16(buf);
1034 std::wstring message;
1035 for(u16 i=0; i<len; i++)
1037 is.read((char*)buf, 2);
1038 message += (wchar_t)readU16(buf);
1041 /*dstream<<"Client received chat message: "
1042 <<wide_to_narrow(message)<<std::endl;*/
1044 m_chat_queue.push_back(message);
1046 // Default to queueing it (for slow commands)
1049 JMutexAutoLock lock(m_incoming_queue_mutex);
1051 IncomingPacket packet(data, datasize);
1052 m_incoming_queue.push_back(packet);
1057 Returns true if there was something in queue
1059 bool Client::AsyncProcessPacket()
1061 DSTACK(__FUNCTION_NAME);
1063 try //for catching con::PeerNotFoundException
1068 JMutexAutoLock lock(m_con_mutex);
1069 // All data is coming from the server
1070 peer = m_con.GetPeer(PEER_ID_SERVER);
1073 u8 ser_version = m_server_ser_ver;
1075 IncomingPacket packet = getPacket();
1076 u8 *data = packet.m_data;
1077 u32 datasize = packet.m_datalen;
1079 // An empty packet means queue is empty
1087 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1089 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 bool r = AsyncProcessPacket();
1236 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1238 JMutexAutoLock lock(m_con_mutex);
1239 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1242 bool Client::isFetchingBlocks()
1244 JMutexAutoLock conlock(m_con_mutex);
1245 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1246 // Not really fetching but can't fetch more.
1247 if(peer == NULL) return true;
1249 con::Channel *channel = &(peer->channels[1]);
1251 NOTE: Channel 0 should always be used for fetching blocks,
1252 and for nothing else.
1254 if(channel->incoming_reliables.size() > 0)
1256 if(channel->outgoing_reliables.size() > 0)
1261 IncomingPacket Client::getPacket()
1263 JMutexAutoLock lock(m_incoming_queue_mutex);
1265 core::list<IncomingPacket>::Iterator i;
1266 // Refer to first one
1267 i = m_incoming_queue.begin();
1269 // If queue is empty, return empty packet
1270 if(i == m_incoming_queue.end()){
1271 IncomingPacket packet;
1275 // Pop out first packet and return it
1276 IncomingPacket packet = *i;
1277 m_incoming_queue.erase(i);
1281 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1282 v3s16 nodepos_oversurface, u16 item)
1284 if(connectedAndInitialized() == false){
1285 dout_client<<DTIME<<"Client::groundAction() "
1286 "cancelled (not connected)"
1295 [3] v3s16 nodepos_undersurface
1296 [9] v3s16 nodepos_abovesurface
1301 2: stop digging (all parameters ignored)
1303 u8 datasize = 2 + 1 + 6 + 6 + 2;
1304 SharedBuffer<u8> data(datasize);
1305 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1306 writeU8(&data[2], action);
1307 writeV3S16(&data[3], nodepos_undersurface);
1308 writeV3S16(&data[9], nodepos_oversurface);
1309 writeU16(&data[15], item);
1310 Send(0, data, true);
1313 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1315 if(connectedAndInitialized() == false){
1316 dout_client<<DTIME<<"Client::clickObject() "
1317 "cancelled (not connected)"
1323 [0] u16 command=TOSERVER_CLICK_OBJECT
1324 [2] u8 button (0=left, 1=right)
1329 u8 datasize = 2 + 1 + 6 + 2 + 2;
1330 SharedBuffer<u8> data(datasize);
1331 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1332 writeU8(&data[2], button);
1333 writeV3S16(&data[3], blockpos);
1334 writeS16(&data[9], id);
1335 writeU16(&data[11], item);
1336 Send(0, data, true);
1339 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1348 std::ostringstream os(std::ios_base::binary);
1352 writeU16(buf, TOSERVER_SIGNTEXT);
1353 os.write((char*)buf, 2);
1356 writeV3S16(buf, blockpos);
1357 os.write((char*)buf, 6);
1361 os.write((char*)buf, 2);
1363 u16 textlen = text.size();
1364 // Write text length
1365 writeS16(buf, textlen);
1366 os.write((char*)buf, 2);
1369 os.write((char*)text.c_str(), textlen);
1372 std::string s = os.str();
1373 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1375 Send(0, data, true);
1378 void Client::sendInventoryAction(InventoryAction *a)
1380 std::ostringstream os(std::ios_base::binary);
1384 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1385 os.write((char*)buf, 2);
1390 std::string s = os.str();
1391 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1393 Send(0, data, true);
1396 void Client::sendChatMessage(const std::wstring &message)
1398 std::ostringstream os(std::ios_base::binary);
1402 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1403 os.write((char*)buf, 2);
1406 writeU16(buf, message.size());
1407 os.write((char*)buf, 2);
1410 for(u32 i=0; i<message.size(); i++)
1414 os.write((char*)buf, 2);
1418 std::string s = os.str();
1419 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1421 Send(0, data, true);
1424 void Client::sendPlayerPos()
1426 JMutexAutoLock envlock(m_env_mutex);
1428 Player *myplayer = m_env.getLocalPlayer();
1429 if(myplayer == NULL)
1434 JMutexAutoLock lock(m_con_mutex);
1435 our_peer_id = m_con.GetPeerID();
1438 // Set peer id if not set already
1439 if(myplayer->peer_id == PEER_ID_NEW)
1440 myplayer->peer_id = our_peer_id;
1441 // Check that an existing peer_id is the same as the connection's
1442 assert(myplayer->peer_id == our_peer_id);
1444 v3f pf = myplayer->getPosition();
1445 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1446 v3f sf = myplayer->getSpeed();
1447 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1448 s32 pitch = myplayer->getPitch() * 100;
1449 s32 yaw = myplayer->getYaw() * 100;
1454 [2] v3s32 position*100
1455 [2+12] v3s32 speed*100
1456 [2+12+12] s32 pitch*100
1457 [2+12+12+4] s32 yaw*100
1460 SharedBuffer<u8> data(2+12+12+4+4);
1461 writeU16(&data[0], TOSERVER_PLAYERPOS);
1462 writeV3S32(&data[2], position);
1463 writeV3S32(&data[2+12], speed);
1464 writeS32(&data[2+12+12], pitch);
1465 writeS32(&data[2+12+12+4], yaw);
1467 // Send as unreliable
1468 Send(0, data, false);
1471 void Client::removeNode(v3s16 p)
1473 JMutexAutoLock envlock(m_env_mutex);
1475 core::map<v3s16, MapBlock*> modified_blocks;
1479 //TimeTaker t("removeNodeAndUpdate", m_device);
1480 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1482 catch(InvalidPositionException &e)
1486 for(core::map<v3s16, MapBlock * >::Iterator
1487 i = modified_blocks.getIterator();
1488 i.atEnd() == false; i++)
1490 v3s16 p = i.getNode()->getKey();
1491 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1495 void Client::addNode(v3s16 p, MapNode n)
1497 JMutexAutoLock envlock(m_env_mutex);
1499 core::map<v3s16, MapBlock*> modified_blocks;
1503 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1505 catch(InvalidPositionException &e)
1508 for(core::map<v3s16, MapBlock * >::Iterator
1509 i = modified_blocks.getIterator();
1510 i.atEnd() == false; i++)
1512 v3s16 p = i.getNode()->getKey();
1513 m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
1517 void Client::updateCamera(v3f pos, v3f dir)
1519 m_env.getMap().updateCamera(pos, dir);
1520 camera_position = pos;
1521 camera_direction = dir;
1524 MapNode Client::getNode(v3s16 p)
1526 JMutexAutoLock envlock(m_env_mutex);
1527 return m_env.getMap().getNode(p);
1530 /*void Client::getNode(v3s16 p, MapNode n)
1532 JMutexAutoLock envlock(m_env_mutex);
1533 m_env.getMap().setNode(p, n);
1536 /*f32 Client::getGroundHeight(v2s16 p)
1538 JMutexAutoLock envlock(m_env_mutex);
1539 return m_env.getMap().getGroundHeight(p);
1542 /*bool Client::isNodeUnderground(v3s16 p)
1544 JMutexAutoLock envlock(m_env_mutex);
1545 return m_env.getMap().isNodeUnderground(p);
1548 /*Player * Client::getLocalPlayer()
1550 JMutexAutoLock envlock(m_env_mutex);
1551 return m_env.getLocalPlayer();
1554 /*core::list<Player*> Client::getPlayers()
1556 JMutexAutoLock envlock(m_env_mutex);
1557 return m_env.getPlayers();
1560 v3f Client::getPlayerPosition()
1562 JMutexAutoLock envlock(m_env_mutex);
1563 LocalPlayer *player = m_env.getLocalPlayer();
1564 assert(player != NULL);
1565 return player->getPosition();
1568 void Client::setPlayerControl(PlayerControl &control)
1570 JMutexAutoLock envlock(m_env_mutex);
1571 LocalPlayer *player = m_env.getLocalPlayer();
1572 assert(player != NULL);
1573 player->control = control;
1576 // Returns true if the inventory of the local player has been
1577 // updated from the server. If it is true, it is set to false.
1578 bool Client::getLocalInventoryUpdated()
1580 // m_inventory_updated is behind envlock
1581 JMutexAutoLock envlock(m_env_mutex);
1582 bool updated = m_inventory_updated;
1583 m_inventory_updated = false;
1587 // Copies the inventory of the local player to parameter
1588 void Client::getLocalInventory(Inventory &dst)
1590 JMutexAutoLock envlock(m_env_mutex);
1591 Player *player = m_env.getLocalPlayer();
1592 assert(player != NULL);
1593 dst = player->inventory;
1596 MapBlockObject * Client::getSelectedObject(
1598 v3f from_pos_f_on_map,
1599 core::line3d<f32> shootline_on_map
1602 JMutexAutoLock envlock(m_env_mutex);
1604 core::array<DistanceSortedObject> objects;
1606 for(core::map<v3s16, bool>::Iterator
1607 i = m_active_blocks.getIterator();
1608 i.atEnd() == false; i++)
1610 v3s16 p = i.getNode()->getKey();
1612 MapBlock *block = NULL;
1615 block = m_env.getMap().getBlockNoCreate(p);
1617 catch(InvalidPositionException &e)
1622 // Calculate from_pos relative to block
1623 v3s16 block_pos_i_on_map = block->getPosRelative();
1624 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1625 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1627 block->getObjects(from_pos_f_on_block, max_d, objects);
1628 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1631 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1634 // After this, the closest object is the first in the array.
1637 for(u32 i=0; i<objects.size(); i++)
1639 MapBlockObject *obj = objects[i].obj;
1640 MapBlock *block = obj->getBlock();
1642 // Calculate shootline relative to block
1643 v3s16 block_pos_i_on_map = block->getPosRelative();
1644 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1645 core::line3d<f32> shootline_on_block(
1646 shootline_on_map.start - block_pos_f_on_map,
1647 shootline_on_map.end - block_pos_f_on_map
1650 if(obj->isSelected(shootline_on_block))
1652 //dstream<<"Returning selected object"<<std::endl;
1657 //dstream<<"No object selected; returning NULL."<<std::endl;
1661 void Client::printDebugInfo(std::ostream &os)
1663 //JMutexAutoLock lock1(m_fetchblock_mutex);
1664 JMutexAutoLock lock2(m_incoming_queue_mutex);
1666 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1667 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1668 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1672 /*s32 Client::getDayNightIndex()
1674 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1675 return m_daynight_i;
1678 u32 Client::getDayNightRatio()
1680 JMutexAutoLock envlock(m_env_mutex);
1681 return m_env.getDayNightRatio();
1684 /*void Client::updateSomeExpiredMeshes()
1686 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1690 JMutexAutoLock envlock(m_env_mutex);
1691 player = m_env.getLocalPlayer();
1694 u32 daynight_ratio = getDayNightRatio();
1696 v3f playerpos = player->getPosition();
1697 v3f playerspeed = player->getSpeed();
1699 v3s16 center_nodepos = floatToInt(playerpos);
1700 v3s16 center = getNodeBlockPos(center_nodepos);
1706 for(s16 d = 0; d <= d_max; d++)
1708 core::list<v3s16> list;
1709 getFacePositions(list, d);
1711 core::list<v3s16>::Iterator li;
1712 for(li=list.begin(); li!=list.end(); li++)
1714 v3s16 p = *li + center;
1715 MapBlock *block = NULL;
1718 //JMutexAutoLock envlock(m_env_mutex);
1719 block = m_env.getMap().getBlockNoCreate(p);
1721 catch(InvalidPositionException &e)
1728 if(block->getMeshExpired() == false)
1731 block->updateMesh(daynight_ratio);