4 #include "clientserver.h"
5 #include "jmutexautolock.h"
11 #define sleep_ms(x) Sleep(x)
14 #define sleep_ms(x) usleep(x*1000)
17 void * ClientUpdateThread::Thread()
21 DSTACK(__FUNCTION_NAME);
23 #if CATCH_UNHANDLED_EXCEPTIONS
29 m_client->asyncStep();
31 bool was = m_client->AsyncProcessData();
36 #if CATCH_UNHANDLED_EXCEPTIONS
39 This is what has to be done in threads to get suitable debug info
41 catch(std::exception &e)
43 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
44 <<e.what()<<std::endl;
52 Client::Client(IrrlichtDevice *device, video::SMaterial *materials,
53 float delete_unused_sectors_timeout,
54 const char *playername):
56 m_env(new ClientMap(this, materials,
57 device->getSceneManager()->getRootSceneNode(),
58 device->getSceneManager(), 666),
60 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
62 camera_position(0,0,0),
63 camera_direction(0,0,1),
64 m_server_ser_ver(SER_FMT_VER_INVALID),
66 m_delete_unused_sectors_timeout(delete_unused_sectors_timeout),
67 m_inventory_updated(false)
69 //m_fetchblock_mutex.Init();
70 m_incoming_queue_mutex.Init();
73 m_step_dtime_mutex.Init();
78 JMutexAutoLock envlock(m_env_mutex);
79 //m_env.getMap().StartUpdater();
81 Player *player = new LocalPlayer();
83 player->updateName(playername);
85 /*f32 y = BS*2 + BS*20;
86 player->setPosition(v3f(0, y, 0));*/
87 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
88 m_env.addPlayer(player);
94 m_thread.setRun(false);
95 while(m_thread.IsRunning())
99 void Client::connect(Address address)
101 DSTACK(__FUNCTION_NAME);
102 JMutexAutoLock lock(m_con_mutex);
103 m_con.setTimeoutMs(0);
104 m_con.Connect(address);
107 bool Client::connectedAndInitialized()
109 JMutexAutoLock lock(m_con_mutex);
111 if(m_con.Connected() == false)
114 if(m_server_ser_ver == SER_FMT_VER_INVALID)
120 void Client::step(float dtime)
122 DSTACK(__FUNCTION_NAME);
128 //dstream<<"Client steps "<<dtime<<std::endl;
131 //TimeTaker timer("ReceiveAll()", m_device);
137 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
139 JMutexAutoLock lock(m_con_mutex);
140 m_con.RunTimeouts(dtime);
147 static float counter = -0.001;
153 dout_client<<"Client packetcounter (20s):"<<std::endl;
154 m_packetcounter.print(dout_client);
155 m_packetcounter.clear();
161 Delete unused sectors
163 NOTE: This jams the game for a while because deleting sectors
167 static float counter = -0.001;
174 JMutexAutoLock lock(m_env_mutex);
176 core::list<v3s16> deleted_blocks;
178 // Delete sector blocks
179 /*u32 num = m_env.getMap().deleteUnusedSectors
180 (m_delete_unused_sectors_timeout,
181 true, &deleted_blocks);*/
183 // Delete whole sectors
184 u32 num = m_env.getMap().deleteUnusedSectors
185 (m_delete_unused_sectors_timeout,
186 false, &deleted_blocks);
190 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
191 <<" unused sectors"<<std::endl;*/
192 dstream<<DTIME<<"Client: Deleted "<<num
193 <<" unused sectors"<<std::endl;
199 // Env is locked so con can be locked.
200 JMutexAutoLock lock(m_con_mutex);
202 core::list<v3s16>::Iterator i = deleted_blocks.begin();
203 core::list<v3s16> sendlist;
206 if(sendlist.size() == 255 || i == deleted_blocks.end())
208 if(sendlist.size() == 0)
217 u32 replysize = 2+1+6*sendlist.size();
218 SharedBuffer<u8> reply(replysize);
219 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
220 reply[2] = sendlist.size();
222 for(core::list<v3s16>::Iterator
223 j = sendlist.begin();
224 j != sendlist.end(); j++)
226 writeV3S16(&reply[2+1+6*k], *j);
229 m_con.Send(PEER_ID_SERVER, 1, reply, true);
231 if(i == deleted_blocks.end())
237 sendlist.push_back(*i);
244 bool connected = connectedAndInitialized();
246 if(connected == false)
248 static float counter = -0.001;
254 JMutexAutoLock envlock(m_env_mutex);
256 Player *myplayer = m_env.getLocalPlayer();
257 assert(myplayer != NULL);
259 // Send TOSERVER_INIT
260 // [0] u16 TOSERVER_INIT
261 // [2] u8 SER_FMT_VER_HIGHEST
262 // [3] u8[20] player_name
263 SharedBuffer<u8> data(2+1+20);
264 writeU16(&data[0], TOSERVER_INIT);
265 writeU8(&data[2], SER_FMT_VER_HIGHEST);
266 memcpy(&data[3], myplayer->getName(), 20);
267 // Send as unreliable
268 Send(0, data, false);
271 // Not connected, return
276 Do stuff if connected
281 JMutexAutoLock lock(m_env_mutex);
283 // Control local player (0ms)
284 LocalPlayer *player = m_env.getLocalPlayer();
285 assert(player != NULL);
286 player->applyControl(dtime);
288 //TimeTaker envtimer("env step", m_device);
292 // Step active blocks
293 for(core::map<v3s16, bool>::Iterator
294 i = m_active_blocks.getIterator();
295 i.atEnd() == false; i++)
297 v3s16 p = i.getNode()->getKey();
299 MapBlock *block = NULL;
302 block = m_env.getMap().getBlockNoCreate(p);
303 block->stepObjects(dtime, false);
305 catch(InvalidPositionException &e)
312 // Fetch some nearby blocks
317 static float counter = 0.0;
322 JMutexAutoLock lock(m_con_mutex);
323 // connectedAndInitialized() is true, peer exists.
324 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
325 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
329 // Update at reasonable intervals (0.2s)
330 static float counter = 0.0;
341 Clear old entries from fetchblock history
344 JMutexAutoLock lock(m_fetchblock_mutex);
346 core::list<v3s16> remove_queue;
347 core::map<v3s16, float>::Iterator i;
348 i = m_fetchblock_history.getIterator();
349 for(; i.atEnd() == false; i++)
351 float value = i.getNode()->getValue();
353 i.getNode()->setValue(value);
355 remove_queue.push_back(i.getNode()->getKey());
357 core::list<v3s16>::Iterator j;
358 j = remove_queue.begin();
359 for(; j != remove_queue.end(); j++)
361 m_fetchblock_history.remove(*j);
367 JMutexAutoLock lock(m_step_dtime_mutex);
368 m_step_dtime += dtime;
380 float Client::asyncStep()
382 DSTACK(__FUNCTION_NAME);
383 //dstream<<"Client::asyncStep()"<<std::endl;
387 JMutexAutoLock lock1(m_step_dtime_mutex);
388 if(m_step_dtime < 0.001)
390 dtime = m_step_dtime;
398 // Virtual methods from con::PeerHandler
399 void Client::peerAdded(con::Peer *peer)
401 derr_client<<"Client::peerAdded(): peer->id="
402 <<peer->id<<std::endl;
404 void Client::deletingPeer(con::Peer *peer, bool timeout)
406 derr_client<<"Client::deletingPeer(): "
407 "Server Peer is getting deleted "
408 <<"(timeout="<<timeout<<")"<<std::endl;
411 void Client::ReceiveAll()
413 DSTACK(__FUNCTION_NAME);
419 catch(con::NoIncomingDataException &e)
423 catch(con::InvalidIncomingDataException &e)
425 dout_client<<DTIME<<"Client::ReceiveAll(): "
426 "InvalidIncomingDataException: what()="
427 <<e.what()<<std::endl;
434 void Client::Receive()
436 DSTACK(__FUNCTION_NAME);
437 u32 data_maxsize = 10000;
438 Buffer<u8> data(data_maxsize);
442 //TimeTaker t1("con mutex and receive", m_device);
443 JMutexAutoLock lock(m_con_mutex);
444 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
446 //TimeTaker t1("ProcessData", m_device);
447 ProcessData(*data, datasize, sender_peer_id);
451 sender_peer_id given to this shall be quaranteed to be a valid peer
453 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
455 DSTACK(__FUNCTION_NAME);
457 // Ignore packets that don't even fit a command
460 m_packetcounter.add(60000);
464 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
466 //dstream<<"Client: received command="<<command<<std::endl;
467 m_packetcounter.add((u16)command);
470 If this check is removed, be sure to change the queue
471 system to know the ids
473 if(sender_peer_id != PEER_ID_SERVER)
475 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
476 "coming from server: peer_id="<<sender_peer_id
483 JMutexAutoLock lock(m_con_mutex);
484 // All data is coming from the server
485 // PeerNotFoundException is handled by caller.
486 peer = m_con.GetPeer(PEER_ID_SERVER);
489 u8 ser_version = m_server_ser_ver;
491 //dstream<<"Client received command="<<(int)command<<std::endl;
493 // Execute fast commands straight away
495 if(command == TOCLIENT_INIT)
500 u8 deployed = data[2];
502 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
503 "deployed="<<((int)deployed&0xff)<<std::endl;
505 if(deployed < SER_FMT_VER_LOWEST
506 || deployed > SER_FMT_VER_HIGHEST)
508 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
509 <<"unsupported ser_fmt_ver"<<std::endl;
513 m_server_ser_ver = deployed;
515 // Get player position
516 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
517 if(datasize >= 2+1+6)
518 playerpos_s16 = readV3S16(&data[2+1]);
519 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
522 JMutexAutoLock envlock(m_env_mutex);
524 // Set player position
525 Player *player = m_env.getLocalPlayer();
526 assert(player != NULL);
527 player->setPosition(playerpos_f);
532 SharedBuffer<u8> reply(replysize);
533 writeU16(&reply[0], TOSERVER_INIT2);
535 m_con.Send(PEER_ID_SERVER, 1, reply, true);
540 if(ser_version == SER_FMT_VER_INVALID)
542 dout_client<<DTIME<<"WARNING: Client: Server serialization"
543 " format invalid or not initialized."
544 " Skipping incoming command="<<command<<std::endl;
548 // Just here to avoid putting the two if's together when
549 // making some copypasta
552 if(command == TOCLIENT_PLAYERPOS)
554 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
558 JMutexAutoLock lock(m_con_mutex);
559 our_peer_id = m_con.GetPeerID();
561 // Cancel if we don't have a peer id
562 if(our_peer_id == PEER_ID_NEW){
563 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
570 JMutexAutoLock envlock(m_env_mutex);
572 u32 player_size = 2+12+12+4+4;
574 u32 player_count = (datasize-2) / player_size;
576 for(u32 i=0; i<player_count; i++)
578 u16 peer_id = readU16(&data[start]);
580 Player *player = m_env.getPlayer(peer_id);
582 // Skip if player doesn't exist
585 start += player_size;
589 // Skip if player is local player
590 if(player->isLocal())
592 start += player_size;
596 v3s32 ps = readV3S32(&data[start+2]);
597 v3s32 ss = readV3S32(&data[start+2+12]);
598 s32 pitch_i = readS32(&data[start+2+12+12]);
599 s32 yaw_i = readS32(&data[start+2+12+12+4]);
600 /*dstream<<"Client: got "
601 <<"pitch_i="<<pitch_i
602 <<" yaw_i="<<yaw_i<<std::endl;*/
603 f32 pitch = (f32)pitch_i / 100.0;
604 f32 yaw = (f32)yaw_i / 100.0;
605 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
606 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
607 player->setPosition(position);
608 player->setSpeed(speed);
609 player->setPitch(pitch);
612 /*dstream<<"Client: player "<<peer_id
614 <<" yaw="<<yaw<<std::endl;*/
616 start += player_size;
620 else if(command == TOCLIENT_PLAYERINFO)
624 JMutexAutoLock lock(m_con_mutex);
625 our_peer_id = m_con.GetPeerID();
627 // Cancel if we don't have a peer id
628 if(our_peer_id == PEER_ID_NEW){
629 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
635 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
638 JMutexAutoLock envlock(m_env_mutex);
640 u32 item_size = 2+PLAYERNAME_SIZE;
641 u32 player_count = (datasize-2) / item_size;
644 core::list<u16> players_alive;
645 for(u32 i=0; i<player_count; i++)
647 // Make sure the name ends in '\0'
648 data[start+2+20-1] = 0;
650 u16 peer_id = readU16(&data[start]);
652 players_alive.push_back(peer_id);
654 /*dstream<<DTIME<<"peer_id="<<peer_id
655 <<" name="<<((char*)&data[start+2])<<std::endl;*/
657 // Don't update the info of the local player
658 if(peer_id == our_peer_id)
664 Player *player = m_env.getPlayer(peer_id);
666 // Create a player if it doesn't exist
669 player = new RemotePlayer(
670 m_device->getSceneManager()->getRootSceneNode(),
673 player->peer_id = peer_id;
674 m_env.addPlayer(player);
675 dout_client<<DTIME<<"Client: Adding new player "
676 <<peer_id<<std::endl;
679 player->updateName((char*)&data[start+2]);
685 Remove those players from the environment that
686 weren't listed by the server.
688 //dstream<<DTIME<<"Removing dead players"<<std::endl;
689 core::list<Player*> players = m_env.getPlayers();
690 core::list<Player*>::Iterator ip;
691 for(ip=players.begin(); ip!=players.end(); ip++)
693 // Ingore local player
697 // Warn about a special case
698 if((*ip)->peer_id == 0)
700 dstream<<DTIME<<"WARNING: Client: Removing "
701 "dead player with id=0"<<std::endl;
704 bool is_alive = false;
705 core::list<u16>::Iterator i;
706 for(i=players_alive.begin(); i!=players_alive.end(); i++)
708 if((*ip)->peer_id == *i)
714 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
715 <<" is_alive="<<is_alive<<std::endl;*/
718 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
720 m_env.removePlayer((*ip)->peer_id);
724 else if(command == TOCLIENT_SECTORMETA)
729 [3...] v2s16 pos + sector metadata
734 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
737 JMutexAutoLock envlock(m_env_mutex);
739 std::string datastring((char*)&data[2], datasize-2);
740 std::istringstream is(datastring, std::ios_base::binary);
744 is.read((char*)buf, 1);
745 u16 sector_count = readU8(buf);
747 //dstream<<"sector_count="<<sector_count<<std::endl;
749 for(u16 i=0; i<sector_count; i++)
752 is.read((char*)buf, 4);
753 v2s16 pos = readV2S16(buf);
754 /*dstream<<"Client: deserializing sector at "
755 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
757 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
758 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
762 else if(command == TOCLIENT_INVENTORY)
767 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
770 //TimeTaker t2("mutex locking", m_device);
771 JMutexAutoLock envlock(m_env_mutex);
774 //TimeTaker t3("istringstream init", m_device);
775 std::string datastring((char*)&data[2], datasize-2);
776 std::istringstream is(datastring, std::ios_base::binary);
779 //m_env.printPlayers(dstream);
781 //TimeTaker t4("player get", m_device);
782 Player *player = m_env.getLocalPlayer();
783 assert(player != NULL);
786 //TimeTaker t1("inventory.deSerialize()", m_device);
787 player->inventory.deSerialize(is);
790 m_inventory_updated = true;
792 //dstream<<"Client got player inventory:"<<std::endl;
793 //player->inventory.print(dstream);
797 else if(command == TOCLIENT_OBJECTDATA)
800 // Strip command word and create a stringstream
801 std::string datastring((char*)&data[2], datasize-2);
802 std::istringstream is(datastring, std::ios_base::binary);
806 JMutexAutoLock envlock(m_env_mutex);
814 is.read((char*)buf, 2);
815 u16 playercount = readU16(buf);
817 for(u16 i=0; i<playercount; i++)
819 is.read((char*)buf, 2);
820 u16 peer_id = readU16(buf);
821 is.read((char*)buf, 12);
822 v3s32 p_i = readV3S32(buf);
823 is.read((char*)buf, 12);
824 v3s32 s_i = readV3S32(buf);
825 is.read((char*)buf, 4);
826 s32 pitch_i = readS32(buf);
827 is.read((char*)buf, 4);
828 s32 yaw_i = readS32(buf);
830 Player *player = m_env.getPlayer(peer_id);
832 // Skip if player doesn't exist
838 // Skip if player is local player
839 if(player->isLocal())
844 f32 pitch = (f32)pitch_i / 100.0;
845 f32 yaw = (f32)yaw_i / 100.0;
846 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
847 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
849 player->setPosition(position);
850 player->setSpeed(speed);
851 player->setPitch(pitch);
859 // Read active block count
860 is.read((char*)buf, 2);
861 u16 blockcount = readU16(buf);
863 // Initialize delete queue with all active blocks
864 core::map<v3s16, bool> abs_to_delete;
865 for(core::map<v3s16, bool>::Iterator
866 i = m_active_blocks.getIterator();
867 i.atEnd() == false; i++)
869 v3s16 p = i.getNode()->getKey();
871 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
872 <<" to abs_to_delete"
874 abs_to_delete.insert(p, true);
877 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
880 for(u16 i=0; i<blockcount; i++)
883 is.read((char*)buf, 6);
884 v3s16 p = readV3S16(buf);
885 // Get block from somewhere
886 MapBlock *block = NULL;
888 block = m_env.getMap().getBlockNoCreate(p);
890 catch(InvalidPositionException &e)
892 //TODO: Create a dummy block?
897 <<"Could not get block at blockpos "
898 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
899 <<"in TOCLIENT_OBJECTDATA. Ignoring "
900 <<"following block object data."
905 /*dstream<<"Client updating objects for block "
906 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
909 // Insert to active block list
910 m_active_blocks.insert(p, true);
912 // Remove from deletion queue
913 if(abs_to_delete.find(p) != NULL)
914 abs_to_delete.remove(p);
916 // Update objects of block
917 block->updateObjects(is, m_server_ser_ver,
918 m_device->getSceneManager());
921 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
924 // Delete objects of blocks in delete queue
925 for(core::map<v3s16, bool>::Iterator
926 i = abs_to_delete.getIterator();
927 i.atEnd() == false; i++)
929 v3s16 p = i.getNode()->getKey();
932 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
935 block->clearObjects();
936 // Remove from active blocks list
937 m_active_blocks.remove(p);
939 catch(InvalidPositionException &e)
941 dstream<<"WARNAING: Client: "
942 <<"Couldn't clear objects of active->inactive"
944 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
945 <<" because block was not found"
953 // Default to queueing it (for slow commands)
956 JMutexAutoLock lock(m_incoming_queue_mutex);
958 IncomingPacket packet(data, datasize);
959 m_incoming_queue.push_back(packet);
964 Returns true if there was something in queue
966 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
968 DSTACK(__FUNCTION_NAME);
970 try //for catching con::PeerNotFoundException
975 JMutexAutoLock lock(m_con_mutex);
976 // All data is coming from the server
977 peer = m_con.GetPeer(PEER_ID_SERVER);
980 u8 ser_version = m_server_ser_ver;
982 IncomingPacket packet = getPacket();
983 u8 *data = packet.m_data;
984 u32 datasize = packet.m_datalen;
986 // An empty packet means queue is empty
994 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
996 if(command == TOCLIENT_REMOVENODE)
1001 p.X = readS16(&data[2]);
1002 p.Y = readS16(&data[4]);
1003 p.Z = readS16(&data[6]);
1005 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1007 core::map<v3s16, MapBlock*> modified_blocks;
1011 JMutexAutoLock envlock(m_env_mutex);
1012 //TimeTaker t("removeNodeAndUpdate", m_device);
1013 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1015 catch(InvalidPositionException &e)
1019 for(core::map<v3s16, MapBlock * >::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 v3s16 p = i.getNode()->getKey();
1024 //m_env.getMap().updateMeshes(p);
1025 mesh_updater.add(p);
1028 else if(command == TOCLIENT_ADDNODE)
1030 if(datasize < 8 + MapNode::serializedLength(ser_version))
1034 p.X = readS16(&data[2]);
1035 p.Y = readS16(&data[4]);
1036 p.Z = readS16(&data[6]);
1038 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1041 n.deSerialize(&data[8], ser_version);
1043 core::map<v3s16, MapBlock*> modified_blocks;
1047 JMutexAutoLock envlock(m_env_mutex);
1048 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1050 catch(InvalidPositionException &e)
1053 for(core::map<v3s16, MapBlock * >::Iterator
1054 i = modified_blocks.getIterator();
1055 i.atEnd() == false; i++)
1057 v3s16 p = i.getNode()->getKey();
1058 //m_env.getMap().updateMeshes(p);
1059 mesh_updater.add(p);
1062 else if(command == TOCLIENT_BLOCKDATA)
1064 // Ignore too small packet
1067 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1071 p.X = readS16(&data[2]);
1072 p.Y = readS16(&data[4]);
1073 p.Z = readS16(&data[6]);
1075 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1076 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1078 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1079 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1081 std::string datastring((char*)&data[8], datasize-8);
1082 std::istringstream istr(datastring, std::ios_base::binary);
1088 JMutexAutoLock envlock(m_env_mutex);
1090 v2s16 p2d(p.X, p.Z);
1091 sector = m_env.getMap().emergeSector(p2d);
1093 v2s16 sp = sector->getPos();
1096 dstream<<"ERROR: Got sector with getPos()="
1097 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1098 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1102 //assert(sector->getPos() == p2d);
1105 block = sector->getBlockNoCreate(p.Y);
1107 Update an existing block
1109 //dstream<<"Updating"<<std::endl;
1110 block->deSerialize(istr, ser_version);
1111 //block->setChangedFlag();
1113 catch(InvalidPositionException &e)
1118 //dstream<<"Creating new"<<std::endl;
1119 block = new MapBlock(&m_env.getMap(), p);
1120 block->deSerialize(istr, ser_version);
1121 sector->insertBlock(block);
1122 //block->setChangedFlag();
1127 // Old version has zero lighting, update it.
1128 if(ser_version == 0 || ser_version == 1)
1130 derr_client<<"Client: Block in old format: "
1131 "Calculating lighting"<<std::endl;
1132 core::map<v3s16, MapBlock*> blocks_changed;
1133 blocks_changed.insert(block->getPos(), block);
1134 core::map<v3s16, MapBlock*> modified_blocks;
1135 m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1139 Update Mesh of this block and blocks at x-, y- and z-
1142 //m_env.getMap().updateMeshes(block->getPos());
1143 mesh_updater.add(block->getPos());
1155 u32 replysize = 2+1+6;
1156 SharedBuffer<u8> reply(replysize);
1157 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1159 writeV3S16(&reply[3], p);
1161 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1168 JMutexAutoLock lock(m_fetchblock_mutex);
1170 if(m_fetchblock_history.find(p) != NULL)
1172 m_fetchblock_history.remove(p);
1186 u32 replysize = 2+1+6;
1187 SharedBuffer<u8> reply(replysize);
1188 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1190 writeV3S16(&reply[3], p);
1192 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1199 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1200 <<command<<std::endl;
1206 catch(con::PeerNotFoundException &e)
1208 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1209 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1214 bool Client::AsyncProcessData()
1218 // We want to update the meshes as soon as a single packet has
1220 LazyMeshUpdater mesh_updater(&m_env);
1221 bool r = AsyncProcessPacket(mesh_updater);
1228 LazyMeshUpdater mesh_updater(&m_env);
1231 bool r = AsyncProcessPacket(mesh_updater);
1238 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1240 JMutexAutoLock lock(m_con_mutex);
1241 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1245 void Client::fetchBlock(v3s16 p, u8 flags)
1247 if(connectedAndInitialized() == false)
1248 throw ClientNotReadyException
1249 ("ClientNotReadyException: connectedAndInitialized() == false");
1251 /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1252 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1254 JMutexAutoLock conlock(m_con_mutex);
1256 SharedBuffer<u8> data(9);
1257 writeU16(&data[0], TOSERVER_GETBLOCK);
1258 writeS16(&data[2], p.X);
1259 writeS16(&data[4], p.Y);
1260 writeS16(&data[6], p.Z);
1261 writeU8(&data[8], flags);
1262 m_con.Send(PEER_ID_SERVER, 1, data, true);
1266 Calls fetchBlock() on some nearby missing blocks.
1268 Returns when any of various network load indicators go over limit.
1270 Does nearly the same thing as the old updateChangedVisibleArea()
1272 void Client::fetchBlocks()
1274 if(connectedAndInitialized() == false)
1275 throw ClientNotReadyException
1276 ("ClientNotReadyException: connectedAndInitialized() == false");
1280 bool Client::isFetchingBlocks()
1282 JMutexAutoLock conlock(m_con_mutex);
1283 con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1284 // Not really fetching but can't fetch more.
1285 if(peer == NULL) return true;
1287 con::Channel *channel = &(peer->channels[1]);
1289 NOTE: Channel 0 should always be used for fetching blocks,
1290 and for nothing else.
1292 if(channel->incoming_reliables.size() > 0)
1294 if(channel->outgoing_reliables.size() > 0)
1299 IncomingPacket Client::getPacket()
1301 JMutexAutoLock lock(m_incoming_queue_mutex);
1303 core::list<IncomingPacket>::Iterator i;
1304 // Refer to first one
1305 i = m_incoming_queue.begin();
1307 // If queue is empty, return empty packet
1308 if(i == m_incoming_queue.end()){
1309 IncomingPacket packet;
1313 // Pop out first packet and return it
1314 IncomingPacket packet = *i;
1315 m_incoming_queue.erase(i);
1320 void Client::removeNode(v3s16 nodepos)
1322 if(connectedAndInitialized() == false){
1323 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1328 // Test that the position exists
1330 JMutexAutoLock envlock(m_env_mutex);
1331 m_env.getMap().getNode(nodepos);
1333 catch(InvalidPositionException &e)
1335 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1340 SharedBuffer<u8> data(8);
1341 writeU16(&data[0], TOSERVER_REMOVENODE);
1342 writeS16(&data[2], nodepos.X);
1343 writeS16(&data[4], nodepos.Y);
1344 writeS16(&data[6], nodepos.Z);
1345 Send(0, data, true);
1348 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1350 if(connectedAndInitialized() == false){
1351 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1352 "cancelled (not connected)"
1357 // Test that the position exists
1359 JMutexAutoLock envlock(m_env_mutex);
1360 m_env.getMap().getNode(nodepos);
1362 catch(InvalidPositionException &e)
1364 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1369 //u8 ser_version = m_server_ser_ver;
1371 // SUGGESTION: The validity of the operation could be checked here too
1373 u8 datasize = 2 + 6 + 2;
1374 SharedBuffer<u8> data(datasize);
1375 writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1376 writeS16(&data[2], nodepos.X);
1377 writeS16(&data[4], nodepos.Y);
1378 writeS16(&data[6], nodepos.Z);
1379 writeU16(&data[8], i);
1380 Send(0, data, true);
1384 void Client::clickGround(u8 button, v3s16 nodepos_undersurface,
1385 v3s16 nodepos_oversurface, u16 item)
1387 if(connectedAndInitialized() == false){
1388 dout_client<<DTIME<<"Client::clickGround() "
1389 "cancelled (not connected)"
1397 [2] u8 button (0=left, 1=right)
1398 [3] v3s16 nodepos_undersurface
1399 [9] v3s16 nodepos_abovesurface
1402 u8 datasize = 2 + 1 + 6 + 6 + 2;
1403 SharedBuffer<u8> data(datasize);
1404 writeU16(&data[0], TOSERVER_CLICK_GROUND);
1405 writeU8(&data[2], button);
1406 writeV3S16(&data[3], nodepos_undersurface);
1407 writeV3S16(&data[9], nodepos_oversurface);
1408 writeU16(&data[15], item);
1409 Send(0, data, true);
1412 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1414 if(connectedAndInitialized() == false){
1415 dout_client<<DTIME<<"Client::clickObject() "
1416 "cancelled (not connected)"
1423 [2] u8 button (0=left, 1=right)
1428 u8 datasize = 2 + 1 + 6 + 2 + 2;
1429 SharedBuffer<u8> data(datasize);
1430 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1431 writeU8(&data[2], button);
1432 writeV3S16(&data[3], blockpos);
1433 writeS16(&data[9], id);
1434 writeU16(&data[11], item);
1435 Send(0, data, true);
1438 void Client::release(u8 button)
1443 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1452 std::ostringstream os(std::ios_base::binary);
1456 writeU16(buf, TOSERVER_SIGNTEXT);
1457 os.write((char*)buf, 2);
1460 writeV3S16(buf, blockpos);
1461 os.write((char*)buf, 6);
1465 os.write((char*)buf, 2);
1467 u16 textlen = text.size();
1468 // Write text length
1469 writeS16(buf, textlen);
1470 os.write((char*)buf, 2);
1473 os.write((char*)text.c_str(), textlen);
1476 std::string s = os.str();
1477 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1479 Send(0, data, true);
1482 void Client::sendPlayerPos()
1484 JMutexAutoLock envlock(m_env_mutex);
1486 Player *myplayer = m_env.getLocalPlayer();
1487 if(myplayer == NULL)
1492 JMutexAutoLock lock(m_con_mutex);
1493 our_peer_id = m_con.GetPeerID();
1496 // Set peer id if not set already
1497 if(myplayer->peer_id == PEER_ID_NEW)
1498 myplayer->peer_id = our_peer_id;
1499 // Check that an existing peer_id is the same as the connection's
1500 assert(myplayer->peer_id == our_peer_id);
1502 v3f pf = myplayer->getPosition();
1503 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1504 v3f sf = myplayer->getSpeed();
1505 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1506 s32 pitch = myplayer->getPitch() * 100;
1507 s32 yaw = myplayer->getYaw() * 100;
1512 [2] v3s32 position*100
1513 [2+12] v3s32 speed*100
1514 [2+12+12] s32 pitch*100
1515 [2+12+12+4] s32 yaw*100
1518 SharedBuffer<u8> data(2+12+12+4+4);
1519 writeU16(&data[0], TOSERVER_PLAYERPOS);
1520 writeV3S32(&data[2], position);
1521 writeV3S32(&data[2+12], speed);
1522 writeS32(&data[2+12+12], pitch);
1523 writeS32(&data[2+12+12+4], yaw);
1525 // Send as unreliable
1526 Send(0, data, false);
1530 void Client::updateCamera(v3f pos, v3f dir)
1532 m_env.getMap().updateCamera(pos, dir);
1533 camera_position = pos;
1534 camera_direction = dir;
1537 MapNode Client::getNode(v3s16 p)
1539 JMutexAutoLock envlock(m_env_mutex);
1540 return m_env.getMap().getNode(p);
1543 /*f32 Client::getGroundHeight(v2s16 p)
1545 JMutexAutoLock envlock(m_env_mutex);
1546 return m_env.getMap().getGroundHeight(p);
1549 bool Client::isNodeUnderground(v3s16 p)
1551 JMutexAutoLock envlock(m_env_mutex);
1552 return m_env.getMap().isNodeUnderground(p);
1555 /*Player * Client::getLocalPlayer()
1557 JMutexAutoLock envlock(m_env_mutex);
1558 return m_env.getLocalPlayer();
1561 /*core::list<Player*> Client::getPlayers()
1563 JMutexAutoLock envlock(m_env_mutex);
1564 return m_env.getPlayers();
1567 v3f Client::getPlayerPosition()
1569 JMutexAutoLock envlock(m_env_mutex);
1570 LocalPlayer *player = m_env.getLocalPlayer();
1571 assert(player != NULL);
1572 return player->getPosition();
1575 void Client::setPlayerControl(PlayerControl &control)
1577 JMutexAutoLock envlock(m_env_mutex);
1578 LocalPlayer *player = m_env.getLocalPlayer();
1579 assert(player != NULL);
1580 player->control = control;
1583 // Returns true if the inventory of the local player has been
1584 // updated from the server. If it is true, it is set to false.
1585 bool Client::getLocalInventoryUpdated()
1587 // m_inventory_updated is behind envlock
1588 JMutexAutoLock envlock(m_env_mutex);
1589 bool updated = m_inventory_updated;
1590 m_inventory_updated = false;
1594 // Copies the inventory of the local player to parameter
1595 void Client::getLocalInventory(Inventory &dst)
1597 JMutexAutoLock envlock(m_env_mutex);
1598 Player *player = m_env.getLocalPlayer();
1599 assert(player != NULL);
1600 dst = player->inventory;
1603 MapBlockObject * Client::getSelectedObject(
1605 v3f from_pos_f_on_map,
1606 core::line3d<f32> shootline_on_map
1609 JMutexAutoLock envlock(m_env_mutex);
1611 core::array<DistanceSortedObject> objects;
1613 for(core::map<v3s16, bool>::Iterator
1614 i = m_active_blocks.getIterator();
1615 i.atEnd() == false; i++)
1617 v3s16 p = i.getNode()->getKey();
1619 MapBlock *block = NULL;
1622 block = m_env.getMap().getBlockNoCreate(p);
1624 catch(InvalidPositionException &e)
1629 // Calculate from_pos relative to block
1630 v3s16 block_pos_i_on_map = block->getPosRelative();
1631 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1632 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1634 block->getObjects(from_pos_f_on_block, max_d, objects);
1637 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1640 // After this, the closest object is the first in the array.
1643 for(u32 i=0; i<objects.size(); i++)
1645 MapBlockObject *obj = objects[i].obj;
1646 MapBlock *block = obj->getBlock();
1648 // Calculate shootline relative to block
1649 v3s16 block_pos_i_on_map = block->getPosRelative();
1650 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1651 core::line3d<f32> shootline_on_block(
1652 shootline_on_map.start - block_pos_f_on_map,
1653 shootline_on_map.end - block_pos_f_on_map
1656 if(obj->isSelected(shootline_on_block))
1658 //dstream<<"Returning selected object"<<std::endl;
1663 //dstream<<"No object selected; returning NULL."<<std::endl;
1667 void Client::printDebugInfo(std::ostream &os)
1669 //JMutexAutoLock lock1(m_fetchblock_mutex);
1670 JMutexAutoLock lock2(m_incoming_queue_mutex);
1672 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1673 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1674 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()