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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
22 #include "clientserver.h"
23 #include "jmutexautolock.h"
27 #include "mapsector.h"
28 #include "mapblock_mesh.h"
33 #include "nodemetadata.h"
36 #include <IFileSystem.h>
39 #include "clientmap.h"
40 #include "filecache.h"
42 #include "util/string.h"
45 static std::string getMediaCacheDir()
47 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
54 MediaRequest(const std::string &name_=""):
63 QueuedMeshUpdate::QueuedMeshUpdate():
66 ack_block_to_server(false)
70 QueuedMeshUpdate::~QueuedMeshUpdate()
80 MeshUpdateQueue::MeshUpdateQueue()
85 MeshUpdateQueue::~MeshUpdateQueue()
87 JMutexAutoLock lock(m_mutex);
89 for(std::vector<QueuedMeshUpdate*>::iterator
91 i != m_queue.end(); i++)
93 QueuedMeshUpdate *q = *i;
99 peer_id=0 adds with nobody to send to
101 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
103 DSTACK(__FUNCTION_NAME);
107 JMutexAutoLock lock(m_mutex);
113 Find if block is already in queue.
114 If it is, update the data and quit.
116 for(std::vector<QueuedMeshUpdate*>::iterator
118 i != m_queue.end(); i++)
120 QueuedMeshUpdate *q = *i;
126 if(ack_block_to_server)
127 q->ack_block_to_server = true;
135 QueuedMeshUpdate *q = new QueuedMeshUpdate;
138 q->ack_block_to_server = ack_block_to_server;
139 m_queue.push_back(q);
142 // Returned pointer must be deleted
143 // Returns NULL if queue is empty
144 QueuedMeshUpdate * MeshUpdateQueue::pop()
146 JMutexAutoLock lock(m_mutex);
148 bool must_be_urgent = !m_urgents.empty();
149 for(std::vector<QueuedMeshUpdate*>::iterator
151 i != m_queue.end(); i++)
153 QueuedMeshUpdate *q = *i;
154 if(must_be_urgent && m_urgents.count(q->p) == 0)
157 m_urgents.erase(q->p);
167 void * MeshUpdateThread::Thread()
171 log_register_thread("MeshUpdateThread");
173 DSTACK(__FUNCTION_NAME);
175 BEGIN_DEBUG_EXCEPTION_HANDLER
179 /*// Wait for output queue to flush.
180 // Allow 2 in queue, this makes less frametime jitter.
181 // Umm actually, there is no much difference
182 if(m_queue_out.size() >= 2)
188 QueuedMeshUpdate *q = m_queue_in.pop();
195 ScopeProfiler sp(g_profiler, "Client: Mesh making");
197 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
198 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
207 r.ack_block_to_server = q->ack_block_to_server;
209 /*infostream<<"MeshUpdateThread: Processed "
210 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
213 m_queue_out.push_back(r);
218 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 IrrlichtDevice *device,
225 const char *playername,
226 std::string password,
227 MapDrawControl &control,
228 IWritableTextureSource *tsrc,
229 IWritableItemDefManager *itemdef,
230 IWritableNodeDefManager *nodedef,
231 ISoundManager *sound,
232 MtEventManager *event
239 m_mesh_update_thread(this),
241 new ClientMap(this, this, control,
242 device->getSceneManager()->getRootSceneNode(),
243 device->getSceneManager(), 666),
244 device->getSceneManager(),
247 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
249 m_server_ser_ver(SER_FMT_VER_INVALID),
251 m_inventory_updated(false),
252 m_inventory_from_server(NULL),
253 m_inventory_from_server_age(0.0),
258 m_password(password),
259 m_access_denied(false),
260 m_media_cache(getMediaCacheDir()),
261 m_media_receive_progress(0),
262 m_media_received(false),
263 m_itemdef_received(false),
264 m_nodedef_received(false),
265 m_time_of_day_set(false),
266 m_last_time_of_day_f(-1),
267 m_time_of_day_update_timer(0),
268 m_removed_sounds_check_timer(0)
270 m_packetcounter_timer = 0.0;
271 //m_delete_unused_sectors_timer = 0.0;
272 m_connection_reinit_timer = 0.0;
273 m_avg_rtt_timer = 0.0;
274 m_playerpos_send_timer = 0.0;
275 m_ignore_damage_timer = 0.0;
277 // Build main texture atlas, now that the GameDef exists (that is, us)
278 if(g_settings->getBool("enable_texture_atlas"))
279 m_tsrc->buildMainAtlas(this);
281 infostream<<"Not building texture atlas."<<std::endl;
287 Player *player = new LocalPlayer(this);
289 player->updateName(playername);
291 m_env.addPlayer(player);
298 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
302 m_mesh_update_thread.setRun(false);
303 while(m_mesh_update_thread.IsRunning())
306 delete m_inventory_from_server;
309 void Client::connect(Address address)
311 DSTACK(__FUNCTION_NAME);
312 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
313 m_con.SetTimeoutMs(0);
314 m_con.Connect(address);
317 bool Client::connectedAndInitialized()
319 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
321 if(m_con.Connected() == false)
324 if(m_server_ser_ver == SER_FMT_VER_INVALID)
330 void Client::step(float dtime)
332 DSTACK(__FUNCTION_NAME);
338 if(m_ignore_damage_timer > dtime)
339 m_ignore_damage_timer -= dtime;
341 m_ignore_damage_timer = 0.0;
343 m_animation_time += dtime;
344 if(m_animation_time > 60.0)
345 m_animation_time -= 60.0;
347 m_time_of_day_update_timer += dtime;
349 //infostream<<"Client steps "<<dtime<<std::endl;
352 //TimeTaker timer("ReceiveAll()", m_device);
358 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
360 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
361 m_con.RunTimeouts(dtime);
368 float &counter = m_packetcounter_timer;
374 infostream<<"Client packetcounter (20s):"<<std::endl;
375 m_packetcounter.print(infostream);
376 m_packetcounter.clear();
380 // Get connection status
381 bool connected = connectedAndInitialized();
386 Delete unused sectors
388 NOTE: This jams the game for a while because deleting sectors
392 float &counter = m_delete_unused_sectors_timer;
400 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
402 core::list<v3s16> deleted_blocks;
404 float delete_unused_sectors_timeout =
405 g_settings->getFloat("client_delete_unused_sectors_timeout");
407 // Delete sector blocks
408 /*u32 num = m_env.getMap().unloadUnusedData
409 (delete_unused_sectors_timeout,
410 true, &deleted_blocks);*/
412 // Delete whole sectors
413 m_env.getMap().unloadUnusedData
414 (delete_unused_sectors_timeout,
417 if(deleted_blocks.size() > 0)
419 /*infostream<<"Client: Deleted blocks of "<<num
420 <<" unused sectors"<<std::endl;*/
421 /*infostream<<"Client: Deleted "<<num
422 <<" unused sectors"<<std::endl;*/
428 // Env is locked so con can be locked.
429 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
431 core::list<v3s16>::Iterator i = deleted_blocks.begin();
432 core::list<v3s16> sendlist;
435 if(sendlist.size() == 255 || i == deleted_blocks.end())
437 if(sendlist.size() == 0)
446 u32 replysize = 2+1+6*sendlist.size();
447 SharedBuffer<u8> reply(replysize);
448 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
449 reply[2] = sendlist.size();
451 for(core::list<v3s16>::Iterator
452 j = sendlist.begin();
453 j != sendlist.end(); j++)
455 writeV3S16(&reply[2+1+6*k], *j);
458 m_con.Send(PEER_ID_SERVER, 1, reply, true);
460 if(i == deleted_blocks.end())
466 sendlist.push_back(*i);
474 if(connected == false)
476 float &counter = m_connection_reinit_timer;
482 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
484 Player *myplayer = m_env.getLocalPlayer();
485 assert(myplayer != NULL);
487 // Send TOSERVER_INIT
488 // [0] u16 TOSERVER_INIT
489 // [2] u8 SER_FMT_VER_HIGHEST
490 // [3] u8[20] player_name
491 // [23] u8[28] password (new in some version)
492 // [51] u16 client network protocol version (new in some version)
493 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
494 writeU16(&data[0], TOSERVER_INIT);
495 writeU8(&data[2], SER_FMT_VER_HIGHEST);
497 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
498 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
500 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
503 memset((char*)&data[23], 0, PASSWORD_SIZE);
504 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
506 // This should be incremented in each version
507 writeU16(&data[51], PROTOCOL_VERSION);
509 // Send as unreliable
510 Send(0, data, false);
513 // Not connected, return
518 Do stuff if connected
522 Run Map's timers and unload unused data
524 const float map_timer_and_unload_dtime = 5.25;
525 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
527 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
528 core::list<v3s16> deleted_blocks;
529 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
530 g_settings->getFloat("client_unload_unused_data_timeout"),
533 /*if(deleted_blocks.size() > 0)
534 infostream<<"Client: Unloaded "<<deleted_blocks.size()
535 <<" unused blocks"<<std::endl;*/
539 NOTE: This loop is intentionally iterated the way it is.
542 core::list<v3s16>::Iterator i = deleted_blocks.begin();
543 core::list<v3s16> sendlist;
546 if(sendlist.size() == 255 || i == deleted_blocks.end())
548 if(sendlist.size() == 0)
557 u32 replysize = 2+1+6*sendlist.size();
558 SharedBuffer<u8> reply(replysize);
559 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
560 reply[2] = sendlist.size();
562 for(core::list<v3s16>::Iterator
563 j = sendlist.begin();
564 j != sendlist.end(); j++)
566 writeV3S16(&reply[2+1+6*k], *j);
569 m_con.Send(PEER_ID_SERVER, 1, reply, true);
571 if(i == deleted_blocks.end())
577 sendlist.push_back(*i);
587 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
589 // Control local player (0ms)
590 LocalPlayer *player = m_env.getLocalPlayer();
591 assert(player != NULL);
592 player->applyControl(dtime);
594 //TimeTaker envtimer("env step", m_device);
603 ClientEnvEvent event = m_env.getClientEvent();
604 if(event.type == CEE_NONE)
608 else if(event.type == CEE_PLAYER_DAMAGE)
610 if(m_ignore_damage_timer <= 0)
612 u8 damage = event.player_damage.amount;
614 if(event.player_damage.send_to_server)
617 // Add to ClientEvent queue
619 event.type = CE_PLAYER_DAMAGE;
620 event.player_damage.amount = damage;
621 m_client_event_queue.push_back(event);
631 float &counter = m_avg_rtt_timer;
636 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
637 // connectedAndInitialized() is true, peer exists.
638 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
639 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
644 Send player position to server
647 float &counter = m_playerpos_send_timer;
657 Replace updated meshes
660 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
662 //TimeTaker timer("** Processing mesh update result queue");
665 /*infostream<<"Mesh update result queue size is "
666 <<m_mesh_update_thread.m_queue_out.size()
669 int num_processed_meshes = 0;
670 while(m_mesh_update_thread.m_queue_out.size() > 0)
672 num_processed_meshes++;
673 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
674 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
677 //JMutexAutoLock lock(block->mesh_mutex);
679 // Delete the old mesh
680 if(block->mesh != NULL)
682 // TODO: Remove hardware buffers of meshbuffers of block->mesh
687 // Replace with the new mesh
688 block->mesh = r.mesh;
690 if(r.ack_block_to_server)
692 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
693 <<","<<r.p.Z<<")"<<std::endl;*/
704 u32 replysize = 2+1+6;
705 SharedBuffer<u8> reply(replysize);
706 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
708 writeV3S16(&reply[3], r.p);
710 m_con.Send(PEER_ID_SERVER, 1, reply, true);
713 if(num_processed_meshes > 0)
714 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
718 If the server didn't update the inventory in a while, revert
719 the local inventory (so the player notices the lag problem
720 and knows something is wrong).
722 if(m_inventory_from_server)
724 float interval = 10.0;
725 float count_before = floor(m_inventory_from_server_age / interval);
727 m_inventory_from_server_age += dtime;
729 float count_after = floor(m_inventory_from_server_age / interval);
731 if(count_after != count_before)
733 // Do this every <interval> seconds after TOCLIENT_INVENTORY
734 // Reset the locally changed inventory to the authoritative inventory
735 Player *player = m_env.getLocalPlayer();
736 player->inventory = *m_inventory_from_server;
737 m_inventory_updated = true;
742 Update positions of sounds attached to objects
745 for(std::map<int, u16>::iterator
746 i = m_sounds_to_objects.begin();
747 i != m_sounds_to_objects.end(); i++)
749 int client_id = i->first;
750 u16 object_id = i->second;
751 ClientActiveObject *cao = m_env.getActiveObject(object_id);
754 v3f pos = cao->getPosition();
755 m_sound->updateSoundPosition(client_id, pos);
760 Handle removed remotely initiated sounds
762 m_removed_sounds_check_timer += dtime;
763 if(m_removed_sounds_check_timer >= 2.32)
765 m_removed_sounds_check_timer = 0;
766 // Find removed sounds and clear references to them
767 std::set<s32> removed_server_ids;
768 for(std::map<s32, int>::iterator
769 i = m_sounds_server_to_client.begin();
770 i != m_sounds_server_to_client.end();)
772 s32 server_id = i->first;
773 int client_id = i->second;
775 if(!m_sound->soundExists(client_id)){
776 m_sounds_server_to_client.erase(server_id);
777 m_sounds_client_to_server.erase(client_id);
778 m_sounds_to_objects.erase(client_id);
779 removed_server_ids.insert(server_id);
783 if(removed_server_ids.size() != 0)
785 std::ostringstream os(std::ios_base::binary);
786 writeU16(os, TOSERVER_REMOVED_SOUNDS);
787 writeU16(os, removed_server_ids.size());
788 for(std::set<s32>::iterator i = removed_server_ids.begin();
789 i != removed_server_ids.end(); i++)
791 std::string s = os.str();
792 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
799 bool Client::loadMedia(const std::string &data, const std::string &filename)
801 // Silly irrlicht's const-incorrectness
802 Buffer<char> data_rw(data.c_str(), data.size());
806 const char *image_ext[] = {
807 ".png", ".jpg", ".bmp", ".tga",
808 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
811 name = removeStringEnd(filename, image_ext);
814 verbosestream<<"Client: Attempting to load image "
815 <<"file \""<<filename<<"\""<<std::endl;
817 io::IFileSystem *irrfs = m_device->getFileSystem();
818 video::IVideoDriver *vdrv = m_device->getVideoDriver();
820 // Create an irrlicht memory file
821 io::IReadFile *rfile = irrfs->createMemoryReadFile(
822 *data_rw, data_rw.getSize(), "_tempreadfile");
825 video::IImage *img = vdrv->createImageFromFile(rfile);
827 errorstream<<"Client: Cannot create image from data of "
828 <<"file \""<<filename<<"\""<<std::endl;
833 m_tsrc->insertSourceImage(filename, img);
840 const char *sound_ext[] = {
841 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
842 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
845 name = removeStringEnd(filename, sound_ext);
848 verbosestream<<"Client: Attempting to load sound "
849 <<"file \""<<filename<<"\""<<std::endl;
850 m_sound->loadSoundData(name, data);
854 errorstream<<"Client: Don't know how to load file \""
855 <<filename<<"\""<<std::endl;
859 // Virtual methods from con::PeerHandler
860 void Client::peerAdded(con::Peer *peer)
862 infostream<<"Client::peerAdded(): peer->id="
863 <<peer->id<<std::endl;
865 void Client::deletingPeer(con::Peer *peer, bool timeout)
867 infostream<<"Client::deletingPeer(): "
868 "Server Peer is getting deleted "
869 <<"(timeout="<<timeout<<")"<<std::endl;
872 void Client::ReceiveAll()
874 DSTACK(__FUNCTION_NAME);
875 u32 start_ms = porting::getTimeMs();
878 // Limit time even if there would be huge amounts of data to
880 if(porting::getTimeMs() > start_ms + 100)
885 g_profiler->graphAdd("client_received_packets", 1);
887 catch(con::NoIncomingDataException &e)
891 catch(con::InvalidIncomingDataException &e)
893 infostream<<"Client::ReceiveAll(): "
894 "InvalidIncomingDataException: what()="
895 <<e.what()<<std::endl;
900 void Client::Receive()
902 DSTACK(__FUNCTION_NAME);
903 SharedBuffer<u8> data;
907 //TimeTaker t1("con mutex and receive", m_device);
908 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
909 datasize = m_con.Receive(sender_peer_id, data);
911 //TimeTaker t1("ProcessData", m_device);
912 ProcessData(*data, datasize, sender_peer_id);
916 sender_peer_id given to this shall be quaranteed to be a valid peer
918 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
920 DSTACK(__FUNCTION_NAME);
922 // Ignore packets that don't even fit a command
925 m_packetcounter.add(60000);
929 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
931 //infostream<<"Client: received command="<<command<<std::endl;
932 m_packetcounter.add((u16)command);
935 If this check is removed, be sure to change the queue
936 system to know the ids
938 if(sender_peer_id != PEER_ID_SERVER)
940 infostream<<"Client::ProcessData(): Discarding data not "
941 "coming from server: peer_id="<<sender_peer_id
946 u8 ser_version = m_server_ser_ver;
948 //infostream<<"Client received command="<<(int)command<<std::endl;
950 if(command == TOCLIENT_INIT)
955 u8 deployed = data[2];
957 infostream<<"Client: TOCLIENT_INIT received with "
958 "deployed="<<((int)deployed&0xff)<<std::endl;
960 if(deployed < SER_FMT_VER_LOWEST
961 || deployed > SER_FMT_VER_HIGHEST)
963 infostream<<"Client: TOCLIENT_INIT: Server sent "
964 <<"unsupported ser_fmt_ver"<<std::endl;
968 m_server_ser_ver = deployed;
970 // Get player position
971 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
972 if(datasize >= 2+1+6)
973 playerpos_s16 = readV3S16(&data[2+1]);
974 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
977 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
979 // Set player position
980 Player *player = m_env.getLocalPlayer();
981 assert(player != NULL);
982 player->setPosition(playerpos_f);
985 if(datasize >= 2+1+6+8)
988 m_map_seed = readU64(&data[2+1+6]);
989 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
994 SharedBuffer<u8> reply(replysize);
995 writeU16(&reply[0], TOSERVER_INIT2);
997 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1002 if(command == TOCLIENT_ACCESS_DENIED)
1004 // The server didn't like our password. Note, this needs
1005 // to be processed even if the serialisation format has
1006 // not been agreed yet, the same as TOCLIENT_INIT.
1007 m_access_denied = true;
1008 m_access_denied_reason = L"Unknown";
1011 std::string datastring((char*)&data[2], datasize-2);
1012 std::istringstream is(datastring, std::ios_base::binary);
1013 m_access_denied_reason = deSerializeWideString(is);
1018 if(ser_version == SER_FMT_VER_INVALID)
1020 infostream<<"Client: Server serialization"
1021 " format invalid or not initialized."
1022 " Skipping incoming command="<<command<<std::endl;
1026 // Just here to avoid putting the two if's together when
1027 // making some copypasta
1030 if(command == TOCLIENT_REMOVENODE)
1035 p.X = readS16(&data[2]);
1036 p.Y = readS16(&data[4]);
1037 p.Z = readS16(&data[6]);
1039 //TimeTaker t1("TOCLIENT_REMOVENODE");
1043 else if(command == TOCLIENT_ADDNODE)
1045 if(datasize < 8 + MapNode::serializedLength(ser_version))
1049 p.X = readS16(&data[2]);
1050 p.Y = readS16(&data[4]);
1051 p.Z = readS16(&data[6]);
1053 //TimeTaker t1("TOCLIENT_ADDNODE");
1056 n.deSerialize(&data[8], ser_version);
1060 else if(command == TOCLIENT_BLOCKDATA)
1062 // Ignore too small packet
1067 p.X = readS16(&data[2]);
1068 p.Y = readS16(&data[4]);
1069 p.Z = readS16(&data[6]);
1071 /*infostream<<"Client: Thread: BLOCKDATA for ("
1072 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1073 /*infostream<<"Client: Thread: BLOCKDATA for ("
1074 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1076 std::string datastring((char*)&data[8], datasize-8);
1077 std::istringstream istr(datastring, std::ios_base::binary);
1082 v2s16 p2d(p.X, p.Z);
1083 sector = m_env.getMap().emergeSector(p2d);
1085 assert(sector->getPos() == p2d);
1087 //TimeTaker timer("MapBlock deSerialize");
1090 block = sector->getBlockNoCreateNoEx(p.Y);
1094 Update an existing block
1096 //infostream<<"Updating"<<std::endl;
1097 block->deSerialize(istr, ser_version, false);
1104 //infostream<<"Creating new"<<std::endl;
1105 block = new MapBlock(&m_env.getMap(), p, this);
1106 block->deSerialize(istr, ser_version, false);
1107 sector->insertBlock(block);
1121 u32 replysize = 2+1+6;
1122 SharedBuffer<u8> reply(replysize);
1123 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1125 writeV3S16(&reply[3], p);
1127 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1131 Add it to mesh update queue and set it to be acknowledged after update.
1133 //infostream<<"Adding mesh update task for received block"<<std::endl;
1134 addUpdateMeshTaskWithEdge(p, true);
1136 else if(command == TOCLIENT_INVENTORY)
1141 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1144 //TimeTaker t2("mutex locking", m_device);
1145 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1148 //TimeTaker t3("istringstream init", m_device);
1149 std::string datastring((char*)&data[2], datasize-2);
1150 std::istringstream is(datastring, std::ios_base::binary);
1153 //m_env.printPlayers(infostream);
1155 //TimeTaker t4("player get", m_device);
1156 Player *player = m_env.getLocalPlayer();
1157 assert(player != NULL);
1160 //TimeTaker t1("inventory.deSerialize()", m_device);
1161 player->inventory.deSerialize(is);
1164 m_inventory_updated = true;
1166 delete m_inventory_from_server;
1167 m_inventory_from_server = new Inventory(player->inventory);
1168 m_inventory_from_server_age = 0.0;
1170 //infostream<<"Client got player inventory:"<<std::endl;
1171 //player->inventory.print(infostream);
1174 else if(command == TOCLIENT_TIME_OF_DAY)
1179 u16 time_of_day = readU16(&data[2]);
1180 time_of_day = time_of_day % 24000;
1181 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1182 float time_speed = 0;
1183 if(datasize >= 2 + 2 + 4){
1184 time_speed = readF1000(&data[4]);
1186 // Old message; try to approximate speed of time by ourselves
1187 float time_of_day_f = (float)time_of_day / 24000.0;
1188 float tod_diff_f = 0;
1189 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1190 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1192 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1193 m_last_time_of_day_f = time_of_day_f;
1194 float time_diff = m_time_of_day_update_timer;
1195 m_time_of_day_update_timer = 0;
1196 if(m_time_of_day_set){
1197 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1198 infostream<<"Client: Measured time_of_day speed (old format): "
1199 <<time_speed<<" tod_diff_f="<<tod_diff_f
1200 <<" time_diff="<<time_diff<<std::endl;
1204 // Update environment
1205 m_env.setTimeOfDay(time_of_day);
1206 m_env.setTimeOfDaySpeed(time_speed);
1207 m_time_of_day_set = true;
1209 u32 dr = m_env.getDayNightRatio();
1210 verbosestream<<"Client: time_of_day="<<time_of_day
1211 <<" time_speed="<<time_speed
1212 <<" dr="<<dr<<std::endl;
1214 else if(command == TOCLIENT_CHAT_MESSAGE)
1222 std::string datastring((char*)&data[2], datasize-2);
1223 std::istringstream is(datastring, std::ios_base::binary);
1226 is.read((char*)buf, 2);
1227 u16 len = readU16(buf);
1229 std::wstring message;
1230 for(u16 i=0; i<len; i++)
1232 is.read((char*)buf, 2);
1233 message += (wchar_t)readU16(buf);
1236 /*infostream<<"Client received chat message: "
1237 <<wide_to_narrow(message)<<std::endl;*/
1239 m_chat_queue.push_back(message);
1241 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1243 //if(g_settings->getBool("enable_experimental"))
1247 u16 count of removed objects
1248 for all removed objects {
1251 u16 count of added objects
1252 for all added objects {
1255 u32 initialization data length
1256 string initialization data
1261 // Get all data except the command number
1262 std::string datastring((char*)&data[2], datasize-2);
1263 // Throw them in an istringstream
1264 std::istringstream is(datastring, std::ios_base::binary);
1268 // Read removed objects
1270 u16 removed_count = readU16((u8*)buf);
1271 for(u16 i=0; i<removed_count; i++)
1274 u16 id = readU16((u8*)buf);
1277 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1278 m_env.removeActiveObject(id);
1282 // Read added objects
1284 u16 added_count = readU16((u8*)buf);
1285 for(u16 i=0; i<added_count; i++)
1288 u16 id = readU16((u8*)buf);
1290 u8 type = readU8((u8*)buf);
1291 std::string data = deSerializeLongString(is);
1294 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1295 m_env.addActiveObject(id, type, data);
1300 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1302 //if(g_settings->getBool("enable_experimental"))
1314 // Get all data except the command number
1315 std::string datastring((char*)&data[2], datasize-2);
1316 // Throw them in an istringstream
1317 std::istringstream is(datastring, std::ios_base::binary);
1319 while(is.eof() == false)
1323 u16 id = readU16((u8*)buf);
1327 u16 message_size = readU16((u8*)buf);
1328 std::string message;
1329 message.reserve(message_size);
1330 for(u16 i=0; i<message_size; i++)
1333 message.append(buf, 1);
1335 // Pass on to the environment
1337 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1338 m_env.processActiveObjectMessage(id, message);
1343 else if(command == TOCLIENT_HP)
1345 std::string datastring((char*)&data[2], datasize-2);
1346 std::istringstream is(datastring, std::ios_base::binary);
1347 Player *player = m_env.getLocalPlayer();
1348 assert(player != NULL);
1349 u8 oldhp = player->hp;
1355 // Add to ClientEvent queue
1357 event.type = CE_PLAYER_DAMAGE;
1358 event.player_damage.amount = oldhp - hp;
1359 m_client_event_queue.push_back(event);
1362 else if(command == TOCLIENT_MOVE_PLAYER)
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);
1368 v3f pos = readV3F1000(is);
1369 f32 pitch = readF1000(is);
1370 f32 yaw = readF1000(is);
1371 player->setPosition(pos);
1372 /*player->setPitch(pitch);
1373 player->setYaw(yaw);*/
1375 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1376 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1382 Add to ClientEvent queue.
1383 This has to be sent to the main program because otherwise
1384 it would just force the pitch and yaw values to whatever
1385 the camera points to.
1388 event.type = CE_PLAYER_FORCE_MOVE;
1389 event.player_force_move.pitch = pitch;
1390 event.player_force_move.yaw = yaw;
1391 m_client_event_queue.push_back(event);
1393 // Ignore damage for a few seconds, so that the player doesn't
1394 // get damage from falling on ground
1395 m_ignore_damage_timer = 3.0;
1397 else if(command == TOCLIENT_PLAYERITEM)
1399 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1401 else if(command == TOCLIENT_DEATHSCREEN)
1403 std::string datastring((char*)&data[2], datasize-2);
1404 std::istringstream is(datastring, std::ios_base::binary);
1406 bool set_camera_point_target = readU8(is);
1407 v3f camera_point_target = readV3F1000(is);
1410 event.type = CE_DEATHSCREEN;
1411 event.deathscreen.set_camera_point_target = set_camera_point_target;
1412 event.deathscreen.camera_point_target_x = camera_point_target.X;
1413 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1414 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1415 m_client_event_queue.push_back(event);
1417 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1419 std::string datastring((char*)&data[2], datasize-2);
1420 std::istringstream is(datastring, std::ios_base::binary);
1422 // Mesh update thread must be stopped while
1423 // updating content definitions
1424 assert(!m_mesh_update_thread.IsRunning());
1426 int num_files = readU16(is);
1428 verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
1429 <<num_files<<" files)"<<std::endl;
1431 core::list<MediaRequest> file_requests;
1433 for(int i=0; i<num_files; i++)
1435 //read file from cache
1436 std::string name = deSerializeString(is);
1437 std::string sha1_base64 = deSerializeString(is);
1439 // if name contains illegal characters, ignore the file
1440 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1441 errorstream<<"Client: ignoring illegal file name "
1442 <<"sent by server: \""<<name<<"\""<<std::endl;
1446 std::string sha1_raw = base64_decode(sha1_base64);
1447 std::string sha1_hex = hex_encode(sha1_raw);
1448 std::ostringstream tmp_os(std::ios_base::binary);
1449 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1450 m_media_name_sha1_map.set(name, sha1_raw);
1452 // If found in cache, try to load it from there
1455 bool success = loadMedia(tmp_os.str(), name);
1457 verbosestream<<"Client: Loaded cached media: "
1458 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1461 infostream<<"Client: Failed to load cached media: "
1462 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1465 // Didn't load from cache; queue it to be requested
1466 verbosestream<<"Client: Adding file to request list: \""
1467 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1468 file_requests.push_back(MediaRequest(name));
1472 event.type = CE_TEXTURES_UPDATED;
1473 m_client_event_queue.push_back(event);
1477 u16 number of files requested
1483 std::ostringstream os(std::ios_base::binary);
1484 writeU16(os, TOSERVER_REQUEST_MEDIA);
1485 writeU16(os, file_requests.size());
1487 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1488 i != file_requests.end(); i++) {
1489 os<<serializeString(i->name);
1493 std::string s = os.str();
1494 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1496 Send(0, data, true);
1497 infostream<<"Client: Sending media request list to server ("
1498 <<file_requests.size()<<" files)"<<std::endl;
1500 else if(command == TOCLIENT_MEDIA)
1502 std::string datastring((char*)&data[2], datasize-2);
1503 std::istringstream is(datastring, std::ios_base::binary);
1505 // Mesh update thread must be stopped while
1506 // updating content definitions
1507 assert(!m_mesh_update_thread.IsRunning());
1511 u16 total number of file bunches
1512 u16 index of this bunch
1513 u32 number of files in this bunch
1521 int num_bunches = readU16(is);
1522 int bunch_i = readU16(is);
1523 if(num_bunches >= 2)
1524 m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1526 m_media_receive_progress = 1.0;
1527 if(bunch_i == num_bunches - 1)
1528 m_media_received = true;
1529 int num_files = readU32(is);
1530 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1531 <<num_bunches<<" files="<<num_files
1532 <<" size="<<datasize<<std::endl;
1533 for(int i=0; i<num_files; i++){
1534 std::string name = deSerializeString(is);
1535 std::string data = deSerializeLongString(is);
1537 // if name contains illegal characters, ignore the file
1538 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1539 errorstream<<"Client: ignoring illegal file name "
1540 <<"sent by server: \""<<name<<"\""<<std::endl;
1544 bool success = loadMedia(data, name);
1546 verbosestream<<"Client: Loaded received media: "
1547 <<"\""<<name<<"\". Caching."<<std::endl;
1549 infostream<<"Client: Failed to load received media: "
1550 <<"\""<<name<<"\". Not caching."<<std::endl;
1554 bool did = fs::CreateAllDirs(getMediaCacheDir());
1556 errorstream<<"Could not create media cache directory"
1561 core::map<std::string, std::string>::Node *n;
1562 n = m_media_name_sha1_map.find(name);
1564 errorstream<<"The server sent a file that has not "
1565 <<"been announced."<<std::endl;
1567 m_media_cache.update_sha1(data);
1572 event.type = CE_TEXTURES_UPDATED;
1573 m_client_event_queue.push_back(event);
1575 else if(command == TOCLIENT_TOOLDEF)
1577 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1579 else if(command == TOCLIENT_NODEDEF)
1581 infostream<<"Client: Received node definitions: packet size: "
1582 <<datasize<<std::endl;
1584 // Mesh update thread must be stopped while
1585 // updating content definitions
1586 assert(!m_mesh_update_thread.IsRunning());
1588 // Decompress node definitions
1589 std::string datastring((char*)&data[2], datasize-2);
1590 std::istringstream is(datastring, std::ios_base::binary);
1591 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1592 std::ostringstream tmp_os;
1593 decompressZlib(tmp_is, tmp_os);
1595 // Deserialize node definitions
1596 std::istringstream tmp_is2(tmp_os.str());
1597 m_nodedef->deSerialize(tmp_is2);
1598 m_nodedef_received = true;
1600 else if(command == TOCLIENT_CRAFTITEMDEF)
1602 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1604 else if(command == TOCLIENT_ITEMDEF)
1606 infostream<<"Client: Received item definitions: packet size: "
1607 <<datasize<<std::endl;
1609 // Mesh update thread must be stopped while
1610 // updating content definitions
1611 assert(!m_mesh_update_thread.IsRunning());
1613 // Decompress item definitions
1614 std::string datastring((char*)&data[2], datasize-2);
1615 std::istringstream is(datastring, std::ios_base::binary);
1616 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1617 std::ostringstream tmp_os;
1618 decompressZlib(tmp_is, tmp_os);
1620 // Deserialize node definitions
1621 std::istringstream tmp_is2(tmp_os.str());
1622 m_itemdef->deSerialize(tmp_is2);
1623 m_itemdef_received = true;
1625 else if(command == TOCLIENT_PLAY_SOUND)
1627 std::string datastring((char*)&data[2], datasize-2);
1628 std::istringstream is(datastring, std::ios_base::binary);
1630 s32 server_id = readS32(is);
1631 std::string name = deSerializeString(is);
1632 float gain = readF1000(is);
1633 int type = readU8(is); // 0=local, 1=positional, 2=object
1634 v3f pos = readV3F1000(is);
1635 u16 object_id = readU16(is);
1636 bool loop = readU8(is);
1641 client_id = m_sound->playSound(name, loop, gain);
1643 case 1: // positional
1644 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1647 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1649 pos = cao->getPosition();
1650 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1651 // TODO: Set up sound to move with object
1656 if(client_id != -1){
1657 m_sounds_server_to_client[server_id] = client_id;
1658 m_sounds_client_to_server[client_id] = server_id;
1660 m_sounds_to_objects[client_id] = object_id;
1663 else if(command == TOCLIENT_STOP_SOUND)
1665 std::string datastring((char*)&data[2], datasize-2);
1666 std::istringstream is(datastring, std::ios_base::binary);
1668 s32 server_id = readS32(is);
1669 std::map<s32, int>::iterator i =
1670 m_sounds_server_to_client.find(server_id);
1671 if(i != m_sounds_server_to_client.end()){
1672 int client_id = i->second;
1673 m_sound->stopSound(client_id);
1676 else if(command == TOCLIENT_PRIVILEGES)
1678 std::string datastring((char*)&data[2], datasize-2);
1679 std::istringstream is(datastring, std::ios_base::binary);
1681 m_privileges.clear();
1682 infostream<<"Client: Privileges updated: ";
1683 u16 num_privileges = readU16(is);
1684 for(u16 i=0; i<num_privileges; i++){
1685 std::string priv = deSerializeString(is);
1686 m_privileges.insert(priv);
1687 infostream<<priv<<" ";
1689 infostream<<std::endl;
1691 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1693 std::string datastring((char*)&data[2], datasize-2);
1694 std::istringstream is(datastring, std::ios_base::binary);
1696 // Store formspec in LocalPlayer
1697 Player *player = m_env.getLocalPlayer();
1698 assert(player != NULL);
1699 player->inventory_formspec = deSerializeLongString(is);
1703 infostream<<"Client: Ignoring unknown command "
1704 <<command<<std::endl;
1708 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1710 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1711 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1714 void Client::interact(u8 action, const PointedThing& pointed)
1716 if(connectedAndInitialized() == false){
1717 infostream<<"Client::interact() "
1718 "cancelled (not connected)"
1723 std::ostringstream os(std::ios_base::binary);
1729 [5] u32 length of the next item
1730 [9] serialized PointedThing
1732 0: start digging (from undersurface) or use
1733 1: stop digging (all parameters ignored)
1734 2: digging completed
1735 3: place block or item (to abovesurface)
1738 writeU16(os, TOSERVER_INTERACT);
1739 writeU8(os, action);
1740 writeU16(os, getPlayerItem());
1741 std::ostringstream tmp_os(std::ios::binary);
1742 pointed.serialize(tmp_os);
1743 os<<serializeLongString(tmp_os.str());
1745 std::string s = os.str();
1746 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1749 Send(0, data, true);
1752 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1753 const std::map<std::string, std::string> &fields)
1755 std::ostringstream os(std::ios_base::binary);
1757 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1759 os<<serializeString(formname);
1760 writeU16(os, fields.size());
1761 for(std::map<std::string, std::string>::const_iterator
1762 i = fields.begin(); i != fields.end(); i++){
1763 const std::string &name = i->first;
1764 const std::string &value = i->second;
1765 os<<serializeString(name);
1766 os<<serializeLongString(value);
1770 std::string s = os.str();
1771 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1773 Send(0, data, true);
1776 void Client::sendInventoryFields(const std::string &formname,
1777 const std::map<std::string, std::string> &fields)
1779 std::ostringstream os(std::ios_base::binary);
1781 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1782 os<<serializeString(formname);
1783 writeU16(os, fields.size());
1784 for(std::map<std::string, std::string>::const_iterator
1785 i = fields.begin(); i != fields.end(); i++){
1786 const std::string &name = i->first;
1787 const std::string &value = i->second;
1788 os<<serializeString(name);
1789 os<<serializeLongString(value);
1793 std::string s = os.str();
1794 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1796 Send(0, data, true);
1799 void Client::sendInventoryAction(InventoryAction *a)
1801 std::ostringstream os(std::ios_base::binary);
1805 writeU16(buf, TOSERVER_INVENTORY_ACTION);
1806 os.write((char*)buf, 2);
1811 std::string s = os.str();
1812 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1814 Send(0, data, true);
1817 void Client::sendChatMessage(const std::wstring &message)
1819 std::ostringstream os(std::ios_base::binary);
1823 writeU16(buf, TOSERVER_CHAT_MESSAGE);
1824 os.write((char*)buf, 2);
1827 writeU16(buf, message.size());
1828 os.write((char*)buf, 2);
1831 for(u32 i=0; i<message.size(); i++)
1835 os.write((char*)buf, 2);
1839 std::string s = os.str();
1840 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1842 Send(0, data, true);
1845 void Client::sendChangePassword(const std::wstring oldpassword,
1846 const std::wstring newpassword)
1848 Player *player = m_env.getLocalPlayer();
1852 std::string playername = player->getName();
1853 std::string oldpwd = translatePassword(playername, oldpassword);
1854 std::string newpwd = translatePassword(playername, newpassword);
1856 std::ostringstream os(std::ios_base::binary);
1857 u8 buf[2+PASSWORD_SIZE*2];
1859 [0] u16 TOSERVER_PASSWORD
1860 [2] u8[28] old password
1861 [30] u8[28] new password
1864 writeU16(buf, TOSERVER_PASSWORD);
1865 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1867 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1868 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1870 buf[2+PASSWORD_SIZE-1] = 0;
1871 buf[30+PASSWORD_SIZE-1] = 0;
1872 os.write((char*)buf, 2+PASSWORD_SIZE*2);
1875 std::string s = os.str();
1876 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1878 Send(0, data, true);
1882 void Client::sendDamage(u8 damage)
1884 DSTACK(__FUNCTION_NAME);
1885 std::ostringstream os(std::ios_base::binary);
1887 writeU16(os, TOSERVER_DAMAGE);
1888 writeU8(os, damage);
1891 std::string s = os.str();
1892 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1894 Send(0, data, true);
1897 void Client::sendRespawn()
1899 DSTACK(__FUNCTION_NAME);
1900 std::ostringstream os(std::ios_base::binary);
1902 writeU16(os, TOSERVER_RESPAWN);
1905 std::string s = os.str();
1906 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1908 Send(0, data, true);
1911 void Client::sendPlayerPos()
1913 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1915 Player *myplayer = m_env.getLocalPlayer();
1916 if(myplayer == NULL)
1921 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1922 our_peer_id = m_con.GetPeerID();
1925 // Set peer id if not set already
1926 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1927 myplayer->peer_id = our_peer_id;
1928 // Check that an existing peer_id is the same as the connection's
1929 assert(myplayer->peer_id == our_peer_id);
1931 v3f pf = myplayer->getPosition();
1932 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1933 v3f sf = myplayer->getSpeed();
1934 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1935 s32 pitch = myplayer->getPitch() * 100;
1936 s32 yaw = myplayer->getYaw() * 100;
1941 [2] v3s32 position*100
1942 [2+12] v3s32 speed*100
1943 [2+12+12] s32 pitch*100
1944 [2+12+12+4] s32 yaw*100
1947 SharedBuffer<u8> data(2+12+12+4+4);
1948 writeU16(&data[0], TOSERVER_PLAYERPOS);
1949 writeV3S32(&data[2], position);
1950 writeV3S32(&data[2+12], speed);
1951 writeS32(&data[2+12+12], pitch);
1952 writeS32(&data[2+12+12+4], yaw);
1954 // Send as unreliable
1955 Send(0, data, false);
1958 void Client::sendPlayerItem(u16 item)
1960 Player *myplayer = m_env.getLocalPlayer();
1961 if(myplayer == NULL)
1964 u16 our_peer_id = m_con.GetPeerID();
1966 // Set peer id if not set already
1967 if(myplayer->peer_id == PEER_ID_INEXISTENT)
1968 myplayer->peer_id = our_peer_id;
1969 // Check that an existing peer_id is the same as the connection's
1970 assert(myplayer->peer_id == our_peer_id);
1972 SharedBuffer<u8> data(2+2);
1973 writeU16(&data[0], TOSERVER_PLAYERITEM);
1974 writeU16(&data[2], item);
1977 Send(0, data, true);
1980 void Client::removeNode(v3s16 p)
1982 core::map<v3s16, MapBlock*> modified_blocks;
1986 //TimeTaker t("removeNodeAndUpdate", m_device);
1987 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1989 catch(InvalidPositionException &e)
1993 // add urgent task to update the modified node
1994 addUpdateMeshTaskForNode(p, false, true);
1996 for(core::map<v3s16, MapBlock * >::Iterator
1997 i = modified_blocks.getIterator();
1998 i.atEnd() == false; i++)
2000 v3s16 p = i.getNode()->getKey();
2001 addUpdateMeshTaskWithEdge(p);
2005 void Client::addNode(v3s16 p, MapNode n)
2007 TimeTaker timer1("Client::addNode()");
2009 core::map<v3s16, MapBlock*> modified_blocks;
2013 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2014 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2016 catch(InvalidPositionException &e)
2019 for(core::map<v3s16, MapBlock * >::Iterator
2020 i = modified_blocks.getIterator();
2021 i.atEnd() == false; i++)
2023 v3s16 p = i.getNode()->getKey();
2024 addUpdateMeshTaskWithEdge(p);
2028 void Client::setPlayerControl(PlayerControl &control)
2030 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2031 LocalPlayer *player = m_env.getLocalPlayer();
2032 assert(player != NULL);
2033 player->control = control;
2036 void Client::selectPlayerItem(u16 item)
2038 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2039 m_playeritem = item;
2040 m_inventory_updated = true;
2041 sendPlayerItem(item);
2044 // Returns true if the inventory of the local player has been
2045 // updated from the server. If it is true, it is set to false.
2046 bool Client::getLocalInventoryUpdated()
2048 // m_inventory_updated is behind envlock
2049 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2050 bool updated = m_inventory_updated;
2051 m_inventory_updated = false;
2055 // Copies the inventory of the local player to parameter
2056 void Client::getLocalInventory(Inventory &dst)
2058 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2059 Player *player = m_env.getLocalPlayer();
2060 assert(player != NULL);
2061 dst = player->inventory;
2064 Inventory* Client::getInventory(const InventoryLocation &loc)
2067 case InventoryLocation::UNDEFINED:
2070 case InventoryLocation::CURRENT_PLAYER:
2072 Player *player = m_env.getLocalPlayer();
2073 assert(player != NULL);
2074 return &player->inventory;
2077 case InventoryLocation::PLAYER:
2079 Player *player = m_env.getPlayer(loc.name.c_str());
2082 return &player->inventory;
2085 case InventoryLocation::NODEMETA:
2087 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2090 return meta->getInventory();
2098 void Client::inventoryAction(InventoryAction *a)
2101 Send it to the server
2103 sendInventoryAction(a);
2106 Predict some local inventory changes
2108 a->clientApply(this, this);
2111 ClientActiveObject * Client::getSelectedActiveObject(
2113 v3f from_pos_f_on_map,
2114 core::line3d<f32> shootline_on_map
2117 core::array<DistanceSortedActiveObject> objects;
2119 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2121 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2124 // After this, the closest object is the first in the array.
2127 for(u32 i=0; i<objects.size(); i++)
2129 ClientActiveObject *obj = objects[i].obj;
2131 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2132 if(selection_box == NULL)
2135 v3f pos = obj->getPosition();
2137 core::aabbox3d<f32> offsetted_box(
2138 selection_box->MinEdge + pos,
2139 selection_box->MaxEdge + pos
2142 if(offsetted_box.intersectsWithLine(shootline_on_map))
2144 //infostream<<"Returning selected object"<<std::endl;
2149 //infostream<<"No object selected; returning NULL."<<std::endl;
2153 void Client::printDebugInfo(std::ostream &os)
2155 //JMutexAutoLock lock1(m_fetchblock_mutex);
2156 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2158 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2159 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2160 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2164 core::list<std::wstring> Client::getConnectedPlayerNames()
2166 core::list<Player*> players = m_env.getPlayers(true);
2167 core::list<std::wstring> playerNames;
2168 for(core::list<Player*>::Iterator
2169 i = players.begin();
2170 i != players.end(); i++)
2172 Player *player = *i;
2173 playerNames.push_back(narrow_to_wide(player->getName()));
2178 float Client::getAnimationTime()
2180 return m_animation_time;
2183 int Client::getCrackLevel()
2185 return m_crack_level;
2188 void Client::setCrack(int level, v3s16 pos)
2190 int old_crack_level = m_crack_level;
2191 v3s16 old_crack_pos = m_crack_pos;
2193 m_crack_level = level;
2196 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2199 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2201 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2204 addUpdateMeshTaskForNode(pos, false, true);
2210 Player *player = m_env.getLocalPlayer();
2211 assert(player != NULL);
2215 bool Client::getChatMessage(std::wstring &message)
2217 if(m_chat_queue.size() == 0)
2219 message = m_chat_queue.pop_front();
2223 void Client::typeChatMessage(const std::wstring &message)
2225 // Discard empty line
2230 sendChatMessage(message);
2233 if (message[0] == L'/')
2235 m_chat_queue.push_back(
2236 (std::wstring)L"issued command: "+message);
2240 LocalPlayer *player = m_env.getLocalPlayer();
2241 assert(player != NULL);
2242 std::wstring name = narrow_to_wide(player->getName());
2243 m_chat_queue.push_back(
2244 (std::wstring)L"<"+name+L"> "+message);
2248 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2250 /*infostream<<"Client::addUpdateMeshTask(): "
2251 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2252 <<" ack_to_server="<<ack_to_server
2253 <<" urgent="<<urgent
2256 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2261 Create a task to update the mesh of the block
2264 MeshMakeData *data = new MeshMakeData(this);
2267 //TimeTaker timer("data fill");
2269 // Debug: 1-6ms, avg=2ms
2271 data->setCrack(m_crack_level, m_crack_pos);
2272 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2276 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2278 // Add task to queue
2279 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2281 /*infostream<<"Mesh update input queue size is "
2282 <<m_mesh_update_thread.m_queue_in.size()
2286 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2290 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2291 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2296 v3s16 p = blockpos + v3s16(0,0,0);
2297 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2298 addUpdateMeshTask(p, ack_to_server, urgent);
2300 catch(InvalidPositionException &e){}
2303 v3s16 p = blockpos + v3s16(-1,0,0);
2304 addUpdateMeshTask(p, false, urgent);
2306 catch(InvalidPositionException &e){}
2308 v3s16 p = blockpos + v3s16(0,-1,0);
2309 addUpdateMeshTask(p, false, urgent);
2311 catch(InvalidPositionException &e){}
2313 v3s16 p = blockpos + v3s16(0,0,-1);
2314 addUpdateMeshTask(p, false, urgent);
2316 catch(InvalidPositionException &e){}
2319 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2323 infostream<<"Client::addUpdateMeshTaskForNode(): "
2324 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2328 v3s16 blockpos = getNodeBlockPos(nodepos);
2329 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2332 v3s16 p = blockpos + v3s16(0,0,0);
2333 addUpdateMeshTask(p, ack_to_server, urgent);
2335 catch(InvalidPositionException &e){}
2337 if(nodepos.X == blockpos_relative.X){
2339 v3s16 p = blockpos + v3s16(-1,0,0);
2340 addUpdateMeshTask(p, false, urgent);
2342 catch(InvalidPositionException &e){}
2344 if(nodepos.Y == blockpos_relative.Y){
2346 v3s16 p = blockpos + v3s16(0,-1,0);
2347 addUpdateMeshTask(p, false, urgent);
2349 catch(InvalidPositionException &e){}
2351 if(nodepos.Z == blockpos_relative.Z){
2353 v3s16 p = blockpos + v3s16(0,0,-1);
2354 addUpdateMeshTask(p, false, urgent);
2356 catch(InvalidPositionException &e){}
2360 ClientEvent Client::getClientEvent()
2362 if(m_client_event_queue.size() == 0)
2365 event.type = CE_NONE;
2368 return m_client_event_queue.pop_front();
2371 void Client::afterContentReceived()
2373 verbosestream<<"Client::afterContentReceived() started"<<std::endl;
2374 assert(m_itemdef_received);
2375 assert(m_nodedef_received);
2376 assert(m_media_received);
2378 // remove the information about which checksum each texture
2380 m_media_name_sha1_map.clear();
2382 // Rebuild inherited images and recreate textures
2383 verbosestream<<"Rebuilding images and textures"<<std::endl;
2384 m_tsrc->rebuildImagesAndTextures();
2386 // Update texture atlas
2387 verbosestream<<"Updating texture atlas"<<std::endl;
2388 if(g_settings->getBool("enable_texture_atlas"))
2389 m_tsrc->buildMainAtlas(this);
2391 // Update node aliases
2392 verbosestream<<"Updating node aliases"<<std::endl;
2393 m_nodedef->updateAliases(m_itemdef);
2395 // Update node textures
2396 verbosestream<<"Updating node textures"<<std::endl;
2397 m_nodedef->updateTextures(m_tsrc);
2399 // Update item textures and meshes
2400 verbosestream<<"Updating item textures and meshes"<<std::endl;
2401 m_itemdef->updateTexturesAndMeshes(this);
2403 // Start mesh update thread after setting up content definitions
2404 verbosestream<<"Starting mesh update thread"<<std::endl;
2405 m_mesh_update_thread.Start();
2407 verbosestream<<"Client::afterContentReceived() done"<<std::endl;
2410 float Client::getRTT(void)
2413 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2414 } catch(con::PeerNotFoundException &e){
2419 // IGameDef interface
2421 IItemDefManager* Client::getItemDefManager()
2425 INodeDefManager* Client::getNodeDefManager()
2429 ICraftDefManager* Client::getCraftDefManager()
2432 //return m_craftdef;
2434 ITextureSource* Client::getTextureSource()
2438 u16 Client::allocateUnknownNodeId(const std::string &name)
2440 errorstream<<"Client::allocateUnknownNodeId(): "
2441 <<"Client cannot allocate node IDs"<<std::endl;
2443 return CONTENT_IGNORE;
2445 ISoundManager* Client::getSoundManager()
2449 MtEventManager* Client::getEventManager()