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);
725 //block->setChangedFlag();
727 catch(InvalidPositionException &e)
732 //dstream<<"Creating new"<<std::endl;
733 block = new MapBlock(&m_env.getMap(), p);
734 block->deSerialize(istr, ser_version);
735 sector->insertBlock(block);
736 //block->setChangedFlag();
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);*/
750 Well, this is a dumb way to do it, they should just
751 be drawn as separate objects. But the looks of them
752 can be tested this way.
757 mod.type = NODEMOD_CHANGECONTENT;
758 mod.param = CONTENT_CLOUD;
761 for(p2.X=3; p2.X<=13; p2.X++)
762 for(p2.Z=3; p2.Z<=13; p2.Z++)
764 block->setTempMod(p2, mod);
782 u32 replysize = 2+1+6;
783 SharedBuffer<u8> reply(replysize);
784 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
786 writeV3S16(&reply[3], p);
788 m_con.Send(PEER_ID_SERVER, 1, reply, true);
792 Update Mesh of this block and blocks at x-, y- and z-.
793 Environment should not be locked as it interlocks with the
794 main thread, from which is will want to retrieve textures.
797 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
799 Add it to mesh update queue and set it to be acknowledged after update.
801 addUpdateMeshTaskWithEdge(p, true);
803 else if(command == TOCLIENT_PLAYERPOS)
805 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
809 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
810 our_peer_id = m_con.GetPeerID();
812 // Cancel if we don't have a peer id
813 if(our_peer_id == PEER_ID_INEXISTENT){
814 dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
821 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
823 u32 player_size = 2+12+12+4+4;
825 u32 player_count = (datasize-2) / player_size;
827 for(u32 i=0; i<player_count; i++)
829 u16 peer_id = readU16(&data[start]);
831 Player *player = m_env.getPlayer(peer_id);
833 // Skip if player doesn't exist
836 start += player_size;
840 // Skip if player is local player
841 if(player->isLocal())
843 start += player_size;
847 v3s32 ps = readV3S32(&data[start+2]);
848 v3s32 ss = readV3S32(&data[start+2+12]);
849 s32 pitch_i = readS32(&data[start+2+12+12]);
850 s32 yaw_i = readS32(&data[start+2+12+12+4]);
851 /*dstream<<"Client: got "
852 <<"pitch_i="<<pitch_i
853 <<" yaw_i="<<yaw_i<<std::endl;*/
854 f32 pitch = (f32)pitch_i / 100.0;
855 f32 yaw = (f32)yaw_i / 100.0;
856 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
857 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
858 player->setPosition(position);
859 player->setSpeed(speed);
860 player->setPitch(pitch);
863 /*dstream<<"Client: player "<<peer_id
865 <<" yaw="<<yaw<<std::endl;*/
867 start += player_size;
871 else if(command == TOCLIENT_PLAYERINFO)
875 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
876 our_peer_id = m_con.GetPeerID();
878 // Cancel if we don't have a peer id
879 if(our_peer_id == PEER_ID_INEXISTENT){
880 dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
886 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
889 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
891 u32 item_size = 2+PLAYERNAME_SIZE;
892 u32 player_count = (datasize-2) / item_size;
895 core::list<u16> players_alive;
896 for(u32 i=0; i<player_count; i++)
898 // Make sure the name ends in '\0'
899 data[start+2+20-1] = 0;
901 u16 peer_id = readU16(&data[start]);
903 players_alive.push_back(peer_id);
905 /*dstream<<DTIME<<"peer_id="<<peer_id
906 <<" name="<<((char*)&data[start+2])<<std::endl;*/
908 // Don't update the info of the local player
909 if(peer_id == our_peer_id)
915 Player *player = m_env.getPlayer(peer_id);
917 // Create a player if it doesn't exist
920 player = new RemotePlayer(
921 m_device->getSceneManager()->getRootSceneNode(),
924 player->peer_id = peer_id;
925 m_env.addPlayer(player);
926 dout_client<<DTIME<<"Client: Adding new player "
927 <<peer_id<<std::endl;
930 player->updateName((char*)&data[start+2]);
936 Remove those players from the environment that
937 weren't listed by the server.
939 //dstream<<DTIME<<"Removing dead players"<<std::endl;
940 core::list<Player*> players = m_env.getPlayers();
941 core::list<Player*>::Iterator ip;
942 for(ip=players.begin(); ip!=players.end(); ip++)
944 // Ingore local player
948 // Warn about a special case
949 if((*ip)->peer_id == 0)
951 dstream<<DTIME<<"WARNING: Client: Removing "
952 "dead player with id=0"<<std::endl;
955 bool is_alive = false;
956 core::list<u16>::Iterator i;
957 for(i=players_alive.begin(); i!=players_alive.end(); i++)
959 if((*ip)->peer_id == *i)
965 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
966 <<" is_alive="<<is_alive<<std::endl;*/
969 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
971 m_env.removePlayer((*ip)->peer_id);
975 else if(command == TOCLIENT_SECTORMETA)
980 [3...] v2s16 pos + sector metadata
985 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
988 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
990 std::string datastring((char*)&data[2], datasize-2);
991 std::istringstream is(datastring, std::ios_base::binary);
995 is.read((char*)buf, 1);
996 u16 sector_count = readU8(buf);
998 //dstream<<"sector_count="<<sector_count<<std::endl;
1000 for(u16 i=0; i<sector_count; i++)
1003 is.read((char*)buf, 4);
1004 v2s16 pos = readV2S16(buf);
1005 /*dstream<<"Client: deserializing sector at "
1006 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1008 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1009 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1013 else if(command == TOCLIENT_INVENTORY)
1018 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1021 //TimeTaker t2("mutex locking", m_device);
1022 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1025 //TimeTaker t3("istringstream init", m_device);
1026 std::string datastring((char*)&data[2], datasize-2);
1027 std::istringstream is(datastring, std::ios_base::binary);
1030 //m_env.printPlayers(dstream);
1032 //TimeTaker t4("player get", m_device);
1033 Player *player = m_env.getLocalPlayer();
1034 assert(player != NULL);
1037 //TimeTaker t1("inventory.deSerialize()", m_device);
1038 player->inventory.deSerialize(is);
1041 m_inventory_updated = true;
1043 //dstream<<"Client got player inventory:"<<std::endl;
1044 //player->inventory.print(dstream);
1048 else if(command == TOCLIENT_OBJECTDATA)
1051 // Strip command word and create a stringstream
1052 std::string datastring((char*)&data[2], datasize-2);
1053 std::istringstream is(datastring, std::ios_base::binary);
1057 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1065 is.read((char*)buf, 2);
1066 u16 playercount = readU16(buf);
1068 for(u16 i=0; i<playercount; i++)
1070 is.read((char*)buf, 2);
1071 u16 peer_id = readU16(buf);
1072 is.read((char*)buf, 12);
1073 v3s32 p_i = readV3S32(buf);
1074 is.read((char*)buf, 12);
1075 v3s32 s_i = readV3S32(buf);
1076 is.read((char*)buf, 4);
1077 s32 pitch_i = readS32(buf);
1078 is.read((char*)buf, 4);
1079 s32 yaw_i = readS32(buf);
1081 Player *player = m_env.getPlayer(peer_id);
1083 // Skip if player doesn't exist
1089 // Skip if player is local player
1090 if(player->isLocal())
1095 f32 pitch = (f32)pitch_i / 100.0;
1096 f32 yaw = (f32)yaw_i / 100.0;
1097 v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1098 v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1100 player->setPosition(position);
1101 player->setSpeed(speed);
1102 player->setPitch(pitch);
1103 player->setYaw(yaw);
1110 // Read active block count
1111 is.read((char*)buf, 2);
1112 u16 blockcount = readU16(buf);
1114 // Initialize delete queue with all active blocks
1115 core::map<v3s16, bool> abs_to_delete;
1116 for(core::map<v3s16, bool>::Iterator
1117 i = m_active_blocks.getIterator();
1118 i.atEnd() == false; i++)
1120 v3s16 p = i.getNode()->getKey();
1121 /*dstream<<"adding "
1122 <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1123 <<" to abs_to_delete"
1125 abs_to_delete.insert(p, true);
1128 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1131 for(u16 i=0; i<blockcount; i++)
1134 is.read((char*)buf, 6);
1135 v3s16 p = readV3S16(buf);
1136 // Get block from somewhere
1137 MapBlock *block = NULL;
1139 block = m_env.getMap().getBlockNoCreate(p);
1141 catch(InvalidPositionException &e)
1143 //TODO: Create a dummy block?
1147 dstream<<"WARNING: "
1148 <<"Could not get block at blockpos "
1149 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1150 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1151 <<"following block object data."
1156 /*dstream<<"Client updating objects for block "
1157 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1160 // Insert to active block list
1161 m_active_blocks.insert(p, true);
1163 // Remove from deletion queue
1164 if(abs_to_delete.find(p) != NULL)
1165 abs_to_delete.remove(p);
1168 Update objects of block
1170 NOTE: Be sure this is done in the main thread.
1172 block->updateObjects(is, m_server_ser_ver,
1173 m_device->getSceneManager(), m_env.getDayNightRatio());
1176 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1179 // Delete objects of blocks in delete queue
1180 for(core::map<v3s16, bool>::Iterator
1181 i = abs_to_delete.getIterator();
1182 i.atEnd() == false; i++)
1184 v3s16 p = i.getNode()->getKey();
1187 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1190 block->clearObjects();
1191 // Remove from active blocks list
1192 m_active_blocks.remove(p);
1194 catch(InvalidPositionException &e)
1196 dstream<<"WARNAING: Client: "
1197 <<"Couldn't clear objects of active->inactive"
1199 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1200 <<" because block was not found"
1208 else if(command == TOCLIENT_TIME_OF_DAY)
1213 u16 time_of_day = readU16(&data[2]);
1214 time_of_day = time_of_day % 24000;
1215 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1223 m_env.setTimeOfDay(time_of_day);
1225 u32 dr = m_env.getDayNightRatio();
1227 dstream<<"Client: time_of_day="<<time_of_day
1233 else if(command == TOCLIENT_CHAT_MESSAGE)
1241 std::string datastring((char*)&data[2], datasize-2);
1242 std::istringstream is(datastring, std::ios_base::binary);
1245 is.read((char*)buf, 2);
1246 u16 len = readU16(buf);
1248 std::wstring message;
1249 for(u16 i=0; i<len; i++)
1251 is.read((char*)buf, 2);
1252 message += (wchar_t)readU16(buf);
1255 /*dstream<<"Client received chat message: "
1256 <<wide_to_narrow(message)<<std::endl;*/
1258 m_chat_queue.push_back(message);
1260 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1262 //if(g_settings.getBool("enable_experimental"))
1266 u16 count of removed objects
1267 for all removed objects {
1270 u16 count of added objects
1271 for all added objects {
1274 u16 initialization data length
1275 string initialization data
1280 // Get all data except the command number
1281 std::string datastring((char*)&data[2], datasize-2);
1282 // Throw them in an istringstream
1283 std::istringstream is(datastring, std::ios_base::binary);
1287 // Read removed objects
1289 u16 removed_count = readU16((u8*)buf);
1290 for(u16 i=0; i<removed_count; i++)
1293 u16 id = readU16((u8*)buf);
1296 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1297 m_env.removeActiveObject(id);
1301 // Read added objects
1303 u16 added_count = readU16((u8*)buf);
1304 for(u16 i=0; i<added_count; i++)
1307 u16 id = readU16((u8*)buf);
1309 u8 type = readU8((u8*)buf);
1310 std::string data = deSerializeLongString(is);
1313 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1314 m_env.addActiveObject(id, type, data);
1319 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1321 //if(g_settings.getBool("enable_experimental"))
1333 // Get all data except the command number
1334 std::string datastring((char*)&data[2], datasize-2);
1335 // Throw them in an istringstream
1336 std::istringstream is(datastring, std::ios_base::binary);
1338 while(is.eof() == false)
1342 u16 id = readU16((u8*)buf);
1346 u16 message_size = readU16((u8*)buf);
1347 std::string message;
1348 message.reserve(message_size);
1349 for(u16 i=0; i<message_size; i++)
1352 message.append(buf, 1);
1354 // Pass on to the environment
1356 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1357 m_env.processActiveObjectMessage(id, message);
1362 else if(command == TOCLIENT_HP)
1364 std::string datastring((char*)&data[2], datasize-2);
1365 std::istringstream is(datastring, std::ios_base::binary);
1366 Player *player = m_env.getLocalPlayer();
1367 assert(player != NULL);
1371 else if(command == TOCLIENT_MOVE_PLAYER)
1373 std::string datastring((char*)&data[2], datasize-2);
1374 std::istringstream is(datastring, std::ios_base::binary);
1375 Player *player = m_env.getLocalPlayer();
1376 assert(player != NULL);
1377 v3f pos = readV3F1000(is);
1378 f32 pitch = readF1000(is);
1379 f32 yaw = readF1000(is);
1380 player->setPosition(pos);
1381 /*player->setPitch(pitch);
1382 player->setYaw(yaw);*/
1384 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1385 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1391 Add to ClientEvent queue.
1392 This has to be sent to the main program because otherwise
1393 it would just force the pitch and yaw values to whatever
1394 the camera points to.
1397 event.type = CE_PLAYER_FORCE_MOVE;
1398 event.player_force_move.pitch = pitch;
1399 event.player_force_move.yaw = yaw;
1400 m_client_event_queue.push_back(event);
1402 // Ignore damage for a few seconds, so that the player doesn't
1403 // get damage from falling on ground
1404 m_ignore_damage_timer = 3.0;
1408 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1409 <<command<<std::endl;
1413 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1415 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1416 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1419 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1420 v3s16 nodepos_oversurface, u16 item)
1422 if(connectedAndInitialized() == false){
1423 dout_client<<DTIME<<"Client::groundAction() "
1424 "cancelled (not connected)"
1433 [3] v3s16 nodepos_undersurface
1434 [9] v3s16 nodepos_abovesurface
1439 2: stop digging (all parameters ignored)
1440 3: digging completed
1442 u8 datasize = 2 + 1 + 6 + 6 + 2;
1443 SharedBuffer<u8> data(datasize);
1444 writeU16(&data[0], TOSERVER_GROUND_ACTION);
1445 writeU8(&data[2], action);
1446 writeV3S16(&data[3], nodepos_undersurface);
1447 writeV3S16(&data[9], nodepos_oversurface);
1448 writeU16(&data[15], item);
1449 Send(0, data, true);
1452 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1454 if(connectedAndInitialized() == false){
1455 dout_client<<DTIME<<"Client::clickObject() "
1456 "cancelled (not connected)"
1462 [0] u16 command=TOSERVER_CLICK_OBJECT
1463 [2] u8 button (0=left, 1=right)
1468 u8 datasize = 2 + 1 + 6 + 2 + 2;
1469 SharedBuffer<u8> data(datasize);
1470 writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1471 writeU8(&data[2], button);
1472 writeV3S16(&data[3], blockpos);
1473 writeS16(&data[9], id);
1474 writeU16(&data[11], item);
1475 Send(0, data, true);
1478 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1480 if(connectedAndInitialized() == false){
1481 dout_client<<DTIME<<"Client::clickActiveObject() "
1482 "cancelled (not connected)"
1490 [2] u8 button (0=left, 1=right)
1494 u8 datasize = 2 + 1 + 6 + 2 + 2;
1495 SharedBuffer<u8> data(datasize);
1496 writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1497 writeU8(&data[2], button);
1498 writeU16(&data[3], id);
1499 writeU16(&data[5], item);
1500 Send(0, data, true);
1503 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1512 std::ostringstream os(std::ios_base::binary);
1516 writeU16(buf, TOSERVER_SIGNTEXT);
1517 os.write((char*)buf, 2);
1520 writeV3S16(buf, blockpos);
1521 os.write((char*)buf, 6);
1525 os.write((char*)buf, 2);
1527 u16 textlen = text.size();
1528 // Write text length
1529 writeS16(buf, textlen);
1530 os.write((char*)buf, 2);
1533 os.write((char*)text.c_str(), textlen);
1536 std::string s = os.str();
1537 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1539 Send(0, data, true);
1542 void Client::sendSignNodeText(v3s16 p, std::string text)
1550 std::ostringstream os(std::ios_base::binary);
1554 writeU16(buf, TOSERVER_SIGNNODETEXT);
1555 os.write((char*)buf, 2);
1559 os.write((char*)buf, 6);
1561 u16 textlen = text.size();
1562 // Write text length
1563 writeS16(buf, textlen);
1564 os.write((char*)buf, 2);
1567 os.write((char*)text.c_str(), textlen);
1570 std::string s = os.str();
1571 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1573 Send(0, data, true);
1576 void Client::sendInventoryAction(InventoryAction *a)
1578 std::ostringstream os(std::ios_base::binary);
1582 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1583 os.write((char*)buf, 2);
1588 std::string s = os.str();
1589 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1591 Send(0, data, true);
1594 void Client::sendChatMessage(const std::wstring &message)
1596 std::ostringstream os(std::ios_base::binary);
1600 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1601 os.write((char*)buf, 2);
1604 writeU16(buf, message.size());
1605 os.write((char*)buf, 2);
1608 for(u32 i=0; i<message.size(); i++)
1612 os.write((char*)buf, 2);
1616 std::string s = os.str();
1617 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1619 Send(0, data, true);
1622 void Client::sendChangePassword(const std::wstring oldpassword,
1623 const std::wstring newpassword)
1625 Player *player = m_env.getLocalPlayer();
1629 std::string playername = player->getName();
1630 std::string oldpwd = translatePassword(playername, oldpassword);
1631 std::string newpwd = translatePassword(playername, newpassword);
1633 std::ostringstream os(std::ios_base::binary);
1634 u8 buf[2+PASSWORD_SIZE*2];
1636 [0] u16 TOSERVER_PASSWORD
1637 [2] u8[28] old password
1638 [30] u8[28] new password
1641 writeU16(buf, TOSERVER_PASSWORD);
1642 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1644 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1645 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1647 buf[2+PASSWORD_SIZE-1] = 0;
1648 buf[30+PASSWORD_SIZE-1] = 0;
1649 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1652 std::string s = os.str();
1653 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1655 Send(0, data, true);
1659 void Client::sendDamage(u8 damage)
1661 DSTACK(__FUNCTION_NAME);
1662 std::ostringstream os(std::ios_base::binary);
1664 writeU16(os, TOSERVER_DAMAGE);
1665 writeU8(os, damage);
1668 std::string s = os.str();
1669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1671 Send(0, data, true);
1674 void Client::sendPlayerPos()
1676 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1678 Player *myplayer = m_env.getLocalPlayer();
1679 if(myplayer == NULL)
1684 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1685 our_peer_id = m_con.GetPeerID();
1688 // Set peer id if not set already
1689 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1690 myplayer->peer_id = our_peer_id;
1691 // Check that an existing peer_id is the same as the connection's
1692 assert(myplayer->peer_id == our_peer_id);
1694 v3f pf = myplayer->getPosition();
1695 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1696 v3f sf = myplayer->getSpeed();
1697 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1698 s32 pitch = myplayer->getPitch() * 100;
1699 s32 yaw = myplayer->getYaw() * 100;
1704 [2] v3s32 position*100
1705 [2+12] v3s32 speed*100
1706 [2+12+12] s32 pitch*100
1707 [2+12+12+4] s32 yaw*100
1710 SharedBuffer<u8> data(2+12+12+4+4);
1711 writeU16(&data[0], TOSERVER_PLAYERPOS);
1712 writeV3S32(&data[2], position);
1713 writeV3S32(&data[2+12], speed);
1714 writeS32(&data[2+12+12], pitch);
1715 writeS32(&data[2+12+12+4], yaw);
1717 // Send as unreliable
1718 Send(0, data, false);
1721 void Client::removeNode(v3s16 p)
1723 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1725 core::map<v3s16, MapBlock*> modified_blocks;
1729 //TimeTaker t("removeNodeAndUpdate", m_device);
1730 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1732 catch(InvalidPositionException &e)
1736 for(core::map<v3s16, MapBlock * >::Iterator
1737 i = modified_blocks.getIterator();
1738 i.atEnd() == false; i++)
1740 v3s16 p = i.getNode()->getKey();
1741 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1742 addUpdateMeshTaskWithEdge(p);
1746 void Client::addNode(v3s16 p, MapNode n)
1748 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1750 TimeTaker timer1("Client::addNode()");
1752 core::map<v3s16, MapBlock*> modified_blocks;
1756 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1757 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1759 catch(InvalidPositionException &e)
1762 //TimeTaker timer2("Client::addNode(): updateMeshes");
1764 for(core::map<v3s16, MapBlock * >::Iterator
1765 i = modified_blocks.getIterator();
1766 i.atEnd() == false; i++)
1768 v3s16 p = i.getNode()->getKey();
1769 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1770 addUpdateMeshTaskWithEdge(p);
1774 void Client::updateCamera(v3f pos, v3f dir)
1776 m_env.getClientMap().updateCamera(pos, dir);
1777 camera_position = pos;
1778 camera_direction = dir;
1781 MapNode Client::getNode(v3s16 p)
1783 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1784 return m_env.getMap().getNode(p);
1787 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1789 return m_env.getMap().getNodeMetadata(p);
1792 v3f Client::getPlayerPosition()
1794 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1795 LocalPlayer *player = m_env.getLocalPlayer();
1796 assert(player != NULL);
1797 return player->getPosition();
1800 void Client::setPlayerControl(PlayerControl &control)
1802 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1803 LocalPlayer *player = m_env.getLocalPlayer();
1804 assert(player != NULL);
1805 player->control = control;
1808 // Returns true if the inventory of the local player has been
1809 // updated from the server. If it is true, it is set to false.
1810 bool Client::getLocalInventoryUpdated()
1812 // m_inventory_updated is behind envlock
1813 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1814 bool updated = m_inventory_updated;
1815 m_inventory_updated = false;
1819 // Copies the inventory of the local player to parameter
1820 void Client::getLocalInventory(Inventory &dst)
1822 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1823 Player *player = m_env.getLocalPlayer();
1824 assert(player != NULL);
1825 dst = player->inventory;
1828 InventoryContext *Client::getInventoryContext()
1830 return &m_inventory_context;
1833 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1835 if(id == "current_player")
1837 assert(c->current_player);
1838 return &(c->current_player->inventory);
1842 std::string id0 = fn.next(":");
1844 if(id0 == "nodemeta")
1847 p.X = stoi(fn.next(","));
1848 p.Y = stoi(fn.next(","));
1849 p.Z = stoi(fn.next(","));
1850 NodeMetadata* meta = getNodeMetadata(p);
1852 return meta->getInventory();
1853 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1854 <<"no metadata found"<<std::endl;
1858 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1861 void Client::inventoryAction(InventoryAction *a)
1863 sendInventoryAction(a);
1866 MapBlockObject * Client::getSelectedObject(
1868 v3f from_pos_f_on_map,
1869 core::line3d<f32> shootline_on_map
1872 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1874 core::array<DistanceSortedObject> objects;
1876 for(core::map<v3s16, bool>::Iterator
1877 i = m_active_blocks.getIterator();
1878 i.atEnd() == false; i++)
1880 v3s16 p = i.getNode()->getKey();
1882 MapBlock *block = NULL;
1885 block = m_env.getMap().getBlockNoCreate(p);
1887 catch(InvalidPositionException &e)
1892 // Calculate from_pos relative to block
1893 v3s16 block_pos_i_on_map = block->getPosRelative();
1894 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1895 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1897 block->getObjects(from_pos_f_on_block, max_d, objects);
1898 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1901 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1904 // After this, the closest object is the first in the array.
1907 for(u32 i=0; i<objects.size(); i++)
1909 MapBlockObject *obj = objects[i].obj;
1910 MapBlock *block = obj->getBlock();
1912 // Calculate shootline relative to block
1913 v3s16 block_pos_i_on_map = block->getPosRelative();
1914 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1915 core::line3d<f32> shootline_on_block(
1916 shootline_on_map.start - block_pos_f_on_map,
1917 shootline_on_map.end - block_pos_f_on_map
1920 if(obj->isSelected(shootline_on_block))
1922 //dstream<<"Returning selected object"<<std::endl;
1927 //dstream<<"No object selected; returning NULL."<<std::endl;
1931 ClientActiveObject * Client::getSelectedActiveObject(
1933 v3f from_pos_f_on_map,
1934 core::line3d<f32> shootline_on_map
1937 core::array<DistanceSortedActiveObject> objects;
1939 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1941 //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1944 // After this, the closest object is the first in the array.
1947 for(u32 i=0; i<objects.size(); i++)
1949 ClientActiveObject *obj = objects[i].obj;
1951 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1952 if(selection_box == NULL)
1955 v3f pos = obj->getPosition();
1957 core::aabbox3d<f32> offsetted_box(
1958 selection_box->MinEdge + pos,
1959 selection_box->MaxEdge + pos
1962 if(offsetted_box.intersectsWithLine(shootline_on_map))
1964 //dstream<<"Returning selected object"<<std::endl;
1969 //dstream<<"No object selected; returning NULL."<<std::endl;
1973 void Client::printDebugInfo(std::ostream &os)
1975 //JMutexAutoLock lock1(m_fetchblock_mutex);
1976 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1978 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1979 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1980 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1984 /*s32 Client::getDayNightIndex()
1986 assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1987 return m_daynight_i;
1990 u32 Client::getDayNightRatio()
1992 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1993 return m_env.getDayNightRatio();
1998 Player *player = m_env.getLocalPlayer();
1999 assert(player != NULL);
2003 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2005 /*dstream<<"Client::addUpdateMeshTask(): "
2006 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2009 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2014 Create a task to update the mesh of the block
2017 MeshMakeData *data = new MeshMakeData;
2020 //TimeTaker timer("data fill");
2022 data->fill(getDayNightRatio(), b);
2026 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2028 // Add task to queue
2029 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2031 /*dstream<<"Mesh update input queue size is "
2032 <<m_mesh_update_thread.m_queue_in.size()
2036 // Temporary test: make mesh directly in here
2038 //TimeTaker timer("make mesh");
2040 scene::SMesh *mesh_new = NULL;
2041 mesh_new = makeMapBlockMesh(data);
2042 b->replaceMesh(mesh_new);
2047 b->setMeshExpired(false);
2050 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2054 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2055 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2060 v3s16 p = blockpos + v3s16(0,0,0);
2061 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2062 addUpdateMeshTask(p, ack_to_server);
2064 catch(InvalidPositionException &e){}
2067 v3s16 p = blockpos + v3s16(-1,0,0);
2068 addUpdateMeshTask(p);
2070 catch(InvalidPositionException &e){}
2072 v3s16 p = blockpos + v3s16(0,-1,0);
2073 addUpdateMeshTask(p);
2075 catch(InvalidPositionException &e){}
2077 v3s16 p = blockpos + v3s16(0,0,-1);
2078 addUpdateMeshTask(p);
2080 catch(InvalidPositionException &e){}
2083 ClientEvent Client::getClientEvent()
2085 if(m_client_event_queue.size() == 0)
2088 event.type = CE_NONE;
2091 return m_client_event_queue.pop_front();