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,
72 MapDrawControl &control):
73 m_mesh_update_thread(),
75 new ClientMap(this, control,
76 device->getSceneManager()->getRootSceneNode(),
77 device->getSceneManager(), 666),
78 device->getSceneManager()
80 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
82 camera_position(0,0,0),
83 camera_direction(0,0,1),
84 m_server_ser_ver(SER_FMT_VER_INVALID),
85 m_inventory_updated(false),
89 m_access_denied(false)
91 m_packetcounter_timer = 0.0;
92 m_delete_unused_sectors_timer = 0.0;
93 m_connection_reinit_timer = 0.0;
94 m_avg_rtt_timer = 0.0;
95 m_playerpos_send_timer = 0.0;
96 m_ignore_damage_timer = 0.0;
101 m_mesh_update_thread.Start();
107 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
109 Player *player = new LocalPlayer();
111 player->updateName(playername);
113 m_env.addPlayer(player);
115 // Initialize player in the inventory context
116 m_inventory_context.current_player = player;
123 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
127 m_mesh_update_thread.setRun(false);
128 while(m_mesh_update_thread.IsRunning())
132 void Client::connect(Address address)
134 DSTACK(__FUNCTION_NAME);
135 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
136 m_con.setTimeoutMs(0);
137 m_con.Connect(address);
140 bool Client::connectedAndInitialized()
142 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
144 if(m_con.Connected() == false)
147 if(m_server_ser_ver == SER_FMT_VER_INVALID)
153 void Client::step(float dtime)
155 DSTACK(__FUNCTION_NAME);
161 if(m_ignore_damage_timer > dtime)
162 m_ignore_damage_timer -= dtime;
164 m_ignore_damage_timer = 0.0;
166 //dstream<<"Client steps "<<dtime<<std::endl;
169 //TimeTaker timer("ReceiveAll()", m_device);
175 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
177 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
178 m_con.RunTimeouts(dtime);
185 float &counter = m_packetcounter_timer;
191 dout_client<<"Client packetcounter (20s):"<<std::endl;
192 m_packetcounter.print(dout_client);
193 m_packetcounter.clear();
199 Delete unused sectors
201 NOTE: This jams the game for a while because deleting sectors
205 float &counter = m_delete_unused_sectors_timer;
213 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
215 core::list<v3s16> deleted_blocks;
217 float delete_unused_sectors_timeout =
218 g_settings.getFloat("client_delete_unused_sectors_timeout");
220 // Delete sector blocks
221 /*u32 num = m_env.getMap().deleteUnusedSectors
222 (delete_unused_sectors_timeout,
223 true, &deleted_blocks);*/
225 // Delete whole sectors
226 u32 num = m_env.getMap().deleteUnusedSectors
227 (delete_unused_sectors_timeout,
228 false, &deleted_blocks);
232 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
233 <<" unused sectors"<<std::endl;*/
234 dstream<<DTIME<<"Client: Deleted "<<num
235 <<" unused sectors"<<std::endl;
241 // Env is locked so con can be locked.
242 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
244 core::list<v3s16>::Iterator i = deleted_blocks.begin();
245 core::list<v3s16> sendlist;
248 if(sendlist.size() == 255 || i == deleted_blocks.end())
250 if(sendlist.size() == 0)
259 u32 replysize = 2+1+6*sendlist.size();
260 SharedBuffer<u8> reply(replysize);
261 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
262 reply[2] = sendlist.size();
264 for(core::list<v3s16>::Iterator
265 j = sendlist.begin();
266 j != sendlist.end(); j++)
268 writeV3S16(&reply[2+1+6*k], *j);
271 m_con.Send(PEER_ID_SERVER, 1, reply, true);
273 if(i == deleted_blocks.end())
279 sendlist.push_back(*i);
286 bool connected = connectedAndInitialized();
288 if(connected == false)
290 float &counter = m_connection_reinit_timer;
296 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
298 Player *myplayer = m_env.getLocalPlayer();
299 assert(myplayer != NULL);
301 // Send TOSERVER_INIT
302 // [0] u16 TOSERVER_INIT
303 // [2] u8 SER_FMT_VER_HIGHEST
304 // [3] u8[20] player_name
305 // [23] u8[28] password
306 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
307 writeU16(&data[0], TOSERVER_INIT);
308 writeU8(&data[2], SER_FMT_VER_HIGHEST);
309 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
310 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
311 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
313 // Send as unreliable
314 Send(0, data, false);
317 // Not connected, return
322 Do stuff if connected
330 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
332 // Control local player (0ms)
333 LocalPlayer *player = m_env.getLocalPlayer();
334 assert(player != NULL);
335 player->applyControl(dtime);
337 //TimeTaker envtimer("env step", m_device);
341 // Step active blocks
342 for(core::map<v3s16, bool>::Iterator
343 i = m_active_blocks.getIterator();
344 i.atEnd() == false; i++)
346 v3s16 p = i.getNode()->getKey();
348 MapBlock *block = NULL;
351 block = m_env.getMap().getBlockNoCreate(p);
352 block->stepObjects(dtime, false, m_env.getDayNightRatio());
354 catch(InvalidPositionException &e)
364 ClientEnvEvent event = m_env.getClientEvent();
365 if(event.type == CEE_NONE)
369 else if(event.type == CEE_PLAYER_DAMAGE)
371 if(m_ignore_damage_timer <= 0)
373 u8 damage = event.player_damage.amount;
376 // Add to ClientEvent queue
378 event.type = CE_PLAYER_DAMAGE;
379 event.player_damage.amount = damage;
380 m_client_event_queue.push_back(event);
390 float &counter = m_avg_rtt_timer;
395 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
396 // connectedAndInitialized() is true, peer exists.
397 con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
398 dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
403 Send player position to server
406 float &counter = m_playerpos_send_timer;
416 Replace updated meshes
419 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
421 //TimeTaker timer("** Processing mesh update result queue");
424 /*dstream<<"Mesh update result queue size is "
425 <<m_mesh_update_thread.m_queue_out.size()
428 while(m_mesh_update_thread.m_queue_out.size() > 0)
430 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
431 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
434 block->replaceMesh(r.mesh);
436 if(r.ack_block_to_server)
438 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
439 <<","<<r.p.Z<<")"<<std::endl;*/
450 u32 replysize = 2+1+6;
451 SharedBuffer<u8> reply(replysize);
452 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
454 writeV3S16(&reply[3], r.p);
456 m_con.Send(PEER_ID_SERVER, 1, reply, true);
462 // Virtual methods from con::PeerHandler
463 void Client::peerAdded(con::Peer *peer)
465 derr_client<<"Client::peerAdded(): peer->id="
466 <<peer->id<<std::endl;
468 void Client::deletingPeer(con::Peer *peer, bool timeout)
470 derr_client<<"Client::deletingPeer(): "
471 "Server Peer is getting deleted "
472 <<"(timeout="<<timeout<<")"<<std::endl;
475 void Client::ReceiveAll()
477 DSTACK(__FUNCTION_NAME);
483 catch(con::NoIncomingDataException &e)
487 catch(con::InvalidIncomingDataException &e)
489 dout_client<<DTIME<<"Client::ReceiveAll(): "
490 "InvalidIncomingDataException: what()="
491 <<e.what()<<std::endl;
496 void Client::Receive()
498 DSTACK(__FUNCTION_NAME);
499 u32 data_maxsize = 200000;
500 Buffer<u8> data(data_maxsize);
504 //TimeTaker t1("con mutex and receive", m_device);
505 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
506 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
508 //TimeTaker t1("ProcessData", m_device);
509 ProcessData(*data, datasize, sender_peer_id);
513 sender_peer_id given to this shall be quaranteed to be a valid peer
515 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
517 DSTACK(__FUNCTION_NAME);
519 // Ignore packets that don't even fit a command
522 m_packetcounter.add(60000);
526 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
528 //dstream<<"Client: received command="<<command<<std::endl;
529 m_packetcounter.add((u16)command);
532 If this check is removed, be sure to change the queue
533 system to know the ids
535 if(sender_peer_id != PEER_ID_SERVER)
537 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
538 "coming from server: peer_id="<<sender_peer_id
545 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
546 // All data is coming from the server
547 // PeerNotFoundException is handled by caller.
548 peer = m_con.GetPeer(PEER_ID_SERVER);
551 u8 ser_version = m_server_ser_ver;
553 //dstream<<"Client received command="<<(int)command<<std::endl;
555 if(command == TOCLIENT_INIT)
560 u8 deployed = data[2];
562 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
563 "deployed="<<((int)deployed&0xff)<<std::endl;
565 if(deployed < SER_FMT_VER_LOWEST
566 || deployed > SER_FMT_VER_HIGHEST)
568 derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
569 <<"unsupported ser_fmt_ver"<<std::endl;
573 m_server_ser_ver = deployed;
575 // Get player position
576 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
577 if(datasize >= 2+1+6)
578 playerpos_s16 = readV3S16(&data[2+1]);
579 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
582 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
584 // Set player position
585 Player *player = m_env.getLocalPlayer();
586 assert(player != NULL);
587 player->setPosition(playerpos_f);
590 if(datasize >= 2+1+6+8)
593 m_map_seed = readU64(&data[2+1+6]);
594 dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
599 SharedBuffer<u8> reply(replysize);
600 writeU16(&reply[0], TOSERVER_INIT2);
602 m_con.Send(PEER_ID_SERVER, 1, reply, true);
607 if(command == TOCLIENT_ACCESS_DENIED)
609 // The server didn't like our password. Note, this needs
610 // to be processed even if the serialisation format has
611 // not been agreed yet, the same as TOCLIENT_INIT.
612 m_access_denied = true;
613 m_access_denied_reason = L"Unknown";
616 std::string datastring((char*)&data[2], datasize-2);
617 std::istringstream is(datastring, std::ios_base::binary);
618 m_access_denied_reason = deSerializeWideString(is);
623 if(ser_version == SER_FMT_VER_INVALID)
625 dout_client<<DTIME<<"WARNING: Client: Server serialization"
626 " format invalid or not initialized."
627 " Skipping incoming command="<<command<<std::endl;
631 // Just here to avoid putting the two if's together when
632 // making some copypasta
635 if(command == TOCLIENT_REMOVENODE)
640 p.X = readS16(&data[2]);
641 p.Y = readS16(&data[4]);
642 p.Z = readS16(&data[6]);
644 //TimeTaker t1("TOCLIENT_REMOVENODE");
646 // This will clear the cracking animation after digging
647 ((ClientMap&)m_env.getMap()).clearTempMod(p);
651 else if(command == TOCLIENT_ADDNODE)
653 if(datasize < 8 + MapNode::serializedLength(ser_version))
657 p.X = readS16(&data[2]);
658 p.Y = readS16(&data[4]);
659 p.Z = readS16(&data[6]);
661 //TimeTaker t1("TOCLIENT_ADDNODE");
664 n.deSerialize(&data[8], ser_version);
668 else if(command == TOCLIENT_BLOCKDATA)
670 // Ignore too small packet
675 p.X = readS16(&data[2]);
676 p.Y = readS16(&data[4]);
677 p.Z = readS16(&data[6]);
679 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
680 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
681 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
682 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
684 std::string datastring((char*)&data[8], datasize-8);
685 std::istringstream istr(datastring, std::ios_base::binary);
691 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
694 sector = m_env.getMap().emergeSector(p2d);
696 v2s16 sp = sector->getPos();
699 dstream<<"ERROR: Got sector with getPos()="
700 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
701 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
705 //assert(sector->getPos() == p2d);
707 //TimeTaker timer("MapBlock deSerialize");
711 block = sector->getBlockNoCreate(p.Y);
713 Update an existing block
715 //dstream<<"Updating"<<std::endl;
716 block->deSerialize(istr, ser_version);
717 //block->setChangedFlag();
719 catch(InvalidPositionException &e)
724 //dstream<<"Creating new"<<std::endl;
725 block = new MapBlock(&m_env.getMap(), p);
726 block->deSerialize(istr, ser_version);
727 sector->insertBlock(block);
728 //block->setChangedFlag();
732 mod.type = NODEMOD_CHANGECONTENT;
733 mod.param = CONTENT_MESE;
734 block->setTempMod(v3s16(8,10,8), mod);
735 block->setTempMod(v3s16(8,9,8), mod);
736 block->setTempMod(v3s16(8,8,8), mod);
737 block->setTempMod(v3s16(8,7,8), mod);
738 block->setTempMod(v3s16(8,6,8), mod);*/
742 Well, this is a dumb way to do it, they should just
743 be drawn as separate objects. But the looks of them
744 can be tested this way.
749 mod.type = NODEMOD_CHANGECONTENT;
750 mod.param = CONTENT_CLOUD;
753 for(p2.X=3; p2.X<=13; p2.X++)
754 for(p2.Z=3; p2.Z<=13; p2.Z++)
756 block->setTempMod(p2, mod);
774 u32 replysize = 2+1+6;
775 SharedBuffer<u8> reply(replysize);
776 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
778 writeV3S16(&reply[3], p);
780 m_con.Send(PEER_ID_SERVER, 1, reply, true);
784 Update Mesh of this block and blocks at x-, y- and z-.
785 Environment should not be locked as it interlocks with the
786 main thread, from which is will want to retrieve textures.
789 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
791 addUpdateMeshTaskWithEdge(p, true);
793 else if(command == TOCLIENT_PLAYERPOS)
795 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
799 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
800 our_peer_id = m_con.GetPeerID();
802 // Cancel if we don't have a peer id
803 if(our_peer_id == PEER_ID_INEXISTENT){
804 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
811 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
813 u32 player_size = 2+12+12+4+4;
815 u32 player_count = (datasize-2) / player_size;
817 for(u32 i=0; i<player_count; i++)
819 u16 peer_id = readU16(&data[start]);
821 Player *player = m_env.getPlayer(peer_id);
823 // Skip if player doesn't exist
826 start += player_size;
830 // Skip if player is local player
831 if(player->isLocal())
833 start += player_size;
837 v3s32 ps = readV3S32(&data[start+2]);
838 v3s32 ss = readV3S32(&data[start+2+12]);
839 s32 pitch_i = readS32(&data[start+2+12+12]);
840 s32 yaw_i = readS32(&data[start+2+12+12+4]);
841 /*dstream<<"Client: got "
842 <<"pitch_i="<<pitch_i
843 <<" yaw_i="<<yaw_i<<std::endl;*/
844 f32 pitch = (f32)pitch_i / 100.0;
845 f32 yaw = (f32)yaw_i / 100.0;
846 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
847 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
848 player->setPosition(position);
849 player->setSpeed(speed);
850 player->setPitch(pitch);
853 /*dstream<<"Client: player "<<peer_id
855 <<" yaw="<<yaw<<std::endl;*/
857 start += player_size;
861 else if(command == TOCLIENT_PLAYERINFO)
865 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
866 our_peer_id = m_con.GetPeerID();
868 // Cancel if we don't have a peer id
869 if(our_peer_id == PEER_ID_INEXISTENT){
870 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
876 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
879 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
881 u32 item_size = 2+PLAYERNAME_SIZE;
882 u32 player_count = (datasize-2) / item_size;
885 core::list<u16> players_alive;
886 for(u32 i=0; i<player_count; i++)
888 // Make sure the name ends in '\0'
889 data[start+2+20-1] = 0;
891 u16 peer_id = readU16(&data[start]);
893 players_alive.push_back(peer_id);
895 /*dstream<<DTIME<<"peer_id="<<peer_id
896 <<" name="<<((char*)&data[start+2])<<std::endl;*/
898 // Don't update the info of the local player
899 if(peer_id == our_peer_id)
905 Player *player = m_env.getPlayer(peer_id);
907 // Create a player if it doesn't exist
910 player = new RemotePlayer(
911 m_device->getSceneManager()->getRootSceneNode(),
914 player->peer_id = peer_id;
915 m_env.addPlayer(player);
916 dout_client<<DTIME<<"Client: Adding new player "
917 <<peer_id<<std::endl;
920 player->updateName((char*)&data[start+2]);
926 Remove those players from the environment that
927 weren't listed by the server.
929 //dstream<<DTIME<<"Removing dead players"<<std::endl;
930 core::list<Player*> players = m_env.getPlayers();
931 core::list<Player*>::Iterator ip;
932 for(ip=players.begin(); ip!=players.end(); ip++)
934 // Ingore local player
938 // Warn about a special case
939 if((*ip)->peer_id == 0)
941 dstream<<DTIME<<"WARNING: Client: Removing "
942 "dead player with id=0"<<std::endl;
945 bool is_alive = false;
946 core::list<u16>::Iterator i;
947 for(i=players_alive.begin(); i!=players_alive.end(); i++)
949 if((*ip)->peer_id == *i)
955 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
956 <<" is_alive="<<is_alive<<std::endl;*/
959 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
961 m_env.removePlayer((*ip)->peer_id);
965 else if(command == TOCLIENT_SECTORMETA)
970 [3...] v2s16 pos + sector metadata
975 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
978 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
980 std::string datastring((char*)&data[2], datasize-2);
981 std::istringstream is(datastring, std::ios_base::binary);
985 is.read((char*)buf, 1);
986 u16 sector_count = readU8(buf);
988 //dstream<<"sector_count="<<sector_count<<std::endl;
990 for(u16 i=0; i<sector_count; i++)
993 is.read((char*)buf, 4);
994 v2s16 pos = readV2S16(buf);
995 /*dstream<<"Client: deserializing sector at "
996 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
998 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
999 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1003 else if(command == TOCLIENT_INVENTORY)
1008 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1011 //TimeTaker t2("mutex locking", m_device);
1012 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1015 //TimeTaker t3("istringstream init", m_device);
1016 std::string datastring((char*)&data[2], datasize-2);
1017 std::istringstream is(datastring, std::ios_base::binary);
1020 //m_env.printPlayers(dstream);
1022 //TimeTaker t4("player get", m_device);
1023 Player *player = m_env.getLocalPlayer();
1024 assert(player != NULL);
1027 //TimeTaker t1("inventory.deSerialize()", m_device);
1028 player->inventory.deSerialize(is);
1031 m_inventory_updated = true;
1033 //dstream<<"Client got player inventory:"<<std::endl;
1034 //player->inventory.print(dstream);
1038 else if(command == TOCLIENT_OBJECTDATA)
1041 // Strip command word and create a stringstream
1042 std::string datastring((char*)&data[2], datasize-2);
1043 std::istringstream is(datastring, std::ios_base::binary);
1047 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1055 is.read((char*)buf, 2);
1056 u16 playercount = readU16(buf);
1058 for(u16 i=0; i<playercount; i++)
1060 is.read((char*)buf, 2);
1061 u16 peer_id = readU16(buf);
1062 is.read((char*)buf, 12);
1063 v3s32 p_i = readV3S32(buf);
1064 is.read((char*)buf, 12);
1065 v3s32 s_i = readV3S32(buf);
1066 is.read((char*)buf, 4);
1067 s32 pitch_i = readS32(buf);
1068 is.read((char*)buf, 4);
1069 s32 yaw_i = readS32(buf);
1071 Player *player = m_env.getPlayer(peer_id);
1073 // Skip if player doesn't exist
1079 // Skip if player is local player
1080 if(player->isLocal())
1085 f32 pitch = (f32)pitch_i / 100.0;
1086 f32 yaw = (f32)yaw_i / 100.0;
1087 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1088 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1090 player->setPosition(position);
1091 player->setSpeed(speed);
1092 player->setPitch(pitch);
1093 player->setYaw(yaw);
1100 // Read active block count
1101 is.read((char*)buf, 2);
1102 u16 blockcount = readU16(buf);
1104 // Initialize delete queue with all active blocks
1105 core::map<v3s16, bool> abs_to_delete;
1106 for(core::map<v3s16, bool>::Iterator
1107 i = m_active_blocks.getIterator();
1108 i.atEnd() == false; i++)
1110 v3s16 p = i.getNode()->getKey();
1111 /*dstream<<"adding "
1112 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1113 <<" to abs_to_delete"
1115 abs_to_delete.insert(p, true);
1118 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1121 for(u16 i=0; i<blockcount; i++)
1124 is.read((char*)buf, 6);
1125 v3s16 p = readV3S16(buf);
1126 // Get block from somewhere
1127 MapBlock *block = NULL;
1129 block = m_env.getMap().getBlockNoCreate(p);
1131 catch(InvalidPositionException &e)
1133 //TODO: Create a dummy block?
1137 dstream<<"WARNING: "
1138 <<"Could not get block at blockpos "
1139 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1140 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1141 <<"following block object data."
1146 /*dstream<<"Client updating objects for block "
1147 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1150 // Insert to active block list
1151 m_active_blocks.insert(p, true);
1153 // Remove from deletion queue
1154 if(abs_to_delete.find(p) != NULL)
1155 abs_to_delete.remove(p);
1158 Update objects of block
1160 NOTE: Be sure this is done in the main thread.
1162 block->updateObjects(is, m_server_ser_ver,
1163 m_device->getSceneManager(), m_env.getDayNightRatio());
1166 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1169 // Delete objects of blocks in delete queue
1170 for(core::map<v3s16, bool>::Iterator
1171 i = abs_to_delete.getIterator();
1172 i.atEnd() == false; i++)
1174 v3s16 p = i.getNode()->getKey();
1177 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1180 block->clearObjects();
1181 // Remove from active blocks list
1182 m_active_blocks.remove(p);
1184 catch(InvalidPositionException &e)
1186 dstream<<"WARNAING: Client: "
1187 <<"Couldn't clear objects of active->inactive"
1189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1190 <<" because block was not found"
1198 else if(command == TOCLIENT_TIME_OF_DAY)
1203 u16 time_of_day = readU16(&data[2]);
1204 time_of_day = time_of_day % 24000;
1205 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1213 m_env.setTimeOfDay(time_of_day);
1215 u32 dr = m_env.getDayNightRatio();
1217 dstream<<"Client: time_of_day="<<time_of_day
1223 else if(command == TOCLIENT_CHAT_MESSAGE)
1231 std::string datastring((char*)&data[2], datasize-2);
1232 std::istringstream is(datastring, std::ios_base::binary);
1235 is.read((char*)buf, 2);
1236 u16 len = readU16(buf);
1238 std::wstring message;
1239 for(u16 i=0; i<len; i++)
1241 is.read((char*)buf, 2);
1242 message += (wchar_t)readU16(buf);
1245 /*dstream<<"Client received chat message: "
1246 <<wide_to_narrow(message)<<std::endl;*/
1248 m_chat_queue.push_back(message);
1250 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1252 //if(g_settings.getBool("enable_experimental"))
1256 u16 count of removed objects
1257 for all removed objects {
1260 u16 count of added objects
1261 for all added objects {
1264 u16 initialization data length
1265 string initialization data
1270 // Get all data except the command number
1271 std::string datastring((char*)&data[2], datasize-2);
1272 // Throw them in an istringstream
1273 std::istringstream is(datastring, std::ios_base::binary);
1277 // Read removed objects
1279 u16 removed_count = readU16((u8*)buf);
1280 for(u16 i=0; i<removed_count; i++)
1283 u16 id = readU16((u8*)buf);
1286 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1287 m_env.removeActiveObject(id);
1291 // Read added objects
1293 u16 added_count = readU16((u8*)buf);
1294 for(u16 i=0; i<added_count; i++)
1297 u16 id = readU16((u8*)buf);
1299 u8 type = readU8((u8*)buf);
1300 std::string data = deSerializeLongString(is);
1303 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1304 m_env.addActiveObject(id, type, data);
1309 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1311 //if(g_settings.getBool("enable_experimental"))
1323 // Get all data except the command number
1324 std::string datastring((char*)&data[2], datasize-2);
1325 // Throw them in an istringstream
1326 std::istringstream is(datastring, std::ios_base::binary);
1328 while(is.eof() == false)
1332 u16 id = readU16((u8*)buf);
1336 u16 message_size = readU16((u8*)buf);
1337 std::string message;
1338 message.reserve(message_size);
1339 for(u16 i=0; i<message_size; i++)
1342 message.append(buf, 1);
1344 // Pass on to the environment
1346 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1347 m_env.processActiveObjectMessage(id, message);
1352 else if(command == TOCLIENT_HP)
1354 std::string datastring((char*)&data[2], datasize-2);
1355 std::istringstream is(datastring, std::ios_base::binary);
1356 Player *player = m_env.getLocalPlayer();
1357 assert(player != NULL);
1361 else if(command == TOCLIENT_MOVE_PLAYER)
1363 std::string datastring((char*)&data[2], datasize-2);
1364 std::istringstream is(datastring, std::ios_base::binary);
1365 Player *player = m_env.getLocalPlayer();
1366 assert(player != NULL);
1367 v3f pos = readV3F1000(is);
1368 f32 pitch = readF1000(is);
1369 f32 yaw = readF1000(is);
1370 player->setPosition(pos);
1371 /*player->setPitch(pitch);
1372 player->setYaw(yaw);*/
1374 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1375 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1381 Add to ClientEvent queue.
1382 This has to be sent to the main program because otherwise
1383 it would just force the pitch and yaw values to whatever
1384 the camera points to.
1387 event.type = CE_PLAYER_FORCE_MOVE;
1388 event.player_force_move.pitch = pitch;
1389 event.player_force_move.yaw = yaw;
1390 m_client_event_queue.push_back(event);
1392 // Ignore damage for a few seconds, so that the player doesn't
1393 // get damage from falling on ground
1394 m_ignore_damage_timer = 3.0;
1398 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1399 <<command<<std::endl;
1403 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1405 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1406 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1409 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1410 v3s16 nodepos_oversurface, u16 item)
1412 if(connectedAndInitialized() == false){
1413 dout_client<<DTIME<<"Client::groundAction() "
1414 "cancelled (not connected)"
1423 [3] v3s16 nodepos_undersurface
1424 [9] v3s16 nodepos_abovesurface
1429 2: stop digging (all parameters ignored)
1430 3: digging completed
1432 u8 datasize = 2 + 1 + 6 + 6 + 2;
1433 SharedBuffer<u8> data(datasize);
1434 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1435 writeU8(&data[2], action);
1436 writeV3S16(&data[3], nodepos_undersurface);
1437 writeV3S16(&data[9], nodepos_oversurface);
1438 writeU16(&data[15], item);
1439 Send(0, data, true);
1442 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1444 if(connectedAndInitialized() == false){
1445 dout_client<<DTIME<<"Client::clickObject() "
1446 "cancelled (not connected)"
1452 [0] u16 command=TOSERVER_CLICK_OBJECT
1453 [2] u8 button (0=left, 1=right)
1458 u8 datasize = 2 + 1 + 6 + 2 + 2;
1459 SharedBuffer<u8> data(datasize);
1460 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1461 writeU8(&data[2], button);
1462 writeV3S16(&data[3], blockpos);
1463 writeS16(&data[9], id);
1464 writeU16(&data[11], item);
1465 Send(0, data, true);
1468 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1470 if(connectedAndInitialized() == false){
1471 dout_client<<DTIME<<"Client::clickActiveObject() "
1472 "cancelled (not connected)"
1480 [2] u8 button (0=left, 1=right)
1484 u8 datasize = 2 + 1 + 6 + 2 + 2;
1485 SharedBuffer<u8> data(datasize);
1486 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1487 writeU8(&data[2], button);
1488 writeU16(&data[3], id);
1489 writeU16(&data[5], item);
1490 Send(0, data, true);
1493 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1502 std::ostringstream os(std::ios_base::binary);
1506 writeU16(buf, TOSERVER_SIGNTEXT);
1507 os.write((char*)buf, 2);
1510 writeV3S16(buf, blockpos);
1511 os.write((char*)buf, 6);
1515 os.write((char*)buf, 2);
1517 u16 textlen = text.size();
1518 // Write text length
1519 writeS16(buf, textlen);
1520 os.write((char*)buf, 2);
1523 os.write((char*)text.c_str(), textlen);
1526 std::string s = os.str();
1527 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1529 Send(0, data, true);
1532 void Client::sendSignNodeText(v3s16 p, std::string text)
1540 std::ostringstream os(std::ios_base::binary);
1544 writeU16(buf, TOSERVER_SIGNNODETEXT);
1545 os.write((char*)buf, 2);
1549 os.write((char*)buf, 6);
1551 u16 textlen = text.size();
1552 // Write text length
1553 writeS16(buf, textlen);
1554 os.write((char*)buf, 2);
1557 os.write((char*)text.c_str(), textlen);
1560 std::string s = os.str();
1561 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1563 Send(0, data, true);
1566 void Client::sendInventoryAction(InventoryAction *a)
1568 std::ostringstream os(std::ios_base::binary);
1572 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1573 os.write((char*)buf, 2);
1578 std::string s = os.str();
1579 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1581 Send(0, data, true);
1584 void Client::sendChatMessage(const std::wstring &message)
1586 std::ostringstream os(std::ios_base::binary);
1590 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1591 os.write((char*)buf, 2);
1594 writeU16(buf, message.size());
1595 os.write((char*)buf, 2);
1598 for(u32 i=0; i<message.size(); i++)
1602 os.write((char*)buf, 2);
1606 std::string s = os.str();
1607 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1609 Send(0, data, true);
1612 void Client::sendChangePassword(const std::wstring oldpassword,
1613 const std::wstring newpassword)
1615 Player *player = m_env.getLocalPlayer();
1619 std::string playername = player->getName();
1620 std::string oldpwd = translatePassword(playername, oldpassword);
1621 std::string newpwd = translatePassword(playername, newpassword);
1623 std::ostringstream os(std::ios_base::binary);
1624 u8 buf[2+PASSWORD_SIZE*2];
1626 [0] u16 TOSERVER_PASSWORD
1627 [2] u8[28] old password
1628 [30] u8[28] new password
1631 writeU16(buf, TOSERVER_PASSWORD);
1632 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1634 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1635 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1637 buf[2+PASSWORD_SIZE-1] = 0;
1638 buf[30+PASSWORD_SIZE-1] = 0;
1639 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1642 std::string s = os.str();
1643 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1645 Send(0, data, true);
1649 void Client::sendDamage(u8 damage)
1651 DSTACK(__FUNCTION_NAME);
1652 std::ostringstream os(std::ios_base::binary);
1654 writeU16(os, TOSERVER_DAMAGE);
1655 writeU8(os, damage);
1658 std::string s = os.str();
1659 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1661 Send(0, data, true);
1664 void Client::sendPlayerPos()
1666 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1668 Player *myplayer = m_env.getLocalPlayer();
1669 if(myplayer == NULL)
1674 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1675 our_peer_id = m_con.GetPeerID();
1678 // Set peer id if not set already
1679 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1680 myplayer->peer_id = our_peer_id;
1681 // Check that an existing peer_id is the same as the connection's
1682 assert(myplayer->peer_id == our_peer_id);
1684 v3f pf = myplayer->getPosition();
1685 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1686 v3f sf = myplayer->getSpeed();
1687 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1688 s32 pitch = myplayer->getPitch() * 100;
1689 s32 yaw = myplayer->getYaw() * 100;
1694 [2] v3s32 position*100
1695 [2+12] v3s32 speed*100
1696 [2+12+12] s32 pitch*100
1697 [2+12+12+4] s32 yaw*100
1700 SharedBuffer<u8> data(2+12+12+4+4);
1701 writeU16(&data[0], TOSERVER_PLAYERPOS);
1702 writeV3S32(&data[2], position);
1703 writeV3S32(&data[2+12], speed);
1704 writeS32(&data[2+12+12], pitch);
1705 writeS32(&data[2+12+12+4], yaw);
1707 // Send as unreliable
1708 Send(0, data, false);
1711 void Client::removeNode(v3s16 p)
1713 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1715 core::map<v3s16, MapBlock*> modified_blocks;
1719 //TimeTaker t("removeNodeAndUpdate", m_device);
1720 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1722 catch(InvalidPositionException &e)
1726 for(core::map<v3s16, MapBlock * >::Iterator
1727 i = modified_blocks.getIterator();
1728 i.atEnd() == false; i++)
1730 v3s16 p = i.getNode()->getKey();
1731 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1732 addUpdateMeshTaskWithEdge(p);
1736 void Client::addNode(v3s16 p, MapNode n)
1738 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1740 TimeTaker timer1("Client::addNode()");
1742 core::map<v3s16, MapBlock*> modified_blocks;
1746 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1747 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1749 catch(InvalidPositionException &e)
1752 //TimeTaker timer2("Client::addNode(): updateMeshes");
1754 for(core::map<v3s16, MapBlock * >::Iterator
1755 i = modified_blocks.getIterator();
1756 i.atEnd() == false; i++)
1758 v3s16 p = i.getNode()->getKey();
1759 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1760 addUpdateMeshTaskWithEdge(p);
1764 void Client::updateCamera(v3f pos, v3f dir)
1766 m_env.getClientMap().updateCamera(pos, dir);
1767 camera_position = pos;
1768 camera_direction = dir;
1771 MapNode Client::getNode(v3s16 p)
1773 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1774 return m_env.getMap().getNode(p);
1777 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1779 return m_env.getMap().getNodeMetadata(p);
1782 v3f Client::getPlayerPosition()
1784 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1785 LocalPlayer *player = m_env.getLocalPlayer();
1786 assert(player != NULL);
1787 return player->getPosition();
1790 void Client::setPlayerControl(PlayerControl &control)
1792 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1793 LocalPlayer *player = m_env.getLocalPlayer();
1794 assert(player != NULL);
1795 player->control = control;
1798 // Returns true if the inventory of the local player has been
1799 // updated from the server. If it is true, it is set to false.
1800 bool Client::getLocalInventoryUpdated()
1802 // m_inventory_updated is behind envlock
1803 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1804 bool updated = m_inventory_updated;
1805 m_inventory_updated = false;
1809 // Copies the inventory of the local player to parameter
1810 void Client::getLocalInventory(Inventory &dst)
1812 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1813 Player *player = m_env.getLocalPlayer();
1814 assert(player != NULL);
1815 dst = player->inventory;
1818 InventoryContext *Client::getInventoryContext()
1820 return &m_inventory_context;
1823 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1825 if(id == "current_player")
1827 assert(c->current_player);
1828 return &(c->current_player->inventory);
1832 std::string id0 = fn.next(":");
1834 if(id0 == "nodemeta")
1837 p.X = stoi(fn.next(","));
1838 p.Y = stoi(fn.next(","));
1839 p.Z = stoi(fn.next(","));
1840 NodeMetadata* meta = getNodeMetadata(p);
1842 return meta->getInventory();
1843 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1844 <<"no metadata found"<<std::endl;
1848 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1851 void Client::inventoryAction(InventoryAction *a)
1853 sendInventoryAction(a);
1856 MapBlockObject * Client::getSelectedObject(
1858 v3f from_pos_f_on_map,
1859 core::line3d<f32> shootline_on_map
1862 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1864 core::array<DistanceSortedObject> objects;
1866 for(core::map<v3s16, bool>::Iterator
1867 i = m_active_blocks.getIterator();
1868 i.atEnd() == false; i++)
1870 v3s16 p = i.getNode()->getKey();
1872 MapBlock *block = NULL;
1875 block = m_env.getMap().getBlockNoCreate(p);
1877 catch(InvalidPositionException &e)
1882 // Calculate from_pos relative to block
1883 v3s16 block_pos_i_on_map = block->getPosRelative();
1884 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1885 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1887 block->getObjects(from_pos_f_on_block, max_d, objects);
1888 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1891 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1894 // After this, the closest object is the first in the array.
1897 for(u32 i=0; i<objects.size(); i++)
1899 MapBlockObject *obj = objects[i].obj;
1900 MapBlock *block = obj->getBlock();
1902 // Calculate shootline relative to block
1903 v3s16 block_pos_i_on_map = block->getPosRelative();
1904 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1905 core::line3d<f32> shootline_on_block(
1906 shootline_on_map.start - block_pos_f_on_map,
1907 shootline_on_map.end - block_pos_f_on_map
1910 if(obj->isSelected(shootline_on_block))
1912 //dstream<<"Returning selected object"<<std::endl;
1917 //dstream<<"No object selected; returning NULL."<<std::endl;
1921 ClientActiveObject * Client::getSelectedActiveObject(
1923 v3f from_pos_f_on_map,
1924 core::line3d<f32> shootline_on_map
1927 core::array<DistanceSortedActiveObject> objects;
1929 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1931 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1934 // After this, the closest object is the first in the array.
1937 for(u32 i=0; i<objects.size(); i++)
1939 ClientActiveObject *obj = objects[i].obj;
1941 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1942 if(selection_box == NULL)
1945 v3f pos = obj->getPosition();
1947 core::aabbox3d<f32> offsetted_box(
1948 selection_box->MinEdge + pos,
1949 selection_box->MaxEdge + pos
1952 if(offsetted_box.intersectsWithLine(shootline_on_map))
1954 //dstream<<"Returning selected object"<<std::endl;
1959 //dstream<<"No object selected; returning NULL."<<std::endl;
1963 void Client::printDebugInfo(std::ostream &os)
1965 //JMutexAutoLock lock1(m_fetchblock_mutex);
1966 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1968 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1969 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1970 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1974 /*s32 Client::getDayNightIndex()
1976 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1977 return m_daynight_i;
1980 u32 Client::getDayNightRatio()
1982 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1983 return m_env.getDayNightRatio();
1988 Player *player = m_env.getLocalPlayer();
1989 assert(player != NULL);
1993 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1995 /*dstream<<"Client::addUpdateMeshTask(): "
1996 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1999 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2004 Create a task to update the mesh of the block
2007 MeshMakeData *data = new MeshMakeData;
2010 //TimeTaker timer("data fill");
2012 data->fill(getDayNightRatio(), b);
2016 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2018 // Add task to queue
2019 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2021 /*dstream<<"Mesh update input queue size is "
2022 <<m_mesh_update_thread.m_queue_in.size()
2026 // Temporary test: make mesh directly in here
2028 //TimeTaker timer("make mesh");
2030 scene::SMesh *mesh_new = NULL;
2031 mesh_new = makeMapBlockMesh(data);
2032 b->replaceMesh(mesh_new);
2037 b->setMeshExpired(false);
2040 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2044 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2045 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2050 v3s16 p = blockpos + v3s16(0,0,0);
2051 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2052 addUpdateMeshTask(p, ack_to_server);
2054 catch(InvalidPositionException &e){}
2057 v3s16 p = blockpos + v3s16(-1,0,0);
2058 addUpdateMeshTask(p);
2060 catch(InvalidPositionException &e){}
2062 v3s16 p = blockpos + v3s16(0,-1,0);
2063 addUpdateMeshTask(p);
2065 catch(InvalidPositionException &e){}
2067 v3s16 p = blockpos + v3s16(0,0,-1);
2068 addUpdateMeshTask(p);
2070 catch(InvalidPositionException &e){}
2073 ClientEvent Client::getClientEvent()
2075 if(m_client_event_queue.size() == 0)
2078 event.type = CE_NONE;
2081 return m_client_event_queue.pop_front();