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 BEGIN_DEBUG_EXCEPTION_HANDLER
39 //m_client->asyncStep();
41 //m_client->updateSomeExpiredMeshes();
43 bool was = m_client->AsyncProcessData();
49 END_DEBUG_EXCEPTION_HANDLER
55 IrrlichtDevice *device,
56 const char *playername,
57 MapDrawControl &control):
60 new ClientMap(this, control,
61 device->getSceneManager()->getRootSceneNode(),
62 device->getSceneManager(), 666),
63 device->getSceneManager()
65 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
67 camera_position(0,0,0),
68 camera_direction(0,0,1),
69 m_server_ser_ver(SER_FMT_VER_INVALID),
71 m_inventory_updated(false),
74 m_packetcounter_timer = 0.0;
75 m_delete_unused_sectors_timer = 0.0;
76 m_connection_reinit_timer = 0.0;
77 m_avg_rtt_timer = 0.0;
78 m_playerpos_send_timer = 0.0;
80 //m_fetchblock_mutex.Init();
81 m_incoming_queue_mutex.Init();
84 m_step_dtime_mutex.Init();
92 JMutexAutoLock envlock(m_env_mutex);
94 Player *player = new LocalPlayer();
96 player->updateName(playername);
98 m_env.addPlayer(player);
105 JMutexAutoLock conlock(m_con_mutex);
109 m_thread.setRun(false);
110 while(m_thread.IsRunning())
114 void Client::connect(Address address)
116 DSTACK(__FUNCTION_NAME);
117 JMutexAutoLock lock(m_con_mutex);
118 m_con.setTimeoutMs(0);
119 m_con.Connect(address);
122 bool Client::connectedAndInitialized()
124 JMutexAutoLock lock(m_con_mutex);
126 if(m_con.Connected() == false)
129 if(m_server_ser_ver == SER_FMT_VER_INVALID)
135 void Client::step(float dtime)
137 DSTACK(__FUNCTION_NAME);
144 //dstream<<"Client steps "<<dtime<<std::endl;
147 //TimeTaker timer("ReceiveAll()", m_device);
153 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
155 JMutexAutoLock lock(m_con_mutex);
156 m_con.RunTimeouts(dtime);
163 float &counter = m_packetcounter_timer;
169 dout_client<<"Client packetcounter (20s):"<<std::endl;
170 m_packetcounter.print(dout_client);
171 m_packetcounter.clear();
177 Delete unused sectors
179 NOTE: This jams the game for a while because deleting sectors
183 float &counter = m_delete_unused_sectors_timer;
191 JMutexAutoLock lock(m_env_mutex);
193 core::list<v3s16> deleted_blocks;
195 float delete_unused_sectors_timeout =
196 g_settings.getFloat("client_delete_unused_sectors_timeout");
198 // Delete sector blocks
199 /*u32 num = m_env.getMap().deleteUnusedSectors
200 (delete_unused_sectors_timeout,
201 true, &deleted_blocks);*/
203 // Delete whole sectors
204 u32 num = m_env.getMap().deleteUnusedSectors
205 (delete_unused_sectors_timeout,
206 false, &deleted_blocks);
210 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
211 <<" unused sectors"<<std::endl;*/
212 dstream<<DTIME<<"Client: Deleted "<<num
213 <<" unused sectors"<<std::endl;
219 // Env is locked so con can be locked.
220 JMutexAutoLock lock(m_con_mutex);
222 core::list<v3s16>::Iterator i = deleted_blocks.begin();
223 core::list<v3s16> sendlist;
226 if(sendlist.size() == 255 || i == deleted_blocks.end())
228 if(sendlist.size() == 0)
237 u32 replysize = 2+1+6*sendlist.size();
238 SharedBuffer<u8> reply(replysize);
239 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
240 reply[2] = sendlist.size();
242 for(core::list<v3s16>::Iterator
243 j = sendlist.begin();
244 j != sendlist.end(); j++)
246 writeV3S16(&reply[2+1+6*k], *j);
249 m_con.Send(PEER_ID_SERVER, 1, reply, true);
251 if(i == deleted_blocks.end())
257 sendlist.push_back(*i);
264 bool connected = connectedAndInitialized();
266 if(connected == false)
268 float &counter = m_connection_reinit_timer;
274 JMutexAutoLock envlock(m_env_mutex);
276 Player *myplayer = m_env.getLocalPlayer();
277 assert(myplayer != NULL);
279 // Send TOSERVER_INIT
280 // [0] u16 TOSERVER_INIT
281 // [2] u8 SER_FMT_VER_HIGHEST
282 // [3] u8[20] player_name
283 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE);
284 writeU16(&data[0], TOSERVER_INIT);
285 writeU8(&data[2], SER_FMT_VER_HIGHEST);
286 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
287 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
288 // Send as unreliable
289 Send(0, data, false);
292 // Not connected, return
297 Do stuff if connected
302 JMutexAutoLock lock(m_env_mutex);
304 // Control local player (0ms)
305 LocalPlayer *player = m_env.getLocalPlayer();
306 assert(player != NULL);
307 player->applyControl(dtime);
309 //TimeTaker envtimer("env step", m_device);
313 // Step active blocks
314 for(core::map<v3s16, bool>::Iterator
315 i = m_active_blocks.getIterator();
316 i.atEnd() == false; i++)
318 v3s16 p = i.getNode()->getKey();
320 MapBlock *block = NULL;
323 block = m_env.getMap().getBlockNoCreate(p);
324 block->stepObjects(dtime, false, m_env.getDayNightRatio());
326 catch(InvalidPositionException &e)
333 float &counter = m_avg_rtt_timer;
338 JMutexAutoLock lock(m_con_mutex);
339 // connectedAndInitialized() is true, peer exists.
340 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
341 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
345 float &counter = m_playerpos_send_timer;
355 JMutexAutoLock lock(m_step_dtime_mutex);
356 m_step_dtime += dtime;
361 float Client::asyncStep()
363 DSTACK(__FUNCTION_NAME);
364 //dstream<<"Client::asyncStep()"<<std::endl;
368 JMutexAutoLock lock1(m_step_dtime_mutex);
369 if(m_step_dtime < 0.001)
371 dtime = m_step_dtime;
380 // Virtual methods from con::PeerHandler
381 void Client::peerAdded(con::Peer *peer)
383 derr_client<<"Client::peerAdded(): peer->id="
384 <<peer->id<<std::endl;
386 void Client::deletingPeer(con::Peer *peer, bool timeout)
388 derr_client<<"Client::deletingPeer(): "
389 "Server Peer is getting deleted "
390 <<"(timeout="<<timeout<<")"<<std::endl;
393 void Client::ReceiveAll()
395 DSTACK(__FUNCTION_NAME);
401 catch(con::NoIncomingDataException &e)
405 catch(con::InvalidIncomingDataException &e)
407 dout_client<<DTIME<<"Client::ReceiveAll(): "
408 "InvalidIncomingDataException: what()="
409 <<e.what()<<std::endl;
414 void Client::Receive()
416 DSTACK(__FUNCTION_NAME);
417 u32 data_maxsize = 10000;
418 Buffer<u8> data(data_maxsize);
422 //TimeTaker t1("con mutex and receive", m_device);
423 JMutexAutoLock lock(m_con_mutex);
424 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
426 //TimeTaker t1("ProcessData", m_device);
427 ProcessData(*data, datasize, sender_peer_id);
431 sender_peer_id given to this shall be quaranteed to be a valid peer
433 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
435 DSTACK(__FUNCTION_NAME);
437 // Ignore packets that don't even fit a command
440 m_packetcounter.add(60000);
444 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
446 //dstream<<"Client: received command="<<command<<std::endl;
447 m_packetcounter.add((u16)command);
450 If this check is removed, be sure to change the queue
451 system to know the ids
453 if(sender_peer_id != PEER_ID_SERVER)
455 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
456 "coming from server: peer_id="<<sender_peer_id
463 JMutexAutoLock lock(m_con_mutex);
464 // All data is coming from the server
465 // PeerNotFoundException is handled by caller.
466 peer = m_con.GetPeer(PEER_ID_SERVER);
469 u8 ser_version = m_server_ser_ver;
471 //dstream<<"Client received command="<<(int)command<<std::endl;
473 // Execute fast commands straight away
475 if(command == TOCLIENT_INIT)
480 u8 deployed = data[2];
482 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
483 "deployed="<<((int)deployed&0xff)<<std::endl;
485 if(deployed < SER_FMT_VER_LOWEST
486 || deployed > SER_FMT_VER_HIGHEST)
488 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
489 <<"unsupported ser_fmt_ver"<<std::endl;
493 m_server_ser_ver = deployed;
495 // Get player position
496 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
497 if(datasize >= 2+1+6)
498 playerpos_s16 = readV3S16(&data[2+1]);
499 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
502 JMutexAutoLock envlock(m_env_mutex);
504 // Set player position
505 Player *player = m_env.getLocalPlayer();
506 assert(player != NULL);
507 player->setPosition(playerpos_f);
510 if(datasize >= 2+1+6+8)
513 m_map_seed = readU64(&data[2+1+6]);
514 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
519 SharedBuffer<u8> reply(replysize);
520 writeU16(&reply[0], TOSERVER_INIT2);
522 m_con.Send(PEER_ID_SERVER, 1, reply, true);
527 if(ser_version == SER_FMT_VER_INVALID)
529 dout_client<<DTIME<<"WARNING: Client: Server serialization"
530 " format invalid or not initialized."
531 " Skipping incoming command="<<command<<std::endl;
535 // Just here to avoid putting the two if's together when
536 // making some copypasta
539 if(command == TOCLIENT_REMOVENODE)
544 p.X = readS16(&data[2]);
545 p.Y = readS16(&data[4]);
546 p.Z = readS16(&data[6]);
548 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
550 // This will clear the cracking animation after digging
551 ((ClientMap&)m_env.getMap()).clearTempMod(p);
555 else if(command == TOCLIENT_ADDNODE)
557 if(datasize < 8 + MapNode::serializedLength(ser_version))
561 p.X = readS16(&data[2]);
562 p.Y = readS16(&data[4]);
563 p.Z = readS16(&data[6]);
565 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
568 n.deSerialize(&data[8], ser_version);
572 else if(command == TOCLIENT_PLAYERPOS)
574 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
578 JMutexAutoLock lock(m_con_mutex);
579 our_peer_id = m_con.GetPeerID();
581 // Cancel if we don't have a peer id
582 if(our_peer_id == PEER_ID_INEXISTENT){
583 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
590 JMutexAutoLock envlock(m_env_mutex);
592 u32 player_size = 2+12+12+4+4;
594 u32 player_count = (datasize-2) / player_size;
596 for(u32 i=0; i<player_count; i++)
598 u16 peer_id = readU16(&data[start]);
600 Player *player = m_env.getPlayer(peer_id);
602 // Skip if player doesn't exist
605 start += player_size;
609 // Skip if player is local player
610 if(player->isLocal())
612 start += player_size;
616 v3s32 ps = readV3S32(&data[start+2]);
617 v3s32 ss = readV3S32(&data[start+2+12]);
618 s32 pitch_i = readS32(&data[start+2+12+12]);
619 s32 yaw_i = readS32(&data[start+2+12+12+4]);
620 /*dstream<<"Client: got "
621 <<"pitch_i="<<pitch_i
622 <<" yaw_i="<<yaw_i<<std::endl;*/
623 f32 pitch = (f32)pitch_i / 100.0;
624 f32 yaw = (f32)yaw_i / 100.0;
625 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
626 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
627 player->setPosition(position);
628 player->setSpeed(speed);
629 player->setPitch(pitch);
632 /*dstream<<"Client: player "<<peer_id
634 <<" yaw="<<yaw<<std::endl;*/
636 start += player_size;
640 else if(command == TOCLIENT_PLAYERINFO)
644 JMutexAutoLock lock(m_con_mutex);
645 our_peer_id = m_con.GetPeerID();
647 // Cancel if we don't have a peer id
648 if(our_peer_id == PEER_ID_INEXISTENT){
649 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
655 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
658 JMutexAutoLock envlock(m_env_mutex);
660 u32 item_size = 2+PLAYERNAME_SIZE;
661 u32 player_count = (datasize-2) / item_size;
664 core::list<u16> players_alive;
665 for(u32 i=0; i<player_count; i++)
667 // Make sure the name ends in '\0'
668 data[start+2+20-1] = 0;
670 u16 peer_id = readU16(&data[start]);
672 players_alive.push_back(peer_id);
674 /*dstream<<DTIME<<"peer_id="<<peer_id
675 <<" name="<<((char*)&data[start+2])<<std::endl;*/
677 // Don't update the info of the local player
678 if(peer_id == our_peer_id)
684 Player *player = m_env.getPlayer(peer_id);
686 // Create a player if it doesn't exist
689 player = new RemotePlayer(
690 m_device->getSceneManager()->getRootSceneNode(),
693 player->peer_id = peer_id;
694 m_env.addPlayer(player);
695 dout_client<<DTIME<<"Client: Adding new player "
696 <<peer_id<<std::endl;
699 player->updateName((char*)&data[start+2]);
705 Remove those players from the environment that
706 weren't listed by the server.
708 //dstream<<DTIME<<"Removing dead players"<<std::endl;
709 core::list<Player*> players = m_env.getPlayers();
710 core::list<Player*>::Iterator ip;
711 for(ip=players.begin(); ip!=players.end(); ip++)
713 // Ingore local player
717 // Warn about a special case
718 if((*ip)->peer_id == 0)
720 dstream<<DTIME<<"WARNING: Client: Removing "
721 "dead player with id=0"<<std::endl;
724 bool is_alive = false;
725 core::list<u16>::Iterator i;
726 for(i=players_alive.begin(); i!=players_alive.end(); i++)
728 if((*ip)->peer_id == *i)
734 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
735 <<" is_alive="<<is_alive<<std::endl;*/
738 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
740 m_env.removePlayer((*ip)->peer_id);
744 else if(command == TOCLIENT_SECTORMETA)
749 [3...] v2s16 pos + sector metadata
754 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
757 JMutexAutoLock envlock(m_env_mutex);
759 std::string datastring((char*)&data[2], datasize-2);
760 std::istringstream is(datastring, std::ios_base::binary);
764 is.read((char*)buf, 1);
765 u16 sector_count = readU8(buf);
767 //dstream<<"sector_count="<<sector_count<<std::endl;
769 for(u16 i=0; i<sector_count; i++)
772 is.read((char*)buf, 4);
773 v2s16 pos = readV2S16(buf);
774 /*dstream<<"Client: deserializing sector at "
775 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
777 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
778 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
782 else if(command == TOCLIENT_INVENTORY)
787 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
790 //TimeTaker t2("mutex locking", m_device);
791 JMutexAutoLock envlock(m_env_mutex);
794 //TimeTaker t3("istringstream init", m_device);
795 std::string datastring((char*)&data[2], datasize-2);
796 std::istringstream is(datastring, std::ios_base::binary);
799 //m_env.printPlayers(dstream);
801 //TimeTaker t4("player get", m_device);
802 Player *player = m_env.getLocalPlayer();
803 assert(player != NULL);
806 //TimeTaker t1("inventory.deSerialize()", m_device);
807 player->inventory.deSerialize(is);
810 m_inventory_updated = true;
812 //dstream<<"Client got player inventory:"<<std::endl;
813 //player->inventory.print(dstream);
817 else if(command == TOCLIENT_OBJECTDATA)
820 // Strip command word and create a stringstream
821 std::string datastring((char*)&data[2], datasize-2);
822 std::istringstream is(datastring, std::ios_base::binary);
826 JMutexAutoLock envlock(m_env_mutex);
834 is.read((char*)buf, 2);
835 u16 playercount = readU16(buf);
837 for(u16 i=0; i<playercount; i++)
839 is.read((char*)buf, 2);
840 u16 peer_id = readU16(buf);
841 is.read((char*)buf, 12);
842 v3s32 p_i = readV3S32(buf);
843 is.read((char*)buf, 12);
844 v3s32 s_i = readV3S32(buf);
845 is.read((char*)buf, 4);
846 s32 pitch_i = readS32(buf);
847 is.read((char*)buf, 4);
848 s32 yaw_i = readS32(buf);
850 Player *player = m_env.getPlayer(peer_id);
852 // Skip if player doesn't exist
858 // Skip if player is local player
859 if(player->isLocal())
864 f32 pitch = (f32)pitch_i / 100.0;
865 f32 yaw = (f32)yaw_i / 100.0;
866 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
867 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
869 player->setPosition(position);
870 player->setSpeed(speed);
871 player->setPitch(pitch);
879 // Read active block count
880 is.read((char*)buf, 2);
881 u16 blockcount = readU16(buf);
883 // Initialize delete queue with all active blocks
884 core::map<v3s16, bool> abs_to_delete;
885 for(core::map<v3s16, bool>::Iterator
886 i = m_active_blocks.getIterator();
887 i.atEnd() == false; i++)
889 v3s16 p = i.getNode()->getKey();
891 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
892 <<" to abs_to_delete"
894 abs_to_delete.insert(p, true);
897 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
900 for(u16 i=0; i<blockcount; i++)
903 is.read((char*)buf, 6);
904 v3s16 p = readV3S16(buf);
905 // Get block from somewhere
906 MapBlock *block = NULL;
908 block = m_env.getMap().getBlockNoCreate(p);
910 catch(InvalidPositionException &e)
912 //TODO: Create a dummy block?
917 <<"Could not get block at blockpos "
918 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
919 <<"in TOCLIENT_OBJECTDATA. Ignoring "
920 <<"following block object data."
925 /*dstream<<"Client updating objects for block "
926 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
929 // Insert to active block list
930 m_active_blocks.insert(p, true);
932 // Remove from deletion queue
933 if(abs_to_delete.find(p) != NULL)
934 abs_to_delete.remove(p);
937 Update objects of block
939 NOTE: Be sure this is done in the main thread.
941 block->updateObjects(is, m_server_ser_ver,
942 m_device->getSceneManager(), m_env.getDayNightRatio());
945 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
948 // Delete objects of blocks in delete queue
949 for(core::map<v3s16, bool>::Iterator
950 i = abs_to_delete.getIterator();
951 i.atEnd() == false; i++)
953 v3s16 p = i.getNode()->getKey();
956 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
959 block->clearObjects();
960 // Remove from active blocks list
961 m_active_blocks.remove(p);
963 catch(InvalidPositionException &e)
965 dstream<<"WARNAING: Client: "
966 <<"Couldn't clear objects of active->inactive"
968 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
969 <<" because block was not found"
977 else if(command == TOCLIENT_TIME_OF_DAY)
982 u16 time = readU16(&data[2]);
984 m_time_of_day.set(time);
985 //dstream<<"Client: time="<<time<<std::endl;
995 u32 dr = time_to_daynight_ratio(m_time_of_day.get());
997 dstream<<"Client: time_of_day="<<m_time_of_day.get()
1001 if(dr != m_env.getDayNightRatio())
1003 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
1004 m_env.setDayNightRatio(dr);
1005 m_env.expireMeshes(true);
1010 else if(command == TOCLIENT_CHAT_MESSAGE)
1018 std::string datastring((char*)&data[2], datasize-2);
1019 std::istringstream is(datastring, std::ios_base::binary);
1022 is.read((char*)buf, 2);
1023 u16 len = readU16(buf);
1025 std::wstring message;
1026 for(u16 i=0; i<len; i++)
1028 is.read((char*)buf, 2);
1029 message += (wchar_t)readU16(buf);
1032 /*dstream<<"Client received chat message: "
1033 <<wide_to_narrow(message)<<std::endl;*/
1035 m_chat_queue.push_back(message);
1037 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1039 if(g_settings.getBool("enable_experimental"))
1043 u16 count of removed objects
1044 for all removed objects {
1047 u16 count of added objects
1048 for all added objects {
1051 u16 initialization data length
1052 string initialization data
1057 // Get all data except the command number
1058 std::string datastring((char*)&data[2], datasize-2);
1059 // Throw them in an istringstream
1060 std::istringstream is(datastring, std::ios_base::binary);
1064 // Read removed objects
1066 u16 removed_count = readU16((u8*)buf);
1067 for(u16 i=0; i<removed_count; i++)
1070 u16 id = readU16((u8*)buf);
1073 JMutexAutoLock envlock(m_env_mutex);
1074 m_env.removeActiveObject(id);
1078 // Read added objects
1080 u16 added_count = readU16((u8*)buf);
1081 for(u16 i=0; i<added_count; i++)
1084 u16 id = readU16((u8*)buf);
1086 u8 type = readU8((u8*)buf);
1087 std::string data = deSerializeLongString(is);
1090 JMutexAutoLock envlock(m_env_mutex);
1091 m_env.addActiveObject(id, type, data);
1096 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1098 if(g_settings.getBool("enable_experimental"))
1110 // Get all data except the command number
1111 std::string datastring((char*)&data[2], datasize-2);
1112 // Throw them in an istringstream
1113 std::istringstream is(datastring, std::ios_base::binary);
1115 while(is.eof() == false)
1119 u16 id = readU16((u8*)buf);
1123 u16 message_size = readU16((u8*)buf);
1124 std::string message;
1125 message.reserve(message_size);
1126 for(u16 i=0; i<message_size; i++)
1129 message.append(buf, 1);
1131 // Pass on to the environment
1133 JMutexAutoLock envlock(m_env_mutex);
1134 m_env.processActiveObjectMessage(id, message);
1139 // Default to queueing it (for slow commands)
1142 JMutexAutoLock lock(m_incoming_queue_mutex);
1144 IncomingPacket packet(data, datasize);
1145 m_incoming_queue.push_back(packet);
1150 Returns true if there was something in queue
1152 bool Client::AsyncProcessPacket()
1154 DSTACK(__FUNCTION_NAME);
1156 try //for catching con::PeerNotFoundException
1161 JMutexAutoLock lock(m_con_mutex);
1162 // All data is coming from the server
1163 peer = m_con.GetPeer(PEER_ID_SERVER);
1166 u8 ser_version = m_server_ser_ver;
1168 IncomingPacket packet = getPacket();
1169 u8 *data = packet.m_data;
1170 u32 datasize = packet.m_datalen;
1172 // An empty packet means queue is empty
1180 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1182 if(command == TOCLIENT_BLOCKDATA)
1184 // Ignore too small packet
1189 p.X = readS16(&data[2]);
1190 p.Y = readS16(&data[4]);
1191 p.Z = readS16(&data[6]);
1193 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1194 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1196 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1197 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1199 std::string datastring((char*)&data[8], datasize-8);
1200 std::istringstream istr(datastring, std::ios_base::binary);
1206 JMutexAutoLock envlock(m_env_mutex);
1208 v2s16 p2d(p.X, p.Z);
1209 sector = m_env.getMap().emergeSector(p2d);
1211 v2s16 sp = sector->getPos();
1214 dstream<<"ERROR: Got sector with getPos()="
1215 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1216 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1220 //assert(sector->getPos() == p2d);
1223 block = sector->getBlockNoCreate(p.Y);
1225 Update an existing block
1227 //dstream<<"Updating"<<std::endl;
1228 block->deSerialize(istr, ser_version);
1229 //block->setChangedFlag();
1231 catch(InvalidPositionException &e)
1236 //dstream<<"Creating new"<<std::endl;
1237 block = new MapBlock(&m_env.getMap(), p);
1238 block->deSerialize(istr, ser_version);
1239 sector->insertBlock(block);
1240 //block->setChangedFlag();
1254 u32 replysize = 2+1+6;
1255 SharedBuffer<u8> reply(replysize);
1256 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1258 writeV3S16(&reply[3], p);
1260 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1263 Update Mesh of this block and blocks at x-, y- and z-.
1264 Environment should not be locked as it interlocks with the
1265 main thread, from which is will want to retrieve textures.
1268 m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
1272 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1273 <<command<<std::endl;
1279 catch(con::PeerNotFoundException &e)
1281 /*dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1282 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;*/
1287 bool Client::AsyncProcessData()
1291 bool r = AsyncProcessPacket();
1298 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1300 JMutexAutoLock lock(m_con_mutex);
1301 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1304 IncomingPacket Client::getPacket()
1306 JMutexAutoLock lock(m_incoming_queue_mutex);
1308 core::list<IncomingPacket>::Iterator i;
1309 // Refer to first one
1310 i = m_incoming_queue.begin();
1312 // If queue is empty, return empty packet
1313 if(i == m_incoming_queue.end()){
1314 IncomingPacket packet;
1318 // Pop out first packet and return it
1319 IncomingPacket packet = *i;
1320 m_incoming_queue.erase(i);
1324 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1325 v3s16 nodepos_oversurface, u16 item)
1327 if(connectedAndInitialized() == false){
1328 dout_client<<DTIME<<"Client::groundAction() "
1329 "cancelled (not connected)"
1338 [3] v3s16 nodepos_undersurface
1339 [9] v3s16 nodepos_abovesurface
1344 2: stop digging (all parameters ignored)
1345 3: digging completed
1347 u8 datasize = 2 + 1 + 6 + 6 + 2;
1348 SharedBuffer<u8> data(datasize);
1349 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1350 writeU8(&data[2], action);
1351 writeV3S16(&data[3], nodepos_undersurface);
1352 writeV3S16(&data[9], nodepos_oversurface);
1353 writeU16(&data[15], item);
1354 Send(0, data, true);
1357 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1359 if(connectedAndInitialized() == false){
1360 dout_client<<DTIME<<"Client::clickObject() "
1361 "cancelled (not connected)"
1367 [0] u16 command=TOSERVER_CLICK_OBJECT
1368 [2] u8 button (0=left, 1=right)
1373 u8 datasize = 2 + 1 + 6 + 2 + 2;
1374 SharedBuffer<u8> data(datasize);
1375 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1376 writeU8(&data[2], button);
1377 writeV3S16(&data[3], blockpos);
1378 writeS16(&data[9], id);
1379 writeU16(&data[11], item);
1380 Send(0, data, true);
1383 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1392 std::ostringstream os(std::ios_base::binary);
1396 writeU16(buf, TOSERVER_SIGNTEXT);
1397 os.write((char*)buf, 2);
1400 writeV3S16(buf, blockpos);
1401 os.write((char*)buf, 6);
1405 os.write((char*)buf, 2);
1407 u16 textlen = text.size();
1408 // Write text length
1409 writeS16(buf, textlen);
1410 os.write((char*)buf, 2);
1413 os.write((char*)text.c_str(), textlen);
1416 std::string s = os.str();
1417 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1419 Send(0, data, true);
1422 void Client::sendInventoryAction(InventoryAction *a)
1424 std::ostringstream os(std::ios_base::binary);
1428 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1429 os.write((char*)buf, 2);
1434 std::string s = os.str();
1435 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1437 Send(0, data, true);
1440 void Client::sendChatMessage(const std::wstring &message)
1442 std::ostringstream os(std::ios_base::binary);
1446 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1447 os.write((char*)buf, 2);
1450 writeU16(buf, message.size());
1451 os.write((char*)buf, 2);
1454 for(u32 i=0; i<message.size(); i++)
1458 os.write((char*)buf, 2);
1462 std::string s = os.str();
1463 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1465 Send(0, data, true);
1468 void Client::sendPlayerPos()
1470 JMutexAutoLock envlock(m_env_mutex);
1472 Player *myplayer = m_env.getLocalPlayer();
1473 if(myplayer == NULL)
1478 JMutexAutoLock lock(m_con_mutex);
1479 our_peer_id = m_con.GetPeerID();
1482 // Set peer id if not set already
1483 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1484 myplayer->peer_id = our_peer_id;
1485 // Check that an existing peer_id is the same as the connection's
1486 assert(myplayer->peer_id == our_peer_id);
1488 v3f pf = myplayer->getPosition();
1489 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1490 v3f sf = myplayer->getSpeed();
1491 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1492 s32 pitch = myplayer->getPitch() * 100;
1493 s32 yaw = myplayer->getYaw() * 100;
1498 [2] v3s32 position*100
1499 [2+12] v3s32 speed*100
1500 [2+12+12] s32 pitch*100
1501 [2+12+12+4] s32 yaw*100
1504 SharedBuffer<u8> data(2+12+12+4+4);
1505 writeU16(&data[0], TOSERVER_PLAYERPOS);
1506 writeV3S32(&data[2], position);
1507 writeV3S32(&data[2+12], speed);
1508 writeS32(&data[2+12+12], pitch);
1509 writeS32(&data[2+12+12+4], yaw);
1511 // Send as unreliable
1512 Send(0, data, false);
1515 void Client::removeNode(v3s16 p)
1517 JMutexAutoLock envlock(m_env_mutex);
1519 core::map<v3s16, MapBlock*> modified_blocks;
1523 //TimeTaker t("removeNodeAndUpdate", m_device);
1524 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1526 catch(InvalidPositionException &e)
1530 for(core::map<v3s16, MapBlock * >::Iterator
1531 i = modified_blocks.getIterator();
1532 i.atEnd() == false; i++)
1534 v3s16 p = i.getNode()->getKey();
1535 m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1539 void Client::addNode(v3s16 p, MapNode n)
1541 JMutexAutoLock envlock(m_env_mutex);
1543 TimeTaker timer1("Client::addNode()");
1545 core::map<v3s16, MapBlock*> modified_blocks;
1549 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1550 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1552 catch(InvalidPositionException &e)
1555 TimeTaker timer2("Client::addNode(): updateMeshes");
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.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1566 void Client::updateCamera(v3f pos, v3f dir)
1568 m_env.getClientMap().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 NodeMetadata* Client::getNodeMetadataClone(v3s16 p)
1581 JMutexAutoLock envlock(m_env_mutex);
1582 return m_env.getMap().getNodeMetadataClone(p);
1585 v3f Client::getPlayerPosition()
1587 JMutexAutoLock envlock(m_env_mutex);
1588 LocalPlayer *player = m_env.getLocalPlayer();
1589 assert(player != NULL);
1590 return player->getPosition();
1593 void Client::setPlayerControl(PlayerControl &control)
1595 JMutexAutoLock envlock(m_env_mutex);
1596 LocalPlayer *player = m_env.getLocalPlayer();
1597 assert(player != NULL);
1598 player->control = control;
1601 // Returns true if the inventory of the local player has been
1602 // updated from the server. If it is true, it is set to false.
1603 bool Client::getLocalInventoryUpdated()
1605 // m_inventory_updated is behind envlock
1606 JMutexAutoLock envlock(m_env_mutex);
1607 bool updated = m_inventory_updated;
1608 m_inventory_updated = false;
1612 // Copies the inventory of the local player to parameter
1613 void Client::getLocalInventory(Inventory &dst)
1615 JMutexAutoLock envlock(m_env_mutex);
1616 Player *player = m_env.getLocalPlayer();
1617 assert(player != NULL);
1618 dst = player->inventory;
1621 MapBlockObject * Client::getSelectedObject(
1623 v3f from_pos_f_on_map,
1624 core::line3d<f32> shootline_on_map
1627 JMutexAutoLock envlock(m_env_mutex);
1629 core::array<DistanceSortedObject> objects;
1631 for(core::map<v3s16, bool>::Iterator
1632 i = m_active_blocks.getIterator();
1633 i.atEnd() == false; i++)
1635 v3s16 p = i.getNode()->getKey();
1637 MapBlock *block = NULL;
1640 block = m_env.getMap().getBlockNoCreate(p);
1642 catch(InvalidPositionException &e)
1647 // Calculate from_pos 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, BS);
1650 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1652 block->getObjects(from_pos_f_on_block, max_d, objects);
1653 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1656 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1659 // After this, the closest object is the first in the array.
1662 for(u32 i=0; i<objects.size(); i++)
1664 MapBlockObject *obj = objects[i].obj;
1665 MapBlock *block = obj->getBlock();
1667 // Calculate shootline relative to block
1668 v3s16 block_pos_i_on_map = block->getPosRelative();
1669 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1670 core::line3d<f32> shootline_on_block(
1671 shootline_on_map.start - block_pos_f_on_map,
1672 shootline_on_map.end - block_pos_f_on_map
1675 if(obj->isSelected(shootline_on_block))
1677 //dstream<<"Returning selected object"<<std::endl;
1682 //dstream<<"No object selected; returning NULL."<<std::endl;
1686 void Client::printDebugInfo(std::ostream &os)
1688 //JMutexAutoLock lock1(m_fetchblock_mutex);
1689 JMutexAutoLock lock2(m_incoming_queue_mutex);
1691 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1692 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1693 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1697 /*s32 Client::getDayNightIndex()
1699 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1700 return m_daynight_i;
1703 u32 Client::getDayNightRatio()
1705 JMutexAutoLock envlock(m_env_mutex);
1706 return m_env.getDayNightRatio();
1709 /*void Client::updateSomeExpiredMeshes()
1711 TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1715 JMutexAutoLock envlock(m_env_mutex);
1716 player = m_env.getLocalPlayer();
1719 u32 daynight_ratio = getDayNightRatio();
1721 v3f playerpos = player->getPosition();
1722 v3f playerspeed = player->getSpeed();
1724 v3s16 center_nodepos = floatToInt(playerpos, BS);
1725 v3s16 center = getNodeBlockPos(center_nodepos);
1731 for(s16 d = 0; d <= d_max; d++)
1733 core::list<v3s16> list;
1734 getFacePositions(list, d);
1736 core::list<v3s16>::Iterator li;
1737 for(li=list.begin(); li!=list.end(); li++)
1739 v3s16 p = *li + center;
1740 MapBlock *block = NULL;
1743 //JMutexAutoLock envlock(m_env_mutex);
1744 block = m_env.getMap().getBlockNoCreate(p);
1746 catch(InvalidPositionException &e)
1753 if(block->getMeshExpired() == false)
1756 block->updateMesh(daynight_ratio);