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"
28 #include "mapsector.h"
30 void * MeshUpdateThread::Thread()
34 DSTACK(__FUNCTION_NAME);
36 BEGIN_DEBUG_EXCEPTION_HANDLER
40 QueuedMeshUpdate *q = m_queue_in.pop();
47 ScopeProfiler sp(&g_profiler, "mesh make");
49 scene::SMesh *mesh_new = NULL;
50 mesh_new = makeMapBlockMesh(q->data);
55 r.ack_block_to_server = q->ack_block_to_server;
57 /*dstream<<"MeshUpdateThread: Processed "
58 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
61 m_queue_out.push_back(r);
66 END_DEBUG_EXCEPTION_HANDLER
72 IrrlichtDevice *device,
73 const char *playername,
75 MapDrawControl &control):
76 m_mesh_update_thread(),
78 new ClientMap(this, control,
79 device->getSceneManager()->getRootSceneNode(),
80 device->getSceneManager(), 666),
81 device->getSceneManager()
83 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
85 camera_position(0,0,0),
86 camera_direction(0,0,1),
87 m_server_ser_ver(SER_FMT_VER_INVALID),
88 m_inventory_updated(false),
92 m_access_denied(false)
94 m_packetcounter_timer = 0.0;
95 m_delete_unused_sectors_timer = 0.0;
96 m_connection_reinit_timer = 0.0;
97 m_avg_rtt_timer = 0.0;
98 m_playerpos_send_timer = 0.0;
99 m_ignore_damage_timer = 0.0;
101 //m_env_mutex.Init();
102 //m_con_mutex.Init();
104 m_mesh_update_thread.Start();
110 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
112 Player *player = new LocalPlayer();
114 player->updateName(playername);
116 m_env.addPlayer(player);
118 // Initialize player in the inventory context
119 m_inventory_context.current_player = player;
126 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
130 m_mesh_update_thread.setRun(false);
131 while(m_mesh_update_thread.IsRunning())
135 void Client::connect(Address address)
137 DSTACK(__FUNCTION_NAME);
138 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
139 m_con.setTimeoutMs(0);
140 m_con.Connect(address);
143 bool Client::connectedAndInitialized()
145 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
147 if(m_con.Connected() == false)
150 if(m_server_ser_ver == SER_FMT_VER_INVALID)
156 void Client::step(float dtime)
158 DSTACK(__FUNCTION_NAME);
164 if(m_ignore_damage_timer > dtime)
165 m_ignore_damage_timer -= dtime;
167 m_ignore_damage_timer = 0.0;
169 //dstream<<"Client steps "<<dtime<<std::endl;
172 //TimeTaker timer("ReceiveAll()", m_device);
178 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
180 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
181 m_con.RunTimeouts(dtime);
188 float &counter = m_packetcounter_timer;
194 dout_client<<"Client packetcounter (20s):"<<std::endl;
195 m_packetcounter.print(dout_client);
196 m_packetcounter.clear();
202 Delete unused sectors
204 NOTE: This jams the game for a while because deleting sectors
208 float &counter = m_delete_unused_sectors_timer;
216 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
218 core::list<v3s16> deleted_blocks;
220 float delete_unused_sectors_timeout =
221 g_settings.getFloat("client_delete_unused_sectors_timeout");
223 // Delete sector blocks
224 /*u32 num = m_env.getMap().unloadUnusedData
225 (delete_unused_sectors_timeout,
226 true, &deleted_blocks);*/
228 // Delete whole sectors
229 u32 num = m_env.getMap().unloadUnusedData
230 (delete_unused_sectors_timeout,
231 false, &deleted_blocks);
235 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
236 <<" unused sectors"<<std::endl;*/
237 dstream<<DTIME<<"Client: Deleted "<<num
238 <<" unused sectors"<<std::endl;
244 // Env is locked so con can be locked.
245 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
247 core::list<v3s16>::Iterator i = deleted_blocks.begin();
248 core::list<v3s16> sendlist;
251 if(sendlist.size() == 255 || i == deleted_blocks.end())
253 if(sendlist.size() == 0)
262 u32 replysize = 2+1+6*sendlist.size();
263 SharedBuffer<u8> reply(replysize);
264 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
265 reply[2] = sendlist.size();
267 for(core::list<v3s16>::Iterator
268 j = sendlist.begin();
269 j != sendlist.end(); j++)
271 writeV3S16(&reply[2+1+6*k], *j);
274 m_con.Send(PEER_ID_SERVER, 1, reply, true);
276 if(i == deleted_blocks.end())
282 sendlist.push_back(*i);
289 bool connected = connectedAndInitialized();
291 if(connected == false)
293 float &counter = m_connection_reinit_timer;
299 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
301 Player *myplayer = m_env.getLocalPlayer();
302 assert(myplayer != NULL);
304 // Send TOSERVER_INIT
305 // [0] u16 TOSERVER_INIT
306 // [2] u8 SER_FMT_VER_HIGHEST
307 // [3] u8[20] player_name
308 // [23] u8[28] password
309 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
310 writeU16(&data[0], TOSERVER_INIT);
311 writeU8(&data[2], SER_FMT_VER_HIGHEST);
313 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
314 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
316 /*dstream<<"Client: password hash is \""<<m_password<<"\""
319 memset((char*)&data[23], 0, PASSWORD_SIZE);
320 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
322 // Send as unreliable
323 Send(0, data, false);
326 // Not connected, return
331 Do stuff if connected
339 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
341 // Control local player (0ms)
342 LocalPlayer *player = m_env.getLocalPlayer();
343 assert(player != NULL);
344 player->applyControl(dtime);
346 //TimeTaker envtimer("env step", m_device);
350 // Step active blocks
351 for(core::map<v3s16, bool>::Iterator
352 i = m_active_blocks.getIterator();
353 i.atEnd() == false; i++)
355 v3s16 p = i.getNode()->getKey();
357 MapBlock *block = NULL;
360 block = m_env.getMap().getBlockNoCreate(p);
361 block->stepObjects(dtime, false, m_env.getDayNightRatio());
363 catch(InvalidPositionException &e)
373 ClientEnvEvent event = m_env.getClientEvent();
374 if(event.type == CEE_NONE)
378 else if(event.type == CEE_PLAYER_DAMAGE)
380 if(m_ignore_damage_timer <= 0)
382 u8 damage = event.player_damage.amount;
385 // Add to ClientEvent queue
387 event.type = CE_PLAYER_DAMAGE;
388 event.player_damage.amount = damage;
389 m_client_event_queue.push_back(event);
399 float &counter = m_avg_rtt_timer;
404 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
405 // connectedAndInitialized() is true, peer exists.
406 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
407 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
412 Send player position to server
415 float &counter = m_playerpos_send_timer;
425 Replace updated meshes
428 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
430 //TimeTaker timer("** Processing mesh update result queue");
433 /*dstream<<"Mesh update result queue size is "
434 <<m_mesh_update_thread.m_queue_out.size()
437 while(m_mesh_update_thread.m_queue_out.size() > 0)
439 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
440 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
443 block->replaceMesh(r.mesh);
445 if(r.ack_block_to_server)
447 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
448 <<","<<r.p.Z<<")"<<std::endl;*/
459 u32 replysize = 2+1+6;
460 SharedBuffer<u8> reply(replysize);
461 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
463 writeV3S16(&reply[3], r.p);
465 m_con.Send(PEER_ID_SERVER, 1, reply, true);
471 // Virtual methods from con::PeerHandler
472 void Client::peerAdded(con::Peer *peer)
474 derr_client<<"Client::peerAdded(): peer->id="
475 <<peer->id<<std::endl;
477 void Client::deletingPeer(con::Peer *peer, bool timeout)
479 derr_client<<"Client::deletingPeer(): "
480 "Server Peer is getting deleted "
481 <<"(timeout="<<timeout<<")"<<std::endl;
484 void Client::ReceiveAll()
486 DSTACK(__FUNCTION_NAME);
492 catch(con::NoIncomingDataException &e)
496 catch(con::InvalidIncomingDataException &e)
498 dout_client<<DTIME<<"Client::ReceiveAll(): "
499 "InvalidIncomingDataException: what()="
500 <<e.what()<<std::endl;
505 void Client::Receive()
507 DSTACK(__FUNCTION_NAME);
508 u32 data_maxsize = 200000;
509 Buffer<u8> data(data_maxsize);
513 //TimeTaker t1("con mutex and receive", m_device);
514 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
515 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
517 //TimeTaker t1("ProcessData", m_device);
518 ProcessData(*data, datasize, sender_peer_id);
522 sender_peer_id given to this shall be quaranteed to be a valid peer
524 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
526 DSTACK(__FUNCTION_NAME);
528 // Ignore packets that don't even fit a command
531 m_packetcounter.add(60000);
535 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
537 //dstream<<"Client: received command="<<command<<std::endl;
538 m_packetcounter.add((u16)command);
541 If this check is removed, be sure to change the queue
542 system to know the ids
544 if(sender_peer_id != PEER_ID_SERVER)
546 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
547 "coming from server: peer_id="<<sender_peer_id
554 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
555 // All data is coming from the server
556 // PeerNotFoundException is handled by caller.
557 peer = m_con.GetPeer(PEER_ID_SERVER);
560 u8 ser_version = m_server_ser_ver;
562 //dstream<<"Client received command="<<(int)command<<std::endl;
564 if(command == TOCLIENT_INIT)
569 u8 deployed = data[2];
571 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
572 "deployed="<<((int)deployed&0xff)<<std::endl;
574 if(deployed < SER_FMT_VER_LOWEST
575 || deployed > SER_FMT_VER_HIGHEST)
577 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
578 <<"unsupported ser_fmt_ver"<<std::endl;
582 m_server_ser_ver = deployed;
584 // Get player position
585 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
586 if(datasize >= 2+1+6)
587 playerpos_s16 = readV3S16(&data[2+1]);
588 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
591 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
593 // Set player position
594 Player *player = m_env.getLocalPlayer();
595 assert(player != NULL);
596 player->setPosition(playerpos_f);
599 if(datasize >= 2+1+6+8)
602 m_map_seed = readU64(&data[2+1+6]);
603 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
608 SharedBuffer<u8> reply(replysize);
609 writeU16(&reply[0], TOSERVER_INIT2);
611 m_con.Send(PEER_ID_SERVER, 1, reply, true);
616 if(command == TOCLIENT_ACCESS_DENIED)
618 // The server didn't like our password. Note, this needs
619 // to be processed even if the serialisation format has
620 // not been agreed yet, the same as TOCLIENT_INIT.
621 m_access_denied = true;
622 m_access_denied_reason = L"Unknown";
625 std::string datastring((char*)&data[2], datasize-2);
626 std::istringstream is(datastring, std::ios_base::binary);
627 m_access_denied_reason = deSerializeWideString(is);
632 if(ser_version == SER_FMT_VER_INVALID)
634 dout_client<<DTIME<<"WARNING: Client: Server serialization"
635 " format invalid or not initialized."
636 " Skipping incoming command="<<command<<std::endl;
640 // Just here to avoid putting the two if's together when
641 // making some copypasta
644 if(command == TOCLIENT_REMOVENODE)
649 p.X = readS16(&data[2]);
650 p.Y = readS16(&data[4]);
651 p.Z = readS16(&data[6]);
653 //TimeTaker t1("TOCLIENT_REMOVENODE");
655 // This will clear the cracking animation after digging
656 ((ClientMap&)m_env.getMap()).clearTempMod(p);
660 else if(command == TOCLIENT_ADDNODE)
662 if(datasize < 8 + MapNode::serializedLength(ser_version))
666 p.X = readS16(&data[2]);
667 p.Y = readS16(&data[4]);
668 p.Z = readS16(&data[6]);
670 //TimeTaker t1("TOCLIENT_ADDNODE");
673 n.deSerialize(&data[8], ser_version);
677 else if(command == TOCLIENT_BLOCKDATA)
679 // Ignore too small packet
684 p.X = readS16(&data[2]);
685 p.Y = readS16(&data[4]);
686 p.Z = readS16(&data[6]);
688 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
689 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
690 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
691 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
693 std::string datastring((char*)&data[8], datasize-8);
694 std::istringstream istr(datastring, std::ios_base::binary);
700 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
703 sector = m_env.getMap().emergeSector(p2d);
705 v2s16 sp = sector->getPos();
708 dstream<<"ERROR: Got sector with getPos()="
709 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
710 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
714 //assert(sector->getPos() == p2d);
716 //TimeTaker timer("MapBlock deSerialize");
719 block = sector->getBlockNoCreateNoEx(p.Y);
723 Update an existing block
725 //dstream<<"Updating"<<std::endl;
726 block->deSerialize(istr, ser_version);
733 //dstream<<"Creating new"<<std::endl;
734 block = new MapBlock(&m_env.getMap(), p);
735 block->deSerialize(istr, ser_version);
736 sector->insertBlock(block);
740 mod.type = NODEMOD_CHANGECONTENT;
741 mod.param = CONTENT_MESE;
742 block->setTempMod(v3s16(8,10,8), mod);
743 block->setTempMod(v3s16(8,9,8), mod);
744 block->setTempMod(v3s16(8,8,8), mod);
745 block->setTempMod(v3s16(8,7,8), mod);
746 block->setTempMod(v3s16(8,6,8), mod);*/
761 u32 replysize = 2+1+6;
762 SharedBuffer<u8> reply(replysize);
763 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
765 writeV3S16(&reply[3], p);
767 m_con.Send(PEER_ID_SERVER, 1, reply, true);
771 Update Mesh of this block and blocks at x-, y- and z-.
772 Environment should not be locked as it interlocks with the
773 main thread, from which is will want to retrieve textures.
776 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
779 Add it to mesh update queue and set it to be acknowledged after update.
781 addUpdateMeshTaskWithEdge(p, true);
783 else if(command == TOCLIENT_PLAYERPOS)
785 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
789 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
790 our_peer_id = m_con.GetPeerID();
792 // Cancel if we don't have a peer id
793 if(our_peer_id == PEER_ID_INEXISTENT){
794 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
801 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
803 u32 player_size = 2+12+12+4+4;
805 u32 player_count = (datasize-2) / player_size;
807 for(u32 i=0; i<player_count; i++)
809 u16 peer_id = readU16(&data[start]);
811 Player *player = m_env.getPlayer(peer_id);
813 // Skip if player doesn't exist
816 start += player_size;
820 // Skip if player is local player
821 if(player->isLocal())
823 start += player_size;
827 v3s32 ps = readV3S32(&data[start+2]);
828 v3s32 ss = readV3S32(&data[start+2+12]);
829 s32 pitch_i = readS32(&data[start+2+12+12]);
830 s32 yaw_i = readS32(&data[start+2+12+12+4]);
831 /*dstream<<"Client: got "
832 <<"pitch_i="<<pitch_i
833 <<" yaw_i="<<yaw_i<<std::endl;*/
834 f32 pitch = (f32)pitch_i / 100.0;
835 f32 yaw = (f32)yaw_i / 100.0;
836 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
837 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
838 player->setPosition(position);
839 player->setSpeed(speed);
840 player->setPitch(pitch);
843 /*dstream<<"Client: player "<<peer_id
845 <<" yaw="<<yaw<<std::endl;*/
847 start += player_size;
851 else if(command == TOCLIENT_PLAYERINFO)
855 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
856 our_peer_id = m_con.GetPeerID();
858 // Cancel if we don't have a peer id
859 if(our_peer_id == PEER_ID_INEXISTENT){
860 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
866 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
869 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
871 u32 item_size = 2+PLAYERNAME_SIZE;
872 u32 player_count = (datasize-2) / item_size;
875 core::list<u16> players_alive;
876 for(u32 i=0; i<player_count; i++)
878 // Make sure the name ends in '\0'
879 data[start+2+20-1] = 0;
881 u16 peer_id = readU16(&data[start]);
883 players_alive.push_back(peer_id);
885 /*dstream<<DTIME<<"peer_id="<<peer_id
886 <<" name="<<((char*)&data[start+2])<<std::endl;*/
888 // Don't update the info of the local player
889 if(peer_id == our_peer_id)
895 Player *player = m_env.getPlayer(peer_id);
897 // Create a player if it doesn't exist
900 player = new RemotePlayer(
901 m_device->getSceneManager()->getRootSceneNode(),
904 player->peer_id = peer_id;
905 m_env.addPlayer(player);
906 dout_client<<DTIME<<"Client: Adding new player "
907 <<peer_id<<std::endl;
910 player->updateName((char*)&data[start+2]);
916 Remove those players from the environment that
917 weren't listed by the server.
919 //dstream<<DTIME<<"Removing dead players"<<std::endl;
920 core::list<Player*> players = m_env.getPlayers();
921 core::list<Player*>::Iterator ip;
922 for(ip=players.begin(); ip!=players.end(); ip++)
924 // Ingore local player
928 // Warn about a special case
929 if((*ip)->peer_id == 0)
931 dstream<<DTIME<<"WARNING: Client: Removing "
932 "dead player with id=0"<<std::endl;
935 bool is_alive = false;
936 core::list<u16>::Iterator i;
937 for(i=players_alive.begin(); i!=players_alive.end(); i++)
939 if((*ip)->peer_id == *i)
945 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
946 <<" is_alive="<<is_alive<<std::endl;*/
949 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
951 m_env.removePlayer((*ip)->peer_id);
955 else if(command == TOCLIENT_SECTORMETA)
957 dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
962 [3...] v2s16 pos + sector metadata
967 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
970 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
972 std::string datastring((char*)&data[2], datasize-2);
973 std::istringstream is(datastring, std::ios_base::binary);
977 is.read((char*)buf, 1);
978 u16 sector_count = readU8(buf);
980 //dstream<<"sector_count="<<sector_count<<std::endl;
982 for(u16 i=0; i<sector_count; i++)
985 is.read((char*)buf, 4);
986 v2s16 pos = readV2S16(buf);
987 /*dstream<<"Client: deserializing sector at "
988 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
990 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
991 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
996 else if(command == TOCLIENT_INVENTORY)
1001 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1004 //TimeTaker t2("mutex locking", m_device);
1005 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1008 //TimeTaker t3("istringstream init", m_device);
1009 std::string datastring((char*)&data[2], datasize-2);
1010 std::istringstream is(datastring, std::ios_base::binary);
1013 //m_env.printPlayers(dstream);
1015 //TimeTaker t4("player get", m_device);
1016 Player *player = m_env.getLocalPlayer();
1017 assert(player != NULL);
1020 //TimeTaker t1("inventory.deSerialize()", m_device);
1021 player->inventory.deSerialize(is);
1024 m_inventory_updated = true;
1026 //dstream<<"Client got player inventory:"<<std::endl;
1027 //player->inventory.print(dstream);
1031 else if(command == TOCLIENT_OBJECTDATA)
1034 // Strip command word and create a stringstream
1035 std::string datastring((char*)&data[2], datasize-2);
1036 std::istringstream is(datastring, std::ios_base::binary);
1040 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1048 is.read((char*)buf, 2);
1049 u16 playercount = readU16(buf);
1051 for(u16 i=0; i<playercount; i++)
1053 is.read((char*)buf, 2);
1054 u16 peer_id = readU16(buf);
1055 is.read((char*)buf, 12);
1056 v3s32 p_i = readV3S32(buf);
1057 is.read((char*)buf, 12);
1058 v3s32 s_i = readV3S32(buf);
1059 is.read((char*)buf, 4);
1060 s32 pitch_i = readS32(buf);
1061 is.read((char*)buf, 4);
1062 s32 yaw_i = readS32(buf);
1064 Player *player = m_env.getPlayer(peer_id);
1066 // Skip if player doesn't exist
1072 // Skip if player is local player
1073 if(player->isLocal())
1078 f32 pitch = (f32)pitch_i / 100.0;
1079 f32 yaw = (f32)yaw_i / 100.0;
1080 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1081 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1083 player->setPosition(position);
1084 player->setSpeed(speed);
1085 player->setPitch(pitch);
1086 player->setYaw(yaw);
1093 // Read active block count
1094 is.read((char*)buf, 2);
1095 u16 blockcount = readU16(buf);
1097 // Initialize delete queue with all active blocks
1098 core::map<v3s16, bool> abs_to_delete;
1099 for(core::map<v3s16, bool>::Iterator
1100 i = m_active_blocks.getIterator();
1101 i.atEnd() == false; i++)
1103 v3s16 p = i.getNode()->getKey();
1104 /*dstream<<"adding "
1105 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1106 <<" to abs_to_delete"
1108 abs_to_delete.insert(p, true);
1111 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1114 for(u16 i=0; i<blockcount; i++)
1117 is.read((char*)buf, 6);
1118 v3s16 p = readV3S16(buf);
1119 // Get block from somewhere
1120 MapBlock *block = NULL;
1122 block = m_env.getMap().getBlockNoCreate(p);
1124 catch(InvalidPositionException &e)
1126 //TODO: Create a dummy block?
1130 dstream<<"WARNING: "
1131 <<"Could not get block at blockpos "
1132 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1133 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1134 <<"following block object data."
1139 /*dstream<<"Client updating objects for block "
1140 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1143 // Insert to active block list
1144 m_active_blocks.insert(p, true);
1146 // Remove from deletion queue
1147 if(abs_to_delete.find(p) != NULL)
1148 abs_to_delete.remove(p);
1151 Update objects of block
1153 NOTE: Be sure this is done in the main thread.
1155 block->updateObjects(is, m_server_ser_ver,
1156 m_device->getSceneManager(), m_env.getDayNightRatio());
1159 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1162 // Delete objects of blocks in delete queue
1163 for(core::map<v3s16, bool>::Iterator
1164 i = abs_to_delete.getIterator();
1165 i.atEnd() == false; i++)
1167 v3s16 p = i.getNode()->getKey();
1170 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1173 block->clearObjects();
1174 // Remove from active blocks list
1175 m_active_blocks.remove(p);
1177 catch(InvalidPositionException &e)
1179 dstream<<"WARNAING: Client: "
1180 <<"Couldn't clear objects of active->inactive"
1182 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1183 <<" because block was not found"
1191 else if(command == TOCLIENT_TIME_OF_DAY)
1196 u16 time_of_day = readU16(&data[2]);
1197 time_of_day = time_of_day % 24000;
1198 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1206 m_env.setTimeOfDay(time_of_day);
1208 u32 dr = m_env.getDayNightRatio();
1210 dstream<<"Client: time_of_day="<<time_of_day
1216 else if(command == TOCLIENT_CHAT_MESSAGE)
1224 std::string datastring((char*)&data[2], datasize-2);
1225 std::istringstream is(datastring, std::ios_base::binary);
1228 is.read((char*)buf, 2);
1229 u16 len = readU16(buf);
1231 std::wstring message;
1232 for(u16 i=0; i<len; i++)
1234 is.read((char*)buf, 2);
1235 message += (wchar_t)readU16(buf);
1238 /*dstream<<"Client received chat message: "
1239 <<wide_to_narrow(message)<<std::endl;*/
1241 m_chat_queue.push_back(message);
1243 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1245 //if(g_settings.getBool("enable_experimental"))
1249 u16 count of removed objects
1250 for all removed objects {
1253 u16 count of added objects
1254 for all added objects {
1257 u16 initialization data length
1258 string initialization data
1263 // Get all data except the command number
1264 std::string datastring((char*)&data[2], datasize-2);
1265 // Throw them in an istringstream
1266 std::istringstream is(datastring, std::ios_base::binary);
1270 // Read removed objects
1272 u16 removed_count = readU16((u8*)buf);
1273 for(u16 i=0; i<removed_count; i++)
1276 u16 id = readU16((u8*)buf);
1279 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1280 m_env.removeActiveObject(id);
1284 // Read added objects
1286 u16 added_count = readU16((u8*)buf);
1287 for(u16 i=0; i<added_count; i++)
1290 u16 id = readU16((u8*)buf);
1292 u8 type = readU8((u8*)buf);
1293 std::string data = deSerializeLongString(is);
1296 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1297 m_env.addActiveObject(id, type, data);
1302 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1304 //if(g_settings.getBool("enable_experimental"))
1316 // Get all data except the command number
1317 std::string datastring((char*)&data[2], datasize-2);
1318 // Throw them in an istringstream
1319 std::istringstream is(datastring, std::ios_base::binary);
1321 while(is.eof() == false)
1325 u16 id = readU16((u8*)buf);
1329 u16 message_size = readU16((u8*)buf);
1330 std::string message;
1331 message.reserve(message_size);
1332 for(u16 i=0; i<message_size; i++)
1335 message.append(buf, 1);
1337 // Pass on to the environment
1339 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1340 m_env.processActiveObjectMessage(id, message);
1345 else if(command == TOCLIENT_HP)
1347 std::string datastring((char*)&data[2], datasize-2);
1348 std::istringstream is(datastring, std::ios_base::binary);
1349 Player *player = m_env.getLocalPlayer();
1350 assert(player != NULL);
1354 else if(command == TOCLIENT_MOVE_PLAYER)
1356 std::string datastring((char*)&data[2], datasize-2);
1357 std::istringstream is(datastring, std::ios_base::binary);
1358 Player *player = m_env.getLocalPlayer();
1359 assert(player != NULL);
1360 v3f pos = readV3F1000(is);
1361 f32 pitch = readF1000(is);
1362 f32 yaw = readF1000(is);
1363 player->setPosition(pos);
1364 /*player->setPitch(pitch);
1365 player->setYaw(yaw);*/
1367 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1368 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1374 Add to ClientEvent queue.
1375 This has to be sent to the main program because otherwise
1376 it would just force the pitch and yaw values to whatever
1377 the camera points to.
1380 event.type = CE_PLAYER_FORCE_MOVE;
1381 event.player_force_move.pitch = pitch;
1382 event.player_force_move.yaw = yaw;
1383 m_client_event_queue.push_back(event);
1385 // Ignore damage for a few seconds, so that the player doesn't
1386 // get damage from falling on ground
1387 m_ignore_damage_timer = 3.0;
1391 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1392 <<command<<std::endl;
1396 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1398 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1399 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1402 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1403 v3s16 nodepos_oversurface, u16 item)
1405 if(connectedAndInitialized() == false){
1406 dout_client<<DTIME<<"Client::groundAction() "
1407 "cancelled (not connected)"
1416 [3] v3s16 nodepos_undersurface
1417 [9] v3s16 nodepos_abovesurface
1422 2: stop digging (all parameters ignored)
1423 3: digging completed
1425 u8 datasize = 2 + 1 + 6 + 6 + 2;
1426 SharedBuffer<u8> data(datasize);
1427 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1428 writeU8(&data[2], action);
1429 writeV3S16(&data[3], nodepos_undersurface);
1430 writeV3S16(&data[9], nodepos_oversurface);
1431 writeU16(&data[15], item);
1432 Send(0, data, true);
1435 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1437 if(connectedAndInitialized() == false){
1438 dout_client<<DTIME<<"Client::clickObject() "
1439 "cancelled (not connected)"
1445 [0] u16 command=TOSERVER_CLICK_OBJECT
1446 [2] u8 button (0=left, 1=right)
1451 u8 datasize = 2 + 1 + 6 + 2 + 2;
1452 SharedBuffer<u8> data(datasize);
1453 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1454 writeU8(&data[2], button);
1455 writeV3S16(&data[3], blockpos);
1456 writeS16(&data[9], id);
1457 writeU16(&data[11], item);
1458 Send(0, data, true);
1461 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1463 if(connectedAndInitialized() == false){
1464 dout_client<<DTIME<<"Client::clickActiveObject() "
1465 "cancelled (not connected)"
1473 [2] u8 button (0=left, 1=right)
1477 u8 datasize = 2 + 1 + 6 + 2 + 2;
1478 SharedBuffer<u8> data(datasize);
1479 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1480 writeU8(&data[2], button);
1481 writeU16(&data[3], id);
1482 writeU16(&data[5], item);
1483 Send(0, data, true);
1486 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1495 std::ostringstream os(std::ios_base::binary);
1499 writeU16(buf, TOSERVER_SIGNTEXT);
1500 os.write((char*)buf, 2);
1503 writeV3S16(buf, blockpos);
1504 os.write((char*)buf, 6);
1508 os.write((char*)buf, 2);
1510 u16 textlen = text.size();
1511 // Write text length
1512 writeS16(buf, textlen);
1513 os.write((char*)buf, 2);
1516 os.write((char*)text.c_str(), textlen);
1519 std::string s = os.str();
1520 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1522 Send(0, data, true);
1525 void Client::sendSignNodeText(v3s16 p, std::string text)
1533 std::ostringstream os(std::ios_base::binary);
1537 writeU16(buf, TOSERVER_SIGNNODETEXT);
1538 os.write((char*)buf, 2);
1542 os.write((char*)buf, 6);
1544 u16 textlen = text.size();
1545 // Write text length
1546 writeS16(buf, textlen);
1547 os.write((char*)buf, 2);
1550 os.write((char*)text.c_str(), textlen);
1553 std::string s = os.str();
1554 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1556 Send(0, data, true);
1559 void Client::sendInventoryAction(InventoryAction *a)
1561 std::ostringstream os(std::ios_base::binary);
1565 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1566 os.write((char*)buf, 2);
1571 std::string s = os.str();
1572 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1574 Send(0, data, true);
1577 void Client::sendChatMessage(const std::wstring &message)
1579 std::ostringstream os(std::ios_base::binary);
1583 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1584 os.write((char*)buf, 2);
1587 writeU16(buf, message.size());
1588 os.write((char*)buf, 2);
1591 for(u32 i=0; i<message.size(); i++)
1595 os.write((char*)buf, 2);
1599 std::string s = os.str();
1600 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1602 Send(0, data, true);
1605 void Client::sendChangePassword(const std::wstring oldpassword,
1606 const std::wstring newpassword)
1608 Player *player = m_env.getLocalPlayer();
1612 std::string playername = player->getName();
1613 std::string oldpwd = translatePassword(playername, oldpassword);
1614 std::string newpwd = translatePassword(playername, newpassword);
1616 std::ostringstream os(std::ios_base::binary);
1617 u8 buf[2+PASSWORD_SIZE*2];
1619 [0] u16 TOSERVER_PASSWORD
1620 [2] u8[28] old password
1621 [30] u8[28] new password
1624 writeU16(buf, TOSERVER_PASSWORD);
1625 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1627 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1628 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1630 buf[2+PASSWORD_SIZE-1] = 0;
1631 buf[30+PASSWORD_SIZE-1] = 0;
1632 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1635 std::string s = os.str();
1636 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1638 Send(0, data, true);
1642 void Client::sendDamage(u8 damage)
1644 DSTACK(__FUNCTION_NAME);
1645 std::ostringstream os(std::ios_base::binary);
1647 writeU16(os, TOSERVER_DAMAGE);
1648 writeU8(os, damage);
1651 std::string s = os.str();
1652 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1654 Send(0, data, true);
1657 void Client::sendPlayerPos()
1659 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1661 Player *myplayer = m_env.getLocalPlayer();
1662 if(myplayer == NULL)
1667 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1668 our_peer_id = m_con.GetPeerID();
1671 // Set peer id if not set already
1672 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1673 myplayer->peer_id = our_peer_id;
1674 // Check that an existing peer_id is the same as the connection's
1675 assert(myplayer->peer_id == our_peer_id);
1677 v3f pf = myplayer->getPosition();
1678 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1679 v3f sf = myplayer->getSpeed();
1680 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1681 s32 pitch = myplayer->getPitch() * 100;
1682 s32 yaw = myplayer->getYaw() * 100;
1687 [2] v3s32 position*100
1688 [2+12] v3s32 speed*100
1689 [2+12+12] s32 pitch*100
1690 [2+12+12+4] s32 yaw*100
1693 SharedBuffer<u8> data(2+12+12+4+4);
1694 writeU16(&data[0], TOSERVER_PLAYERPOS);
1695 writeV3S32(&data[2], position);
1696 writeV3S32(&data[2+12], speed);
1697 writeS32(&data[2+12+12], pitch);
1698 writeS32(&data[2+12+12+4], yaw);
1700 // Send as unreliable
1701 Send(0, data, false);
1704 void Client::removeNode(v3s16 p)
1706 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1708 core::map<v3s16, MapBlock*> modified_blocks;
1712 //TimeTaker t("removeNodeAndUpdate", m_device);
1713 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1715 catch(InvalidPositionException &e)
1719 for(core::map<v3s16, MapBlock * >::Iterator
1720 i = modified_blocks.getIterator();
1721 i.atEnd() == false; i++)
1723 v3s16 p = i.getNode()->getKey();
1724 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1725 addUpdateMeshTaskWithEdge(p);
1729 void Client::addNode(v3s16 p, MapNode n)
1731 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1733 TimeTaker timer1("Client::addNode()");
1735 core::map<v3s16, MapBlock*> modified_blocks;
1739 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1740 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1742 catch(InvalidPositionException &e)
1745 //TimeTaker timer2("Client::addNode(): updateMeshes");
1747 for(core::map<v3s16, MapBlock * >::Iterator
1748 i = modified_blocks.getIterator();
1749 i.atEnd() == false; i++)
1751 v3s16 p = i.getNode()->getKey();
1752 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1753 addUpdateMeshTaskWithEdge(p);
1757 void Client::updateCamera(v3f pos, v3f dir)
1759 m_env.getClientMap().updateCamera(pos, dir);
1760 camera_position = pos;
1761 camera_direction = dir;
1764 MapNode Client::getNode(v3s16 p)
1766 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1767 return m_env.getMap().getNode(p);
1770 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1772 return m_env.getMap().getNodeMetadata(p);
1775 v3f Client::getPlayerPosition()
1777 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1778 LocalPlayer *player = m_env.getLocalPlayer();
1779 assert(player != NULL);
1780 return player->getPosition();
1783 void Client::setPlayerControl(PlayerControl &control)
1785 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1786 LocalPlayer *player = m_env.getLocalPlayer();
1787 assert(player != NULL);
1788 player->control = control;
1791 // Returns true if the inventory of the local player has been
1792 // updated from the server. If it is true, it is set to false.
1793 bool Client::getLocalInventoryUpdated()
1795 // m_inventory_updated is behind envlock
1796 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1797 bool updated = m_inventory_updated;
1798 m_inventory_updated = false;
1802 // Copies the inventory of the local player to parameter
1803 void Client::getLocalInventory(Inventory &dst)
1805 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1806 Player *player = m_env.getLocalPlayer();
1807 assert(player != NULL);
1808 dst = player->inventory;
1811 InventoryContext *Client::getInventoryContext()
1813 return &m_inventory_context;
1816 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1818 if(id == "current_player")
1820 assert(c->current_player);
1821 return &(c->current_player->inventory);
1825 std::string id0 = fn.next(":");
1827 if(id0 == "nodemeta")
1830 p.X = stoi(fn.next(","));
1831 p.Y = stoi(fn.next(","));
1832 p.Z = stoi(fn.next(","));
1833 NodeMetadata* meta = getNodeMetadata(p);
1835 return meta->getInventory();
1836 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1837 <<"no metadata found"<<std::endl;
1841 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1844 void Client::inventoryAction(InventoryAction *a)
1846 sendInventoryAction(a);
1849 MapBlockObject * Client::getSelectedObject(
1851 v3f from_pos_f_on_map,
1852 core::line3d<f32> shootline_on_map
1855 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1857 core::array<DistanceSortedObject> objects;
1859 for(core::map<v3s16, bool>::Iterator
1860 i = m_active_blocks.getIterator();
1861 i.atEnd() == false; i++)
1863 v3s16 p = i.getNode()->getKey();
1865 MapBlock *block = NULL;
1868 block = m_env.getMap().getBlockNoCreate(p);
1870 catch(InvalidPositionException &e)
1875 // Calculate from_pos relative to block
1876 v3s16 block_pos_i_on_map = block->getPosRelative();
1877 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1878 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1880 block->getObjects(from_pos_f_on_block, max_d, objects);
1881 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1884 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1887 // After this, the closest object is the first in the array.
1890 for(u32 i=0; i<objects.size(); i++)
1892 MapBlockObject *obj = objects[i].obj;
1893 MapBlock *block = obj->getBlock();
1895 // Calculate shootline relative to block
1896 v3s16 block_pos_i_on_map = block->getPosRelative();
1897 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1898 core::line3d<f32> shootline_on_block(
1899 shootline_on_map.start - block_pos_f_on_map,
1900 shootline_on_map.end - block_pos_f_on_map
1903 if(obj->isSelected(shootline_on_block))
1905 //dstream<<"Returning selected object"<<std::endl;
1910 //dstream<<"No object selected; returning NULL."<<std::endl;
1914 ClientActiveObject * Client::getSelectedActiveObject(
1916 v3f from_pos_f_on_map,
1917 core::line3d<f32> shootline_on_map
1920 core::array<DistanceSortedActiveObject> objects;
1922 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1924 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1927 // After this, the closest object is the first in the array.
1930 for(u32 i=0; i<objects.size(); i++)
1932 ClientActiveObject *obj = objects[i].obj;
1934 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1935 if(selection_box == NULL)
1938 v3f pos = obj->getPosition();
1940 core::aabbox3d<f32> offsetted_box(
1941 selection_box->MinEdge + pos,
1942 selection_box->MaxEdge + pos
1945 if(offsetted_box.intersectsWithLine(shootline_on_map))
1947 //dstream<<"Returning selected object"<<std::endl;
1952 //dstream<<"No object selected; returning NULL."<<std::endl;
1956 void Client::printDebugInfo(std::ostream &os)
1958 //JMutexAutoLock lock1(m_fetchblock_mutex);
1959 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1961 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1962 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1963 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1967 /*s32 Client::getDayNightIndex()
1969 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1970 return m_daynight_i;
1973 u32 Client::getDayNightRatio()
1975 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1976 return m_env.getDayNightRatio();
1981 Player *player = m_env.getLocalPlayer();
1982 assert(player != NULL);
1986 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1988 /*dstream<<"Client::addUpdateMeshTask(): "
1989 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1992 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1997 Create a task to update the mesh of the block
2000 MeshMakeData *data = new MeshMakeData;
2003 //TimeTaker timer("data fill");
2005 data->fill(getDayNightRatio(), b);
2009 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2011 // Add task to queue
2012 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2014 /*dstream<<"Mesh update input queue size is "
2015 <<m_mesh_update_thread.m_queue_in.size()
2019 // Temporary test: make mesh directly in here
2021 //TimeTaker timer("make mesh");
2023 scene::SMesh *mesh_new = NULL;
2024 mesh_new = makeMapBlockMesh(data);
2025 b->replaceMesh(mesh_new);
2030 b->setMeshExpired(false);
2033 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2037 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2038 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2043 v3s16 p = blockpos + v3s16(0,0,0);
2044 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2045 addUpdateMeshTask(p, ack_to_server);
2047 catch(InvalidPositionException &e){}
2050 v3s16 p = blockpos + v3s16(-1,0,0);
2051 addUpdateMeshTask(p);
2053 catch(InvalidPositionException &e){}
2055 v3s16 p = blockpos + v3s16(0,-1,0);
2056 addUpdateMeshTask(p);
2058 catch(InvalidPositionException &e){}
2060 v3s16 p = blockpos + v3s16(0,0,-1);
2061 addUpdateMeshTask(p);
2063 catch(InvalidPositionException &e){}
2066 ClientEvent Client::getClientEvent()
2068 if(m_client_event_queue.size() == 0)
2071 event.type = CE_NONE;
2074 return m_client_event_queue.pop_front();