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 ScopeProfiler sp(&g_profiler, "mesh make");
48 scene::SMesh *mesh_new = NULL;
49 mesh_new = makeMapBlockMesh(q->data);
54 r.ack_block_to_server = q->ack_block_to_server;
56 /*dstream<<"MeshUpdateThread: Processed "
57 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
60 m_queue_out.push_back(r);
65 END_DEBUG_EXCEPTION_HANDLER
71 IrrlichtDevice *device,
72 const char *playername,
74 MapDrawControl &control):
75 m_mesh_update_thread(),
77 new ClientMap(this, control,
78 device->getSceneManager()->getRootSceneNode(),
79 device->getSceneManager(), 666),
80 device->getSceneManager()
82 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
84 camera_position(0,0,0),
85 camera_direction(0,0,1),
86 m_server_ser_ver(SER_FMT_VER_INVALID),
87 m_inventory_updated(false),
91 m_access_denied(false)
93 m_packetcounter_timer = 0.0;
94 m_delete_unused_sectors_timer = 0.0;
95 m_connection_reinit_timer = 0.0;
96 m_avg_rtt_timer = 0.0;
97 m_playerpos_send_timer = 0.0;
98 m_ignore_damage_timer = 0.0;
100 //m_env_mutex.Init();
101 //m_con_mutex.Init();
103 m_mesh_update_thread.Start();
109 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
111 Player *player = new LocalPlayer();
113 player->updateName(playername);
115 m_env.addPlayer(player);
117 // Initialize player in the inventory context
118 m_inventory_context.current_player = player;
125 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
129 m_mesh_update_thread.setRun(false);
130 while(m_mesh_update_thread.IsRunning())
134 void Client::connect(Address address)
136 DSTACK(__FUNCTION_NAME);
137 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
138 m_con.setTimeoutMs(0);
139 m_con.Connect(address);
142 bool Client::connectedAndInitialized()
144 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
146 if(m_con.Connected() == false)
149 if(m_server_ser_ver == SER_FMT_VER_INVALID)
155 void Client::step(float dtime)
157 DSTACK(__FUNCTION_NAME);
163 if(m_ignore_damage_timer > dtime)
164 m_ignore_damage_timer -= dtime;
166 m_ignore_damage_timer = 0.0;
168 //dstream<<"Client steps "<<dtime<<std::endl;
171 //TimeTaker timer("ReceiveAll()", m_device);
177 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
179 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
180 m_con.RunTimeouts(dtime);
187 float &counter = m_packetcounter_timer;
193 dout_client<<"Client packetcounter (20s):"<<std::endl;
194 m_packetcounter.print(dout_client);
195 m_packetcounter.clear();
201 Delete unused sectors
203 NOTE: This jams the game for a while because deleting sectors
207 float &counter = m_delete_unused_sectors_timer;
215 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
217 core::list<v3s16> deleted_blocks;
219 float delete_unused_sectors_timeout =
220 g_settings.getFloat("client_delete_unused_sectors_timeout");
222 // Delete sector blocks
223 /*u32 num = m_env.getMap().unloadUnusedData
224 (delete_unused_sectors_timeout,
225 true, &deleted_blocks);*/
227 // Delete whole sectors
228 u32 num = m_env.getMap().unloadUnusedData
229 (delete_unused_sectors_timeout,
230 false, &deleted_blocks);
234 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
235 <<" unused sectors"<<std::endl;*/
236 dstream<<DTIME<<"Client: Deleted "<<num
237 <<" unused sectors"<<std::endl;
243 // Env is locked so con can be locked.
244 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
246 core::list<v3s16>::Iterator i = deleted_blocks.begin();
247 core::list<v3s16> sendlist;
250 if(sendlist.size() == 255 || i == deleted_blocks.end())
252 if(sendlist.size() == 0)
261 u32 replysize = 2+1+6*sendlist.size();
262 SharedBuffer<u8> reply(replysize);
263 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
264 reply[2] = sendlist.size();
266 for(core::list<v3s16>::Iterator
267 j = sendlist.begin();
268 j != sendlist.end(); j++)
270 writeV3S16(&reply[2+1+6*k], *j);
273 m_con.Send(PEER_ID_SERVER, 1, reply, true);
275 if(i == deleted_blocks.end())
281 sendlist.push_back(*i);
288 bool connected = connectedAndInitialized();
290 if(connected == false)
292 float &counter = m_connection_reinit_timer;
298 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
300 Player *myplayer = m_env.getLocalPlayer();
301 assert(myplayer != NULL);
303 // Send TOSERVER_INIT
304 // [0] u16 TOSERVER_INIT
305 // [2] u8 SER_FMT_VER_HIGHEST
306 // [3] u8[20] player_name
307 // [23] u8[28] password
308 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
309 writeU16(&data[0], TOSERVER_INIT);
310 writeU8(&data[2], SER_FMT_VER_HIGHEST);
312 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
313 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
315 /*dstream<<"Client: password hash is \""<<m_password<<"\""
318 memset((char*)&data[23], 0, PASSWORD_SIZE);
319 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
321 // Send as unreliable
322 Send(0, data, false);
325 // Not connected, return
330 Do stuff if connected
338 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
340 // Control local player (0ms)
341 LocalPlayer *player = m_env.getLocalPlayer();
342 assert(player != NULL);
343 player->applyControl(dtime);
345 //TimeTaker envtimer("env step", m_device);
349 // Step active blocks
350 for(core::map<v3s16, bool>::Iterator
351 i = m_active_blocks.getIterator();
352 i.atEnd() == false; i++)
354 v3s16 p = i.getNode()->getKey();
356 MapBlock *block = NULL;
359 block = m_env.getMap().getBlockNoCreate(p);
360 block->stepObjects(dtime, false, m_env.getDayNightRatio());
362 catch(InvalidPositionException &e)
372 ClientEnvEvent event = m_env.getClientEvent();
373 if(event.type == CEE_NONE)
377 else if(event.type == CEE_PLAYER_DAMAGE)
379 if(m_ignore_damage_timer <= 0)
381 u8 damage = event.player_damage.amount;
384 // Add to ClientEvent queue
386 event.type = CE_PLAYER_DAMAGE;
387 event.player_damage.amount = damage;
388 m_client_event_queue.push_back(event);
398 float &counter = m_avg_rtt_timer;
403 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
404 // connectedAndInitialized() is true, peer exists.
405 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
406 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
411 Send player position to server
414 float &counter = m_playerpos_send_timer;
424 Replace updated meshes
427 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
429 //TimeTaker timer("** Processing mesh update result queue");
432 /*dstream<<"Mesh update result queue size is "
433 <<m_mesh_update_thread.m_queue_out.size()
436 while(m_mesh_update_thread.m_queue_out.size() > 0)
438 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
439 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
442 block->replaceMesh(r.mesh);
444 if(r.ack_block_to_server)
446 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
447 <<","<<r.p.Z<<")"<<std::endl;*/
458 u32 replysize = 2+1+6;
459 SharedBuffer<u8> reply(replysize);
460 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
462 writeV3S16(&reply[3], r.p);
464 m_con.Send(PEER_ID_SERVER, 1, reply, true);
470 // Virtual methods from con::PeerHandler
471 void Client::peerAdded(con::Peer *peer)
473 derr_client<<"Client::peerAdded(): peer->id="
474 <<peer->id<<std::endl;
476 void Client::deletingPeer(con::Peer *peer, bool timeout)
478 derr_client<<"Client::deletingPeer(): "
479 "Server Peer is getting deleted "
480 <<"(timeout="<<timeout<<")"<<std::endl;
483 void Client::ReceiveAll()
485 DSTACK(__FUNCTION_NAME);
491 catch(con::NoIncomingDataException &e)
495 catch(con::InvalidIncomingDataException &e)
497 dout_client<<DTIME<<"Client::ReceiveAll(): "
498 "InvalidIncomingDataException: what()="
499 <<e.what()<<std::endl;
504 void Client::Receive()
506 DSTACK(__FUNCTION_NAME);
507 u32 data_maxsize = 200000;
508 Buffer<u8> data(data_maxsize);
512 //TimeTaker t1("con mutex and receive", m_device);
513 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
514 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
516 //TimeTaker t1("ProcessData", m_device);
517 ProcessData(*data, datasize, sender_peer_id);
521 sender_peer_id given to this shall be quaranteed to be a valid peer
523 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
525 DSTACK(__FUNCTION_NAME);
527 // Ignore packets that don't even fit a command
530 m_packetcounter.add(60000);
534 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
536 //dstream<<"Client: received command="<<command<<std::endl;
537 m_packetcounter.add((u16)command);
540 If this check is removed, be sure to change the queue
541 system to know the ids
543 if(sender_peer_id != PEER_ID_SERVER)
545 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
546 "coming from server: peer_id="<<sender_peer_id
553 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
554 // All data is coming from the server
555 // PeerNotFoundException is handled by caller.
556 peer = m_con.GetPeer(PEER_ID_SERVER);
559 u8 ser_version = m_server_ser_ver;
561 //dstream<<"Client received command="<<(int)command<<std::endl;
563 if(command == TOCLIENT_INIT)
568 u8 deployed = data[2];
570 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
571 "deployed="<<((int)deployed&0xff)<<std::endl;
573 if(deployed < SER_FMT_VER_LOWEST
574 || deployed > SER_FMT_VER_HIGHEST)
576 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
577 <<"unsupported ser_fmt_ver"<<std::endl;
581 m_server_ser_ver = deployed;
583 // Get player position
584 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
585 if(datasize >= 2+1+6)
586 playerpos_s16 = readV3S16(&data[2+1]);
587 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
590 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
592 // Set player position
593 Player *player = m_env.getLocalPlayer();
594 assert(player != NULL);
595 player->setPosition(playerpos_f);
598 if(datasize >= 2+1+6+8)
601 m_map_seed = readU64(&data[2+1+6]);
602 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
607 SharedBuffer<u8> reply(replysize);
608 writeU16(&reply[0], TOSERVER_INIT2);
610 m_con.Send(PEER_ID_SERVER, 1, reply, true);
615 if(command == TOCLIENT_ACCESS_DENIED)
617 // The server didn't like our password. Note, this needs
618 // to be processed even if the serialisation format has
619 // not been agreed yet, the same as TOCLIENT_INIT.
620 m_access_denied = true;
621 m_access_denied_reason = L"Unknown";
624 std::string datastring((char*)&data[2], datasize-2);
625 std::istringstream is(datastring, std::ios_base::binary);
626 m_access_denied_reason = deSerializeWideString(is);
631 if(ser_version == SER_FMT_VER_INVALID)
633 dout_client<<DTIME<<"WARNING: Client: Server serialization"
634 " format invalid or not initialized."
635 " Skipping incoming command="<<command<<std::endl;
639 // Just here to avoid putting the two if's together when
640 // making some copypasta
643 if(command == TOCLIENT_REMOVENODE)
648 p.X = readS16(&data[2]);
649 p.Y = readS16(&data[4]);
650 p.Z = readS16(&data[6]);
652 //TimeTaker t1("TOCLIENT_REMOVENODE");
654 // This will clear the cracking animation after digging
655 ((ClientMap&)m_env.getMap()).clearTempMod(p);
659 else if(command == TOCLIENT_ADDNODE)
661 if(datasize < 8 + MapNode::serializedLength(ser_version))
665 p.X = readS16(&data[2]);
666 p.Y = readS16(&data[4]);
667 p.Z = readS16(&data[6]);
669 //TimeTaker t1("TOCLIENT_ADDNODE");
672 n.deSerialize(&data[8], ser_version);
676 else if(command == TOCLIENT_BLOCKDATA)
678 // Ignore too small packet
683 p.X = readS16(&data[2]);
684 p.Y = readS16(&data[4]);
685 p.Z = readS16(&data[6]);
687 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
688 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
689 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
690 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
692 std::string datastring((char*)&data[8], datasize-8);
693 std::istringstream istr(datastring, std::ios_base::binary);
699 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
702 sector = m_env.getMap().emergeSector(p2d);
704 v2s16 sp = sector->getPos();
707 dstream<<"ERROR: Got sector with getPos()="
708 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
709 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
713 //assert(sector->getPos() == p2d);
715 //TimeTaker timer("MapBlock deSerialize");
719 block = sector->getBlockNoCreate(p.Y);
721 Update an existing block
723 //dstream<<"Updating"<<std::endl;
724 block->deSerialize(istr, ser_version);
726 catch(InvalidPositionException &e)
731 //dstream<<"Creating new"<<std::endl;
732 block = new MapBlock(&m_env.getMap(), p);
733 block->deSerialize(istr, ser_version);
734 sector->insertBlock(block);
738 mod.type = NODEMOD_CHANGECONTENT;
739 mod.param = CONTENT_MESE;
740 block->setTempMod(v3s16(8,10,8), mod);
741 block->setTempMod(v3s16(8,9,8), mod);
742 block->setTempMod(v3s16(8,8,8), mod);
743 block->setTempMod(v3s16(8,7,8), mod);
744 block->setTempMod(v3s16(8,6,8), mod);*/
759 u32 replysize = 2+1+6;
760 SharedBuffer<u8> reply(replysize);
761 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
763 writeV3S16(&reply[3], p);
765 m_con.Send(PEER_ID_SERVER, 1, reply, true);
769 Update Mesh of this block and blocks at x-, y- and z-.
770 Environment should not be locked as it interlocks with the
771 main thread, from which is will want to retrieve textures.
774 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
777 Add it to mesh update queue and set it to be acknowledged after update.
779 addUpdateMeshTaskWithEdge(p, true);
781 else if(command == TOCLIENT_PLAYERPOS)
783 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
787 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
788 our_peer_id = m_con.GetPeerID();
790 // Cancel if we don't have a peer id
791 if(our_peer_id == PEER_ID_INEXISTENT){
792 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
799 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
801 u32 player_size = 2+12+12+4+4;
803 u32 player_count = (datasize-2) / player_size;
805 for(u32 i=0; i<player_count; i++)
807 u16 peer_id = readU16(&data[start]);
809 Player *player = m_env.getPlayer(peer_id);
811 // Skip if player doesn't exist
814 start += player_size;
818 // Skip if player is local player
819 if(player->isLocal())
821 start += player_size;
825 v3s32 ps = readV3S32(&data[start+2]);
826 v3s32 ss = readV3S32(&data[start+2+12]);
827 s32 pitch_i = readS32(&data[start+2+12+12]);
828 s32 yaw_i = readS32(&data[start+2+12+12+4]);
829 /*dstream<<"Client: got "
830 <<"pitch_i="<<pitch_i
831 <<" yaw_i="<<yaw_i<<std::endl;*/
832 f32 pitch = (f32)pitch_i / 100.0;
833 f32 yaw = (f32)yaw_i / 100.0;
834 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
835 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
836 player->setPosition(position);
837 player->setSpeed(speed);
838 player->setPitch(pitch);
841 /*dstream<<"Client: player "<<peer_id
843 <<" yaw="<<yaw<<std::endl;*/
845 start += player_size;
849 else if(command == TOCLIENT_PLAYERINFO)
853 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
854 our_peer_id = m_con.GetPeerID();
856 // Cancel if we don't have a peer id
857 if(our_peer_id == PEER_ID_INEXISTENT){
858 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
864 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
867 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
869 u32 item_size = 2+PLAYERNAME_SIZE;
870 u32 player_count = (datasize-2) / item_size;
873 core::list<u16> players_alive;
874 for(u32 i=0; i<player_count; i++)
876 // Make sure the name ends in '\0'
877 data[start+2+20-1] = 0;
879 u16 peer_id = readU16(&data[start]);
881 players_alive.push_back(peer_id);
883 /*dstream<<DTIME<<"peer_id="<<peer_id
884 <<" name="<<((char*)&data[start+2])<<std::endl;*/
886 // Don't update the info of the local player
887 if(peer_id == our_peer_id)
893 Player *player = m_env.getPlayer(peer_id);
895 // Create a player if it doesn't exist
898 player = new RemotePlayer(
899 m_device->getSceneManager()->getRootSceneNode(),
902 player->peer_id = peer_id;
903 m_env.addPlayer(player);
904 dout_client<<DTIME<<"Client: Adding new player "
905 <<peer_id<<std::endl;
908 player->updateName((char*)&data[start+2]);
914 Remove those players from the environment that
915 weren't listed by the server.
917 //dstream<<DTIME<<"Removing dead players"<<std::endl;
918 core::list<Player*> players = m_env.getPlayers();
919 core::list<Player*>::Iterator ip;
920 for(ip=players.begin(); ip!=players.end(); ip++)
922 // Ingore local player
926 // Warn about a special case
927 if((*ip)->peer_id == 0)
929 dstream<<DTIME<<"WARNING: Client: Removing "
930 "dead player with id=0"<<std::endl;
933 bool is_alive = false;
934 core::list<u16>::Iterator i;
935 for(i=players_alive.begin(); i!=players_alive.end(); i++)
937 if((*ip)->peer_id == *i)
943 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
944 <<" is_alive="<<is_alive<<std::endl;*/
947 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
949 m_env.removePlayer((*ip)->peer_id);
953 else if(command == TOCLIENT_SECTORMETA)
958 [3...] v2s16 pos + sector metadata
963 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
966 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
968 std::string datastring((char*)&data[2], datasize-2);
969 std::istringstream is(datastring, std::ios_base::binary);
973 is.read((char*)buf, 1);
974 u16 sector_count = readU8(buf);
976 //dstream<<"sector_count="<<sector_count<<std::endl;
978 for(u16 i=0; i<sector_count; i++)
981 is.read((char*)buf, 4);
982 v2s16 pos = readV2S16(buf);
983 /*dstream<<"Client: deserializing sector at "
984 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
986 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
987 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
991 else if(command == TOCLIENT_INVENTORY)
996 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
999 //TimeTaker t2("mutex locking", m_device);
1000 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1003 //TimeTaker t3("istringstream init", m_device);
1004 std::string datastring((char*)&data[2], datasize-2);
1005 std::istringstream is(datastring, std::ios_base::binary);
1008 //m_env.printPlayers(dstream);
1010 //TimeTaker t4("player get", m_device);
1011 Player *player = m_env.getLocalPlayer();
1012 assert(player != NULL);
1015 //TimeTaker t1("inventory.deSerialize()", m_device);
1016 player->inventory.deSerialize(is);
1019 m_inventory_updated = true;
1021 //dstream<<"Client got player inventory:"<<std::endl;
1022 //player->inventory.print(dstream);
1026 else if(command == TOCLIENT_OBJECTDATA)
1029 // Strip command word and create a stringstream
1030 std::string datastring((char*)&data[2], datasize-2);
1031 std::istringstream is(datastring, std::ios_base::binary);
1035 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1043 is.read((char*)buf, 2);
1044 u16 playercount = readU16(buf);
1046 for(u16 i=0; i<playercount; i++)
1048 is.read((char*)buf, 2);
1049 u16 peer_id = readU16(buf);
1050 is.read((char*)buf, 12);
1051 v3s32 p_i = readV3S32(buf);
1052 is.read((char*)buf, 12);
1053 v3s32 s_i = readV3S32(buf);
1054 is.read((char*)buf, 4);
1055 s32 pitch_i = readS32(buf);
1056 is.read((char*)buf, 4);
1057 s32 yaw_i = readS32(buf);
1059 Player *player = m_env.getPlayer(peer_id);
1061 // Skip if player doesn't exist
1067 // Skip if player is local player
1068 if(player->isLocal())
1073 f32 pitch = (f32)pitch_i / 100.0;
1074 f32 yaw = (f32)yaw_i / 100.0;
1075 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1076 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1078 player->setPosition(position);
1079 player->setSpeed(speed);
1080 player->setPitch(pitch);
1081 player->setYaw(yaw);
1088 // Read active block count
1089 is.read((char*)buf, 2);
1090 u16 blockcount = readU16(buf);
1092 // Initialize delete queue with all active blocks
1093 core::map<v3s16, bool> abs_to_delete;
1094 for(core::map<v3s16, bool>::Iterator
1095 i = m_active_blocks.getIterator();
1096 i.atEnd() == false; i++)
1098 v3s16 p = i.getNode()->getKey();
1099 /*dstream<<"adding "
1100 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1101 <<" to abs_to_delete"
1103 abs_to_delete.insert(p, true);
1106 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1109 for(u16 i=0; i<blockcount; i++)
1112 is.read((char*)buf, 6);
1113 v3s16 p = readV3S16(buf);
1114 // Get block from somewhere
1115 MapBlock *block = NULL;
1117 block = m_env.getMap().getBlockNoCreate(p);
1119 catch(InvalidPositionException &e)
1121 //TODO: Create a dummy block?
1125 dstream<<"WARNING: "
1126 <<"Could not get block at blockpos "
1127 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1128 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1129 <<"following block object data."
1134 /*dstream<<"Client updating objects for block "
1135 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1138 // Insert to active block list
1139 m_active_blocks.insert(p, true);
1141 // Remove from deletion queue
1142 if(abs_to_delete.find(p) != NULL)
1143 abs_to_delete.remove(p);
1146 Update objects of block
1148 NOTE: Be sure this is done in the main thread.
1150 block->updateObjects(is, m_server_ser_ver,
1151 m_device->getSceneManager(), m_env.getDayNightRatio());
1154 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1157 // Delete objects of blocks in delete queue
1158 for(core::map<v3s16, bool>::Iterator
1159 i = abs_to_delete.getIterator();
1160 i.atEnd() == false; i++)
1162 v3s16 p = i.getNode()->getKey();
1165 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1168 block->clearObjects();
1169 // Remove from active blocks list
1170 m_active_blocks.remove(p);
1172 catch(InvalidPositionException &e)
1174 dstream<<"WARNAING: Client: "
1175 <<"Couldn't clear objects of active->inactive"
1177 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1178 <<" because block was not found"
1186 else if(command == TOCLIENT_TIME_OF_DAY)
1191 u16 time_of_day = readU16(&data[2]);
1192 time_of_day = time_of_day % 24000;
1193 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1201 m_env.setTimeOfDay(time_of_day);
1203 u32 dr = m_env.getDayNightRatio();
1205 dstream<<"Client: time_of_day="<<time_of_day
1211 else if(command == TOCLIENT_CHAT_MESSAGE)
1219 std::string datastring((char*)&data[2], datasize-2);
1220 std::istringstream is(datastring, std::ios_base::binary);
1223 is.read((char*)buf, 2);
1224 u16 len = readU16(buf);
1226 std::wstring message;
1227 for(u16 i=0; i<len; i++)
1229 is.read((char*)buf, 2);
1230 message += (wchar_t)readU16(buf);
1233 /*dstream<<"Client received chat message: "
1234 <<wide_to_narrow(message)<<std::endl;*/
1236 m_chat_queue.push_back(message);
1238 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1240 //if(g_settings.getBool("enable_experimental"))
1244 u16 count of removed objects
1245 for all removed objects {
1248 u16 count of added objects
1249 for all added objects {
1252 u16 initialization data length
1253 string initialization data
1258 // Get all data except the command number
1259 std::string datastring((char*)&data[2], datasize-2);
1260 // Throw them in an istringstream
1261 std::istringstream is(datastring, std::ios_base::binary);
1265 // Read removed objects
1267 u16 removed_count = readU16((u8*)buf);
1268 for(u16 i=0; i<removed_count; i++)
1271 u16 id = readU16((u8*)buf);
1274 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1275 m_env.removeActiveObject(id);
1279 // Read added objects
1281 u16 added_count = readU16((u8*)buf);
1282 for(u16 i=0; i<added_count; i++)
1285 u16 id = readU16((u8*)buf);
1287 u8 type = readU8((u8*)buf);
1288 std::string data = deSerializeLongString(is);
1291 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1292 m_env.addActiveObject(id, type, data);
1297 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1299 //if(g_settings.getBool("enable_experimental"))
1311 // Get all data except the command number
1312 std::string datastring((char*)&data[2], datasize-2);
1313 // Throw them in an istringstream
1314 std::istringstream is(datastring, std::ios_base::binary);
1316 while(is.eof() == false)
1320 u16 id = readU16((u8*)buf);
1324 u16 message_size = readU16((u8*)buf);
1325 std::string message;
1326 message.reserve(message_size);
1327 for(u16 i=0; i<message_size; i++)
1330 message.append(buf, 1);
1332 // Pass on to the environment
1334 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1335 m_env.processActiveObjectMessage(id, message);
1340 else if(command == TOCLIENT_HP)
1342 std::string datastring((char*)&data[2], datasize-2);
1343 std::istringstream is(datastring, std::ios_base::binary);
1344 Player *player = m_env.getLocalPlayer();
1345 assert(player != NULL);
1349 else if(command == TOCLIENT_MOVE_PLAYER)
1351 std::string datastring((char*)&data[2], datasize-2);
1352 std::istringstream is(datastring, std::ios_base::binary);
1353 Player *player = m_env.getLocalPlayer();
1354 assert(player != NULL);
1355 v3f pos = readV3F1000(is);
1356 f32 pitch = readF1000(is);
1357 f32 yaw = readF1000(is);
1358 player->setPosition(pos);
1359 /*player->setPitch(pitch);
1360 player->setYaw(yaw);*/
1362 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1363 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1369 Add to ClientEvent queue.
1370 This has to be sent to the main program because otherwise
1371 it would just force the pitch and yaw values to whatever
1372 the camera points to.
1375 event.type = CE_PLAYER_FORCE_MOVE;
1376 event.player_force_move.pitch = pitch;
1377 event.player_force_move.yaw = yaw;
1378 m_client_event_queue.push_back(event);
1380 // Ignore damage for a few seconds, so that the player doesn't
1381 // get damage from falling on ground
1382 m_ignore_damage_timer = 3.0;
1386 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1387 <<command<<std::endl;
1391 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1393 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1394 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1397 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1398 v3s16 nodepos_oversurface, u16 item)
1400 if(connectedAndInitialized() == false){
1401 dout_client<<DTIME<<"Client::groundAction() "
1402 "cancelled (not connected)"
1411 [3] v3s16 nodepos_undersurface
1412 [9] v3s16 nodepos_abovesurface
1417 2: stop digging (all parameters ignored)
1418 3: digging completed
1420 u8 datasize = 2 + 1 + 6 + 6 + 2;
1421 SharedBuffer<u8> data(datasize);
1422 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1423 writeU8(&data[2], action);
1424 writeV3S16(&data[3], nodepos_undersurface);
1425 writeV3S16(&data[9], nodepos_oversurface);
1426 writeU16(&data[15], item);
1427 Send(0, data, true);
1430 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1432 if(connectedAndInitialized() == false){
1433 dout_client<<DTIME<<"Client::clickObject() "
1434 "cancelled (not connected)"
1440 [0] u16 command=TOSERVER_CLICK_OBJECT
1441 [2] u8 button (0=left, 1=right)
1446 u8 datasize = 2 + 1 + 6 + 2 + 2;
1447 SharedBuffer<u8> data(datasize);
1448 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1449 writeU8(&data[2], button);
1450 writeV3S16(&data[3], blockpos);
1451 writeS16(&data[9], id);
1452 writeU16(&data[11], item);
1453 Send(0, data, true);
1456 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1458 if(connectedAndInitialized() == false){
1459 dout_client<<DTIME<<"Client::clickActiveObject() "
1460 "cancelled (not connected)"
1468 [2] u8 button (0=left, 1=right)
1472 u8 datasize = 2 + 1 + 6 + 2 + 2;
1473 SharedBuffer<u8> data(datasize);
1474 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1475 writeU8(&data[2], button);
1476 writeU16(&data[3], id);
1477 writeU16(&data[5], item);
1478 Send(0, data, true);
1481 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1490 std::ostringstream os(std::ios_base::binary);
1494 writeU16(buf, TOSERVER_SIGNTEXT);
1495 os.write((char*)buf, 2);
1498 writeV3S16(buf, blockpos);
1499 os.write((char*)buf, 6);
1503 os.write((char*)buf, 2);
1505 u16 textlen = text.size();
1506 // Write text length
1507 writeS16(buf, textlen);
1508 os.write((char*)buf, 2);
1511 os.write((char*)text.c_str(), textlen);
1514 std::string s = os.str();
1515 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1517 Send(0, data, true);
1520 void Client::sendSignNodeText(v3s16 p, std::string text)
1528 std::ostringstream os(std::ios_base::binary);
1532 writeU16(buf, TOSERVER_SIGNNODETEXT);
1533 os.write((char*)buf, 2);
1537 os.write((char*)buf, 6);
1539 u16 textlen = text.size();
1540 // Write text length
1541 writeS16(buf, textlen);
1542 os.write((char*)buf, 2);
1545 os.write((char*)text.c_str(), textlen);
1548 std::string s = os.str();
1549 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1551 Send(0, data, true);
1554 void Client::sendInventoryAction(InventoryAction *a)
1556 std::ostringstream os(std::ios_base::binary);
1560 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1561 os.write((char*)buf, 2);
1566 std::string s = os.str();
1567 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1569 Send(0, data, true);
1572 void Client::sendChatMessage(const std::wstring &message)
1574 std::ostringstream os(std::ios_base::binary);
1578 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1579 os.write((char*)buf, 2);
1582 writeU16(buf, message.size());
1583 os.write((char*)buf, 2);
1586 for(u32 i=0; i<message.size(); i++)
1590 os.write((char*)buf, 2);
1594 std::string s = os.str();
1595 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1597 Send(0, data, true);
1600 void Client::sendChangePassword(const std::wstring oldpassword,
1601 const std::wstring newpassword)
1603 Player *player = m_env.getLocalPlayer();
1607 std::string playername = player->getName();
1608 std::string oldpwd = translatePassword(playername, oldpassword);
1609 std::string newpwd = translatePassword(playername, newpassword);
1611 std::ostringstream os(std::ios_base::binary);
1612 u8 buf[2+PASSWORD_SIZE*2];
1614 [0] u16 TOSERVER_PASSWORD
1615 [2] u8[28] old password
1616 [30] u8[28] new password
1619 writeU16(buf, TOSERVER_PASSWORD);
1620 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1622 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1623 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1625 buf[2+PASSWORD_SIZE-1] = 0;
1626 buf[30+PASSWORD_SIZE-1] = 0;
1627 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1630 std::string s = os.str();
1631 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1633 Send(0, data, true);
1637 void Client::sendDamage(u8 damage)
1639 DSTACK(__FUNCTION_NAME);
1640 std::ostringstream os(std::ios_base::binary);
1642 writeU16(os, TOSERVER_DAMAGE);
1643 writeU8(os, damage);
1646 std::string s = os.str();
1647 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1649 Send(0, data, true);
1652 void Client::sendPlayerPos()
1654 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1656 Player *myplayer = m_env.getLocalPlayer();
1657 if(myplayer == NULL)
1662 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1663 our_peer_id = m_con.GetPeerID();
1666 // Set peer id if not set already
1667 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1668 myplayer->peer_id = our_peer_id;
1669 // Check that an existing peer_id is the same as the connection's
1670 assert(myplayer->peer_id == our_peer_id);
1672 v3f pf = myplayer->getPosition();
1673 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1674 v3f sf = myplayer->getSpeed();
1675 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1676 s32 pitch = myplayer->getPitch() * 100;
1677 s32 yaw = myplayer->getYaw() * 100;
1682 [2] v3s32 position*100
1683 [2+12] v3s32 speed*100
1684 [2+12+12] s32 pitch*100
1685 [2+12+12+4] s32 yaw*100
1688 SharedBuffer<u8> data(2+12+12+4+4);
1689 writeU16(&data[0], TOSERVER_PLAYERPOS);
1690 writeV3S32(&data[2], position);
1691 writeV3S32(&data[2+12], speed);
1692 writeS32(&data[2+12+12], pitch);
1693 writeS32(&data[2+12+12+4], yaw);
1695 // Send as unreliable
1696 Send(0, data, false);
1699 void Client::removeNode(v3s16 p)
1701 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1703 core::map<v3s16, MapBlock*> modified_blocks;
1707 //TimeTaker t("removeNodeAndUpdate", m_device);
1708 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1710 catch(InvalidPositionException &e)
1714 for(core::map<v3s16, MapBlock * >::Iterator
1715 i = modified_blocks.getIterator();
1716 i.atEnd() == false; i++)
1718 v3s16 p = i.getNode()->getKey();
1719 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1720 addUpdateMeshTaskWithEdge(p);
1724 void Client::addNode(v3s16 p, MapNode n)
1726 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1728 TimeTaker timer1("Client::addNode()");
1730 core::map<v3s16, MapBlock*> modified_blocks;
1734 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1735 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1737 catch(InvalidPositionException &e)
1740 //TimeTaker timer2("Client::addNode(): updateMeshes");
1742 for(core::map<v3s16, MapBlock * >::Iterator
1743 i = modified_blocks.getIterator();
1744 i.atEnd() == false; i++)
1746 v3s16 p = i.getNode()->getKey();
1747 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1748 addUpdateMeshTaskWithEdge(p);
1752 void Client::updateCamera(v3f pos, v3f dir)
1754 m_env.getClientMap().updateCamera(pos, dir);
1755 camera_position = pos;
1756 camera_direction = dir;
1759 MapNode Client::getNode(v3s16 p)
1761 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1762 return m_env.getMap().getNode(p);
1765 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1767 return m_env.getMap().getNodeMetadata(p);
1770 v3f Client::getPlayerPosition()
1772 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1773 LocalPlayer *player = m_env.getLocalPlayer();
1774 assert(player != NULL);
1775 return player->getPosition();
1778 void Client::setPlayerControl(PlayerControl &control)
1780 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1781 LocalPlayer *player = m_env.getLocalPlayer();
1782 assert(player != NULL);
1783 player->control = control;
1786 // Returns true if the inventory of the local player has been
1787 // updated from the server. If it is true, it is set to false.
1788 bool Client::getLocalInventoryUpdated()
1790 // m_inventory_updated is behind envlock
1791 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1792 bool updated = m_inventory_updated;
1793 m_inventory_updated = false;
1797 // Copies the inventory of the local player to parameter
1798 void Client::getLocalInventory(Inventory &dst)
1800 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1801 Player *player = m_env.getLocalPlayer();
1802 assert(player != NULL);
1803 dst = player->inventory;
1806 InventoryContext *Client::getInventoryContext()
1808 return &m_inventory_context;
1811 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1813 if(id == "current_player")
1815 assert(c->current_player);
1816 return &(c->current_player->inventory);
1820 std::string id0 = fn.next(":");
1822 if(id0 == "nodemeta")
1825 p.X = stoi(fn.next(","));
1826 p.Y = stoi(fn.next(","));
1827 p.Z = stoi(fn.next(","));
1828 NodeMetadata* meta = getNodeMetadata(p);
1830 return meta->getInventory();
1831 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1832 <<"no metadata found"<<std::endl;
1836 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1839 void Client::inventoryAction(InventoryAction *a)
1841 sendInventoryAction(a);
1844 MapBlockObject * Client::getSelectedObject(
1846 v3f from_pos_f_on_map,
1847 core::line3d<f32> shootline_on_map
1850 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1852 core::array<DistanceSortedObject> objects;
1854 for(core::map<v3s16, bool>::Iterator
1855 i = m_active_blocks.getIterator();
1856 i.atEnd() == false; i++)
1858 v3s16 p = i.getNode()->getKey();
1860 MapBlock *block = NULL;
1863 block = m_env.getMap().getBlockNoCreate(p);
1865 catch(InvalidPositionException &e)
1870 // Calculate from_pos relative to block
1871 v3s16 block_pos_i_on_map = block->getPosRelative();
1872 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1873 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1875 block->getObjects(from_pos_f_on_block, max_d, objects);
1876 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1879 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1882 // After this, the closest object is the first in the array.
1885 for(u32 i=0; i<objects.size(); i++)
1887 MapBlockObject *obj = objects[i].obj;
1888 MapBlock *block = obj->getBlock();
1890 // Calculate shootline relative to block
1891 v3s16 block_pos_i_on_map = block->getPosRelative();
1892 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1893 core::line3d<f32> shootline_on_block(
1894 shootline_on_map.start - block_pos_f_on_map,
1895 shootline_on_map.end - block_pos_f_on_map
1898 if(obj->isSelected(shootline_on_block))
1900 //dstream<<"Returning selected object"<<std::endl;
1905 //dstream<<"No object selected; returning NULL."<<std::endl;
1909 ClientActiveObject * Client::getSelectedActiveObject(
1911 v3f from_pos_f_on_map,
1912 core::line3d<f32> shootline_on_map
1915 core::array<DistanceSortedActiveObject> objects;
1917 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1919 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1922 // After this, the closest object is the first in the array.
1925 for(u32 i=0; i<objects.size(); i++)
1927 ClientActiveObject *obj = objects[i].obj;
1929 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1930 if(selection_box == NULL)
1933 v3f pos = obj->getPosition();
1935 core::aabbox3d<f32> offsetted_box(
1936 selection_box->MinEdge + pos,
1937 selection_box->MaxEdge + pos
1940 if(offsetted_box.intersectsWithLine(shootline_on_map))
1942 //dstream<<"Returning selected object"<<std::endl;
1947 //dstream<<"No object selected; returning NULL."<<std::endl;
1951 void Client::printDebugInfo(std::ostream &os)
1953 //JMutexAutoLock lock1(m_fetchblock_mutex);
1954 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1956 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1957 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1958 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1962 /*s32 Client::getDayNightIndex()
1964 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1965 return m_daynight_i;
1968 u32 Client::getDayNightRatio()
1970 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1971 return m_env.getDayNightRatio();
1976 Player *player = m_env.getLocalPlayer();
1977 assert(player != NULL);
1981 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1983 /*dstream<<"Client::addUpdateMeshTask(): "
1984 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1987 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1992 Create a task to update the mesh of the block
1995 MeshMakeData *data = new MeshMakeData;
1998 //TimeTaker timer("data fill");
2000 data->fill(getDayNightRatio(), b);
2004 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2006 // Add task to queue
2007 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2009 /*dstream<<"Mesh update input queue size is "
2010 <<m_mesh_update_thread.m_queue_in.size()
2014 // Temporary test: make mesh directly in here
2016 //TimeTaker timer("make mesh");
2018 scene::SMesh *mesh_new = NULL;
2019 mesh_new = makeMapBlockMesh(data);
2020 b->replaceMesh(mesh_new);
2025 b->setMeshExpired(false);
2028 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2032 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2033 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2038 v3s16 p = blockpos + v3s16(0,0,0);
2039 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2040 addUpdateMeshTask(p, ack_to_server);
2042 catch(InvalidPositionException &e){}
2045 v3s16 p = blockpos + v3s16(-1,0,0);
2046 addUpdateMeshTask(p);
2048 catch(InvalidPositionException &e){}
2050 v3s16 p = blockpos + v3s16(0,-1,0);
2051 addUpdateMeshTask(p);
2053 catch(InvalidPositionException &e){}
2055 v3s16 p = blockpos + v3s16(0,0,-1);
2056 addUpdateMeshTask(p);
2058 catch(InvalidPositionException &e){}
2061 ClientEvent Client::getClientEvent()
2063 if(m_client_event_queue.size() == 0)
2066 event.type = CE_NONE;
2069 return m_client_event_queue.pop_front();