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"
29 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
43 static std::string getTextureCacheDir()
45 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
52 TextureRequest(const std::string &name_=""):
61 QueuedMeshUpdate::QueuedMeshUpdate():
64 ack_block_to_server(false)
68 QueuedMeshUpdate::~QueuedMeshUpdate()
78 MeshUpdateQueue::MeshUpdateQueue()
83 MeshUpdateQueue::~MeshUpdateQueue()
85 JMutexAutoLock lock(m_mutex);
87 for(std::vector<QueuedMeshUpdate*>::iterator
89 i != m_queue.end(); i++)
91 QueuedMeshUpdate *q = *i;
97 peer_id=0 adds with nobody to send to
99 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
101 DSTACK(__FUNCTION_NAME);
105 JMutexAutoLock lock(m_mutex);
111 Find if block is already in queue.
112 If it is, update the data and quit.
114 for(std::vector<QueuedMeshUpdate*>::iterator
116 i != m_queue.end(); i++)
118 QueuedMeshUpdate *q = *i;
124 if(ack_block_to_server)
125 q->ack_block_to_server = true;
133 QueuedMeshUpdate *q = new QueuedMeshUpdate;
136 q->ack_block_to_server = ack_block_to_server;
137 m_queue.push_back(q);
140 // Returned pointer must be deleted
141 // Returns NULL if queue is empty
142 QueuedMeshUpdate * MeshUpdateQueue::pop()
144 JMutexAutoLock lock(m_mutex);
146 bool must_be_urgent = !m_urgents.empty();
147 for(std::vector<QueuedMeshUpdate*>::iterator
149 i != m_queue.end(); i++)
151 QueuedMeshUpdate *q = *i;
152 if(must_be_urgent && m_urgents.count(q->p) == 0)
155 m_urgents.erase(q->p);
165 void * MeshUpdateThread::Thread()
169 log_register_thread("MeshUpdateThread");
171 DSTACK(__FUNCTION_NAME);
173 BEGIN_DEBUG_EXCEPTION_HANDLER
177 /*// Wait for output queue to flush.
178 // Allow 2 in queue, this makes less frametime jitter.
179 // Umm actually, there is no much difference
180 if(m_queue_out.size() >= 2)
186 QueuedMeshUpdate *q = m_queue_in.pop();
193 ScopeProfiler sp(g_profiler, "Client: Mesh making");
195 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
196 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
205 r.ack_block_to_server = q->ack_block_to_server;
207 /*infostream<<"MeshUpdateThread: Processed "
208 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
211 m_queue_out.push_back(r);
216 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 IrrlichtDevice *device,
223 const char *playername,
224 std::string password,
225 MapDrawControl &control,
226 IWritableTextureSource *tsrc,
227 IWritableItemDefManager *itemdef,
228 IWritableNodeDefManager *nodedef,
229 ISoundManager *sound,
230 MtEventManager *event
237 m_mesh_update_thread(this),
239 new ClientMap(this, this, control,
240 device->getSceneManager()->getRootSceneNode(),
241 device->getSceneManager(), 666),
242 device->getSceneManager(),
245 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
247 m_server_ser_ver(SER_FMT_VER_INVALID),
249 m_inventory_updated(false),
250 m_inventory_from_server(NULL),
251 m_inventory_from_server_age(0.0),
256 m_password(password),
257 m_access_denied(false),
258 m_texture_receive_progress(0),
259 m_textures_received(false),
260 m_itemdef_received(false),
261 m_nodedef_received(false),
262 m_time_of_day_set(false),
263 m_last_time_of_day_f(-1),
264 m_time_of_day_update_timer(0),
265 m_removed_sounds_check_timer(0)
267 m_packetcounter_timer = 0.0;
268 //m_delete_unused_sectors_timer = 0.0;
269 m_connection_reinit_timer = 0.0;
270 m_avg_rtt_timer = 0.0;
271 m_playerpos_send_timer = 0.0;
272 m_ignore_damage_timer = 0.0;
274 // Build main texture atlas, now that the GameDef exists (that is, us)
275 if(g_settings->getBool("enable_texture_atlas"))
276 m_tsrc->buildMainAtlas(this);
278 infostream<<"Not building texture atlas."<<std::endl;
284 Player *player = new LocalPlayer(this);
286 player->updateName(playername);
288 m_env.addPlayer(player);
295 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
299 m_mesh_update_thread.setRun(false);
300 while(m_mesh_update_thread.IsRunning())
303 delete m_inventory_from_server;
306 void Client::connect(Address address)
308 DSTACK(__FUNCTION_NAME);
309 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
310 m_con.SetTimeoutMs(0);
311 m_con.Connect(address);
314 bool Client::connectedAndInitialized()
316 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
318 if(m_con.Connected() == false)
321 if(m_server_ser_ver == SER_FMT_VER_INVALID)
327 void Client::step(float dtime)
329 DSTACK(__FUNCTION_NAME);
335 if(m_ignore_damage_timer > dtime)
336 m_ignore_damage_timer -= dtime;
338 m_ignore_damage_timer = 0.0;
340 m_animation_time += dtime;
341 if(m_animation_time > 60.0)
342 m_animation_time -= 60.0;
344 m_time_of_day_update_timer += dtime;
346 //infostream<<"Client steps "<<dtime<<std::endl;
349 //TimeTaker timer("ReceiveAll()", m_device);
355 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
357 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
358 m_con.RunTimeouts(dtime);
365 float &counter = m_packetcounter_timer;
371 infostream<<"Client packetcounter (20s):"<<std::endl;
372 m_packetcounter.print(infostream);
373 m_packetcounter.clear();
377 // Get connection status
378 bool connected = connectedAndInitialized();
383 Delete unused sectors
385 NOTE: This jams the game for a while because deleting sectors
389 float &counter = m_delete_unused_sectors_timer;
397 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
399 core::list<v3s16> deleted_blocks;
401 float delete_unused_sectors_timeout =
402 g_settings->getFloat("client_delete_unused_sectors_timeout");
404 // Delete sector blocks
405 /*u32 num = m_env.getMap().unloadUnusedData
406 (delete_unused_sectors_timeout,
407 true, &deleted_blocks);*/
409 // Delete whole sectors
410 m_env.getMap().unloadUnusedData
411 (delete_unused_sectors_timeout,
414 if(deleted_blocks.size() > 0)
416 /*infostream<<"Client: Deleted blocks of "<<num
417 <<" unused sectors"<<std::endl;*/
418 /*infostream<<"Client: Deleted "<<num
419 <<" unused sectors"<<std::endl;*/
425 // Env is locked so con can be locked.
426 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
428 core::list<v3s16>::Iterator i = deleted_blocks.begin();
429 core::list<v3s16> sendlist;
432 if(sendlist.size() == 255 || i == deleted_blocks.end())
434 if(sendlist.size() == 0)
443 u32 replysize = 2+1+6*sendlist.size();
444 SharedBuffer<u8> reply(replysize);
445 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
446 reply[2] = sendlist.size();
448 for(core::list<v3s16>::Iterator
449 j = sendlist.begin();
450 j != sendlist.end(); j++)
452 writeV3S16(&reply[2+1+6*k], *j);
455 m_con.Send(PEER_ID_SERVER, 1, reply, true);
457 if(i == deleted_blocks.end())
463 sendlist.push_back(*i);
471 if(connected == false)
473 float &counter = m_connection_reinit_timer;
479 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
481 Player *myplayer = m_env.getLocalPlayer();
482 assert(myplayer != NULL);
484 // Send TOSERVER_INIT
485 // [0] u16 TOSERVER_INIT
486 // [2] u8 SER_FMT_VER_HIGHEST
487 // [3] u8[20] player_name
488 // [23] u8[28] password (new in some version)
489 // [51] u16 client network protocol version (new in some version)
490 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
491 writeU16(&data[0], TOSERVER_INIT);
492 writeU8(&data[2], SER_FMT_VER_HIGHEST);
494 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
495 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
497 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
500 memset((char*)&data[23], 0, PASSWORD_SIZE);
501 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
503 // This should be incremented in each version
504 writeU16(&data[51], PROTOCOL_VERSION);
506 // Send as unreliable
507 Send(0, data, false);
510 // Not connected, return
515 Do stuff if connected
519 Run Map's timers and unload unused data
521 const float map_timer_and_unload_dtime = 5.25;
522 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
524 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
525 core::list<v3s16> deleted_blocks;
526 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
527 g_settings->getFloat("client_unload_unused_data_timeout"),
530 /*if(deleted_blocks.size() > 0)
531 infostream<<"Client: Unloaded "<<deleted_blocks.size()
532 <<" unused blocks"<<std::endl;*/
536 NOTE: This loop is intentionally iterated the way it is.
539 core::list<v3s16>::Iterator i = deleted_blocks.begin();
540 core::list<v3s16> sendlist;
543 if(sendlist.size() == 255 || i == deleted_blocks.end())
545 if(sendlist.size() == 0)
554 u32 replysize = 2+1+6*sendlist.size();
555 SharedBuffer<u8> reply(replysize);
556 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
557 reply[2] = sendlist.size();
559 for(core::list<v3s16>::Iterator
560 j = sendlist.begin();
561 j != sendlist.end(); j++)
563 writeV3S16(&reply[2+1+6*k], *j);
566 m_con.Send(PEER_ID_SERVER, 1, reply, true);
568 if(i == deleted_blocks.end())
574 sendlist.push_back(*i);
584 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
586 // Control local player (0ms)
587 LocalPlayer *player = m_env.getLocalPlayer();
588 assert(player != NULL);
589 player->applyControl(dtime);
591 //TimeTaker envtimer("env step", m_device);
600 ClientEnvEvent event = m_env.getClientEvent();
601 if(event.type == CEE_NONE)
605 else if(event.type == CEE_PLAYER_DAMAGE)
607 if(m_ignore_damage_timer <= 0)
609 u8 damage = event.player_damage.amount;
611 if(event.player_damage.send_to_server)
614 // Add to ClientEvent queue
616 event.type = CE_PLAYER_DAMAGE;
617 event.player_damage.amount = damage;
618 m_client_event_queue.push_back(event);
628 float &counter = m_avg_rtt_timer;
633 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
634 // connectedAndInitialized() is true, peer exists.
635 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
636 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
641 Send player position to server
644 float &counter = m_playerpos_send_timer;
654 Replace updated meshes
657 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
659 //TimeTaker timer("** Processing mesh update result queue");
662 /*infostream<<"Mesh update result queue size is "
663 <<m_mesh_update_thread.m_queue_out.size()
666 int num_processed_meshes = 0;
667 while(m_mesh_update_thread.m_queue_out.size() > 0)
669 num_processed_meshes++;
670 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
671 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
674 //JMutexAutoLock lock(block->mesh_mutex);
676 // Delete the old mesh
677 if(block->mesh != NULL)
679 // TODO: Remove hardware buffers of meshbuffers of block->mesh
684 // Replace with the new mesh
685 block->mesh = r.mesh;
687 if(r.ack_block_to_server)
689 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
690 <<","<<r.p.Z<<")"<<std::endl;*/
701 u32 replysize = 2+1+6;
702 SharedBuffer<u8> reply(replysize);
703 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
705 writeV3S16(&reply[3], r.p);
707 m_con.Send(PEER_ID_SERVER, 1, reply, true);
710 if(num_processed_meshes > 0)
711 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
715 If the server didn't update the inventory in a while, revert
716 the local inventory (so the player notices the lag problem
717 and knows something is wrong).
719 if(m_inventory_from_server)
721 float interval = 10.0;
722 float count_before = floor(m_inventory_from_server_age / interval);
724 m_inventory_from_server_age += dtime;
726 float count_after = floor(m_inventory_from_server_age / interval);
728 if(count_after != count_before)
730 // Do this every <interval> seconds after TOCLIENT_INVENTORY
731 // Reset the locally changed inventory to the authoritative inventory
732 Player *player = m_env.getLocalPlayer();
733 player->inventory = *m_inventory_from_server;
734 m_inventory_updated = true;
739 Update positions of sounds attached to objects
742 for(std::map<int, u16>::iterator
743 i = m_sounds_to_objects.begin();
744 i != m_sounds_to_objects.end(); i++)
746 int client_id = i->first;
747 u16 object_id = i->second;
748 ClientActiveObject *cao = m_env.getActiveObject(object_id);
751 v3f pos = cao->getPosition();
752 m_sound->updateSoundPosition(client_id, pos);
757 Handle removed remotely initiated sounds
759 m_removed_sounds_check_timer += dtime;
760 if(m_removed_sounds_check_timer >= 2.32)
762 m_removed_sounds_check_timer = 0;
763 // Find removed sounds and clear references to them
764 std::set<s32> removed_server_ids;
765 for(std::map<s32, int>::iterator
766 i = m_sounds_server_to_client.begin();
767 i != m_sounds_server_to_client.end();)
769 s32 server_id = i->first;
770 int client_id = i->second;
772 if(!m_sound->soundExists(client_id)){
773 m_sounds_server_to_client.erase(server_id);
774 m_sounds_client_to_server.erase(client_id);
775 m_sounds_to_objects.erase(client_id);
776 removed_server_ids.insert(server_id);
780 if(removed_server_ids.size() != 0)
782 std::ostringstream os(std::ios_base::binary);
783 writeU16(os, TOSERVER_REMOVED_SOUNDS);
784 writeU16(os, removed_server_ids.size());
785 for(std::set<s32>::iterator i = removed_server_ids.begin();
786 i != removed_server_ids.end(); i++)
788 std::string s = os.str();
789 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
796 // Virtual methods from con::PeerHandler
797 void Client::peerAdded(con::Peer *peer)
799 infostream<<"Client::peerAdded(): peer->id="
800 <<peer->id<<std::endl;
802 void Client::deletingPeer(con::Peer *peer, bool timeout)
804 infostream<<"Client::deletingPeer(): "
805 "Server Peer is getting deleted "
806 <<"(timeout="<<timeout<<")"<<std::endl;
809 void Client::ReceiveAll()
811 DSTACK(__FUNCTION_NAME);
812 u32 start_ms = porting::getTimeMs();
815 // Limit time even if there would be huge amounts of data to
817 if(porting::getTimeMs() > start_ms + 100)
822 g_profiler->graphAdd("client_received_packets", 1);
824 catch(con::NoIncomingDataException &e)
828 catch(con::InvalidIncomingDataException &e)
830 infostream<<"Client::ReceiveAll(): "
831 "InvalidIncomingDataException: what()="
832 <<e.what()<<std::endl;
837 void Client::Receive()
839 DSTACK(__FUNCTION_NAME);
840 SharedBuffer<u8> data;
844 //TimeTaker t1("con mutex and receive", m_device);
845 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
846 datasize = m_con.Receive(sender_peer_id, data);
848 //TimeTaker t1("ProcessData", m_device);
849 ProcessData(*data, datasize, sender_peer_id);
853 sender_peer_id given to this shall be quaranteed to be a valid peer
855 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
857 DSTACK(__FUNCTION_NAME);
859 // Ignore packets that don't even fit a command
862 m_packetcounter.add(60000);
866 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
868 //infostream<<"Client: received command="<<command<<std::endl;
869 m_packetcounter.add((u16)command);
872 If this check is removed, be sure to change the queue
873 system to know the ids
875 if(sender_peer_id != PEER_ID_SERVER)
877 infostream<<"Client::ProcessData(): Discarding data not "
878 "coming from server: peer_id="<<sender_peer_id
883 u8 ser_version = m_server_ser_ver;
885 //infostream<<"Client received command="<<(int)command<<std::endl;
887 if(command == TOCLIENT_INIT)
892 u8 deployed = data[2];
894 infostream<<"Client: TOCLIENT_INIT received with "
895 "deployed="<<((int)deployed&0xff)<<std::endl;
897 if(deployed < SER_FMT_VER_LOWEST
898 || deployed > SER_FMT_VER_HIGHEST)
900 infostream<<"Client: TOCLIENT_INIT: Server sent "
901 <<"unsupported ser_fmt_ver"<<std::endl;
905 m_server_ser_ver = deployed;
907 // Get player position
908 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
909 if(datasize >= 2+1+6)
910 playerpos_s16 = readV3S16(&data[2+1]);
911 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
914 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
916 // Set player position
917 Player *player = m_env.getLocalPlayer();
918 assert(player != NULL);
919 player->setPosition(playerpos_f);
922 if(datasize >= 2+1+6+8)
925 m_map_seed = readU64(&data[2+1+6]);
926 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
931 SharedBuffer<u8> reply(replysize);
932 writeU16(&reply[0], TOSERVER_INIT2);
934 m_con.Send(PEER_ID_SERVER, 1, reply, true);
939 if(command == TOCLIENT_ACCESS_DENIED)
941 // The server didn't like our password. Note, this needs
942 // to be processed even if the serialisation format has
943 // not been agreed yet, the same as TOCLIENT_INIT.
944 m_access_denied = true;
945 m_access_denied_reason = L"Unknown";
948 std::string datastring((char*)&data[2], datasize-2);
949 std::istringstream is(datastring, std::ios_base::binary);
950 m_access_denied_reason = deSerializeWideString(is);
955 if(ser_version == SER_FMT_VER_INVALID)
957 infostream<<"Client: Server serialization"
958 " format invalid or not initialized."
959 " Skipping incoming command="<<command<<std::endl;
963 // Just here to avoid putting the two if's together when
964 // making some copypasta
967 if(command == TOCLIENT_REMOVENODE)
972 p.X = readS16(&data[2]);
973 p.Y = readS16(&data[4]);
974 p.Z = readS16(&data[6]);
976 //TimeTaker t1("TOCLIENT_REMOVENODE");
980 else if(command == TOCLIENT_ADDNODE)
982 if(datasize < 8 + MapNode::serializedLength(ser_version))
986 p.X = readS16(&data[2]);
987 p.Y = readS16(&data[4]);
988 p.Z = readS16(&data[6]);
990 //TimeTaker t1("TOCLIENT_ADDNODE");
993 n.deSerialize(&data[8], ser_version);
997 else if(command == TOCLIENT_BLOCKDATA)
999 // Ignore too small packet
1004 p.X = readS16(&data[2]);
1005 p.Y = readS16(&data[4]);
1006 p.Z = readS16(&data[6]);
1008 /*infostream<<"Client: Thread: BLOCKDATA for ("
1009 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1010 /*infostream<<"Client: Thread: BLOCKDATA for ("
1011 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1013 std::string datastring((char*)&data[8], datasize-8);
1014 std::istringstream istr(datastring, std::ios_base::binary);
1019 v2s16 p2d(p.X, p.Z);
1020 sector = m_env.getMap().emergeSector(p2d);
1022 assert(sector->getPos() == p2d);
1024 //TimeTaker timer("MapBlock deSerialize");
1027 block = sector->getBlockNoCreateNoEx(p.Y);
1031 Update an existing block
1033 //infostream<<"Updating"<<std::endl;
1034 block->deSerialize(istr, ser_version, false);
1041 //infostream<<"Creating new"<<std::endl;
1042 block = new MapBlock(&m_env.getMap(), p, this);
1043 block->deSerialize(istr, ser_version, false);
1044 sector->insertBlock(block);
1058 u32 replysize = 2+1+6;
1059 SharedBuffer<u8> reply(replysize);
1060 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1062 writeV3S16(&reply[3], p);
1064 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1068 Add it to mesh update queue and set it to be acknowledged after update.
1070 //infostream<<"Adding mesh update task for received block"<<std::endl;
1071 addUpdateMeshTaskWithEdge(p, true);
1073 else if(command == TOCLIENT_INVENTORY)
1078 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1081 //TimeTaker t2("mutex locking", m_device);
1082 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1085 //TimeTaker t3("istringstream init", m_device);
1086 std::string datastring((char*)&data[2], datasize-2);
1087 std::istringstream is(datastring, std::ios_base::binary);
1090 //m_env.printPlayers(infostream);
1092 //TimeTaker t4("player get", m_device);
1093 Player *player = m_env.getLocalPlayer();
1094 assert(player != NULL);
1097 //TimeTaker t1("inventory.deSerialize()", m_device);
1098 player->inventory.deSerialize(is);
1101 m_inventory_updated = true;
1103 delete m_inventory_from_server;
1104 m_inventory_from_server = new Inventory(player->inventory);
1105 m_inventory_from_server_age = 0.0;
1107 //infostream<<"Client got player inventory:"<<std::endl;
1108 //player->inventory.print(infostream);
1111 else if(command == TOCLIENT_TIME_OF_DAY)
1116 u16 time_of_day = readU16(&data[2]);
1117 time_of_day = time_of_day % 24000;
1118 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1119 float time_speed = 0;
1120 if(datasize >= 2 + 2 + 4){
1121 time_speed = readF1000(&data[4]);
1123 // Old message; try to approximate speed of time by ourselves
1124 float time_of_day_f = (float)time_of_day / 24000.0;
1125 float tod_diff_f = 0;
1126 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1127 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1129 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1130 m_last_time_of_day_f = time_of_day_f;
1131 float time_diff = m_time_of_day_update_timer;
1132 m_time_of_day_update_timer = 0;
1133 if(m_time_of_day_set){
1134 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1135 infostream<<"Client: Measured time_of_day speed (old format): "
1136 <<time_speed<<" tod_diff_f="<<tod_diff_f
1137 <<" time_diff="<<time_diff<<std::endl;
1141 // Update environment
1142 m_env.setTimeOfDay(time_of_day);
1143 m_env.setTimeOfDaySpeed(time_speed);
1144 m_time_of_day_set = true;
1146 u32 dr = m_env.getDayNightRatio();
1147 verbosestream<<"Client: time_of_day="<<time_of_day
1148 <<" time_speed="<<time_speed
1149 <<" dr="<<dr<<std::endl;
1151 else if(command == TOCLIENT_CHAT_MESSAGE)
1159 std::string datastring((char*)&data[2], datasize-2);
1160 std::istringstream is(datastring, std::ios_base::binary);
1163 is.read((char*)buf, 2);
1164 u16 len = readU16(buf);
1166 std::wstring message;
1167 for(u16 i=0; i<len; i++)
1169 is.read((char*)buf, 2);
1170 message += (wchar_t)readU16(buf);
1173 /*infostream<<"Client received chat message: "
1174 <<wide_to_narrow(message)<<std::endl;*/
1176 m_chat_queue.push_back(message);
1178 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1180 //if(g_settings->getBool("enable_experimental"))
1184 u16 count of removed objects
1185 for all removed objects {
1188 u16 count of added objects
1189 for all added objects {
1192 u32 initialization data length
1193 string initialization data
1198 // Get all data except the command number
1199 std::string datastring((char*)&data[2], datasize-2);
1200 // Throw them in an istringstream
1201 std::istringstream is(datastring, std::ios_base::binary);
1205 // Read removed objects
1207 u16 removed_count = readU16((u8*)buf);
1208 for(u16 i=0; i<removed_count; i++)
1211 u16 id = readU16((u8*)buf);
1214 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1215 m_env.removeActiveObject(id);
1219 // Read added objects
1221 u16 added_count = readU16((u8*)buf);
1222 for(u16 i=0; i<added_count; i++)
1225 u16 id = readU16((u8*)buf);
1227 u8 type = readU8((u8*)buf);
1228 std::string data = deSerializeLongString(is);
1231 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1232 m_env.addActiveObject(id, type, data);
1237 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1239 //if(g_settings->getBool("enable_experimental"))
1251 // Get all data except the command number
1252 std::string datastring((char*)&data[2], datasize-2);
1253 // Throw them in an istringstream
1254 std::istringstream is(datastring, std::ios_base::binary);
1256 while(is.eof() == false)
1260 u16 id = readU16((u8*)buf);
1264 u16 message_size = readU16((u8*)buf);
1265 std::string message;
1266 message.reserve(message_size);
1267 for(u16 i=0; i<message_size; i++)
1270 message.append(buf, 1);
1272 // Pass on to the environment
1274 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1275 m_env.processActiveObjectMessage(id, message);
1280 else if(command == TOCLIENT_HP)
1282 std::string datastring((char*)&data[2], datasize-2);
1283 std::istringstream is(datastring, std::ios_base::binary);
1284 Player *player = m_env.getLocalPlayer();
1285 assert(player != NULL);
1286 u8 oldhp = player->hp;
1292 // Add to ClientEvent queue
1294 event.type = CE_PLAYER_DAMAGE;
1295 event.player_damage.amount = oldhp - hp;
1296 m_client_event_queue.push_back(event);
1299 else if(command == TOCLIENT_MOVE_PLAYER)
1301 std::string datastring((char*)&data[2], datasize-2);
1302 std::istringstream is(datastring, std::ios_base::binary);
1303 Player *player = m_env.getLocalPlayer();
1304 assert(player != NULL);
1305 v3f pos = readV3F1000(is);
1306 f32 pitch = readF1000(is);
1307 f32 yaw = readF1000(is);
1308 player->setPosition(pos);
1309 /*player->setPitch(pitch);
1310 player->setYaw(yaw);*/
1312 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1313 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1319 Add to ClientEvent queue.
1320 This has to be sent to the main program because otherwise
1321 it would just force the pitch and yaw values to whatever
1322 the camera points to.
1325 event.type = CE_PLAYER_FORCE_MOVE;
1326 event.player_force_move.pitch = pitch;
1327 event.player_force_move.yaw = yaw;
1328 m_client_event_queue.push_back(event);
1330 // Ignore damage for a few seconds, so that the player doesn't
1331 // get damage from falling on ground
1332 m_ignore_damage_timer = 3.0;
1334 else if(command == TOCLIENT_PLAYERITEM)
1336 std::string datastring((char*)&data[2], datasize-2);
1337 std::istringstream is(datastring, std::ios_base::binary);
1339 u16 count = readU16(is);
1341 for (u16 i = 0; i < count; ++i) {
1342 u16 peer_id = readU16(is);
1343 Player *player = m_env.getPlayer(peer_id);
1347 infostream<<"Client: ignoring player item "
1348 << deSerializeString(is)
1349 << " for non-existing peer id " << peer_id
1352 } else if (player->isLocal()) {
1353 infostream<<"Client: ignoring player item "
1354 << deSerializeString(is)
1355 << " for local player" << std::endl;
1358 InventoryList *inv = player->inventory.getList("main");
1359 std::string itemstring(deSerializeString(is));
1361 item.deSerialize(itemstring, m_itemdef);
1362 inv->changeItem(0, item);
1363 if(itemstring.empty())
1365 infostream<<"Client: empty player item for peer "
1366 <<peer_id<<std::endl;
1370 infostream<<"Client: player item for peer "
1371 <<peer_id<<": "<<itemstring<<std::endl;
1376 else if(command == TOCLIENT_DEATHSCREEN)
1378 std::string datastring((char*)&data[2], datasize-2);
1379 std::istringstream is(datastring, std::ios_base::binary);
1381 bool set_camera_point_target = readU8(is);
1382 v3f camera_point_target = readV3F1000(is);
1385 event.type = CE_DEATHSCREEN;
1386 event.deathscreen.set_camera_point_target = set_camera_point_target;
1387 event.deathscreen.camera_point_target_x = camera_point_target.X;
1388 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1389 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1390 m_client_event_queue.push_back(event);
1392 else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1394 io::IFileSystem *irrfs = m_device->getFileSystem();
1395 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1397 std::string datastring((char*)&data[2], datasize-2);
1398 std::istringstream is(datastring, std::ios_base::binary);
1400 // Mesh update thread must be stopped while
1401 // updating content definitions
1402 assert(!m_mesh_update_thread.IsRunning());
1404 int num_textures = readU16(is);
1406 core::list<TextureRequest> texture_requests;
1408 for(int i=0; i<num_textures; i++){
1410 bool texture_found = false;
1412 //read texture from cache
1413 std::string name = deSerializeString(is);
1414 std::string sha1_texture = deSerializeString(is);
1416 // if name contains illegal characters, ignore the texture
1417 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1418 errorstream<<"Client: ignoring illegal texture name "
1419 <<"sent by server: \""<<name<<"\""<<std::endl;
1423 std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1425 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1428 if(fis.good() == false){
1429 infostream<<"Client::Texture not found in cache: "
1430 <<name << " expected it at: "<<tpath<<std::endl;
1434 std::ostringstream tmp_os(std::ios_base::binary);
1438 fis.read(buf, 1024);
1439 std::streamsize len = fis.gcount();
1440 tmp_os.write(buf, len);
1449 infostream<<"Client: Failed to read texture from cache\""
1450 <<name<<"\""<<std::endl;
1455 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1457 unsigned char *digest = sha1.getDigest();
1459 std::string digest_string = base64_encode(digest, 20);
1461 if (digest_string == sha1_texture) {
1462 // Silly irrlicht's const-incorrectness
1463 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1465 // Create an irrlicht memory file
1466 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1467 *data_rw, tmp_os.str().size(), "_tempreadfile");
1470 video::IImage *img = vdrv->createImageFromFile(rfile);
1472 infostream<<"Client: Cannot create image from data of "
1473 <<"received texture \""<<name<<"\""<<std::endl;
1477 m_tsrc->insertSourceImage(name, img);
1481 texture_found = true;
1485 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1486 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1493 //add texture request
1494 if (!texture_found) {
1495 infostream<<"Client: Adding texture to request list: \""
1496 <<name<<"\""<<std::endl;
1497 texture_requests.push_back(TextureRequest(name));
1503 event.type = CE_TEXTURES_UPDATED;
1504 m_client_event_queue.push_back(event);
1507 //send Texture request
1510 u16 number of textures requested
1516 std::ostringstream os(std::ios_base::binary);
1521 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1522 os.write((char*)buf, 2);
1524 writeU16(buf,texture_requests.size());
1525 os.write((char*)buf, 2);
1528 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1529 i != texture_requests.end(); i++) {
1530 os<<serializeString(i->name);
1534 std::string s = os.str();
1535 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1537 Send(0, data, true);
1538 infostream<<"Client: Sending request list to server " <<std::endl;
1540 else if(command == TOCLIENT_TEXTURES)
1542 io::IFileSystem *irrfs = m_device->getFileSystem();
1543 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1548 // Mesh update thread must be stopped while
1549 // updating content definitions
1550 assert(!m_mesh_update_thread.IsRunning());
1554 u16 total number of texture bunches
1555 u16 index of this bunch
1556 u32 number of textures in this bunch
1564 int num_bunches = readU16(is);
1565 int bunch_i = readU16(is);
1566 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1567 if(bunch_i == num_bunches - 1)
1568 m_textures_received = true;
1569 int num_textures = readU32(is);
1570 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1571 <<num_bunches<<" textures="<<num_textures
1572 <<" size="<<datasize<<std::endl;
1573 for(int i=0; i<num_textures; i++){
1574 std::string name = deSerializeString(is);
1575 std::string data = deSerializeLongString(is);
1577 // if name contains illegal characters, ignore the texture
1578 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1579 errorstream<<"Client: ignoring illegal texture name "
1580 <<"sent by server: \""<<name<<"\""<<std::endl;
1584 // Silly irrlicht's const-incorrectness
1585 Buffer<char> data_rw(data.c_str(), data.size());
1586 // Create an irrlicht memory file
1587 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1588 *data_rw, data.size(), "_tempreadfile");
1591 video::IImage *img = vdrv->createImageFromFile(rfile);
1593 errorstream<<"Client: Cannot create image from data of "
1594 <<"received texture \""<<name<<"\""<<std::endl;
1599 fs::CreateAllDirs(getTextureCacheDir());
1601 std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1602 std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1604 if (outfile.good()) {
1605 outfile.write(data.c_str(),data.length());
1609 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1612 m_tsrc->insertSourceImage(name, img);
1618 event.type = CE_TEXTURES_UPDATED;
1619 m_client_event_queue.push_back(event);
1621 else if(command == TOCLIENT_TOOLDEF)
1623 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1625 else if(command == TOCLIENT_NODEDEF)
1627 infostream<<"Client: Received node definitions: packet size: "
1628 <<datasize<<std::endl;
1630 // Mesh update thread must be stopped while
1631 // updating content definitions
1632 assert(!m_mesh_update_thread.IsRunning());
1634 // Decompress node definitions
1635 std::string datastring((char*)&data[2], datasize-2);
1636 std::istringstream is(datastring, std::ios_base::binary);
1637 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1638 std::ostringstream tmp_os;
1639 decompressZlib(tmp_is, tmp_os);
1641 // Deserialize node definitions
1642 std::istringstream tmp_is2(tmp_os.str());
1643 m_nodedef->deSerialize(tmp_is2);
1644 m_nodedef_received = true;
1646 else if(command == TOCLIENT_CRAFTITEMDEF)
1648 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1650 else if(command == TOCLIENT_ITEMDEF)
1652 infostream<<"Client: Received item definitions: packet size: "
1653 <<datasize<<std::endl;
1655 // Mesh update thread must be stopped while
1656 // updating content definitions
1657 assert(!m_mesh_update_thread.IsRunning());
1659 // Decompress item definitions
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1662 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1663 std::ostringstream tmp_os;
1664 decompressZlib(tmp_is, tmp_os);
1666 // Deserialize node definitions
1667 std::istringstream tmp_is2(tmp_os.str());
1668 m_itemdef->deSerialize(tmp_is2);
1669 m_itemdef_received = true;
1671 else if(command == TOCLIENT_PLAY_SOUND)
1673 std::string datastring((char*)&data[2], datasize-2);
1674 std::istringstream is(datastring, std::ios_base::binary);
1676 s32 server_id = readS32(is);
1677 std::string name = deSerializeString(is);
1678 float gain = readF1000(is);
1679 int type = readU8(is); // 0=local, 1=positional, 2=object
1680 v3f pos = readV3F1000(is);
1681 u16 object_id = readU16(is);
1682 bool loop = readU8(is);
1687 client_id = m_sound->playSound(name, false, gain);
1689 case 1: // positional
1690 client_id = m_sound->playSoundAt(name, false, gain, pos);
1693 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1695 pos = cao->getPosition();
1696 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1697 // TODO: Set up sound to move with object
1702 if(client_id != -1){
1703 m_sounds_server_to_client[server_id] = client_id;
1704 m_sounds_client_to_server[client_id] = server_id;
1706 m_sounds_to_objects[client_id] = object_id;
1709 else if(command == TOCLIENT_STOP_SOUND)
1711 std::string datastring((char*)&data[2], datasize-2);
1712 std::istringstream is(datastring, std::ios_base::binary);
1714 s32 server_id = readS32(is);
1715 std::map<s32, int>::iterator i =
1716 m_sounds_server_to_client.find(server_id);
1717 if(i != m_sounds_server_to_client.end()){
1718 int client_id = i->second;
1719 m_sound->stopSound(client_id);
1724 infostream<<"Client: Ignoring unknown command "
1725 <<command<<std::endl;
1729 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1731 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1732 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1735 void Client::interact(u8 action, const PointedThing& pointed)
1737 if(connectedAndInitialized() == false){
1738 infostream<<"Client::interact() "
1739 "cancelled (not connected)"
1744 std::ostringstream os(std::ios_base::binary);
1750 [5] u32 length of the next item
1751 [9] serialized PointedThing
1753 0: start digging (from undersurface) or use
1754 1: stop digging (all parameters ignored)
1755 2: digging completed
1756 3: place block or item (to abovesurface)
1759 writeU16(os, TOSERVER_INTERACT);
1760 writeU8(os, action);
1761 writeU16(os, getPlayerItem());
1762 std::ostringstream tmp_os(std::ios::binary);
1763 pointed.serialize(tmp_os);
1764 os<<serializeLongString(tmp_os.str());
1766 std::string s = os.str();
1767 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1770 Send(0, data, true);
1773 void Client::sendSignNodeText(v3s16 p, std::string text)
1781 std::ostringstream os(std::ios_base::binary);
1785 writeU16(buf, TOSERVER_SIGNNODETEXT);
1786 os.write((char*)buf, 2);
1790 os.write((char*)buf, 6);
1792 u16 textlen = text.size();
1793 // Write text length
1794 writeS16(buf, textlen);
1795 os.write((char*)buf, 2);
1798 os.write((char*)text.c_str(), textlen);
1801 std::string s = os.str();
1802 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1804 Send(0, data, true);
1807 void Client::sendInventoryAction(InventoryAction *a)
1809 std::ostringstream os(std::ios_base::binary);
1813 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1814 os.write((char*)buf, 2);
1819 std::string s = os.str();
1820 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1822 Send(0, data, true);
1825 void Client::sendChatMessage(const std::wstring &message)
1827 std::ostringstream os(std::ios_base::binary);
1831 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1832 os.write((char*)buf, 2);
1835 writeU16(buf, message.size());
1836 os.write((char*)buf, 2);
1839 for(u32 i=0; i<message.size(); i++)
1843 os.write((char*)buf, 2);
1847 std::string s = os.str();
1848 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1850 Send(0, data, true);
1853 void Client::sendChangePassword(const std::wstring oldpassword,
1854 const std::wstring newpassword)
1856 Player *player = m_env.getLocalPlayer();
1860 std::string playername = player->getName();
1861 std::string oldpwd = translatePassword(playername, oldpassword);
1862 std::string newpwd = translatePassword(playername, newpassword);
1864 std::ostringstream os(std::ios_base::binary);
1865 u8 buf[2+PASSWORD_SIZE*2];
1867 [0] u16 TOSERVER_PASSWORD
1868 [2] u8[28] old password
1869 [30] u8[28] new password
1872 writeU16(buf, TOSERVER_PASSWORD);
1873 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1875 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1876 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1878 buf[2+PASSWORD_SIZE-1] = 0;
1879 buf[30+PASSWORD_SIZE-1] = 0;
1880 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1883 std::string s = os.str();
1884 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1886 Send(0, data, true);
1890 void Client::sendDamage(u8 damage)
1892 DSTACK(__FUNCTION_NAME);
1893 std::ostringstream os(std::ios_base::binary);
1895 writeU16(os, TOSERVER_DAMAGE);
1896 writeU8(os, damage);
1899 std::string s = os.str();
1900 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1902 Send(0, data, true);
1905 void Client::sendRespawn()
1907 DSTACK(__FUNCTION_NAME);
1908 std::ostringstream os(std::ios_base::binary);
1910 writeU16(os, TOSERVER_RESPAWN);
1913 std::string s = os.str();
1914 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1916 Send(0, data, true);
1919 void Client::sendPlayerPos()
1921 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1923 Player *myplayer = m_env.getLocalPlayer();
1924 if(myplayer == NULL)
1929 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1930 our_peer_id = m_con.GetPeerID();
1933 // Set peer id if not set already
1934 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1935 myplayer->peer_id = our_peer_id;
1936 // Check that an existing peer_id is the same as the connection's
1937 assert(myplayer->peer_id == our_peer_id);
1939 v3f pf = myplayer->getPosition();
1940 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1941 v3f sf = myplayer->getSpeed();
1942 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1943 s32 pitch = myplayer->getPitch() * 100;
1944 s32 yaw = myplayer->getYaw() * 100;
1949 [2] v3s32 position*100
1950 [2+12] v3s32 speed*100
1951 [2+12+12] s32 pitch*100
1952 [2+12+12+4] s32 yaw*100
1955 SharedBuffer<u8> data(2+12+12+4+4);
1956 writeU16(&data[0], TOSERVER_PLAYERPOS);
1957 writeV3S32(&data[2], position);
1958 writeV3S32(&data[2+12], speed);
1959 writeS32(&data[2+12+12], pitch);
1960 writeS32(&data[2+12+12+4], yaw);
1962 // Send as unreliable
1963 Send(0, data, false);
1966 void Client::sendPlayerItem(u16 item)
1968 Player *myplayer = m_env.getLocalPlayer();
1969 if(myplayer == NULL)
1972 u16 our_peer_id = m_con.GetPeerID();
1974 // Set peer id if not set already
1975 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1976 myplayer->peer_id = our_peer_id;
1977 // Check that an existing peer_id is the same as the connection's
1978 assert(myplayer->peer_id == our_peer_id);
1980 SharedBuffer<u8> data(2+2);
1981 writeU16(&data[0], TOSERVER_PLAYERITEM);
1982 writeU16(&data[2], item);
1985 Send(0, data, true);
1988 void Client::removeNode(v3s16 p)
1990 core::map<v3s16, MapBlock*> modified_blocks;
1994 //TimeTaker t("removeNodeAndUpdate", m_device);
1995 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1997 catch(InvalidPositionException &e)
2001 // add urgent task to update the modified node
2002 addUpdateMeshTaskForNode(p, false, true);
2004 for(core::map<v3s16, MapBlock * >::Iterator
2005 i = modified_blocks.getIterator();
2006 i.atEnd() == false; i++)
2008 v3s16 p = i.getNode()->getKey();
2009 addUpdateMeshTaskWithEdge(p);
2013 void Client::addNode(v3s16 p, MapNode n)
2015 TimeTaker timer1("Client::addNode()");
2017 core::map<v3s16, MapBlock*> modified_blocks;
2021 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2022 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2024 catch(InvalidPositionException &e)
2027 for(core::map<v3s16, MapBlock * >::Iterator
2028 i = modified_blocks.getIterator();
2029 i.atEnd() == false; i++)
2031 v3s16 p = i.getNode()->getKey();
2032 addUpdateMeshTaskWithEdge(p);
2036 void Client::setPlayerControl(PlayerControl &control)
2038 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2039 LocalPlayer *player = m_env.getLocalPlayer();
2040 assert(player != NULL);
2041 player->control = control;
2044 void Client::selectPlayerItem(u16 item)
2046 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2047 m_playeritem = item;
2048 m_inventory_updated = true;
2049 sendPlayerItem(item);
2052 // Returns true if the inventory of the local player has been
2053 // updated from the server. If it is true, it is set to false.
2054 bool Client::getLocalInventoryUpdated()
2056 // m_inventory_updated is behind envlock
2057 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2058 bool updated = m_inventory_updated;
2059 m_inventory_updated = false;
2063 // Copies the inventory of the local player to parameter
2064 void Client::getLocalInventory(Inventory &dst)
2066 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2067 Player *player = m_env.getLocalPlayer();
2068 assert(player != NULL);
2069 dst = player->inventory;
2072 Inventory* Client::getInventory(const InventoryLocation &loc)
2075 case InventoryLocation::UNDEFINED:
2078 case InventoryLocation::CURRENT_PLAYER:
2080 Player *player = m_env.getLocalPlayer();
2081 assert(player != NULL);
2082 return &player->inventory;
2085 case InventoryLocation::PLAYER:
2087 Player *player = m_env.getPlayer(loc.name.c_str());
2090 return &player->inventory;
2093 case InventoryLocation::NODEMETA:
2095 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2098 return meta->getInventory();
2106 void Client::inventoryAction(InventoryAction *a)
2109 Send it to the server
2111 sendInventoryAction(a);
2114 Predict some local inventory changes
2116 a->clientApply(this, this);
2119 ClientActiveObject * Client::getSelectedActiveObject(
2121 v3f from_pos_f_on_map,
2122 core::line3d<f32> shootline_on_map
2125 core::array<DistanceSortedActiveObject> objects;
2127 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2129 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2132 // After this, the closest object is the first in the array.
2135 for(u32 i=0; i<objects.size(); i++)
2137 ClientActiveObject *obj = objects[i].obj;
2139 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2140 if(selection_box == NULL)
2143 v3f pos = obj->getPosition();
2145 core::aabbox3d<f32> offsetted_box(
2146 selection_box->MinEdge + pos,
2147 selection_box->MaxEdge + pos
2150 if(offsetted_box.intersectsWithLine(shootline_on_map))
2152 //infostream<<"Returning selected object"<<std::endl;
2157 //infostream<<"No object selected; returning NULL."<<std::endl;
2161 void Client::printDebugInfo(std::ostream &os)
2163 //JMutexAutoLock lock1(m_fetchblock_mutex);
2164 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2166 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2167 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2168 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2172 core::list<std::wstring> Client::getConnectedPlayerNames()
2174 core::list<Player*> players = m_env.getPlayers(true);
2175 core::list<std::wstring> playerNames;
2176 for(core::list<Player*>::Iterator
2177 i = players.begin();
2178 i != players.end(); i++)
2180 Player *player = *i;
2181 playerNames.push_back(narrow_to_wide(player->getName()));
2186 float Client::getAnimationTime()
2188 return m_animation_time;
2191 int Client::getCrackLevel()
2193 return m_crack_level;
2196 void Client::setCrack(int level, v3s16 pos)
2198 int old_crack_level = m_crack_level;
2199 v3s16 old_crack_pos = m_crack_pos;
2201 m_crack_level = level;
2204 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2207 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2209 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2212 addUpdateMeshTaskForNode(pos, false, true);
2218 Player *player = m_env.getLocalPlayer();
2219 assert(player != NULL);
2223 bool Client::getChatMessage(std::wstring &message)
2225 if(m_chat_queue.size() == 0)
2227 message = m_chat_queue.pop_front();
2231 void Client::typeChatMessage(const std::wstring &message)
2233 // Discard empty line
2238 sendChatMessage(message);
2241 if (message[0] == L'/')
2243 m_chat_queue.push_back(
2244 (std::wstring)L"issued command: "+message);
2248 LocalPlayer *player = m_env.getLocalPlayer();
2249 assert(player != NULL);
2250 std::wstring name = narrow_to_wide(player->getName());
2251 m_chat_queue.push_back(
2252 (std::wstring)L"<"+name+L"> "+message);
2256 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2258 /*infostream<<"Client::addUpdateMeshTask(): "
2259 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2260 <<" ack_to_server="<<ack_to_server
2261 <<" urgent="<<urgent
2264 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2269 Create a task to update the mesh of the block
2272 MeshMakeData *data = new MeshMakeData(this);
2275 //TimeTaker timer("data fill");
2277 // Debug: 1-6ms, avg=2ms
2279 data->setCrack(m_crack_level, m_crack_pos);
2280 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2284 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2286 // Add task to queue
2287 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2289 /*infostream<<"Mesh update input queue size is "
2290 <<m_mesh_update_thread.m_queue_in.size()
2294 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2298 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2299 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2304 v3s16 p = blockpos + v3s16(0,0,0);
2305 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2306 addUpdateMeshTask(p, ack_to_server, urgent);
2308 catch(InvalidPositionException &e){}
2311 v3s16 p = blockpos + v3s16(-1,0,0);
2312 addUpdateMeshTask(p, false, urgent);
2314 catch(InvalidPositionException &e){}
2316 v3s16 p = blockpos + v3s16(0,-1,0);
2317 addUpdateMeshTask(p, false, urgent);
2319 catch(InvalidPositionException &e){}
2321 v3s16 p = blockpos + v3s16(0,0,-1);
2322 addUpdateMeshTask(p, false, urgent);
2324 catch(InvalidPositionException &e){}
2327 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2331 infostream<<"Client::addUpdateMeshTaskForNode(): "
2332 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2336 v3s16 blockpos = getNodeBlockPos(nodepos);
2337 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2340 v3s16 p = blockpos + v3s16(0,0,0);
2341 addUpdateMeshTask(p, ack_to_server, urgent);
2343 catch(InvalidPositionException &e){}
2345 if(nodepos.X == blockpos_relative.X){
2347 v3s16 p = blockpos + v3s16(-1,0,0);
2348 addUpdateMeshTask(p, false, urgent);
2350 catch(InvalidPositionException &e){}
2352 if(nodepos.Y == blockpos_relative.Y){
2354 v3s16 p = blockpos + v3s16(0,-1,0);
2355 addUpdateMeshTask(p, false, urgent);
2357 catch(InvalidPositionException &e){}
2359 if(nodepos.Z == blockpos_relative.Z){
2361 v3s16 p = blockpos + v3s16(0,0,-1);
2362 addUpdateMeshTask(p, false, urgent);
2364 catch(InvalidPositionException &e){}
2368 ClientEvent Client::getClientEvent()
2370 if(m_client_event_queue.size() == 0)
2373 event.type = CE_NONE;
2376 return m_client_event_queue.pop_front();
2379 void Client::afterContentReceived()
2381 assert(m_itemdef_received);
2382 assert(m_nodedef_received);
2383 assert(m_textures_received);
2385 // Rebuild inherited images and recreate textures
2386 m_tsrc->rebuildImagesAndTextures();
2388 // Update texture atlas
2389 if(g_settings->getBool("enable_texture_atlas"))
2390 m_tsrc->buildMainAtlas(this);
2392 // Update node aliases
2393 m_nodedef->updateAliases(m_itemdef);
2395 // Update node textures
2396 m_nodedef->updateTextures(m_tsrc);
2398 // Update item textures and meshes
2399 m_itemdef->updateTexturesAndMeshes(this);
2401 // Start mesh update thread after setting up content definitions
2402 m_mesh_update_thread.Start();
2405 float Client::getRTT(void)
2408 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2409 } catch(con::PeerNotFoundException &e){
2414 // IGameDef interface
2416 IItemDefManager* Client::getItemDefManager()
2420 INodeDefManager* Client::getNodeDefManager()
2424 ICraftDefManager* Client::getCraftDefManager()
2427 //return m_craftdef;
2429 ITextureSource* Client::getTextureSource()
2433 u16 Client::allocateUnknownNodeId(const std::string &name)
2435 errorstream<<"Client::allocateUnknownNodeId(): "
2436 <<"Client cannot allocate node IDs"<<std::endl;
2438 return CONTENT_IGNORE;
2440 ISoundManager* Client::getSoundManager()
2444 MtEventManager* Client::getEventManager()