3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
30 #define sleep_ms(x) Sleep(x)
33 #define sleep_ms(x) usleep(x*1000)
36 void * ClientUpdateThread::Thread()
40 DSTACK(__FUNCTION_NAME);
42 #if CATCH_UNHANDLED_EXCEPTIONS
48 m_client->asyncStep();
50 bool was = m_client->AsyncProcessData();
55 #if CATCH_UNHANDLED_EXCEPTIONS
58 This is what has to be done in threads to get suitable debug info
60 catch(std::exception &e)
62 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
63 <<e.what()<<std::endl;
71 Client::Client(IrrlichtDevice *device,
72 const char *playername):
74 m_env(new ClientMap(this,
75 device->getSceneManager()->getRootSceneNode(),
76 device->getSceneManager(), 666),
78 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
80 camera_position(0,0,0),
81 camera_direction(0,0,1),
82 m_server_ser_ver(SER_FMT_VER_INVALID),
84 m_inventory_updated(false)
86 //m_fetchblock_mutex.Init();
87 m_incoming_queue_mutex.Init();
90 m_step_dtime_mutex.Init();
95 JMutexAutoLock envlock(m_env_mutex);
96 //m_env.getMap().StartUpdater();
98 Player *player = new LocalPlayer();
100 player->updateName(playername);
102 /*f32 y = BS*2 + BS*20;
103 player->setPosition(v3f(0, y, 0));*/
104 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
105 m_env.addPlayer(player);
111 m_thread.setRun(false);
112 while(m_thread.IsRunning())
116 void Client::connect(Address address)
118 DSTACK(__FUNCTION_NAME);
119 JMutexAutoLock lock(m_con_mutex);
120 m_con.setTimeoutMs(0);
121 m_con.Connect(address);
124 bool Client::connectedAndInitialized()
126 JMutexAutoLock lock(m_con_mutex);
128 if(m_con.Connected() == false)
131 if(m_server_ser_ver == SER_FMT_VER_INVALID)
137 void Client::step(float dtime)
139 DSTACK(__FUNCTION_NAME);
145 //dstream<<"Client steps "<<dtime<<std::endl;
148 //TimeTaker timer("ReceiveAll()", m_device);
154 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
156 JMutexAutoLock lock(m_con_mutex);
157 m_con.RunTimeouts(dtime);
164 static float counter = -0.001;
170 dout_client<<"Client packetcounter (20s):"<<std::endl;
171 m_packetcounter.print(dout_client);
172 m_packetcounter.clear();
178 Delete unused sectors
180 NOTE: This jams the game for a while because deleting sectors
184 static float counter = -0.001;
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 static float counter = -0.001;
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+20);
284 writeU16(&data[0], TOSERVER_INIT);
285 writeU8(&data[2], SER_FMT_VER_HIGHEST);
286 memcpy(&data[3], myplayer->getName(), 20);
287 // Send as unreliable
288 Send(0, data, false);
291 // Not connected, return
296 Do stuff if connected
301 JMutexAutoLock lock(m_env_mutex);
303 // Control local player (0ms)
304 LocalPlayer *player = m_env.getLocalPlayer();
305 assert(player != NULL);
306 player->applyControl(dtime);
308 //TimeTaker envtimer("env step", m_device);
312 // Step active blocks
313 for(core::map<v3s16, bool>::Iterator
314 i = m_active_blocks.getIterator();
315 i.atEnd() == false; i++)
317 v3s16 p = i.getNode()->getKey();
319 MapBlock *block = NULL;
322 block = m_env.getMap().getBlockNoCreate(p);
323 block->stepObjects(dtime, false);
325 catch(InvalidPositionException &e)
332 // Fetch some nearby blocks
337 static float counter = 0.0;
342 JMutexAutoLock lock(m_con_mutex);
343 // connectedAndInitialized() is true, peer exists.
344 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
345 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
349 // Update at reasonable intervals (0.2s)
350 static float counter = 0.0;
361 Clear old entries from fetchblock history
364 JMutexAutoLock lock(m_fetchblock_mutex);
366 core::list<v3s16> remove_queue;
367 core::map<v3s16, float>::Iterator i;
368 i = m_fetchblock_history.getIterator();
369 for(; i.atEnd() == false; i++)
371 float value = i.getNode()->getValue();
373 i.getNode()->setValue(value);
375 remove_queue.push_back(i.getNode()->getKey());
377 core::list<v3s16>::Iterator j;
378 j = remove_queue.begin();
379 for(; j != remove_queue.end(); j++)
381 m_fetchblock_history.remove(*j);
387 JMutexAutoLock lock(m_step_dtime_mutex);
388 m_step_dtime += dtime;
400 float Client::asyncStep()
402 DSTACK(__FUNCTION_NAME);
403 //dstream<<"Client::asyncStep()"<<std::endl;
407 JMutexAutoLock lock1(m_step_dtime_mutex);
408 if(m_step_dtime < 0.001)
410 dtime = m_step_dtime;
418 // Virtual methods from con::PeerHandler
419 void Client::peerAdded(con::Peer *peer)
421 derr_client<<"Client::peerAdded(): peer->id="
422 <<peer->id<<std::endl;
424 void Client::deletingPeer(con::Peer *peer, bool timeout)
426 derr_client<<"Client::deletingPeer(): "
427 "Server Peer is getting deleted "
428 <<"(timeout="<<timeout<<")"<<std::endl;
431 void Client::ReceiveAll()
433 DSTACK(__FUNCTION_NAME);
439 catch(con::NoIncomingDataException &e)
443 catch(con::InvalidIncomingDataException &e)
445 dout_client<<DTIME<<"Client::ReceiveAll(): "
446 "InvalidIncomingDataException: what()="
447 <<e.what()<<std::endl;
454 void Client::Receive()
456 DSTACK(__FUNCTION_NAME);
457 u32 data_maxsize = 10000;
458 Buffer<u8> data(data_maxsize);
462 //TimeTaker t1("con mutex and receive", m_device);
463 JMutexAutoLock lock(m_con_mutex);
464 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
466 //TimeTaker t1("ProcessData", m_device);
467 ProcessData(*data, datasize, sender_peer_id);
471 sender_peer_id given to this shall be quaranteed to be a valid peer
473 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
475 DSTACK(__FUNCTION_NAME);
477 // Ignore packets that don't even fit a command
480 m_packetcounter.add(60000);
484 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
486 //dstream<<"Client: received command="<<command<<std::endl;
487 m_packetcounter.add((u16)command);
490 If this check is removed, be sure to change the queue
491 system to know the ids
493 if(sender_peer_id != PEER_ID_SERVER)
495 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
496 "coming from server: peer_id="<<sender_peer_id
503 JMutexAutoLock lock(m_con_mutex);
504 // All data is coming from the server
505 // PeerNotFoundException is handled by caller.
506 peer = m_con.GetPeer(PEER_ID_SERVER);
509 u8 ser_version = m_server_ser_ver;
511 //dstream<<"Client received command="<<(int)command<<std::endl;
513 // Execute fast commands straight away
515 if(command == TOCLIENT_INIT)
520 u8 deployed = data[2];
522 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
523 "deployed="<<((int)deployed&0xff)<<std::endl;
525 if(deployed < SER_FMT_VER_LOWEST
526 || deployed > SER_FMT_VER_HIGHEST)
528 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
529 <<"unsupported ser_fmt_ver"<<std::endl;
533 m_server_ser_ver = deployed;
535 // Get player position
536 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
537 if(datasize >= 2+1+6)
538 playerpos_s16 = readV3S16(&data[2+1]);
539 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
542 JMutexAutoLock envlock(m_env_mutex);
544 // Set player position
545 Player *player = m_env.getLocalPlayer();
546 assert(player != NULL);
547 player->setPosition(playerpos_f);
552 SharedBuffer<u8> reply(replysize);
553 writeU16(&reply[0], TOSERVER_INIT2);
555 m_con.Send(PEER_ID_SERVER, 1, reply, true);
560 if(ser_version == SER_FMT_VER_INVALID)
562 dout_client<<DTIME<<"WARNING: Client: Server serialization"
563 " format invalid or not initialized."
564 " Skipping incoming command="<<command<<std::endl;
568 // Just here to avoid putting the two if's together when
569 // making some copypasta
572 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_NEW){
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_NEW){
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);
936 // Update objects of block
937 block->updateObjects(is, m_server_ser_ver,
938 m_device->getSceneManager());
941 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
944 // Delete objects of blocks in delete queue
945 for(core::map<v3s16, bool>::Iterator
946 i = abs_to_delete.getIterator();
947 i.atEnd() == false; i++)
949 v3s16 p = i.getNode()->getKey();
952 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
955 block->clearObjects();
956 // Remove from active blocks list
957 m_active_blocks.remove(p);
959 catch(InvalidPositionException &e)
961 dstream<<"WARNAING: Client: "
962 <<"Couldn't clear objects of active->inactive"
964 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
965 <<" because block was not found"
973 // Default to queueing it (for slow commands)
976 JMutexAutoLock lock(m_incoming_queue_mutex);
978 IncomingPacket packet(data, datasize);
979 m_incoming_queue.push_back(packet);
984 Returns true if there was something in queue
986 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
988 DSTACK(__FUNCTION_NAME);
990 try //for catching con::PeerNotFoundException
995 JMutexAutoLock lock(m_con_mutex);
996 // All data is coming from the server
997 peer = m_con.GetPeer(PEER_ID_SERVER);
1000 u8 ser_version = m_server_ser_ver;
1002 IncomingPacket packet = getPacket();
1003 u8 *data = packet.m_data;
1004 u32 datasize = packet.m_datalen;
1006 // An empty packet means queue is empty
1014 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1016 if(command == TOCLIENT_REMOVENODE)
1021 p.X = readS16(&data[2]);
1022 p.Y = readS16(&data[4]);
1023 p.Z = readS16(&data[6]);
1025 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1027 core::map<v3s16, MapBlock*> modified_blocks;
1031 JMutexAutoLock envlock(m_env_mutex);
1032 //TimeTaker t("removeNodeAndUpdate", m_device);
1033 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1035 catch(InvalidPositionException &e)
1039 for(core::map<v3s16, MapBlock * >::Iterator
1040 i = modified_blocks.getIterator();
1041 i.atEnd() == false; i++)
1043 v3s16 p = i.getNode()->getKey();
1044 //m_env.getMap().updateMeshes(p);
1045 mesh_updater.add(p);
1048 else if(command == TOCLIENT_ADDNODE)
1050 if(datasize < 8 + MapNode::serializedLength(ser_version))
1054 p.X = readS16(&data[2]);
1055 p.Y = readS16(&data[4]);
1056 p.Z = readS16(&data[6]);
1058 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1061 n.deSerialize(&data[8], ser_version);
1063 core::map<v3s16, MapBlock*> modified_blocks;
1067 JMutexAutoLock envlock(m_env_mutex);
1068 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1070 catch(InvalidPositionException &e)
1073 for(core::map<v3s16, MapBlock * >::Iterator
1074 i = modified_blocks.getIterator();
1075 i.atEnd() == false; i++)
1077 v3s16 p = i.getNode()->getKey();
1078 //m_env.getMap().updateMeshes(p);
1079 mesh_updater.add(p);
1082 else if(command == TOCLIENT_BLOCKDATA)
1084 // Ignore too small packet
1087 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1091 p.X = readS16(&data[2]);
1092 p.Y = readS16(&data[4]);
1093 p.Z = readS16(&data[6]);
1095 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1096 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1098 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1099 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1101 std::string datastring((char*)&data[8], datasize-8);
1102 std::istringstream istr(datastring, std::ios_base::binary);
1108 JMutexAutoLock envlock(m_env_mutex);
1110 v2s16 p2d(p.X, p.Z);
1111 sector = m_env.getMap().emergeSector(p2d);
1113 v2s16 sp = sector->getPos();
1116 dstream<<"ERROR: Got sector with getPos()="
1117 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1118 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1122 //assert(sector->getPos() == p2d);
1125 block = sector->getBlockNoCreate(p.Y);
1127 Update an existing block
1129 //dstream<<"Updating"<<std::endl;
1130 block->deSerialize(istr, ser_version);
1131 //block->setChangedFlag();
1133 catch(InvalidPositionException &e)
1138 //dstream<<"Creating new"<<std::endl;
1139 block = new MapBlock(&m_env.getMap(), p);
1140 block->deSerialize(istr, ser_version);
1141 sector->insertBlock(block);
1142 //block->setChangedFlag();
1146 mod.type = NODEMOD_CHANGECONTENT;
1147 mod.param = CONTENT_MESE;
1148 block->setTempMod(v3s16(8,10,8), mod);
1149 block->setTempMod(v3s16(8,9,8), mod);
1150 block->setTempMod(v3s16(8,8,8), mod);
1151 block->setTempMod(v3s16(8,7,8), mod);
1152 block->setTempMod(v3s16(8,6,8), mod);*/
1156 Well, this is a dumb way to do it, they should just
1157 be drawn as separate objects.
1162 mod.type = NODEMOD_CHANGECONTENT;
1163 mod.param = CONTENT_CLOUD;
1166 for(p2.X=3; p2.X<=13; p2.X++)
1167 for(p2.Z=3; p2.Z<=13; p2.Z++)
1169 block->setTempMod(p2, mod);
1176 // Old version has zero lighting, update it.
1177 if(ser_version == 0 || ser_version == 1)
1179 derr_client<<"Client: Block in old format: "
1180 "Calculating lighting"<<std::endl;
1181 core::map<v3s16, MapBlock*> blocks_changed;
1182 blocks_changed.insert(block->getPos(), block);
1183 core::map<v3s16, MapBlock*> modified_blocks;
1184 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1188 Update Mesh of this block and blocks at x-, y- and z-
1191 //m_env.getMap().updateMeshes(block->getPos());
1192 mesh_updater.add(block->getPos());
1204 u32 replysize = 2+1+6;
1205 SharedBuffer<u8> reply(replysize);
1206 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1208 writeV3S16(&reply[3], p);
1210 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1217 JMutexAutoLock lock(m_fetchblock_mutex);
1219 if(m_fetchblock_history.find(p) != NULL)
1221 m_fetchblock_history.remove(p);
1235 u32 replysize = 2+1+6;
1236 SharedBuffer<u8> reply(replysize);
1237 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1239 writeV3S16(&reply[3], p);
1241 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1248 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1249 <<command<<std::endl;
1255 catch(con::PeerNotFoundException &e)
1257 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1258 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1263 bool Client::AsyncProcessData()
1267 // We want to update the meshes as soon as a single packet has
1269 LazyMeshUpdater mesh_updater(&m_env);
1270 bool r = AsyncProcessPacket(mesh_updater);
1276 /*LazyMeshUpdater mesh_updater(&m_env);
1279 bool r = AsyncProcessPacket(mesh_updater);
1287 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1289 JMutexAutoLock lock(m_con_mutex);
1290 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1294 void Client::fetchBlock(v3s16 p, u8 flags)
1296 if(connectedAndInitialized() == false)
1297 throw ClientNotReadyException
1298 ("ClientNotReadyException: connectedAndInitialized() == false");
1300 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1301 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1303 JMutexAutoLock conlock(m_con_mutex);
1305 SharedBuffer<u8> data(9);
1306 writeU16(&data[0], TOSERVER_GETBLOCK);
1307 writeS16(&data[2], p.X);
1308 writeS16(&data[4], p.Y);
1309 writeS16(&data[6], p.Z);
1310 writeU8(&data[8], flags);
1311 m_con.Send(PEER_ID_SERVER, 1, data, true);
1315 Calls fetchBlock() on some nearby missing blocks.
1317 Returns when any of various network load indicators go over limit.
1319 Does nearly the same thing as the old updateChangedVisibleArea()
1321 void Client::fetchBlocks()
1323 if(connectedAndInitialized() == false)
1324 throw ClientNotReadyException
1325 ("ClientNotReadyException: connectedAndInitialized() == false");
1329 bool Client::isFetchingBlocks()
1331 JMutexAutoLock conlock(m_con_mutex);
1332 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1333 // Not really fetching but can't fetch more.
1334 if(peer == NULL) return true;
1336 con::Channel *channel = &(peer->channels[1]);
1338 NOTE: Channel 0 should always be used for fetching blocks,
1339 and for nothing else.
1341 if(channel->incoming_reliables.size() > 0)
1343 if(channel->outgoing_reliables.size() > 0)
1348 IncomingPacket Client::getPacket()
1350 JMutexAutoLock lock(m_incoming_queue_mutex);
1352 core::list<IncomingPacket>::Iterator i;
1353 // Refer to first one
1354 i = m_incoming_queue.begin();
1356 // If queue is empty, return empty packet
1357 if(i == m_incoming_queue.end()){
1358 IncomingPacket packet;
1362 // Pop out first packet and return it
1363 IncomingPacket packet = *i;
1364 m_incoming_queue.erase(i);
1369 void Client::removeNode(v3s16 nodepos)
1371 if(connectedAndInitialized() == false){
1372 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1377 // Test that the position exists
1379 JMutexAutoLock envlock(m_env_mutex);
1380 m_env.getMap().getNode(nodepos);
1382 catch(InvalidPositionException &e)
1384 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1389 SharedBuffer<u8> data(8);
1390 writeU16(&data[0], TOSERVER_REMOVENODE);
1391 writeS16(&data[2], nodepos.X);
1392 writeS16(&data[4], nodepos.Y);
1393 writeS16(&data[6], nodepos.Z);
1394 Send(0, data, true);
1397 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1399 if(connectedAndInitialized() == false){
1400 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1401 "cancelled (not connected)"
1406 // Test that the position exists
1408 JMutexAutoLock envlock(m_env_mutex);
1409 m_env.getMap().getNode(nodepos);
1411 catch(InvalidPositionException &e)
1413 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1418 //u8 ser_version = m_server_ser_ver;
1420 // SUGGESTION: The validity of the operation could be checked here too
1422 u8 datasize = 2 + 6 + 2;
1423 SharedBuffer<u8> data(datasize);
1424 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1425 writeS16(&data[2], nodepos.X);
1426 writeS16(&data[4], nodepos.Y);
1427 writeS16(&data[6], nodepos.Z);
1428 writeU16(&data[8], i);
1429 Send(0, data, true);
1433 void Client::pressGround(u8 button, v3s16 nodepos_undersurface,
1434 v3s16 nodepos_oversurface, u16 item)
1436 if(connectedAndInitialized() == false){
1437 dout_client<<DTIME<<"Client::pressGround() "
1438 "cancelled (not connected)"
1447 [3] v3s16 nodepos_undersurface
1448 [9] v3s16 nodepos_abovesurface
1453 2: stop digging (all parameters ignored)
1455 u8 datasize = 2 + 1 + 6 + 6 + 2;
1456 SharedBuffer<u8> data(datasize);
1457 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1458 writeU8(&data[2], button);
1459 writeV3S16(&data[3], nodepos_undersurface);
1460 writeV3S16(&data[9], nodepos_oversurface);
1461 writeU16(&data[15], item);
1462 Send(0, data, true);
1465 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1467 if(connectedAndInitialized() == false){
1468 dout_client<<DTIME<<"Client::clickObject() "
1469 "cancelled (not connected)"
1476 [2] u8 button (0=left, 1=right)
1481 u8 datasize = 2 + 1 + 6 + 2 + 2;
1482 SharedBuffer<u8> data(datasize);
1483 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1484 writeU8(&data[2], button);
1485 writeV3S16(&data[3], blockpos);
1486 writeS16(&data[9], id);
1487 writeU16(&data[11], item);
1488 Send(0, data, true);
1491 void Client::stopDigging()
1493 if(connectedAndInitialized() == false){
1494 dout_client<<DTIME<<"Client::release() "
1495 "cancelled (not connected)"
1504 [3] v3s16 nodepos_undersurface
1505 [9] v3s16 nodepos_abovesurface
1510 2: stop digging (all parameters ignored)
1512 u8 datasize = 2 + 1 + 6 + 6 + 2;
1513 SharedBuffer<u8> data(datasize);
1514 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1515 writeU8(&data[2], 2);
1516 writeV3S16(&data[3], v3s16(0,0,0));
1517 writeV3S16(&data[9], v3s16(0,0,0));
1518 writeU16(&data[15], 0);
1519 Send(0, data, true);
1522 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1531 std::ostringstream os(std::ios_base::binary);
1535 writeU16(buf, TOSERVER_SIGNTEXT);
1536 os.write((char*)buf, 2);
1539 writeV3S16(buf, blockpos);
1540 os.write((char*)buf, 6);
1544 os.write((char*)buf, 2);
1546 u16 textlen = text.size();
1547 // Write text length
1548 writeS16(buf, textlen);
1549 os.write((char*)buf, 2);
1552 os.write((char*)text.c_str(), textlen);
1555 std::string s = os.str();
1556 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1558 Send(0, data, true);
1561 void Client::sendPlayerPos()
1563 JMutexAutoLock envlock(m_env_mutex);
1565 Player *myplayer = m_env.getLocalPlayer();
1566 if(myplayer == NULL)
1571 JMutexAutoLock lock(m_con_mutex);
1572 our_peer_id = m_con.GetPeerID();
1575 // Set peer id if not set already
1576 if(myplayer->peer_id == PEER_ID_NEW)
1577 myplayer->peer_id = our_peer_id;
1578 // Check that an existing peer_id is the same as the connection's
1579 assert(myplayer->peer_id == our_peer_id);
1581 v3f pf = myplayer->getPosition();
1582 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1583 v3f sf = myplayer->getSpeed();
1584 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1585 s32 pitch = myplayer->getPitch() * 100;
1586 s32 yaw = myplayer->getYaw() * 100;
1591 [2] v3s32 position*100
1592 [2+12] v3s32 speed*100
1593 [2+12+12] s32 pitch*100
1594 [2+12+12+4] s32 yaw*100
1597 SharedBuffer<u8> data(2+12+12+4+4);
1598 writeU16(&data[0], TOSERVER_PLAYERPOS);
1599 writeV3S32(&data[2], position);
1600 writeV3S32(&data[2+12], speed);
1601 writeS32(&data[2+12+12], pitch);
1602 writeS32(&data[2+12+12+4], yaw);
1604 // Send as unreliable
1605 Send(0, data, false);
1609 void Client::updateCamera(v3f pos, v3f dir)
1611 m_env.getMap().updateCamera(pos, dir);
1612 camera_position = pos;
1613 camera_direction = dir;
1616 MapNode Client::getNode(v3s16 p)
1618 JMutexAutoLock envlock(m_env_mutex);
1619 return m_env.getMap().getNode(p);
1622 /*f32 Client::getGroundHeight(v2s16 p)
1624 JMutexAutoLock envlock(m_env_mutex);
1625 return m_env.getMap().getGroundHeight(p);
1628 bool Client::isNodeUnderground(v3s16 p)
1630 JMutexAutoLock envlock(m_env_mutex);
1631 return m_env.getMap().isNodeUnderground(p);
1634 /*Player * Client::getLocalPlayer()
1636 JMutexAutoLock envlock(m_env_mutex);
1637 return m_env.getLocalPlayer();
1640 /*core::list<Player*> Client::getPlayers()
1642 JMutexAutoLock envlock(m_env_mutex);
1643 return m_env.getPlayers();
1646 v3f Client::getPlayerPosition()
1648 JMutexAutoLock envlock(m_env_mutex);
1649 LocalPlayer *player = m_env.getLocalPlayer();
1650 assert(player != NULL);
1651 return player->getPosition();
1654 void Client::setPlayerControl(PlayerControl &control)
1656 JMutexAutoLock envlock(m_env_mutex);
1657 LocalPlayer *player = m_env.getLocalPlayer();
1658 assert(player != NULL);
1659 player->control = control;
1662 // Returns true if the inventory of the local player has been
1663 // updated from the server. If it is true, it is set to false.
1664 bool Client::getLocalInventoryUpdated()
1666 // m_inventory_updated is behind envlock
1667 JMutexAutoLock envlock(m_env_mutex);
1668 bool updated = m_inventory_updated;
1669 m_inventory_updated = false;
1673 // Copies the inventory of the local player to parameter
1674 void Client::getLocalInventory(Inventory &dst)
1676 JMutexAutoLock envlock(m_env_mutex);
1677 Player *player = m_env.getLocalPlayer();
1678 assert(player != NULL);
1679 dst = player->inventory;
1682 MapBlockObject * Client::getSelectedObject(
1684 v3f from_pos_f_on_map,
1685 core::line3d<f32> shootline_on_map
1688 JMutexAutoLock envlock(m_env_mutex);
1690 core::array<DistanceSortedObject> objects;
1692 for(core::map<v3s16, bool>::Iterator
1693 i = m_active_blocks.getIterator();
1694 i.atEnd() == false; i++)
1696 v3s16 p = i.getNode()->getKey();
1698 MapBlock *block = NULL;
1701 block = m_env.getMap().getBlockNoCreate(p);
1703 catch(InvalidPositionException &e)
1708 // Calculate from_pos relative to block
1709 v3s16 block_pos_i_on_map = block->getPosRelative();
1710 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1711 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1713 block->getObjects(from_pos_f_on_block, max_d, objects);
1714 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1717 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1720 // After this, the closest object is the first in the array.
1723 for(u32 i=0; i<objects.size(); i++)
1725 MapBlockObject *obj = objects[i].obj;
1726 MapBlock *block = obj->getBlock();
1728 // Calculate shootline relative to block
1729 v3s16 block_pos_i_on_map = block->getPosRelative();
1730 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1731 core::line3d<f32> shootline_on_block(
1732 shootline_on_map.start - block_pos_f_on_map,
1733 shootline_on_map.end - block_pos_f_on_map
1736 if(obj->isSelected(shootline_on_block))
1738 //dstream<<"Returning selected object"<<std::endl;
1743 //dstream<<"No object selected; returning NULL."<<std::endl;
1747 void Client::printDebugInfo(std::ostream &os)
1749 //JMutexAutoLock lock1(m_fetchblock_mutex);
1750 JMutexAutoLock lock2(m_incoming_queue_mutex);
1752 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1753 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1754 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()