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 * MeshUpdateThread::Thread()
33 DSTACK(__FUNCTION_NAME);
35 BEGIN_DEBUG_EXCEPTION_HANDLER
39 QueuedMeshUpdate *q = m_queue_in.pop();
46 scene::SMesh *mesh_new = NULL;
47 mesh_new = makeMapBlockMesh(q->data);
52 r.ack_block_to_server = q->ack_block_to_server;
54 /*dstream<<"MeshUpdateThread: Processed "
55 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
58 m_queue_out.push_back(r);
63 END_DEBUG_EXCEPTION_HANDLER
69 IrrlichtDevice *device,
70 const char *playername,
71 MapDrawControl &control):
72 m_mesh_update_thread(),
74 new ClientMap(this, control,
75 device->getSceneManager()->getRootSceneNode(),
76 device->getSceneManager(), 666),
77 device->getSceneManager()
79 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
81 camera_position(0,0,0),
82 camera_direction(0,0,1),
83 m_server_ser_ver(SER_FMT_VER_INVALID),
84 m_inventory_updated(false),
87 m_packetcounter_timer = 0.0;
88 m_delete_unused_sectors_timer = 0.0;
89 m_connection_reinit_timer = 0.0;
90 m_avg_rtt_timer = 0.0;
91 m_playerpos_send_timer = 0.0;
96 m_mesh_update_thread.Start();
102 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
104 Player *player = new LocalPlayer();
106 player->updateName(playername);
108 m_env.addPlayer(player);
115 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
119 m_mesh_update_thread.setRun(false);
120 while(m_mesh_update_thread.IsRunning())
124 void Client::connect(Address address)
126 DSTACK(__FUNCTION_NAME);
127 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
128 m_con.setTimeoutMs(0);
129 m_con.Connect(address);
132 bool Client::connectedAndInitialized()
134 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
136 if(m_con.Connected() == false)
139 if(m_server_ser_ver == SER_FMT_VER_INVALID)
145 void Client::step(float dtime)
147 DSTACK(__FUNCTION_NAME);
154 //dstream<<"Client steps "<<dtime<<std::endl;
157 //TimeTaker timer("ReceiveAll()", m_device);
163 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
165 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
166 m_con.RunTimeouts(dtime);
173 float &counter = m_packetcounter_timer;
179 dout_client<<"Client packetcounter (20s):"<<std::endl;
180 m_packetcounter.print(dout_client);
181 m_packetcounter.clear();
187 Delete unused sectors
189 NOTE: This jams the game for a while because deleting sectors
193 float &counter = m_delete_unused_sectors_timer;
201 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
203 core::list<v3s16> deleted_blocks;
205 float delete_unused_sectors_timeout =
206 g_settings.getFloat("client_delete_unused_sectors_timeout");
208 // Delete sector blocks
209 /*u32 num = m_env.getMap().deleteUnusedSectors
210 (delete_unused_sectors_timeout,
211 true, &deleted_blocks);*/
213 // Delete whole sectors
214 u32 num = m_env.getMap().deleteUnusedSectors
215 (delete_unused_sectors_timeout,
216 false, &deleted_blocks);
220 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
221 <<" unused sectors"<<std::endl;*/
222 dstream<<DTIME<<"Client: Deleted "<<num
223 <<" unused sectors"<<std::endl;
229 // Env is locked so con can be locked.
230 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
232 core::list<v3s16>::Iterator i = deleted_blocks.begin();
233 core::list<v3s16> sendlist;
236 if(sendlist.size() == 255 || i == deleted_blocks.end())
238 if(sendlist.size() == 0)
247 u32 replysize = 2+1+6*sendlist.size();
248 SharedBuffer<u8> reply(replysize);
249 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
250 reply[2] = sendlist.size();
252 for(core::list<v3s16>::Iterator
253 j = sendlist.begin();
254 j != sendlist.end(); j++)
256 writeV3S16(&reply[2+1+6*k], *j);
259 m_con.Send(PEER_ID_SERVER, 1, reply, true);
261 if(i == deleted_blocks.end())
267 sendlist.push_back(*i);
274 bool connected = connectedAndInitialized();
276 if(connected == false)
278 float &counter = m_connection_reinit_timer;
284 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
286 Player *myplayer = m_env.getLocalPlayer();
287 assert(myplayer != NULL);
289 // Send TOSERVER_INIT
290 // [0] u16 TOSERVER_INIT
291 // [2] u8 SER_FMT_VER_HIGHEST
292 // [3] u8[20] player_name
293 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE);
294 writeU16(&data[0], TOSERVER_INIT);
295 writeU8(&data[2], SER_FMT_VER_HIGHEST);
296 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
297 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
298 // Send as unreliable
299 Send(0, data, false);
302 // Not connected, return
307 Do stuff if connected
312 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
314 // Control local player (0ms)
315 LocalPlayer *player = m_env.getLocalPlayer();
316 assert(player != NULL);
317 player->applyControl(dtime);
319 //TimeTaker envtimer("env step", m_device);
323 // Step active blocks
324 for(core::map<v3s16, bool>::Iterator
325 i = m_active_blocks.getIterator();
326 i.atEnd() == false; i++)
328 v3s16 p = i.getNode()->getKey();
330 MapBlock *block = NULL;
333 block = m_env.getMap().getBlockNoCreate(p);
334 block->stepObjects(dtime, false, m_env.getDayNightRatio());
336 catch(InvalidPositionException &e)
343 float &counter = m_avg_rtt_timer;
348 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
349 // connectedAndInitialized() is true, peer exists.
350 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
351 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
355 float &counter = m_playerpos_send_timer;
365 Replace updated meshes
368 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
370 //TimeTaker timer("** Processing mesh update result queue");
373 /*dstream<<"Mesh update result queue size is "
374 <<m_mesh_update_thread.m_queue_out.size()
377 while(m_mesh_update_thread.m_queue_out.size() > 0)
379 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
380 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
383 block->replaceMesh(r.mesh);
385 if(r.ack_block_to_server)
397 u32 replysize = 2+1+6;
398 SharedBuffer<u8> reply(replysize);
399 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
401 writeV3S16(&reply[3], r.p);
403 m_con.Send(PEER_ID_SERVER, 1, reply, true);
409 // Virtual methods from con::PeerHandler
410 void Client::peerAdded(con::Peer *peer)
412 derr_client<<"Client::peerAdded(): peer->id="
413 <<peer->id<<std::endl;
415 void Client::deletingPeer(con::Peer *peer, bool timeout)
417 derr_client<<"Client::deletingPeer(): "
418 "Server Peer is getting deleted "
419 <<"(timeout="<<timeout<<")"<<std::endl;
422 void Client::ReceiveAll()
424 DSTACK(__FUNCTION_NAME);
430 catch(con::NoIncomingDataException &e)
434 catch(con::InvalidIncomingDataException &e)
436 dout_client<<DTIME<<"Client::ReceiveAll(): "
437 "InvalidIncomingDataException: what()="
438 <<e.what()<<std::endl;
443 void Client::Receive()
445 DSTACK(__FUNCTION_NAME);
446 u32 data_maxsize = 10000;
447 Buffer<u8> data(data_maxsize);
451 //TimeTaker t1("con mutex and receive", m_device);
452 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
453 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
455 //TimeTaker t1("ProcessData", m_device);
456 ProcessData(*data, datasize, sender_peer_id);
460 sender_peer_id given to this shall be quaranteed to be a valid peer
462 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
464 DSTACK(__FUNCTION_NAME);
466 // Ignore packets that don't even fit a command
469 m_packetcounter.add(60000);
473 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
475 //dstream<<"Client: received command="<<command<<std::endl;
476 m_packetcounter.add((u16)command);
479 If this check is removed, be sure to change the queue
480 system to know the ids
482 if(sender_peer_id != PEER_ID_SERVER)
484 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
485 "coming from server: peer_id="<<sender_peer_id
492 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
493 // All data is coming from the server
494 // PeerNotFoundException is handled by caller.
495 peer = m_con.GetPeer(PEER_ID_SERVER);
498 u8 ser_version = m_server_ser_ver;
500 //dstream<<"Client received command="<<(int)command<<std::endl;
502 // Execute fast commands straight away
504 if(command == TOCLIENT_INIT)
509 u8 deployed = data[2];
511 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
512 "deployed="<<((int)deployed&0xff)<<std::endl;
514 if(deployed < SER_FMT_VER_LOWEST
515 || deployed > SER_FMT_VER_HIGHEST)
517 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
518 <<"unsupported ser_fmt_ver"<<std::endl;
522 m_server_ser_ver = deployed;
524 // Get player position
525 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
526 if(datasize >= 2+1+6)
527 playerpos_s16 = readV3S16(&data[2+1]);
528 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
531 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
533 // Set player position
534 Player *player = m_env.getLocalPlayer();
535 assert(player != NULL);
536 player->setPosition(playerpos_f);
539 if(datasize >= 2+1+6+8)
542 m_map_seed = readU64(&data[2+1+6]);
543 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
548 SharedBuffer<u8> reply(replysize);
549 writeU16(&reply[0], TOSERVER_INIT2);
551 m_con.Send(PEER_ID_SERVER, 1, reply, true);
556 if(ser_version == SER_FMT_VER_INVALID)
558 dout_client<<DTIME<<"WARNING: Client: Server serialization"
559 " format invalid or not initialized."
560 " Skipping incoming command="<<command<<std::endl;
564 // Just here to avoid putting the two if's together when
565 // making some copypasta
568 if(command == TOCLIENT_REMOVENODE)
573 p.X = readS16(&data[2]);
574 p.Y = readS16(&data[4]);
575 p.Z = readS16(&data[6]);
577 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
579 // This will clear the cracking animation after digging
580 ((ClientMap&)m_env.getMap()).clearTempMod(p);
584 else if(command == TOCLIENT_ADDNODE)
586 if(datasize < 8 + MapNode::serializedLength(ser_version))
590 p.X = readS16(&data[2]);
591 p.Y = readS16(&data[4]);
592 p.Z = readS16(&data[6]);
594 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
597 n.deSerialize(&data[8], ser_version);
601 else if(command == TOCLIENT_BLOCKDATA)
603 // Ignore too small packet
608 p.X = readS16(&data[2]);
609 p.Y = readS16(&data[4]);
610 p.Z = readS16(&data[6]);
612 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
613 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
614 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
615 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
617 std::string datastring((char*)&data[8], datasize-8);
618 std::istringstream istr(datastring, std::ios_base::binary);
624 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
627 sector = m_env.getMap().emergeSector(p2d);
629 v2s16 sp = sector->getPos();
632 dstream<<"ERROR: Got sector with getPos()="
633 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
634 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
638 //assert(sector->getPos() == p2d);
640 //TimeTaker timer("MapBlock deSerialize");
644 block = sector->getBlockNoCreate(p.Y);
646 Update an existing block
648 //dstream<<"Updating"<<std::endl;
649 block->deSerialize(istr, ser_version);
650 //block->setChangedFlag();
652 catch(InvalidPositionException &e)
657 //dstream<<"Creating new"<<std::endl;
658 block = new MapBlock(&m_env.getMap(), p);
659 block->deSerialize(istr, ser_version);
660 sector->insertBlock(block);
661 //block->setChangedFlag();
665 mod.type = NODEMOD_CHANGECONTENT;
666 mod.param = CONTENT_MESE;
667 block->setTempMod(v3s16(8,10,8), mod);
668 block->setTempMod(v3s16(8,9,8), mod);
669 block->setTempMod(v3s16(8,8,8), mod);
670 block->setTempMod(v3s16(8,7,8), mod);
671 block->setTempMod(v3s16(8,6,8), mod);*/
675 Well, this is a dumb way to do it, they should just
676 be drawn as separate objects. But the looks of them
677 can be tested this way.
682 mod.type = NODEMOD_CHANGECONTENT;
683 mod.param = CONTENT_CLOUD;
686 for(p2.X=3; p2.X<=13; p2.X++)
687 for(p2.Z=3; p2.Z<=13; p2.Z++)
689 block->setTempMod(p2, mod);
707 u32 replysize = 2+1+6;
708 SharedBuffer<u8> reply(replysize);
709 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
711 writeV3S16(&reply[3], p);
713 m_con.Send(PEER_ID_SERVER, 1, reply, true);
717 Update Mesh of this block and blocks at x-, y- and z-.
718 Environment should not be locked as it interlocks with the
719 main thread, from which is will want to retrieve textures.
722 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
724 addUpdateMeshTaskWithEdge(p, true);
726 else if(command == TOCLIENT_PLAYERPOS)
728 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
732 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
733 our_peer_id = m_con.GetPeerID();
735 // Cancel if we don't have a peer id
736 if(our_peer_id == PEER_ID_INEXISTENT){
737 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
744 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
746 u32 player_size = 2+12+12+4+4;
748 u32 player_count = (datasize-2) / player_size;
750 for(u32 i=0; i<player_count; i++)
752 u16 peer_id = readU16(&data[start]);
754 Player *player = m_env.getPlayer(peer_id);
756 // Skip if player doesn't exist
759 start += player_size;
763 // Skip if player is local player
764 if(player->isLocal())
766 start += player_size;
770 v3s32 ps = readV3S32(&data[start+2]);
771 v3s32 ss = readV3S32(&data[start+2+12]);
772 s32 pitch_i = readS32(&data[start+2+12+12]);
773 s32 yaw_i = readS32(&data[start+2+12+12+4]);
774 /*dstream<<"Client: got "
775 <<"pitch_i="<<pitch_i
776 <<" yaw_i="<<yaw_i<<std::endl;*/
777 f32 pitch = (f32)pitch_i / 100.0;
778 f32 yaw = (f32)yaw_i / 100.0;
779 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
780 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
781 player->setPosition(position);
782 player->setSpeed(speed);
783 player->setPitch(pitch);
786 /*dstream<<"Client: player "<<peer_id
788 <<" yaw="<<yaw<<std::endl;*/
790 start += player_size;
794 else if(command == TOCLIENT_PLAYERINFO)
798 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
799 our_peer_id = m_con.GetPeerID();
801 // Cancel if we don't have a peer id
802 if(our_peer_id == PEER_ID_INEXISTENT){
803 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
809 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
812 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
814 u32 item_size = 2+PLAYERNAME_SIZE;
815 u32 player_count = (datasize-2) / item_size;
818 core::list<u16> players_alive;
819 for(u32 i=0; i<player_count; i++)
821 // Make sure the name ends in '\0'
822 data[start+2+20-1] = 0;
824 u16 peer_id = readU16(&data[start]);
826 players_alive.push_back(peer_id);
828 /*dstream<<DTIME<<"peer_id="<<peer_id
829 <<" name="<<((char*)&data[start+2])<<std::endl;*/
831 // Don't update the info of the local player
832 if(peer_id == our_peer_id)
838 Player *player = m_env.getPlayer(peer_id);
840 // Create a player if it doesn't exist
843 player = new RemotePlayer(
844 m_device->getSceneManager()->getRootSceneNode(),
847 player->peer_id = peer_id;
848 m_env.addPlayer(player);
849 dout_client<<DTIME<<"Client: Adding new player "
850 <<peer_id<<std::endl;
853 player->updateName((char*)&data[start+2]);
859 Remove those players from the environment that
860 weren't listed by the server.
862 //dstream<<DTIME<<"Removing dead players"<<std::endl;
863 core::list<Player*> players = m_env.getPlayers();
864 core::list<Player*>::Iterator ip;
865 for(ip=players.begin(); ip!=players.end(); ip++)
867 // Ingore local player
871 // Warn about a special case
872 if((*ip)->peer_id == 0)
874 dstream<<DTIME<<"WARNING: Client: Removing "
875 "dead player with id=0"<<std::endl;
878 bool is_alive = false;
879 core::list<u16>::Iterator i;
880 for(i=players_alive.begin(); i!=players_alive.end(); i++)
882 if((*ip)->peer_id == *i)
888 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
889 <<" is_alive="<<is_alive<<std::endl;*/
892 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
894 m_env.removePlayer((*ip)->peer_id);
898 else if(command == TOCLIENT_SECTORMETA)
903 [3...] v2s16 pos + sector metadata
908 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
911 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
913 std::string datastring((char*)&data[2], datasize-2);
914 std::istringstream is(datastring, std::ios_base::binary);
918 is.read((char*)buf, 1);
919 u16 sector_count = readU8(buf);
921 //dstream<<"sector_count="<<sector_count<<std::endl;
923 for(u16 i=0; i<sector_count; i++)
926 is.read((char*)buf, 4);
927 v2s16 pos = readV2S16(buf);
928 /*dstream<<"Client: deserializing sector at "
929 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
931 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
932 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
936 else if(command == TOCLIENT_INVENTORY)
941 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
944 //TimeTaker t2("mutex locking", m_device);
945 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
948 //TimeTaker t3("istringstream init", m_device);
949 std::string datastring((char*)&data[2], datasize-2);
950 std::istringstream is(datastring, std::ios_base::binary);
953 //m_env.printPlayers(dstream);
955 //TimeTaker t4("player get", m_device);
956 Player *player = m_env.getLocalPlayer();
957 assert(player != NULL);
960 //TimeTaker t1("inventory.deSerialize()", m_device);
961 player->inventory.deSerialize(is);
964 m_inventory_updated = true;
966 //dstream<<"Client got player inventory:"<<std::endl;
967 //player->inventory.print(dstream);
971 else if(command == TOCLIENT_OBJECTDATA)
974 // Strip command word and create a stringstream
975 std::string datastring((char*)&data[2], datasize-2);
976 std::istringstream is(datastring, std::ios_base::binary);
980 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
988 is.read((char*)buf, 2);
989 u16 playercount = readU16(buf);
991 for(u16 i=0; i<playercount; i++)
993 is.read((char*)buf, 2);
994 u16 peer_id = readU16(buf);
995 is.read((char*)buf, 12);
996 v3s32 p_i = readV3S32(buf);
997 is.read((char*)buf, 12);
998 v3s32 s_i = readV3S32(buf);
999 is.read((char*)buf, 4);
1000 s32 pitch_i = readS32(buf);
1001 is.read((char*)buf, 4);
1002 s32 yaw_i = readS32(buf);
1004 Player *player = m_env.getPlayer(peer_id);
1006 // Skip if player doesn't exist
1012 // Skip if player is local player
1013 if(player->isLocal())
1018 f32 pitch = (f32)pitch_i / 100.0;
1019 f32 yaw = (f32)yaw_i / 100.0;
1020 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1021 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1023 player->setPosition(position);
1024 player->setSpeed(speed);
1025 player->setPitch(pitch);
1026 player->setYaw(yaw);
1033 // Read active block count
1034 is.read((char*)buf, 2);
1035 u16 blockcount = readU16(buf);
1037 // Initialize delete queue with all active blocks
1038 core::map<v3s16, bool> abs_to_delete;
1039 for(core::map<v3s16, bool>::Iterator
1040 i = m_active_blocks.getIterator();
1041 i.atEnd() == false; i++)
1043 v3s16 p = i.getNode()->getKey();
1044 /*dstream<<"adding "
1045 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1046 <<" to abs_to_delete"
1048 abs_to_delete.insert(p, true);
1051 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1054 for(u16 i=0; i<blockcount; i++)
1057 is.read((char*)buf, 6);
1058 v3s16 p = readV3S16(buf);
1059 // Get block from somewhere
1060 MapBlock *block = NULL;
1062 block = m_env.getMap().getBlockNoCreate(p);
1064 catch(InvalidPositionException &e)
1066 //TODO: Create a dummy block?
1070 dstream<<"WARNING: "
1071 <<"Could not get block at blockpos "
1072 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1073 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1074 <<"following block object data."
1079 /*dstream<<"Client updating objects for block "
1080 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1083 // Insert to active block list
1084 m_active_blocks.insert(p, true);
1086 // Remove from deletion queue
1087 if(abs_to_delete.find(p) != NULL)
1088 abs_to_delete.remove(p);
1091 Update objects of block
1093 NOTE: Be sure this is done in the main thread.
1095 block->updateObjects(is, m_server_ser_ver,
1096 m_device->getSceneManager(), m_env.getDayNightRatio());
1099 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1102 // Delete objects of blocks in delete queue
1103 for(core::map<v3s16, bool>::Iterator
1104 i = abs_to_delete.getIterator();
1105 i.atEnd() == false; i++)
1107 v3s16 p = i.getNode()->getKey();
1110 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1113 block->clearObjects();
1114 // Remove from active blocks list
1115 m_active_blocks.remove(p);
1117 catch(InvalidPositionException &e)
1119 dstream<<"WARNAING: Client: "
1120 <<"Couldn't clear objects of active->inactive"
1122 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1123 <<" because block was not found"
1131 else if(command == TOCLIENT_TIME_OF_DAY)
1136 u16 time = readU16(&data[2]);
1137 time = time % 24000;
1138 m_time_of_day = time;
1139 //dstream<<"Client: time="<<time<<std::endl;
1149 u32 dr = time_to_daynight_ratio(m_time_of_day);
1151 dstream<<"Client: time_of_day="<<m_time_of_day
1155 if(dr != m_env.getDayNightRatio())
1157 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
1158 m_env.setDayNightRatio(dr);
1159 m_env.expireMeshes(true);
1164 else if(command == TOCLIENT_CHAT_MESSAGE)
1172 std::string datastring((char*)&data[2], datasize-2);
1173 std::istringstream is(datastring, std::ios_base::binary);
1176 is.read((char*)buf, 2);
1177 u16 len = readU16(buf);
1179 std::wstring message;
1180 for(u16 i=0; i<len; i++)
1182 is.read((char*)buf, 2);
1183 message += (wchar_t)readU16(buf);
1186 /*dstream<<"Client received chat message: "
1187 <<wide_to_narrow(message)<<std::endl;*/
1189 m_chat_queue.push_back(message);
1191 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1193 if(g_settings.getBool("enable_experimental"))
1197 u16 count of removed objects
1198 for all removed objects {
1201 u16 count of added objects
1202 for all added objects {
1205 u16 initialization data length
1206 string initialization data
1211 // Get all data except the command number
1212 std::string datastring((char*)&data[2], datasize-2);
1213 // Throw them in an istringstream
1214 std::istringstream is(datastring, std::ios_base::binary);
1218 // Read removed objects
1220 u16 removed_count = readU16((u8*)buf);
1221 for(u16 i=0; i<removed_count; i++)
1224 u16 id = readU16((u8*)buf);
1227 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1228 m_env.removeActiveObject(id);
1232 // Read added objects
1234 u16 added_count = readU16((u8*)buf);
1235 for(u16 i=0; i<added_count; i++)
1238 u16 id = readU16((u8*)buf);
1240 u8 type = readU8((u8*)buf);
1241 std::string data = deSerializeLongString(is);
1244 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1245 m_env.addActiveObject(id, type, data);
1250 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1252 if(g_settings.getBool("enable_experimental"))
1264 // Get all data except the command number
1265 std::string datastring((char*)&data[2], datasize-2);
1266 // Throw them in an istringstream
1267 std::istringstream is(datastring, std::ios_base::binary);
1269 while(is.eof() == false)
1273 u16 id = readU16((u8*)buf);
1277 u16 message_size = readU16((u8*)buf);
1278 std::string message;
1279 message.reserve(message_size);
1280 for(u16 i=0; i<message_size; i++)
1283 message.append(buf, 1);
1285 // Pass on to the environment
1287 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1288 m_env.processActiveObjectMessage(id, message);
1295 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1296 <<command<<std::endl;
1299 // Default to queueing it (for slow commands)
1302 JMutexAutoLock lock(m_incoming_queue_mutex);
1304 IncomingPacket packet(data, datasize);
1305 m_incoming_queue.push_back(packet);
1312 Returns true if there was something in queue
1314 bool Client::AsyncProcessPacket()
1316 DSTACK(__FUNCTION_NAME);
1318 try //for catching con::PeerNotFoundException
1323 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1324 // All data is coming from the server
1325 peer = m_con.GetPeer(PEER_ID_SERVER);
1328 u8 ser_version = m_server_ser_ver;
1330 IncomingPacket packet = getPacket();
1331 u8 *data = packet.m_data;
1332 u32 datasize = packet.m_datalen;
1334 // An empty packet means queue is empty
1342 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1344 if(command == TOCLIENT_BLOCKDATA)
1346 // Ignore too small packet
1351 p.X = readS16(&data[2]);
1352 p.Y = readS16(&data[4]);
1353 p.Z = readS16(&data[6]);
1355 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1356 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1357 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1358 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1360 std::string datastring((char*)&data[8], datasize-8);
1361 std::istringstream istr(datastring, std::ios_base::binary);
1367 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1369 v2s16 p2d(p.X, p.Z);
1370 sector = m_env.getMap().emergeSector(p2d);
1372 v2s16 sp = sector->getPos();
1375 dstream<<"ERROR: Got sector with getPos()="
1376 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1377 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1381 //assert(sector->getPos() == p2d);
1383 //TimeTaker timer("MapBlock deSerialize");
1387 block = sector->getBlockNoCreate(p.Y);
1389 Update an existing block
1391 //dstream<<"Updating"<<std::endl;
1392 block->deSerialize(istr, ser_version);
1393 //block->setChangedFlag();
1395 catch(InvalidPositionException &e)
1400 //dstream<<"Creating new"<<std::endl;
1401 block = new MapBlock(&m_env.getMap(), p);
1402 block->deSerialize(istr, ser_version);
1403 sector->insertBlock(block);
1404 //block->setChangedFlag();
1408 mod.type = NODEMOD_CHANGECONTENT;
1409 mod.param = CONTENT_MESE;
1410 block->setTempMod(v3s16(8,10,8), mod);
1411 block->setTempMod(v3s16(8,9,8), mod);
1412 block->setTempMod(v3s16(8,8,8), mod);
1413 block->setTempMod(v3s16(8,7,8), mod);
1414 block->setTempMod(v3s16(8,6,8), mod);*/
1418 Well, this is a dumb way to do it, they should just
1419 be drawn as separate objects. But the looks of them
1420 can be tested this way.
1425 mod.type = NODEMOD_CHANGECONTENT;
1426 mod.param = CONTENT_CLOUD;
1429 for(p2.X=3; p2.X<=13; p2.X++)
1430 for(p2.Z=3; p2.Z<=13; p2.Z++)
1432 block->setTempMod(p2, mod);
1449 u32 replysize = 2+1+6;
1450 SharedBuffer<u8> reply(replysize);
1451 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1453 writeV3S16(&reply[3], p);
1455 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1458 Update Mesh of this block and blocks at x-, y- and z-.
1459 Environment should not be locked as it interlocks with the
1460 main thread, from which is will want to retrieve textures.
1463 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
1467 //TimeTaker timer("data fill");
1469 data.fill(getDayNightRatio(), block);
1472 TimeTaker timer("make mesh");
1473 scene::SMesh *mesh_new = NULL;
1474 mesh_new = makeMapBlockMesh(&data);
1475 block->replaceMesh(mesh_new);
1480 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1481 <<command<<std::endl;
1487 catch(con::PeerNotFoundException &e)
1489 /*dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1490 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;*/
1495 bool Client::AsyncProcessData()
1499 bool r = AsyncProcessPacket();
1507 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1509 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1510 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1514 IncomingPacket Client::getPacket()
1516 JMutexAutoLock lock(m_incoming_queue_mutex);
1518 core::list<IncomingPacket>::Iterator i;
1519 // Refer to first one
1520 i = m_incoming_queue.begin();
1522 // If queue is empty, return empty packet
1523 if(i == m_incoming_queue.end()){
1524 IncomingPacket packet;
1528 // Pop out first packet and return it
1529 IncomingPacket packet = *i;
1530 m_incoming_queue.erase(i);
1535 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1536 v3s16 nodepos_oversurface, u16 item)
1538 if(connectedAndInitialized() == false){
1539 dout_client<<DTIME<<"Client::groundAction() "
1540 "cancelled (not connected)"
1549 [3] v3s16 nodepos_undersurface
1550 [9] v3s16 nodepos_abovesurface
1555 2: stop digging (all parameters ignored)
1556 3: digging completed
1558 u8 datasize = 2 + 1 + 6 + 6 + 2;
1559 SharedBuffer<u8> data(datasize);
1560 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1561 writeU8(&data[2], action);
1562 writeV3S16(&data[3], nodepos_undersurface);
1563 writeV3S16(&data[9], nodepos_oversurface);
1564 writeU16(&data[15], item);
1565 Send(0, data, true);
1568 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1570 if(connectedAndInitialized() == false){
1571 dout_client<<DTIME<<"Client::clickObject() "
1572 "cancelled (not connected)"
1578 [0] u16 command=TOSERVER_CLICK_OBJECT
1579 [2] u8 button (0=left, 1=right)
1584 u8 datasize = 2 + 1 + 6 + 2 + 2;
1585 SharedBuffer<u8> data(datasize);
1586 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1587 writeU8(&data[2], button);
1588 writeV3S16(&data[3], blockpos);
1589 writeS16(&data[9], id);
1590 writeU16(&data[11], item);
1591 Send(0, data, true);
1594 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1603 std::ostringstream os(std::ios_base::binary);
1607 writeU16(buf, TOSERVER_SIGNTEXT);
1608 os.write((char*)buf, 2);
1611 writeV3S16(buf, blockpos);
1612 os.write((char*)buf, 6);
1616 os.write((char*)buf, 2);
1618 u16 textlen = text.size();
1619 // Write text length
1620 writeS16(buf, textlen);
1621 os.write((char*)buf, 2);
1624 os.write((char*)text.c_str(), textlen);
1627 std::string s = os.str();
1628 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1630 Send(0, data, true);
1633 void Client::sendInventoryAction(InventoryAction *a)
1635 std::ostringstream os(std::ios_base::binary);
1639 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1640 os.write((char*)buf, 2);
1645 std::string s = os.str();
1646 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1648 Send(0, data, true);
1651 void Client::sendChatMessage(const std::wstring &message)
1653 std::ostringstream os(std::ios_base::binary);
1657 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1658 os.write((char*)buf, 2);
1661 writeU16(buf, message.size());
1662 os.write((char*)buf, 2);
1665 for(u32 i=0; i<message.size(); i++)
1669 os.write((char*)buf, 2);
1673 std::string s = os.str();
1674 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1676 Send(0, data, true);
1679 void Client::sendPlayerPos()
1681 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1683 Player *myplayer = m_env.getLocalPlayer();
1684 if(myplayer == NULL)
1689 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1690 our_peer_id = m_con.GetPeerID();
1693 // Set peer id if not set already
1694 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1695 myplayer->peer_id = our_peer_id;
1696 // Check that an existing peer_id is the same as the connection's
1697 assert(myplayer->peer_id == our_peer_id);
1699 v3f pf = myplayer->getPosition();
1700 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1701 v3f sf = myplayer->getSpeed();
1702 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1703 s32 pitch = myplayer->getPitch() * 100;
1704 s32 yaw = myplayer->getYaw() * 100;
1709 [2] v3s32 position*100
1710 [2+12] v3s32 speed*100
1711 [2+12+12] s32 pitch*100
1712 [2+12+12+4] s32 yaw*100
1715 SharedBuffer<u8> data(2+12+12+4+4);
1716 writeU16(&data[0], TOSERVER_PLAYERPOS);
1717 writeV3S32(&data[2], position);
1718 writeV3S32(&data[2+12], speed);
1719 writeS32(&data[2+12+12], pitch);
1720 writeS32(&data[2+12+12+4], yaw);
1722 // Send as unreliable
1723 Send(0, data, false);
1726 void Client::removeNode(v3s16 p)
1728 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1730 core::map<v3s16, MapBlock*> modified_blocks;
1734 //TimeTaker t("removeNodeAndUpdate", m_device);
1735 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1737 catch(InvalidPositionException &e)
1741 for(core::map<v3s16, MapBlock * >::Iterator
1742 i = modified_blocks.getIterator();
1743 i.atEnd() == false; i++)
1745 v3s16 p = i.getNode()->getKey();
1746 m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1750 void Client::addNode(v3s16 p, MapNode n)
1752 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1754 TimeTaker timer1("Client::addNode()");
1756 core::map<v3s16, MapBlock*> modified_blocks;
1760 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1761 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1763 catch(InvalidPositionException &e)
1766 TimeTaker timer2("Client::addNode(): updateMeshes");
1768 for(core::map<v3s16, MapBlock * >::Iterator
1769 i = modified_blocks.getIterator();
1770 i.atEnd() == false; i++)
1772 v3s16 p = i.getNode()->getKey();
1773 m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1777 void Client::updateCamera(v3f pos, v3f dir)
1779 m_env.getClientMap().updateCamera(pos, dir);
1780 camera_position = pos;
1781 camera_direction = dir;
1784 MapNode Client::getNode(v3s16 p)
1786 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1787 return m_env.getMap().getNode(p);
1790 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1792 return m_env.getMap().getNodeMetadata(p);
1795 v3f Client::getPlayerPosition()
1797 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1798 LocalPlayer *player = m_env.getLocalPlayer();
1799 assert(player != NULL);
1800 return player->getPosition();
1803 void Client::setPlayerControl(PlayerControl &control)
1805 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1806 LocalPlayer *player = m_env.getLocalPlayer();
1807 assert(player != NULL);
1808 player->control = control;
1811 // Returns true if the inventory of the local player has been
1812 // updated from the server. If it is true, it is set to false.
1813 bool Client::getLocalInventoryUpdated()
1815 // m_inventory_updated is behind envlock
1816 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1817 bool updated = m_inventory_updated;
1818 m_inventory_updated = false;
1822 // Copies the inventory of the local player to parameter
1823 void Client::getLocalInventory(Inventory &dst)
1825 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1826 Player *player = m_env.getLocalPlayer();
1827 assert(player != NULL);
1828 dst = player->inventory;
1831 MapBlockObject * Client::getSelectedObject(
1833 v3f from_pos_f_on_map,
1834 core::line3d<f32> shootline_on_map
1837 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1839 core::array<DistanceSortedObject> objects;
1841 for(core::map<v3s16, bool>::Iterator
1842 i = m_active_blocks.getIterator();
1843 i.atEnd() == false; i++)
1845 v3s16 p = i.getNode()->getKey();
1847 MapBlock *block = NULL;
1850 block = m_env.getMap().getBlockNoCreate(p);
1852 catch(InvalidPositionException &e)
1857 // Calculate from_pos relative to block
1858 v3s16 block_pos_i_on_map = block->getPosRelative();
1859 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1860 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1862 block->getObjects(from_pos_f_on_block, max_d, objects);
1863 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1866 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1869 // After this, the closest object is the first in the array.
1872 for(u32 i=0; i<objects.size(); i++)
1874 MapBlockObject *obj = objects[i].obj;
1875 MapBlock *block = obj->getBlock();
1877 // Calculate shootline relative to block
1878 v3s16 block_pos_i_on_map = block->getPosRelative();
1879 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1880 core::line3d<f32> shootline_on_block(
1881 shootline_on_map.start - block_pos_f_on_map,
1882 shootline_on_map.end - block_pos_f_on_map
1885 if(obj->isSelected(shootline_on_block))
1887 //dstream<<"Returning selected object"<<std::endl;
1892 //dstream<<"No object selected; returning NULL."<<std::endl;
1896 void Client::printDebugInfo(std::ostream &os)
1898 //JMutexAutoLock lock1(m_fetchblock_mutex);
1899 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1901 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1902 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1903 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1907 /*s32 Client::getDayNightIndex()
1909 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1910 return m_daynight_i;
1913 u32 Client::getDayNightRatio()
1915 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1916 return m_env.getDayNightRatio();
1919 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1921 /*dstream<<"Client::addUpdateMeshTask(): "
1922 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1925 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1930 Create a task to update the mesh of the block
1933 MeshMakeData *data = new MeshMakeData;
1936 //TimeTaker timer("data fill");
1938 data->fill(getDayNightRatio(), b);
1942 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
1944 // Add task to queue
1945 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
1947 /*dstream<<"Mesh update input queue size is "
1948 <<m_mesh_update_thread.m_queue_in.size()
1952 // Temporary test: make mesh directly in here
1954 //TimeTaker timer("make mesh");
1956 scene::SMesh *mesh_new = NULL;
1957 mesh_new = makeMapBlockMesh(data);
1958 b->replaceMesh(mesh_new);
1963 b->setMeshExpired(false);
1966 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
1970 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
1971 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1976 v3s16 p = blockpos + v3s16(0,0,0);
1977 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
1978 addUpdateMeshTask(p, ack_to_server);
1980 catch(InvalidPositionException &e){}
1983 v3s16 p = blockpos + v3s16(-1,0,0);
1984 addUpdateMeshTask(p);
1986 catch(InvalidPositionException &e){}
1988 v3s16 p = blockpos + v3s16(0,-1,0);
1989 addUpdateMeshTask(p);
1991 catch(InvalidPositionException &e){}
1993 v3s16 p = blockpos + v3s16(0,0,-1);
1994 addUpdateMeshTask(p);
1996 catch(InvalidPositionException &e){}