3 Copyright (C) 2010-2013 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.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
33 #include "serverobject.h"
37 #include "script/cpp_api/scriptapi.h"
44 #include "content_mapnode.h"
45 #include "content_nodemeta.h"
46 #include "content_abm.h"
47 #include "content_sao.h"
52 #include "sound.h" // dummySoundManager
53 #include "event_manager.h"
55 #include "serverlist.h"
56 #include "util/string.h"
57 #include "util/pointedthing.h"
58 #include "util/mathconstants.h"
60 #include "util/serialize.h"
61 #include "defaultsettings.h"
63 void * ServerThread::Thread()
67 log_register_thread("ServerThread");
69 DSTACK(__FUNCTION_NAME);
71 BEGIN_DEBUG_EXCEPTION_HANDLER
76 //TimeTaker timer("AsyncRunStep() + Receive()");
79 //TimeTaker timer("AsyncRunStep()");
80 m_server->AsyncRunStep();
83 //infostream<<"Running m_server->Receive()"<<std::endl;
86 catch(con::NoIncomingDataException &e)
89 catch(con::PeerNotFoundException &e)
91 infostream<<"Server: PeerNotFoundException"<<std::endl;
93 catch(con::ConnectionBindFailed &e)
95 m_server->setAsyncFatalError(e.what());
99 m_server->setAsyncFatalError(e.what());
103 END_DEBUG_EXCEPTION_HANDLER(errorstream)
108 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
110 if(pos_exists) *pos_exists = false;
115 if(pos_exists) *pos_exists = true;
120 ServerActiveObject *sao = env->getActiveObject(object);
123 if(pos_exists) *pos_exists = true;
124 return sao->getBasePosition(); }
129 void RemoteClient::GetNextBlocks(Server *server, float dtime,
130 std::vector<PrioritySortedBlockTransfer> &dest)
132 DSTACK(__FUNCTION_NAME);
135 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
138 m_nothing_to_send_pause_timer -= dtime;
139 m_nearest_unsent_reset_timer += dtime;
141 if(m_nothing_to_send_pause_timer >= 0)
144 Player *player = server->m_env->getPlayer(peer_id);
145 // This can happen sometimes; clients and players are not in perfect sync.
149 // Won't send anything if already sending
150 if(m_blocks_sending.size() >= g_settings->getU16
151 ("max_simultaneous_block_sends_per_client"))
153 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
157 //TimeTaker timer("RemoteClient::GetNextBlocks");
159 v3f playerpos = player->getPosition();
160 v3f playerspeed = player->getSpeed();
161 v3f playerspeeddir(0,0,0);
162 if(playerspeed.getLength() > 1.0*BS)
163 playerspeeddir = playerspeed / playerspeed.getLength();
164 // Predict to next block
165 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
167 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
169 v3s16 center = getNodeBlockPos(center_nodepos);
171 // Camera position and direction
172 v3f camera_pos = player->getEyePosition();
173 v3f camera_dir = v3f(0,0,1);
174 camera_dir.rotateYZBy(player->getPitch());
175 camera_dir.rotateXZBy(player->getYaw());
177 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
178 <<camera_dir.Z<<")"<<std::endl;*/
181 Get the starting value of the block finder radius.
184 if(m_last_center != center)
186 m_nearest_unsent_d = 0;
187 m_last_center = center;
190 /*infostream<<"m_nearest_unsent_reset_timer="
191 <<m_nearest_unsent_reset_timer<<std::endl;*/
193 // Reset periodically to workaround for some bugs or stuff
194 if(m_nearest_unsent_reset_timer > 20.0)
196 m_nearest_unsent_reset_timer = 0;
197 m_nearest_unsent_d = 0;
198 //infostream<<"Resetting m_nearest_unsent_d for "
199 // <<server->getPlayerName(peer_id)<<std::endl;
202 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
203 s16 d_start = m_nearest_unsent_d;
205 //infostream<<"d_start="<<d_start<<std::endl;
207 u16 max_simul_sends_setting = g_settings->getU16
208 ("max_simultaneous_block_sends_per_client");
209 u16 max_simul_sends_usually = max_simul_sends_setting;
212 Check the time from last addNode/removeNode.
214 Decrease send rate if player is building stuff.
216 m_time_from_building += dtime;
217 if(m_time_from_building < g_settings->getFloat(
218 "full_block_send_enable_min_time_from_building"))
220 max_simul_sends_usually
221 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
225 Number of blocks sending + number of blocks selected for sending
227 u32 num_blocks_selected = m_blocks_sending.size();
230 next time d will be continued from the d from which the nearest
231 unsent block was found this time.
233 This is because not necessarily any of the blocks found this
234 time are actually sent.
236 s32 new_nearest_unsent_d = -1;
238 s16 d_max = g_settings->getS16("max_block_send_distance");
239 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
241 // Don't loop very much at a time
242 s16 max_d_increment_at_time = 2;
243 if(d_max > d_start + max_d_increment_at_time)
244 d_max = d_start + max_d_increment_at_time;
245 /*if(d_max_gen > d_start+2)
246 d_max_gen = d_start+2;*/
248 //infostream<<"Starting from "<<d_start<<std::endl;
250 s32 nearest_emerged_d = -1;
251 s32 nearest_emergefull_d = -1;
252 s32 nearest_sent_d = -1;
253 bool queue_is_full = false;
256 for(d = d_start; d <= d_max; d++)
258 /*errorstream<<"checking d="<<d<<" for "
259 <<server->getPlayerName(peer_id)<<std::endl;*/
260 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
263 If m_nearest_unsent_d was changed by the EmergeThread
264 (it can change it to 0 through SetBlockNotSent),
266 Else update m_nearest_unsent_d
268 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
270 d = m_nearest_unsent_d;
271 last_nearest_unsent_d = m_nearest_unsent_d;
275 Get the border/face dot coordinates of a "d-radiused"
278 std::list<v3s16> list;
279 getFacePositions(list, d);
281 std::list<v3s16>::iterator li;
282 for(li=list.begin(); li!=list.end(); ++li)
284 v3s16 p = *li + center;
288 - Don't allow too many simultaneous transfers
289 - EXCEPT when the blocks are very close
291 Also, don't send blocks that are already flying.
294 // Start with the usual maximum
295 u16 max_simul_dynamic = max_simul_sends_usually;
297 // If block is very close, allow full maximum
298 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
299 max_simul_dynamic = max_simul_sends_setting;
301 // Don't select too many blocks for sending
302 if(num_blocks_selected >= max_simul_dynamic)
304 queue_is_full = true;
305 goto queue_full_break;
308 // Don't send blocks that are currently being transferred
309 if(m_blocks_sending.find(p) != m_blocks_sending.end())
315 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
316 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
317 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
318 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
319 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
320 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
323 // If this is true, inexistent block will be made from scratch
324 bool generate = d <= d_max_gen;
327 /*// Limit the generating area vertically to 2/3
328 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
331 // Limit the send area vertically to 1/2
332 if(abs(p.Y - center.Y) > d_max / 2)
338 If block is far away, don't generate it unless it is
344 // Block center y in nodes
345 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
346 // Don't generate if it's very high or very low
347 if(y < -64 || y > 64)
351 v2s16 p2d_nodes_center(
355 // Get ground height in nodes
356 s16 gh = server->m_env->getServerMap().findGroundLevel(
359 // If differs a lot, don't generate
360 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
362 // Actually, don't even send it
368 //infostream<<"d="<<d<<std::endl;
371 Don't generate or send if not in sight
372 FIXME This only works if the client uses a small enough
373 FOV setting. The default of 72 degrees is fine.
376 float camera_fov = (72.0*M_PI/180) * 4./3.;
377 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
383 Don't send already sent blocks
386 if(m_blocks_sent.find(p) != m_blocks_sent.end())
393 Check if map has this block
395 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
397 bool surely_not_found_on_disk = false;
398 bool block_is_invalid = false;
401 // Reset usage timer, this block will be of use in the future.
402 block->resetUsageTimer();
404 // Block is dummy if data doesn't exist.
405 // It means it has been not found from disk and not generated
408 surely_not_found_on_disk = true;
411 // Block is valid if lighting is up-to-date and data exists
412 if(block->isValid() == false)
414 block_is_invalid = true;
417 /*if(block->isFullyGenerated() == false)
419 block_is_invalid = true;
424 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
425 v2s16 chunkpos = map->sector_to_chunk(p2d);
426 if(map->chunkNonVolatile(chunkpos) == false)
427 block_is_invalid = true;
429 if(block->isGenerated() == false)
430 block_is_invalid = true;
433 If block is not close, don't send it unless it is near
436 Block is near ground level if night-time mesh
437 differs from day-time mesh.
441 if(block->getDayNightDiff() == false)
448 If block has been marked to not exist on disk (dummy)
449 and generating new ones is not wanted, skip block.
451 if(generate == false && surely_not_found_on_disk == true)
458 Add inexistent block to emerge queue.
460 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
462 /* //TODO: Get value from somewhere
463 // Allow only one block in emerge queue
464 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
465 // Allow two blocks in queue per client
466 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
468 // Make it more responsive when needing to generate stuff
469 if(surely_not_found_on_disk)
471 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
473 //infostream<<"Adding block to emerge queue"<<std::endl;
475 // Add it to the emerge queue and trigger the thread
478 if(generate == false)
479 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
481 server->m_emerge_queue.addBlock(peer_id, p, flags);
482 server->m_emergethread.trigger();
484 if(nearest_emerged_d == -1)
485 nearest_emerged_d = d;
487 if(nearest_emergefull_d == -1)
488 nearest_emergefull_d = d;
489 goto queue_full_break;
493 if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) {
494 if (nearest_emerged_d == -1)
495 nearest_emerged_d = d;
497 if (nearest_emergefull_d == -1)
498 nearest_emergefull_d = d;
499 goto queue_full_break;
506 if(nearest_sent_d == -1)
510 Add block to send queue
513 /*errorstream<<"sending from d="<<d<<" to "
514 <<server->getPlayerName(peer_id)<<std::endl;*/
516 PrioritySortedBlockTransfer q((float)d, p, peer_id);
520 num_blocks_selected += 1;
525 //infostream<<"Stopped at "<<d<<std::endl;
527 // If nothing was found for sending and nothing was queued for
528 // emerging, continue next time browsing from here
529 if(nearest_emerged_d != -1){
530 new_nearest_unsent_d = nearest_emerged_d;
531 } else if(nearest_emergefull_d != -1){
532 new_nearest_unsent_d = nearest_emergefull_d;
534 if(d > g_settings->getS16("max_block_send_distance")){
535 new_nearest_unsent_d = 0;
536 m_nothing_to_send_pause_timer = 2.0;
537 /*infostream<<"GetNextBlocks(): d wrapped around for "
538 <<server->getPlayerName(peer_id)
539 <<"; setting to 0 and pausing"<<std::endl;*/
541 if(nearest_sent_d != -1)
542 new_nearest_unsent_d = nearest_sent_d;
544 new_nearest_unsent_d = d;
548 if(new_nearest_unsent_d != -1)
549 m_nearest_unsent_d = new_nearest_unsent_d;
551 /*timer_result = timer.stop(true);
552 if(timer_result != 0)
553 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
556 void RemoteClient::GotBlock(v3s16 p)
558 if(m_blocks_sending.find(p) != m_blocks_sending.end())
559 m_blocks_sending.erase(p);
562 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
563 " m_blocks_sending"<<std::endl;*/
564 m_excess_gotblocks++;
566 m_blocks_sent.insert(p);
569 void RemoteClient::SentBlock(v3s16 p)
571 if(m_blocks_sending.find(p) == m_blocks_sending.end())
572 m_blocks_sending[p] = 0.0;
574 infostream<<"RemoteClient::SentBlock(): Sent block"
575 " already in m_blocks_sending"<<std::endl;
578 void RemoteClient::SetBlockNotSent(v3s16 p)
580 m_nearest_unsent_d = 0;
582 if(m_blocks_sending.find(p) != m_blocks_sending.end())
583 m_blocks_sending.erase(p);
584 if(m_blocks_sent.find(p) != m_blocks_sent.end())
585 m_blocks_sent.erase(p);
588 void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
590 m_nearest_unsent_d = 0;
592 for(std::map<v3s16, MapBlock*>::iterator
594 i != blocks.end(); ++i)
598 if(m_blocks_sending.find(p) != m_blocks_sending.end())
599 m_blocks_sending.erase(p);
600 if(m_blocks_sent.find(p) != m_blocks_sent.end())
601 m_blocks_sent.erase(p);
609 PlayerInfo::PlayerInfo()
615 void PlayerInfo::PrintLine(std::ostream *s)
618 (*s)<<"\""<<name<<"\" ("
619 <<(position.X/10)<<","<<(position.Y/10)
620 <<","<<(position.Z/10)<<") ";
622 (*s)<<" avg_rtt="<<avg_rtt;
631 const std::string &path_world,
632 const std::string &path_config,
633 const SubgameSpec &gamespec,
634 bool simple_singleplayer_mode
636 m_path_world(path_world),
637 m_path_config(path_config),
638 m_gamespec(gamespec),
639 m_simple_singleplayer_mode(simple_singleplayer_mode),
640 m_async_fatal_error(""),
642 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
643 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
645 m_rollback_sink_enabled(true),
646 m_enable_rollback_recording(false),
649 m_itemdef(createItemDefManager()),
650 m_nodedef(createNodeDefManager()),
651 m_craftdef(createCraftDefManager()),
652 m_event(new EventManager()),
654 m_time_of_day_send_timer(0),
656 m_shutdown_requested(false),
657 m_ignore_map_edit_events(false),
658 m_ignore_map_edit_events_peer_id(0)
660 m_liquid_transform_timer = 0.0;
661 m_liquid_transform_every = 1.0;
662 m_print_info_timer = 0.0;
663 m_masterserver_timer = 0.0;
664 m_objectdata_timer = 0.0;
665 m_emergethread_trigger_timer = 0.0;
666 m_savemap_timer = 0.0;
667 m_clients_number = 0;
671 m_step_dtime_mutex.Init();
675 throw ServerError("Supplied empty world path");
677 if(!gamespec.isValid())
678 throw ServerError("Supplied invalid gamespec");
680 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
681 if(m_simple_singleplayer_mode)
682 infostream<<" in simple singleplayer mode"<<std::endl;
684 infostream<<std::endl;
685 infostream<<"- world: "<<m_path_world<<std::endl;
686 infostream<<"- config: "<<m_path_config<<std::endl;
687 infostream<<"- game: "<<m_gamespec.path<<std::endl;
689 // Initialize default settings and override defaults with those provided
691 set_default_settings(g_settings);
692 Settings gamedefaults;
693 getGameMinetestConfig(gamespec.path, gamedefaults);
694 override_default_settings(g_settings, &gamedefaults);
696 // Create emerge manager
697 m_emerge = new EmergeManager(this);
699 // Create rollback manager
700 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
701 m_rollback = createRollbackManager(rollback_path, this);
703 // Create world if it doesn't exist
704 if(!initializeWorld(m_path_world, m_gamespec.id))
705 throw ServerError("Failed to initialize world");
707 ModConfiguration modconf(m_path_world);
708 m_mods = modconf.getMods();
709 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
710 // complain about mods with unsatisfied dependencies
711 if(!modconf.isConsistent())
713 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
714 it != unsatisfied_mods.end(); ++it)
717 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
718 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
719 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
720 errorstream << " \"" << *dep_it << "\"";
721 errorstream << std::endl;
725 Settings worldmt_settings;
726 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
727 worldmt_settings.readConfigFile(worldmt.c_str());
728 std::vector<std::string> names = worldmt_settings.getNames();
729 std::set<std::string> load_mod_names;
730 for(std::vector<std::string>::iterator it = names.begin();
731 it != names.end(); ++it)
733 std::string name = *it;
734 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
735 load_mod_names.insert(name.substr(9));
737 // complain about mods declared to be loaded, but not found
738 for(std::vector<ModSpec>::iterator it = m_mods.begin();
739 it != m_mods.end(); ++it)
740 load_mod_names.erase((*it).name);
741 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
742 it != unsatisfied_mods.end(); ++it)
743 load_mod_names.erase((*it).name);
744 if(!load_mod_names.empty())
746 errorstream << "The following mods could not be found:";
747 for(std::set<std::string>::iterator it = load_mod_names.begin();
748 it != load_mod_names.end(); ++it)
749 errorstream << " \"" << (*it) << "\"";
750 errorstream << std::endl;
753 // Path to builtin.lua
754 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
757 JMutexAutoLock envlock(m_env_mutex);
758 JMutexAutoLock conlock(m_con_mutex);
760 // Initialize scripting
762 infostream<<"Server: Initializing Lua"<<std::endl;
764 m_script = new ScriptApi(this);
767 // Load and run builtin.lua
768 infostream<<"Server: Loading builtin.lua [\""
769 <<builtinpath<<"\"]"<<std::endl;
770 bool success = m_script->loadMod(builtinpath, "__builtin");
772 errorstream<<"Server: Failed to load and run "
773 <<builtinpath<<std::endl;
774 throw ModError("Failed to load and run "+builtinpath);
777 infostream<<"Server: Loading mods: ";
778 for(std::vector<ModSpec>::iterator i = m_mods.begin();
779 i != m_mods.end(); i++){
780 const ModSpec &mod = *i;
781 infostream<<mod.name<<" ";
783 infostream<<std::endl;
784 // Load and run "mod" scripts
785 for(std::vector<ModSpec>::iterator i = m_mods.begin();
786 i != m_mods.end(); i++){
787 const ModSpec &mod = *i;
788 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
789 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
790 <<scriptpath<<"\"]"<<std::endl;
791 bool success = m_script->loadMod(scriptpath, mod.name);
793 errorstream<<"Server: Failed to load and run "
794 <<scriptpath<<std::endl;
795 throw ModError("Failed to load and run "+scriptpath);
799 // Read Textures and calculate sha1 sums
802 // Apply item aliases in the node definition manager
803 m_nodedef->updateAliases(m_itemdef);
805 // Initialize Environment
806 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
807 m_env = new ServerEnvironment(servermap, m_script, this, this);
809 m_emerge->initMapgens(servermap->getMapgenParams());
811 // Give environment reference to scripting api
812 m_script->initializeEnvironment(m_env);
814 // Register us to receive map edit events
815 servermap->addEventReceiver(this);
817 // If file exists, load environment metadata
818 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
820 infostream<<"Server: Loading environment metadata"<<std::endl;
821 m_env->loadMeta(m_path_world);
825 infostream<<"Server: Loading players"<<std::endl;
826 m_env->deSerializePlayers(m_path_world);
829 Add some test ActiveBlockModifiers to environment
831 add_legacy_abms(m_env, m_nodedef);
833 m_liquid_transform_every = g_settings->getFloat("liquid_update");
838 infostream<<"Server destructing"<<std::endl;
841 Send shutdown message
844 JMutexAutoLock conlock(m_con_mutex);
846 std::wstring line = L"*** Server shutting down";
849 Send the message to clients
851 for(std::map<u16, RemoteClient*>::iterator
852 i = m_clients.begin();
853 i != m_clients.end(); ++i)
855 // Get client and check that it is valid
856 RemoteClient *client = i->second;
857 assert(client->peer_id == i->first);
858 if(client->serialization_version == SER_FMT_VER_INVALID)
862 SendChatMessage(client->peer_id, line);
864 catch(con::PeerNotFoundException &e)
870 JMutexAutoLock envlock(m_env_mutex);
871 JMutexAutoLock conlock(m_con_mutex);
874 Execute script shutdown hooks
876 m_script->on_shutdown();
880 JMutexAutoLock envlock(m_env_mutex);
885 infostream<<"Server: Saving players"<<std::endl;
886 m_env->serializePlayers(m_path_world);
889 Save environment metadata
891 infostream<<"Server: Saving environment metadata"<<std::endl;
892 m_env->saveMeta(m_path_world);
900 //shutdown all emerge threads first!
907 JMutexAutoLock clientslock(m_con_mutex);
909 for(std::map<u16, RemoteClient*>::iterator
910 i = m_clients.begin();
911 i != m_clients.end(); ++i)
919 // Delete things in the reverse order of creation
927 // Deinitialize scripting
928 infostream<<"Server: Deinitializing scripting"<<std::endl;
931 // Delete detached inventories
933 for(std::map<std::string, Inventory*>::iterator
934 i = m_detached_inventories.begin();
935 i != m_detached_inventories.end(); i++){
941 void Server::start(unsigned short port)
943 DSTACK(__FUNCTION_NAME);
944 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
946 // Stop thread if already running
949 // Initialize connection
950 m_con.SetTimeoutMs(30);
954 m_thread.setRun(true);
957 // ASCII art for the win!
959 <<" .__ __ __ "<<std::endl
960 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
961 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
962 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
963 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
964 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
965 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
966 actionstream<<"Server for gameid=\""<<m_gamespec.id
967 <<"\" listening on port "<<port<<"."<<std::endl;
972 DSTACK(__FUNCTION_NAME);
974 infostream<<"Server: Stopping and waiting threads"<<std::endl;
976 // Stop threads (set run=false first so both start stopping)
977 m_thread.setRun(false);
978 //m_emergethread.setRun(false);
980 //m_emergethread.stop();
982 infostream<<"Server: Threads stopped"<<std::endl;
985 void Server::step(float dtime)
987 DSTACK(__FUNCTION_NAME);
992 JMutexAutoLock lock(m_step_dtime_mutex);
993 m_step_dtime += dtime;
995 // Throw if fatal error occurred in thread
996 std::string async_err = m_async_fatal_error.get();
998 throw ServerError(async_err);
1002 void Server::AsyncRunStep()
1004 DSTACK(__FUNCTION_NAME);
1006 g_profiler->add("Server::AsyncRunStep (num)", 1);
1010 JMutexAutoLock lock1(m_step_dtime_mutex);
1011 dtime = m_step_dtime;
1015 // Send blocks to clients
1022 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1024 //infostream<<"Server steps "<<dtime<<std::endl;
1025 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1028 JMutexAutoLock lock1(m_step_dtime_mutex);
1029 m_step_dtime -= dtime;
1036 m_uptime.set(m_uptime.get() + dtime);
1040 // Process connection's timeouts
1041 JMutexAutoLock lock2(m_con_mutex);
1042 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1043 m_con.RunTimeouts(dtime);
1047 // This has to be called so that the client list gets synced
1048 // with the peer list of the connection
1049 handlePeerChanges();
1053 Update time of day and overall game time
1056 JMutexAutoLock envlock(m_env_mutex);
1058 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1061 Send to clients at constant intervals
1064 m_time_of_day_send_timer -= dtime;
1065 if(m_time_of_day_send_timer < 0.0)
1067 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1069 //JMutexAutoLock envlock(m_env_mutex);
1070 JMutexAutoLock conlock(m_con_mutex);
1072 for(std::map<u16, RemoteClient*>::iterator
1073 i = m_clients.begin();
1074 i != m_clients.end(); ++i)
1076 RemoteClient *client = i->second;
1077 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1078 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1080 m_con.Send(client->peer_id, 0, data, true);
1086 JMutexAutoLock lock(m_env_mutex);
1088 ScopeProfiler sp(g_profiler, "SEnv step");
1089 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1093 const float map_timer_and_unload_dtime = 2.92;
1094 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1096 JMutexAutoLock lock(m_env_mutex);
1097 // Run Map's timers and unload unused data
1098 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1099 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1100 g_settings->getFloat("server_unload_unused_data_timeout"));
1111 JMutexAutoLock lock(m_env_mutex);
1112 JMutexAutoLock lock2(m_con_mutex);
1114 ScopeProfiler sp(g_profiler, "Server: handle players");
1116 for(std::map<u16, RemoteClient*>::iterator
1117 i = m_clients.begin();
1118 i != m_clients.end(); ++i)
1120 RemoteClient *client = i->second;
1121 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1122 if(playersao == NULL)
1126 Handle player HPs (die if hp=0)
1128 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
1130 if(playersao->getHP() == 0)
1131 DiePlayer(client->peer_id);
1133 SendPlayerHP(client->peer_id);
1137 Send player inventories if necessary
1139 if(playersao->m_moved){
1140 SendMovePlayer(client->peer_id);
1141 playersao->m_moved = false;
1143 if(playersao->m_inventory_not_sent){
1144 UpdateCrafting(client->peer_id);
1145 SendInventory(client->peer_id);
1150 /* Transform liquids */
1151 m_liquid_transform_timer += dtime;
1152 if(m_liquid_transform_timer >= m_liquid_transform_every)
1154 m_liquid_transform_timer -= m_liquid_transform_every;
1156 JMutexAutoLock lock(m_env_mutex);
1158 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1160 std::map<v3s16, MapBlock*> modified_blocks;
1161 m_env->getMap().transformLiquids(modified_blocks);
1166 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1167 ServerMap &map = ((ServerMap&)m_env->getMap());
1168 map.updateLighting(modified_blocks, lighting_modified_blocks);
1170 // Add blocks modified by lighting to modified_blocks
1171 for(core::map<v3s16, MapBlock*>::Iterator
1172 i = lighting_modified_blocks.getIterator();
1173 i.atEnd() == false; i++)
1175 MapBlock *block = i.getNode()->getValue();
1176 modified_blocks.insert(block->getPos(), block);
1180 Set the modified blocks unsent for all the clients
1183 JMutexAutoLock lock2(m_con_mutex);
1185 for(std::map<u16, RemoteClient*>::iterator
1186 i = m_clients.begin();
1187 i != m_clients.end(); ++i)
1189 RemoteClient *client = i->second;
1191 if(modified_blocks.size() > 0)
1193 // Remove block from sent history
1194 client->SetBlocksNotSent(modified_blocks);
1199 // Periodically print some info
1201 float &counter = m_print_info_timer;
1207 JMutexAutoLock lock2(m_con_mutex);
1208 m_clients_number = 0;
1209 if(m_clients.size() != 0)
1210 infostream<<"Players:"<<std::endl;
1211 for(std::map<u16, RemoteClient*>::iterator
1212 i = m_clients.begin();
1213 i != m_clients.end(); ++i)
1215 //u16 peer_id = i.getNode()->getKey();
1216 RemoteClient *client = i->second;
1217 Player *player = m_env->getPlayer(client->peer_id);
1220 infostream<<"* "<<player->getName()<<"\t";
1221 client->PrintInfo(infostream);
1229 // send masterserver announce
1231 float &counter = m_masterserver_timer;
1232 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1234 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
1241 //if(g_settings->getBool("enable_experimental"))
1245 Check added and deleted active objects
1248 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1249 JMutexAutoLock envlock(m_env_mutex);
1250 JMutexAutoLock conlock(m_con_mutex);
1252 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1254 // Radius inside which objects are active
1255 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1256 radius *= MAP_BLOCKSIZE;
1258 for(std::map<u16, RemoteClient*>::iterator
1259 i = m_clients.begin();
1260 i != m_clients.end(); ++i)
1262 RemoteClient *client = i->second;
1264 // If definitions and textures have not been sent, don't
1265 // send objects either
1266 if(!client->definitions_sent)
1269 Player *player = m_env->getPlayer(client->peer_id);
1272 // This can happen if the client timeouts somehow
1273 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1275 <<" has no associated player"<<std::endl;*/
1278 v3s16 pos = floatToInt(player->getPosition(), BS);
1280 std::set<u16> removed_objects;
1281 std::set<u16> added_objects;
1282 m_env->getRemovedActiveObjects(pos, radius,
1283 client->m_known_objects, removed_objects);
1284 m_env->getAddedActiveObjects(pos, radius,
1285 client->m_known_objects, added_objects);
1287 // Ignore if nothing happened
1288 if(removed_objects.size() == 0 && added_objects.size() == 0)
1290 //infostream<<"active objects: none changed"<<std::endl;
1294 std::string data_buffer;
1298 // Handle removed objects
1299 writeU16((u8*)buf, removed_objects.size());
1300 data_buffer.append(buf, 2);
1301 for(std::set<u16>::iterator
1302 i = removed_objects.begin();
1303 i != removed_objects.end(); ++i)
1307 ServerActiveObject* obj = m_env->getActiveObject(id);
1309 // Add to data buffer for sending
1310 writeU16((u8*)buf, id);
1311 data_buffer.append(buf, 2);
1313 // Remove from known objects
1314 client->m_known_objects.erase(id);
1316 if(obj && obj->m_known_by_count > 0)
1317 obj->m_known_by_count--;
1320 // Handle added objects
1321 writeU16((u8*)buf, added_objects.size());
1322 data_buffer.append(buf, 2);
1323 for(std::set<u16>::iterator
1324 i = added_objects.begin();
1325 i != added_objects.end(); ++i)
1329 ServerActiveObject* obj = m_env->getActiveObject(id);
1332 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1334 infostream<<"WARNING: "<<__FUNCTION_NAME
1335 <<": NULL object"<<std::endl;
1337 type = obj->getSendType();
1339 // Add to data buffer for sending
1340 writeU16((u8*)buf, id);
1341 data_buffer.append(buf, 2);
1342 writeU8((u8*)buf, type);
1343 data_buffer.append(buf, 1);
1346 data_buffer.append(serializeLongString(
1347 obj->getClientInitializationData(client->net_proto_version)));
1349 data_buffer.append(serializeLongString(""));
1351 // Add to known objects
1352 client->m_known_objects.insert(id);
1355 obj->m_known_by_count++;
1359 SharedBuffer<u8> reply(2 + data_buffer.size());
1360 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1361 memcpy((char*)&reply[2], data_buffer.c_str(),
1362 data_buffer.size());
1364 m_con.Send(client->peer_id, 0, reply, true);
1366 verbosestream<<"Server: Sent object remove/add: "
1367 <<removed_objects.size()<<" removed, "
1368 <<added_objects.size()<<" added, "
1369 <<"packet size is "<<reply.getSize()<<std::endl;
1374 Collect a list of all the objects known by the clients
1375 and report it back to the environment.
1378 core::map<u16, bool> all_known_objects;
1380 for(core::map<u16, RemoteClient*>::Iterator
1381 i = m_clients.getIterator();
1382 i.atEnd() == false; i++)
1384 RemoteClient *client = i.getNode()->getValue();
1385 // Go through all known objects of client
1386 for(core::map<u16, bool>::Iterator
1387 i = client->m_known_objects.getIterator();
1388 i.atEnd()==false; i++)
1390 u16 id = i.getNode()->getKey();
1391 all_known_objects[id] = true;
1395 m_env->setKnownActiveObjects(whatever);
1401 Send object messages
1404 JMutexAutoLock envlock(m_env_mutex);
1405 JMutexAutoLock conlock(m_con_mutex);
1407 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1410 // Value = data sent by object
1411 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1413 // Get active object messages from environment
1416 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1420 std::list<ActiveObjectMessage>* message_list = NULL;
1421 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1422 n = buffered_messages.find(aom.id);
1423 if(n == buffered_messages.end())
1425 message_list = new std::list<ActiveObjectMessage>;
1426 buffered_messages[aom.id] = message_list;
1430 message_list = n->second;
1432 message_list->push_back(aom);
1435 // Route data to every client
1436 for(std::map<u16, RemoteClient*>::iterator
1437 i = m_clients.begin();
1438 i != m_clients.end(); ++i)
1440 RemoteClient *client = i->second;
1441 std::string reliable_data;
1442 std::string unreliable_data;
1443 // Go through all objects in message buffer
1444 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1445 j = buffered_messages.begin();
1446 j != buffered_messages.end(); ++j)
1448 // If object is not known by client, skip it
1450 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1452 // Get message list of object
1453 std::list<ActiveObjectMessage>* list = j->second;
1454 // Go through every message
1455 for(std::list<ActiveObjectMessage>::iterator
1456 k = list->begin(); k != list->end(); ++k)
1458 // Compose the full new data with header
1459 ActiveObjectMessage aom = *k;
1460 std::string new_data;
1463 writeU16((u8*)&buf[0], aom.id);
1464 new_data.append(buf, 2);
1466 new_data += serializeString(aom.datastring);
1467 // Add data to buffer
1469 reliable_data += new_data;
1471 unreliable_data += new_data;
1475 reliable_data and unreliable_data are now ready.
1478 if(reliable_data.size() > 0)
1480 SharedBuffer<u8> reply(2 + reliable_data.size());
1481 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1482 memcpy((char*)&reply[2], reliable_data.c_str(),
1483 reliable_data.size());
1485 m_con.Send(client->peer_id, 0, reply, true);
1487 if(unreliable_data.size() > 0)
1489 SharedBuffer<u8> reply(2 + unreliable_data.size());
1490 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1491 memcpy((char*)&reply[2], unreliable_data.c_str(),
1492 unreliable_data.size());
1493 // Send as unreliable
1494 m_con.Send(client->peer_id, 0, reply, false);
1497 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1499 infostream<<"Server: Size of object message data: "
1500 <<"reliable: "<<reliable_data.size()
1501 <<", unreliable: "<<unreliable_data.size()
1506 // Clear buffered_messages
1507 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1508 i = buffered_messages.begin();
1509 i != buffered_messages.end(); ++i)
1515 } // enable_experimental
1518 Send queued-for-sending map edit events.
1521 // We will be accessing the environment and the connection
1522 JMutexAutoLock lock(m_env_mutex);
1523 JMutexAutoLock conlock(m_con_mutex);
1525 // Don't send too many at a time
1528 // Single change sending is disabled if queue size is not small
1529 bool disable_single_change_sending = false;
1530 if(m_unsent_map_edit_queue.size() >= 4)
1531 disable_single_change_sending = true;
1533 int event_count = m_unsent_map_edit_queue.size();
1535 // We'll log the amount of each
1538 while(m_unsent_map_edit_queue.size() != 0)
1540 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1542 // Players far away from the change are stored here.
1543 // Instead of sending the changes, MapBlocks are set not sent
1545 std::list<u16> far_players;
1547 if(event->type == MEET_ADDNODE)
1549 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1550 prof.add("MEET_ADDNODE", 1);
1551 if(disable_single_change_sending)
1552 sendAddNode(event->p, event->n, event->already_known_by_peer,
1555 sendAddNode(event->p, event->n, event->already_known_by_peer,
1558 else if(event->type == MEET_REMOVENODE)
1560 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1561 prof.add("MEET_REMOVENODE", 1);
1562 if(disable_single_change_sending)
1563 sendRemoveNode(event->p, event->already_known_by_peer,
1566 sendRemoveNode(event->p, event->already_known_by_peer,
1569 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1571 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1572 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1573 setBlockNotSent(event->p);
1575 else if(event->type == MEET_OTHER)
1577 infostream<<"Server: MEET_OTHER"<<std::endl;
1578 prof.add("MEET_OTHER", 1);
1579 for(std::set<v3s16>::iterator
1580 i = event->modified_blocks.begin();
1581 i != event->modified_blocks.end(); ++i)
1583 setBlockNotSent(*i);
1588 prof.add("unknown", 1);
1589 infostream<<"WARNING: Server: Unknown MapEditEvent "
1590 <<((u32)event->type)<<std::endl;
1594 Set blocks not sent to far players
1596 if(far_players.size() > 0)
1598 // Convert list format to that wanted by SetBlocksNotSent
1599 std::map<v3s16, MapBlock*> modified_blocks2;
1600 for(std::set<v3s16>::iterator
1601 i = event->modified_blocks.begin();
1602 i != event->modified_blocks.end(); ++i)
1604 modified_blocks2[*i] =
1605 m_env->getMap().getBlockNoCreateNoEx(*i);
1607 // Set blocks not sent
1608 for(std::list<u16>::iterator
1609 i = far_players.begin();
1610 i != far_players.end(); ++i)
1613 RemoteClient *client = getClient(peer_id);
1616 client->SetBlocksNotSent(modified_blocks2);
1622 /*// Don't send too many at a time
1624 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1628 if(event_count >= 5){
1629 infostream<<"Server: MapEditEvents:"<<std::endl;
1630 prof.print(infostream);
1631 } else if(event_count != 0){
1632 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1633 prof.print(verbosestream);
1639 Trigger emergethread (it somehow gets to a non-triggered but
1640 bysy state sometimes)
1643 float &counter = m_emergethread_trigger_timer;
1649 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1650 m_emerge->emergethread[i]->trigger();
1652 // Update m_enable_rollback_recording here too
1653 m_enable_rollback_recording =
1654 g_settings->getBool("enable_rollback_recording");
1658 // Save map, players and auth stuff
1660 float &counter = m_savemap_timer;
1662 if(counter >= g_settings->getFloat("server_map_save_interval"))
1665 JMutexAutoLock lock(m_env_mutex);
1667 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1670 if(m_banmanager.isModified())
1671 m_banmanager.save();
1673 // Save changed parts of map
1674 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1677 m_env->serializePlayers(m_path_world);
1679 // Save environment metadata
1680 m_env->saveMeta(m_path_world);
1685 void Server::Receive()
1687 DSTACK(__FUNCTION_NAME);
1688 SharedBuffer<u8> data;
1693 JMutexAutoLock conlock(m_con_mutex);
1694 datasize = m_con.Receive(peer_id, data);
1697 // This has to be called so that the client list gets synced
1698 // with the peer list of the connection
1699 handlePeerChanges();
1701 ProcessData(*data, datasize, peer_id);
1703 catch(con::InvalidIncomingDataException &e)
1705 infostream<<"Server::Receive(): "
1706 "InvalidIncomingDataException: what()="
1707 <<e.what()<<std::endl;
1709 catch(con::PeerNotFoundException &e)
1711 //NOTE: This is not needed anymore
1713 // The peer has been disconnected.
1714 // Find the associated player and remove it.
1716 /*JMutexAutoLock envlock(m_env_mutex);
1718 infostream<<"ServerThread: peer_id="<<peer_id
1719 <<" has apparently closed connection. "
1720 <<"Removing player."<<std::endl;
1722 m_env->removePlayer(peer_id);*/
1726 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1728 DSTACK(__FUNCTION_NAME);
1729 // Environment is locked first.
1730 JMutexAutoLock envlock(m_env_mutex);
1731 JMutexAutoLock conlock(m_con_mutex);
1733 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1736 Address address = m_con.GetPeerAddress(peer_id);
1737 std::string addr_s = address.serializeString();
1739 // drop player if is ip is banned
1740 if(m_banmanager.isIpBanned(addr_s)){
1741 infostream<<"Server: A banned client tried to connect from "
1742 <<addr_s<<"; banned name was "
1743 <<m_banmanager.getBanName(addr_s)<<std::endl;
1744 // This actually doesn't seem to transfer to the client
1745 SendAccessDenied(m_con, peer_id,
1746 L"Your ip is banned. Banned name was "
1747 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1748 m_con.DeletePeer(peer_id);
1752 catch(con::PeerNotFoundException &e)
1754 infostream<<"Server::ProcessData(): Cancelling: peer "
1755 <<peer_id<<" not found"<<std::endl;
1759 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1761 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1769 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1771 if(command == TOSERVER_INIT)
1773 // [0] u16 TOSERVER_INIT
1774 // [2] u8 SER_FMT_VER_HIGHEST
1775 // [3] u8[20] player_name
1776 // [23] u8[28] password <--- can be sent without this, from old versions
1778 if(datasize < 2+1+PLAYERNAME_SIZE)
1781 verbosestream<<"Server: Got TOSERVER_INIT from "
1782 <<peer_id<<std::endl;
1784 // First byte after command is maximum supported
1785 // serialization version
1786 u8 client_max = data[2];
1787 u8 our_max = SER_FMT_VER_HIGHEST;
1788 // Use the highest version supported by both
1789 u8 deployed = std::min(client_max, our_max);
1790 // If it's lower than the lowest supported, give up.
1791 if(deployed < SER_FMT_VER_LOWEST)
1792 deployed = SER_FMT_VER_INVALID;
1794 //peer->serialization_version = deployed;
1795 getClient(peer_id)->pending_serialization_version = deployed;
1797 if(deployed == SER_FMT_VER_INVALID)
1799 actionstream<<"Server: A mismatched client tried to connect from "
1800 <<addr_s<<std::endl;
1801 infostream<<"Server: Cannot negotiate "
1802 "serialization version with peer "
1803 <<peer_id<<std::endl;
1804 SendAccessDenied(m_con, peer_id, std::wstring(
1805 L"Your client's version is not supported.\n"
1806 L"Server version is ")
1807 + narrow_to_wide(VERSION_STRING) + L"."
1813 Read and check network protocol version
1816 u16 min_net_proto_version = 0;
1817 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1818 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1820 // Use same version as minimum and maximum if maximum version field
1821 // doesn't exist (backwards compatibility)
1822 u16 max_net_proto_version = min_net_proto_version;
1823 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1824 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1826 // Start with client's maximum version
1827 u16 net_proto_version = max_net_proto_version;
1829 // Figure out a working version if it is possible at all
1830 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1831 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1833 // If maximum is larger than our maximum, go with our maximum
1834 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1835 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1836 // Else go with client's maximum
1838 net_proto_version = max_net_proto_version;
1841 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1842 <<min_net_proto_version<<", max: "<<max_net_proto_version
1843 <<", chosen: "<<net_proto_version<<std::endl;
1845 getClient(peer_id)->net_proto_version = net_proto_version;
1847 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1848 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1850 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1852 SendAccessDenied(m_con, peer_id, std::wstring(
1853 L"Your client's version is not supported.\n"
1854 L"Server version is ")
1855 + narrow_to_wide(VERSION_STRING) + L",\n"
1856 + L"server's PROTOCOL_VERSION is "
1857 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1859 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1860 + L", client's PROTOCOL_VERSION is "
1861 + narrow_to_wide(itos(min_net_proto_version))
1863 + narrow_to_wide(itos(max_net_proto_version))
1868 if(g_settings->getBool("strict_protocol_version_checking"))
1870 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1872 actionstream<<"Server: A mismatched (strict) client tried to "
1873 <<"connect from "<<addr_s<<std::endl;
1874 SendAccessDenied(m_con, peer_id, std::wstring(
1875 L"Your client's version is not supported.\n"
1876 L"Server version is ")
1877 + narrow_to_wide(VERSION_STRING) + L",\n"
1878 + L"server's PROTOCOL_VERSION (strict) is "
1879 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1880 + L", client's PROTOCOL_VERSION is "
1881 + narrow_to_wide(itos(min_net_proto_version))
1883 + narrow_to_wide(itos(max_net_proto_version))
1894 char playername[PLAYERNAME_SIZE];
1895 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1897 playername[i] = data[3+i];
1899 playername[PLAYERNAME_SIZE-1] = 0;
1901 if(playername[0]=='\0')
1903 actionstream<<"Server: Player with an empty name "
1904 <<"tried to connect from "<<addr_s<<std::endl;
1905 SendAccessDenied(m_con, peer_id,
1910 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1912 actionstream<<"Server: Player with an invalid name "
1913 <<"tried to connect from "<<addr_s<<std::endl;
1914 SendAccessDenied(m_con, peer_id,
1915 L"Name contains unallowed characters");
1919 infostream<<"Server: New connection: \""<<playername<<"\" from "
1920 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1923 char given_password[PASSWORD_SIZE];
1924 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1926 // old version - assume blank password
1927 given_password[0] = 0;
1931 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1933 given_password[i] = data[23+i];
1935 given_password[PASSWORD_SIZE-1] = 0;
1938 if(!base64_is_valid(given_password)){
1939 infostream<<"Server: "<<playername
1940 <<" supplied invalid password hash"<<std::endl;
1941 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1945 std::string checkpwd; // Password hash to check against
1946 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1948 // If no authentication info exists for user, create it
1950 if(!isSingleplayer() &&
1951 g_settings->getBool("disallow_empty_password") &&
1952 std::string(given_password) == ""){
1953 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1954 L"disallowed. Set a password and try again.");
1957 std::wstring raw_default_password =
1958 narrow_to_wide(g_settings->get("default_password"));
1959 std::string initial_password =
1960 translatePassword(playername, raw_default_password);
1962 // If default_password is empty, allow any initial password
1963 if (raw_default_password.length() == 0)
1964 initial_password = given_password;
1966 m_script->createAuth(playername, initial_password);
1969 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1972 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1976 if(given_password != checkpwd){
1977 infostream<<"Server: peer_id="<<peer_id
1978 <<": supplied invalid password for "
1979 <<playername<<std::endl;
1980 SendAccessDenied(m_con, peer_id, L"Invalid password");
1984 // Do not allow multiple players in simple singleplayer mode.
1985 // This isn't a perfect way to do it, but will suffice for now.
1986 if(m_simple_singleplayer_mode && m_clients.size() > 1){
1987 infostream<<"Server: Not allowing another client to connect in"
1988 <<" simple singleplayer mode"<<std::endl;
1989 SendAccessDenied(m_con, peer_id,
1990 L"Running in simple singleplayer mode.");
1994 // Enforce user limit.
1995 // Don't enforce for users that have some admin right
1996 if(m_clients.size() >= g_settings->getU16("max_users") &&
1997 !checkPriv(playername, "server") &&
1998 !checkPriv(playername, "ban") &&
1999 !checkPriv(playername, "privs") &&
2000 !checkPriv(playername, "password") &&
2001 playername != g_settings->get("name"))
2003 actionstream<<"Server: "<<playername<<" tried to join, but there"
2004 <<" are already max_users="
2005 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2006 SendAccessDenied(m_con, peer_id, L"Too many users.");
2011 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2013 // If failed, cancel
2014 if(playersao == NULL)
2016 errorstream<<"Server: peer_id="<<peer_id
2017 <<": failed to emerge player"<<std::endl;
2022 Answer with a TOCLIENT_INIT
2025 SharedBuffer<u8> reply(2+1+6+8+4);
2026 writeU16(&reply[0], TOCLIENT_INIT);
2027 writeU8(&reply[2], deployed);
2028 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2029 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2030 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2033 m_con.Send(peer_id, 0, reply, true);
2037 Send complete position information
2039 SendMovePlayer(peer_id);
2044 if(command == TOSERVER_INIT2)
2046 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2047 <<peer_id<<std::endl;
2049 Player *player = m_env->getPlayer(peer_id);
2051 verbosestream<<"Server: TOSERVER_INIT2: "
2052 <<"Player not found; ignoring."<<std::endl;
2056 RemoteClient *client = getClient(peer_id);
2057 client->serialization_version =
2058 getClient(peer_id)->pending_serialization_version;
2061 Send some initialization data
2064 infostream<<"Server: Sending content to "
2065 <<getPlayerName(peer_id)<<std::endl;
2067 // Send player movement settings
2068 SendMovement(m_con, peer_id);
2070 // Send item definitions
2071 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2073 // Send node definitions
2074 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2076 // Send media announcement
2077 sendMediaAnnouncement(peer_id);
2080 SendPlayerPrivileges(peer_id);
2082 // Send inventory formspec
2083 SendPlayerInventoryFormspec(peer_id);
2086 UpdateCrafting(peer_id);
2087 SendInventory(peer_id);
2090 if(g_settings->getBool("enable_damage"))
2091 SendPlayerHP(peer_id);
2093 // Send detached inventories
2094 sendDetachedInventories(peer_id);
2096 // Show death screen if necessary
2098 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2102 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2103 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2104 m_con.Send(peer_id, 0, data, true);
2107 // Note things in chat if not in simple singleplayer mode
2108 if(!m_simple_singleplayer_mode)
2110 // Send information about server to player in chat
2111 SendChatMessage(peer_id, getStatusString());
2113 // Send information about joining in chat
2115 std::wstring name = L"unknown";
2116 Player *player = m_env->getPlayer(peer_id);
2118 name = narrow_to_wide(player->getName());
2120 std::wstring message;
2123 message += L" joined the game.";
2124 BroadcastChatMessage(message);
2128 // Warnings about protocol version can be issued here
2129 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2131 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2132 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2139 std::ostringstream os(std::ios_base::binary);
2140 for(std::map<u16, RemoteClient*>::iterator
2141 i = m_clients.begin();
2142 i != m_clients.end(); ++i)
2144 RemoteClient *client = i->second;
2145 assert(client->peer_id == i->first);
2146 if(client->serialization_version == SER_FMT_VER_INVALID)
2149 Player *player = m_env->getPlayer(client->peer_id);
2152 // Get name of player
2153 os<<player->getName()<<" ";
2156 actionstream<<player->getName()<<" joins game. List of players: "
2157 <<os.str()<<std::endl;
2163 if(peer_ser_ver == SER_FMT_VER_INVALID)
2165 infostream<<"Server::ProcessData(): Cancelling: Peer"
2166 " serialization format invalid or not initialized."
2167 " Skipping incoming command="<<command<<std::endl;
2171 Player *player = m_env->getPlayer(peer_id);
2173 infostream<<"Server::ProcessData(): Cancelling: "
2174 "No player for peer_id="<<peer_id
2179 PlayerSAO *playersao = player->getPlayerSAO();
2180 if(playersao == NULL){
2181 infostream<<"Server::ProcessData(): Cancelling: "
2182 "No player object for peer_id="<<peer_id
2187 if(command == TOSERVER_PLAYERPOS)
2189 if(datasize < 2+12+12+4+4)
2193 v3s32 ps = readV3S32(&data[start+2]);
2194 v3s32 ss = readV3S32(&data[start+2+12]);
2195 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2196 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2198 if(datasize >= 2+12+12+4+4+4)
2199 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2200 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2201 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2202 pitch = wrapDegrees(pitch);
2203 yaw = wrapDegrees(yaw);
2205 player->setPosition(position);
2206 player->setSpeed(speed);
2207 player->setPitch(pitch);
2208 player->setYaw(yaw);
2209 player->keyPressed=keyPressed;
2210 player->control.up = (bool)(keyPressed&1);
2211 player->control.down = (bool)(keyPressed&2);
2212 player->control.left = (bool)(keyPressed&4);
2213 player->control.right = (bool)(keyPressed&8);
2214 player->control.jump = (bool)(keyPressed&16);
2215 player->control.aux1 = (bool)(keyPressed&32);
2216 player->control.sneak = (bool)(keyPressed&64);
2217 player->control.LMB = (bool)(keyPressed&128);
2218 player->control.RMB = (bool)(keyPressed&256);
2220 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2221 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2222 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2224 else if(command == TOSERVER_GOTBLOCKS)
2237 u16 count = data[2];
2238 for(u16 i=0; i<count; i++)
2240 if((s16)datasize < 2+1+(i+1)*6)
2241 throw con::InvalidIncomingDataException
2242 ("GOTBLOCKS length is too short");
2243 v3s16 p = readV3S16(&data[2+1+i*6]);
2244 /*infostream<<"Server: GOTBLOCKS ("
2245 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2246 RemoteClient *client = getClient(peer_id);
2247 client->GotBlock(p);
2250 else if(command == TOSERVER_DELETEDBLOCKS)
2263 u16 count = data[2];
2264 for(u16 i=0; i<count; i++)
2266 if((s16)datasize < 2+1+(i+1)*6)
2267 throw con::InvalidIncomingDataException
2268 ("DELETEDBLOCKS length is too short");
2269 v3s16 p = readV3S16(&data[2+1+i*6]);
2270 /*infostream<<"Server: DELETEDBLOCKS ("
2271 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2272 RemoteClient *client = getClient(peer_id);
2273 client->SetBlockNotSent(p);
2276 else if(command == TOSERVER_CLICK_OBJECT)
2278 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2281 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2283 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2286 else if(command == TOSERVER_GROUND_ACTION)
2288 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2292 else if(command == TOSERVER_RELEASE)
2294 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2297 else if(command == TOSERVER_SIGNTEXT)
2299 infostream<<"Server: SIGNTEXT not supported anymore"
2303 else if(command == TOSERVER_SIGNNODETEXT)
2305 infostream<<"Server: SIGNNODETEXT not supported anymore"
2309 else if(command == TOSERVER_INVENTORY_ACTION)
2311 // Strip command and create a stream
2312 std::string datastring((char*)&data[2], datasize-2);
2313 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2314 std::istringstream is(datastring, std::ios_base::binary);
2316 InventoryAction *a = InventoryAction::deSerialize(is);
2319 infostream<<"TOSERVER_INVENTORY_ACTION: "
2320 <<"InventoryAction::deSerialize() returned NULL"
2325 // If something goes wrong, this player is to blame
2326 RollbackScopeActor rollback_scope(m_rollback,
2327 std::string("player:")+player->getName());
2330 Note: Always set inventory not sent, to repair cases
2331 where the client made a bad prediction.
2335 Handle restrictions and special cases of the move action
2337 if(a->getType() == IACTION_MOVE)
2339 IMoveAction *ma = (IMoveAction*)a;
2341 ma->from_inv.applyCurrentPlayer(player->getName());
2342 ma->to_inv.applyCurrentPlayer(player->getName());
2344 setInventoryModified(ma->from_inv);
2345 setInventoryModified(ma->to_inv);
2347 bool from_inv_is_current_player =
2348 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2349 (ma->from_inv.name == player->getName());
2351 bool to_inv_is_current_player =
2352 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2353 (ma->to_inv.name == player->getName());
2356 Disable moving items out of craftpreview
2358 if(ma->from_list == "craftpreview")
2360 infostream<<"Ignoring IMoveAction from "
2361 <<(ma->from_inv.dump())<<":"<<ma->from_list
2362 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2363 <<" because src is "<<ma->from_list<<std::endl;
2369 Disable moving items into craftresult and craftpreview
2371 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2373 infostream<<"Ignoring IMoveAction from "
2374 <<(ma->from_inv.dump())<<":"<<ma->from_list
2375 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2376 <<" because dst is "<<ma->to_list<<std::endl;
2381 // Disallow moving items in elsewhere than player's inventory
2382 // if not allowed to interact
2383 if(!checkPriv(player->getName(), "interact") &&
2384 (!from_inv_is_current_player ||
2385 !to_inv_is_current_player))
2387 infostream<<"Cannot move outside of player's inventory: "
2388 <<"No interact privilege"<<std::endl;
2394 Handle restrictions and special cases of the drop action
2396 else if(a->getType() == IACTION_DROP)
2398 IDropAction *da = (IDropAction*)a;
2400 da->from_inv.applyCurrentPlayer(player->getName());
2402 setInventoryModified(da->from_inv);
2404 // Disallow dropping items if not allowed to interact
2405 if(!checkPriv(player->getName(), "interact"))
2412 Handle restrictions and special cases of the craft action
2414 else if(a->getType() == IACTION_CRAFT)
2416 ICraftAction *ca = (ICraftAction*)a;
2418 ca->craft_inv.applyCurrentPlayer(player->getName());
2420 setInventoryModified(ca->craft_inv);
2422 //bool craft_inv_is_current_player =
2423 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2424 // (ca->craft_inv.name == player->getName());
2426 // Disallow crafting if not allowed to interact
2427 if(!checkPriv(player->getName(), "interact"))
2429 infostream<<"Cannot craft: "
2430 <<"No interact privilege"<<std::endl;
2437 a->apply(this, playersao, this);
2441 else if(command == TOSERVER_CHAT_MESSAGE)
2449 std::string datastring((char*)&data[2], datasize-2);
2450 std::istringstream is(datastring, std::ios_base::binary);
2453 is.read((char*)buf, 2);
2454 u16 len = readU16(buf);
2456 std::wstring message;
2457 for(u16 i=0; i<len; i++)
2459 is.read((char*)buf, 2);
2460 message += (wchar_t)readU16(buf);
2463 // If something goes wrong, this player is to blame
2464 RollbackScopeActor rollback_scope(m_rollback,
2465 std::string("player:")+player->getName());
2467 // Get player name of this client
2468 std::wstring name = narrow_to_wide(player->getName());
2471 bool ate = m_script->on_chat_message(player->getName(),
2472 wide_to_narrow(message));
2473 // If script ate the message, don't proceed
2477 // Line to send to players
2479 // Whether to send to the player that sent the line
2480 bool send_to_sender = false;
2481 // Whether to send to other players
2482 bool send_to_others = false;
2484 // Commands are implemented in Lua, so only catch invalid
2485 // commands that were not "eaten" and send an error back
2486 if(message[0] == L'/')
2488 message = message.substr(1);
2489 send_to_sender = true;
2490 if(message.length() == 0)
2491 line += L"-!- Empty command";
2493 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2497 if(checkPriv(player->getName(), "shout")){
2502 send_to_others = true;
2504 line += L"-!- You don't have permission to shout.";
2505 send_to_sender = true;
2512 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2515 Send the message to clients
2517 for(std::map<u16, RemoteClient*>::iterator
2518 i = m_clients.begin();
2519 i != m_clients.end(); ++i)
2521 // Get client and check that it is valid
2522 RemoteClient *client = i->second;
2523 assert(client->peer_id == i->first);
2524 if(client->serialization_version == SER_FMT_VER_INVALID)
2528 bool sender_selected = (peer_id == client->peer_id);
2529 if(sender_selected == true && send_to_sender == false)
2531 if(sender_selected == false && send_to_others == false)
2534 SendChatMessage(client->peer_id, line);
2538 else if(command == TOSERVER_DAMAGE)
2540 std::string datastring((char*)&data[2], datasize-2);
2541 std::istringstream is(datastring, std::ios_base::binary);
2542 u8 damage = readU8(is);
2544 if(g_settings->getBool("enable_damage"))
2546 actionstream<<player->getName()<<" damaged by "
2547 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2550 playersao->setHP(playersao->getHP() - damage);
2552 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2555 if(playersao->m_hp_not_sent)
2556 SendPlayerHP(peer_id);
2559 else if(command == TOSERVER_PASSWORD)
2562 [0] u16 TOSERVER_PASSWORD
2563 [2] u8[28] old password
2564 [30] u8[28] new password
2567 if(datasize != 2+PASSWORD_SIZE*2)
2569 /*char password[PASSWORD_SIZE];
2570 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2571 password[i] = data[2+i];
2572 password[PASSWORD_SIZE-1] = 0;*/
2574 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2582 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2584 char c = data[2+PASSWORD_SIZE+i];
2590 if(!base64_is_valid(newpwd)){
2591 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2592 // Wrong old password supplied!!
2593 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2597 infostream<<"Server: Client requests a password change from "
2598 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2600 std::string playername = player->getName();
2602 std::string checkpwd;
2603 m_script->getAuth(playername, &checkpwd, NULL);
2605 if(oldpwd != checkpwd)
2607 infostream<<"Server: invalid old password"<<std::endl;
2608 // Wrong old password supplied!!
2609 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2613 bool success = m_script->setPassword(playername, newpwd);
2615 actionstream<<player->getName()<<" changes password"<<std::endl;
2616 SendChatMessage(peer_id, L"Password change successful.");
2618 actionstream<<player->getName()<<" tries to change password but "
2619 <<"it fails"<<std::endl;
2620 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2623 else if(command == TOSERVER_PLAYERITEM)
2628 u16 item = readU16(&data[2]);
2629 playersao->setWieldIndex(item);
2631 else if(command == TOSERVER_RESPAWN)
2633 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2636 RespawnPlayer(peer_id);
2638 actionstream<<player->getName()<<" respawns at "
2639 <<PP(player->getPosition()/BS)<<std::endl;
2641 // ActiveObject is added to environment in AsyncRunStep after
2642 // the previous addition has been succesfully removed
2644 else if(command == TOSERVER_REQUEST_MEDIA) {
2645 std::string datastring((char*)&data[2], datasize-2);
2646 std::istringstream is(datastring, std::ios_base::binary);
2648 std::list<MediaRequest> tosend;
2649 u16 numfiles = readU16(is);
2651 infostream<<"Sending "<<numfiles<<" files to "
2652 <<getPlayerName(peer_id)<<std::endl;
2653 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2655 for(int i = 0; i < numfiles; i++) {
2656 std::string name = deSerializeString(is);
2657 tosend.push_back(MediaRequest(name));
2658 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2662 sendRequestedMedia(peer_id, tosend);
2664 // Now the client should know about everything
2665 // (definitions and files)
2666 getClient(peer_id)->definitions_sent = true;
2668 else if(command == TOSERVER_RECEIVED_MEDIA) {
2669 getClient(peer_id)->definitions_sent = true;
2671 else if(command == TOSERVER_INTERACT)
2673 std::string datastring((char*)&data[2], datasize-2);
2674 std::istringstream is(datastring, std::ios_base::binary);
2680 [5] u32 length of the next item
2681 [9] serialized PointedThing
2683 0: start digging (from undersurface) or use
2684 1: stop digging (all parameters ignored)
2685 2: digging completed
2686 3: place block or item (to abovesurface)
2689 u8 action = readU8(is);
2690 u16 item_i = readU16(is);
2691 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2692 PointedThing pointed;
2693 pointed.deSerialize(tmp_is);
2695 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2696 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2700 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2701 <<" tried to interact, but is dead!"<<std::endl;
2705 v3f player_pos = playersao->getLastGoodPosition();
2707 // Update wielded item
2708 playersao->setWieldIndex(item_i);
2710 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2711 v3s16 p_under = pointed.node_undersurface;
2712 v3s16 p_above = pointed.node_abovesurface;
2714 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2715 ServerActiveObject *pointed_object = NULL;
2716 if(pointed.type == POINTEDTHING_OBJECT)
2718 pointed_object = m_env->getActiveObject(pointed.object_id);
2719 if(pointed_object == NULL)
2721 verbosestream<<"TOSERVER_INTERACT: "
2722 "pointed object is NULL"<<std::endl;
2728 v3f pointed_pos_under = player_pos;
2729 v3f pointed_pos_above = player_pos;
2730 if(pointed.type == POINTEDTHING_NODE)
2732 pointed_pos_under = intToFloat(p_under, BS);
2733 pointed_pos_above = intToFloat(p_above, BS);
2735 else if(pointed.type == POINTEDTHING_OBJECT)
2737 pointed_pos_under = pointed_object->getBasePosition();
2738 pointed_pos_above = pointed_pos_under;
2742 Check that target is reasonably close
2743 (only when digging or placing things)
2745 if(action == 0 || action == 2 || action == 3)
2747 float d = player_pos.getDistanceFrom(pointed_pos_under);
2748 float max_d = BS * 14; // Just some large enough value
2750 actionstream<<"Player "<<player->getName()
2751 <<" tried to access "<<pointed.dump()
2753 <<"d="<<d<<", max_d="<<max_d
2754 <<". ignoring."<<std::endl;
2755 // Re-send block to revert change on client-side
2756 RemoteClient *client = getClient(peer_id);
2757 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2758 client->SetBlockNotSent(blockpos);
2765 Make sure the player is allowed to do it
2767 if(!checkPriv(player->getName(), "interact"))
2769 actionstream<<player->getName()<<" attempted to interact with "
2770 <<pointed.dump()<<" without 'interact' privilege"
2772 // Re-send block to revert change on client-side
2773 RemoteClient *client = getClient(peer_id);
2774 // Digging completed -> under
2776 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2777 client->SetBlockNotSent(blockpos);
2779 // Placement -> above
2781 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2782 client->SetBlockNotSent(blockpos);
2788 If something goes wrong, this player is to blame
2790 RollbackScopeActor rollback_scope(m_rollback,
2791 std::string("player:")+player->getName());
2794 0: start digging or punch object
2798 if(pointed.type == POINTEDTHING_NODE)
2801 NOTE: This can be used in the future to check if
2802 somebody is cheating, by checking the timing.
2804 MapNode n(CONTENT_IGNORE);
2807 n = m_env->getMap().getNode(p_under);
2809 catch(InvalidPositionException &e)
2811 infostream<<"Server: Not punching: Node not found."
2812 <<" Adding block to emerge queue."
2814 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2816 if(n.getContent() != CONTENT_IGNORE)
2817 m_script->node_on_punch(p_under, n, playersao);
2819 playersao->noCheatDigStart(p_under);
2821 else if(pointed.type == POINTEDTHING_OBJECT)
2823 // Skip if object has been removed
2824 if(pointed_object->m_removed)
2827 actionstream<<player->getName()<<" punches object "
2828 <<pointed.object_id<<": "
2829 <<pointed_object->getDescription()<<std::endl;
2831 ItemStack punchitem = playersao->getWieldedItem();
2832 ToolCapabilities toolcap =
2833 punchitem.getToolCapabilities(m_itemdef);
2834 v3f dir = (pointed_object->getBasePosition() -
2835 (player->getPosition() + player->getEyeOffset())
2837 float time_from_last_punch =
2838 playersao->resetTimeFromLastPunch();
2839 pointed_object->punch(dir, &toolcap, playersao,
2840 time_from_last_punch);
2848 else if(action == 1)
2853 2: Digging completed
2855 else if(action == 2)
2857 // Only digging of nodes
2858 if(pointed.type == POINTEDTHING_NODE)
2860 MapNode n(CONTENT_IGNORE);
2863 n = m_env->getMap().getNode(p_under);
2865 catch(InvalidPositionException &e)
2867 infostream<<"Server: Not finishing digging: Node not found."
2868 <<" Adding block to emerge queue."
2870 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2873 /* Cheat prevention */
2874 bool is_valid_dig = true;
2875 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2877 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2878 float nocheat_t = playersao->getNoCheatDigTime();
2879 playersao->noCheatDigEnd();
2880 // If player didn't start digging this, ignore dig
2881 if(nocheat_p != p_under){
2882 infostream<<"Server: NoCheat: "<<player->getName()
2883 <<" started digging "
2884 <<PP(nocheat_p)<<" and completed digging "
2885 <<PP(p_under)<<"; not digging."<<std::endl;
2886 is_valid_dig = false;
2888 // Get player's wielded item
2889 ItemStack playeritem;
2890 InventoryList *mlist = playersao->getInventory()->getList("main");
2892 playeritem = mlist->getItem(playersao->getWieldIndex());
2893 ToolCapabilities playeritem_toolcap =
2894 playeritem.getToolCapabilities(m_itemdef);
2895 // Get diggability and expected digging time
2896 DigParams params = getDigParams(m_nodedef->get(n).groups,
2897 &playeritem_toolcap);
2898 // If can't dig, try hand
2899 if(!params.diggable){
2900 const ItemDefinition &hand = m_itemdef->get("");
2901 const ToolCapabilities *tp = hand.tool_capabilities;
2903 params = getDigParams(m_nodedef->get(n).groups, tp);
2905 // If can't dig, ignore dig
2906 if(!params.diggable){
2907 infostream<<"Server: NoCheat: "<<player->getName()
2908 <<" completed digging "<<PP(p_under)
2909 <<", which is not diggable with tool. not digging."
2911 is_valid_dig = false;
2913 // If time is considerably too short, ignore dig
2914 // Check time only for medium and slow timed digs
2915 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2916 infostream<<"Server: NoCheat: "<<player->getName()
2917 <<" completed digging "
2918 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2919 <<params.time<<"s; not digging."<<std::endl;
2920 is_valid_dig = false;
2924 /* Actually dig node */
2926 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2927 m_script->node_on_dig(p_under, n, playersao);
2929 // Send unusual result (that is, node not being removed)
2930 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2932 // Re-send block to revert change on client-side
2933 RemoteClient *client = getClient(peer_id);
2934 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2935 client->SetBlockNotSent(blockpos);
2941 3: place block or right-click object
2943 else if(action == 3)
2945 ItemStack item = playersao->getWieldedItem();
2947 // Reset build time counter
2948 if(pointed.type == POINTEDTHING_NODE &&
2949 item.getDefinition(m_itemdef).type == ITEM_NODE)
2950 getClient(peer_id)->m_time_from_building = 0.0;
2952 if(pointed.type == POINTEDTHING_OBJECT)
2954 // Right click object
2956 // Skip if object has been removed
2957 if(pointed_object->m_removed)
2960 actionstream<<player->getName()<<" right-clicks object "
2961 <<pointed.object_id<<": "
2962 <<pointed_object->getDescription()<<std::endl;
2965 pointed_object->rightClick(playersao);
2967 else if(m_script->item_OnPlace(
2968 item, playersao, pointed))
2970 // Placement was handled in lua
2972 // Apply returned ItemStack
2973 playersao->setWieldedItem(item);
2976 // If item has node placement prediction, always send the
2977 // blocks to make sure the client knows what exactly happened
2978 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2979 RemoteClient *client = getClient(peer_id);
2980 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2981 client->SetBlockNotSent(blockpos);
2982 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2983 if(blockpos2 != blockpos){
2984 client->SetBlockNotSent(blockpos2);
2992 else if(action == 4)
2994 ItemStack item = playersao->getWieldedItem();
2996 actionstream<<player->getName()<<" uses "<<item.name
2997 <<", pointing at "<<pointed.dump()<<std::endl;
2999 if(m_script->item_OnUse(
3000 item, playersao, pointed))
3002 // Apply returned ItemStack
3003 playersao->setWieldedItem(item);
3010 Catch invalid actions
3014 infostream<<"WARNING: Server: Invalid action "
3015 <<action<<std::endl;
3018 else if(command == TOSERVER_REMOVED_SOUNDS)
3020 std::string datastring((char*)&data[2], datasize-2);
3021 std::istringstream is(datastring, std::ios_base::binary);
3023 int num = readU16(is);
3024 for(int k=0; k<num; k++){
3025 s32 id = readS32(is);
3026 std::map<s32, ServerPlayingSound>::iterator i =
3027 m_playing_sounds.find(id);
3028 if(i == m_playing_sounds.end())
3030 ServerPlayingSound &psound = i->second;
3031 psound.clients.erase(peer_id);
3032 if(psound.clients.size() == 0)
3033 m_playing_sounds.erase(i++);
3036 else if(command == TOSERVER_NODEMETA_FIELDS)
3038 std::string datastring((char*)&data[2], datasize-2);
3039 std::istringstream is(datastring, std::ios_base::binary);
3041 v3s16 p = readV3S16(is);
3042 std::string formname = deSerializeString(is);
3043 int num = readU16(is);
3044 std::map<std::string, std::string> fields;
3045 for(int k=0; k<num; k++){
3046 std::string fieldname = deSerializeString(is);
3047 std::string fieldvalue = deSerializeLongString(is);
3048 fields[fieldname] = fieldvalue;
3051 // If something goes wrong, this player is to blame
3052 RollbackScopeActor rollback_scope(m_rollback,
3053 std::string("player:")+player->getName());
3055 // Check the target node for rollback data; leave others unnoticed
3056 RollbackNode rn_old(&m_env->getMap(), p, this);
3058 m_script->node_on_receive_fields(p, formname, fields,playersao);
3060 // Report rollback data
3061 RollbackNode rn_new(&m_env->getMap(), p, this);
3062 if(rollback() && rn_new != rn_old){
3063 RollbackAction action;
3064 action.setSetNode(p, rn_old, rn_new);
3065 rollback()->reportAction(action);
3068 else if(command == TOSERVER_INVENTORY_FIELDS)
3070 std::string datastring((char*)&data[2], datasize-2);
3071 std::istringstream is(datastring, std::ios_base::binary);
3073 std::string formname = deSerializeString(is);
3074 int num = readU16(is);
3075 std::map<std::string, std::string> fields;
3076 for(int k=0; k<num; k++){
3077 std::string fieldname = deSerializeString(is);
3078 std::string fieldvalue = deSerializeLongString(is);
3079 fields[fieldname] = fieldvalue;
3082 m_script->on_playerReceiveFields(playersao, formname, fields);
3086 infostream<<"Server::ProcessData(): Ignoring "
3087 "unknown command "<<command<<std::endl;
3091 catch(SendFailedException &e)
3093 errorstream<<"Server::ProcessData(): SendFailedException: "
3099 void Server::onMapEditEvent(MapEditEvent *event)
3101 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3102 if(m_ignore_map_edit_events)
3104 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3106 MapEditEvent *e = event->clone();
3107 m_unsent_map_edit_queue.push_back(e);
3110 Inventory* Server::getInventory(const InventoryLocation &loc)
3113 case InventoryLocation::UNDEFINED:
3116 case InventoryLocation::CURRENT_PLAYER:
3119 case InventoryLocation::PLAYER:
3121 Player *player = m_env->getPlayer(loc.name.c_str());
3124 PlayerSAO *playersao = player->getPlayerSAO();
3127 return playersao->getInventory();
3130 case InventoryLocation::NODEMETA:
3132 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3135 return meta->getInventory();
3138 case InventoryLocation::DETACHED:
3140 if(m_detached_inventories.count(loc.name) == 0)
3142 return m_detached_inventories[loc.name];
3150 void Server::setInventoryModified(const InventoryLocation &loc)
3153 case InventoryLocation::UNDEFINED:
3156 case InventoryLocation::PLAYER:
3158 Player *player = m_env->getPlayer(loc.name.c_str());
3161 PlayerSAO *playersao = player->getPlayerSAO();
3164 playersao->m_inventory_not_sent = true;
3165 playersao->m_wielded_item_not_sent = true;
3168 case InventoryLocation::NODEMETA:
3170 v3s16 blockpos = getNodeBlockPos(loc.p);
3172 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3174 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3176 setBlockNotSent(blockpos);
3179 case InventoryLocation::DETACHED:
3181 sendDetachedInventoryToAll(loc.name);
3189 //std::list<PlayerInfo> Server::getPlayerInfo()
3191 // DSTACK(__FUNCTION_NAME);
3192 // JMutexAutoLock envlock(m_env_mutex);
3193 // JMutexAutoLock conlock(m_con_mutex);
3195 // std::list<PlayerInfo> list;
3197 // std::list<Player*> players = m_env->getPlayers();
3199 // std::list<Player*>::iterator i;
3200 // for(i = players.begin();
3201 // i != players.end(); ++i)
3205 // Player *player = *i;
3208 // // Copy info from connection to info struct
3209 // info.id = player->peer_id;
3210 // info.address = m_con.GetPeerAddress(player->peer_id);
3211 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3213 // catch(con::PeerNotFoundException &e)
3215 // // Set dummy peer info
3217 // info.address = Address(0,0,0,0,0);
3218 // info.avg_rtt = 0.0;
3221 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3222 // info.position = player->getPosition();
3224 // list.push_back(info);
3231 void Server::peerAdded(con::Peer *peer)
3233 DSTACK(__FUNCTION_NAME);
3234 verbosestream<<"Server::peerAdded(): peer->id="
3235 <<peer->id<<std::endl;
3238 c.type = PEER_ADDED;
3239 c.peer_id = peer->id;
3241 m_peer_change_queue.push_back(c);
3244 void Server::deletingPeer(con::Peer *peer, bool timeout)
3246 DSTACK(__FUNCTION_NAME);
3247 verbosestream<<"Server::deletingPeer(): peer->id="
3248 <<peer->id<<", timeout="<<timeout<<std::endl;
3251 c.type = PEER_REMOVED;
3252 c.peer_id = peer->id;
3253 c.timeout = timeout;
3254 m_peer_change_queue.push_back(c);
3261 void Server::SendMovement(con::Connection &con, u16 peer_id)
3263 DSTACK(__FUNCTION_NAME);
3264 std::ostringstream os(std::ios_base::binary);
3266 writeU16(os, TOCLIENT_MOVEMENT);
3267 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3268 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3269 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3270 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3271 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3272 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3273 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3274 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3275 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3276 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3277 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3278 writeF1000(os, g_settings->getFloat("movement_gravity"));
3281 std::string s = os.str();
3282 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3284 con.Send(peer_id, 0, data, true);
3287 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3289 DSTACK(__FUNCTION_NAME);
3290 std::ostringstream os(std::ios_base::binary);
3292 writeU16(os, TOCLIENT_HP);
3296 std::string s = os.str();
3297 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3299 con.Send(peer_id, 0, data, true);
3302 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3303 const std::wstring &reason)
3305 DSTACK(__FUNCTION_NAME);
3306 std::ostringstream os(std::ios_base::binary);
3308 writeU16(os, TOCLIENT_ACCESS_DENIED);
3309 os<<serializeWideString(reason);
3312 std::string s = os.str();
3313 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3315 con.Send(peer_id, 0, data, true);
3318 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3319 bool set_camera_point_target, v3f camera_point_target)
3321 DSTACK(__FUNCTION_NAME);
3322 std::ostringstream os(std::ios_base::binary);
3324 writeU16(os, TOCLIENT_DEATHSCREEN);
3325 writeU8(os, set_camera_point_target);
3326 writeV3F1000(os, camera_point_target);
3329 std::string s = os.str();
3330 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3332 con.Send(peer_id, 0, data, true);
3335 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3336 IItemDefManager *itemdef, u16 protocol_version)
3338 DSTACK(__FUNCTION_NAME);
3339 std::ostringstream os(std::ios_base::binary);
3343 u32 length of the next item
3344 zlib-compressed serialized ItemDefManager
3346 writeU16(os, TOCLIENT_ITEMDEF);
3347 std::ostringstream tmp_os(std::ios::binary);
3348 itemdef->serialize(tmp_os, protocol_version);
3349 std::ostringstream tmp_os2(std::ios::binary);
3350 compressZlib(tmp_os.str(), tmp_os2);
3351 os<<serializeLongString(tmp_os2.str());
3354 std::string s = os.str();
3355 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3356 <<"): size="<<s.size()<<std::endl;
3357 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3359 con.Send(peer_id, 0, data, true);
3362 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3363 INodeDefManager *nodedef, u16 protocol_version)
3365 DSTACK(__FUNCTION_NAME);
3366 std::ostringstream os(std::ios_base::binary);
3370 u32 length of the next item
3371 zlib-compressed serialized NodeDefManager
3373 writeU16(os, TOCLIENT_NODEDEF);
3374 std::ostringstream tmp_os(std::ios::binary);
3375 nodedef->serialize(tmp_os, protocol_version);
3376 std::ostringstream tmp_os2(std::ios::binary);
3377 compressZlib(tmp_os.str(), tmp_os2);
3378 os<<serializeLongString(tmp_os2.str());
3381 std::string s = os.str();
3382 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3383 <<"): size="<<s.size()<<std::endl;
3384 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3386 con.Send(peer_id, 0, data, true);
3390 Non-static send methods
3393 void Server::SendInventory(u16 peer_id)
3395 DSTACK(__FUNCTION_NAME);
3397 PlayerSAO *playersao = getPlayerSAO(peer_id);
3400 playersao->m_inventory_not_sent = false;
3406 std::ostringstream os;
3407 playersao->getInventory()->serialize(os);
3409 std::string s = os.str();
3411 SharedBuffer<u8> data(s.size()+2);
3412 writeU16(&data[0], TOCLIENT_INVENTORY);
3413 memcpy(&data[2], s.c_str(), s.size());
3416 m_con.Send(peer_id, 0, data, true);
3419 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3421 DSTACK(__FUNCTION_NAME);
3423 std::ostringstream os(std::ios_base::binary);
3427 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3428 os.write((char*)buf, 2);
3431 writeU16(buf, message.size());
3432 os.write((char*)buf, 2);
3435 for(u32 i=0; i<message.size(); i++)
3439 os.write((char*)buf, 2);
3443 std::string s = os.str();
3444 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3446 m_con.Send(peer_id, 0, data, true);
3449 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3450 const std::string formname)
3452 DSTACK(__FUNCTION_NAME);
3454 std::ostringstream os(std::ios_base::binary);
3458 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3459 os.write((char*)buf, 2);
3460 os<<serializeLongString(formspec);
3461 os<<serializeString(formname);
3464 std::string s = os.str();
3465 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3467 m_con.Send(peer_id, 0, data, true);
3470 // Spawns a particle on peer with peer_id
3471 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3472 float expirationtime, float size, bool collisiondetection,
3473 std::string texture)
3475 DSTACK(__FUNCTION_NAME);
3477 std::ostringstream os(std::ios_base::binary);
3478 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3479 writeV3F1000(os, pos);
3480 writeV3F1000(os, velocity);
3481 writeV3F1000(os, acceleration);
3482 writeF1000(os, expirationtime);
3483 writeF1000(os, size);
3484 writeU8(os, collisiondetection);
3485 os<<serializeLongString(texture);
3488 std::string s = os.str();
3489 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3491 m_con.Send(peer_id, 0, data, true);
3494 // Spawns a particle on all peers
3495 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3496 float expirationtime, float size, bool collisiondetection,
3497 std::string texture)
3499 for(std::map<u16, RemoteClient*>::iterator
3500 i = m_clients.begin();
3501 i != m_clients.end(); i++)
3503 // Get client and check that it is valid
3504 RemoteClient *client = i->second;
3505 assert(client->peer_id == i->first);
3506 if(client->serialization_version == SER_FMT_VER_INVALID)
3509 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3510 expirationtime, size, collisiondetection, texture);
3514 // Adds a ParticleSpawner on peer with peer_id
3515 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3516 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3517 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3519 DSTACK(__FUNCTION_NAME);
3521 std::ostringstream os(std::ios_base::binary);
3522 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3524 writeU16(os, amount);
3525 writeF1000(os, spawntime);
3526 writeV3F1000(os, minpos);
3527 writeV3F1000(os, maxpos);
3528 writeV3F1000(os, minvel);
3529 writeV3F1000(os, maxvel);
3530 writeV3F1000(os, minacc);
3531 writeV3F1000(os, maxacc);
3532 writeF1000(os, minexptime);
3533 writeF1000(os, maxexptime);
3534 writeF1000(os, minsize);
3535 writeF1000(os, maxsize);
3536 writeU8(os, collisiondetection);
3537 os<<serializeLongString(texture);
3541 std::string s = os.str();
3542 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3544 m_con.Send(peer_id, 0, data, true);
3547 // Adds a ParticleSpawner on all peers
3548 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3549 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3550 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3552 for(std::map<u16, RemoteClient*>::iterator
3553 i = m_clients.begin();
3554 i != m_clients.end(); i++)
3556 // Get client and check that it is valid
3557 RemoteClient *client = i->second;
3558 assert(client->peer_id == i->first);
3559 if(client->serialization_version == SER_FMT_VER_INVALID)
3562 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3563 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3564 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3568 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3570 DSTACK(__FUNCTION_NAME);
3572 std::ostringstream os(std::ios_base::binary);
3573 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3578 std::string s = os.str();
3579 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3581 m_con.Send(peer_id, 0, data, true);
3584 void Server::SendDeleteParticleSpawnerAll(u32 id)
3586 for(std::map<u16, RemoteClient*>::iterator
3587 i = m_clients.begin();
3588 i != m_clients.end(); i++)
3590 // Get client and check that it is valid
3591 RemoteClient *client = i->second;
3592 assert(client->peer_id == i->first);
3593 if(client->serialization_version == SER_FMT_VER_INVALID)
3596 SendDeleteParticleSpawner(client->peer_id, id);
3600 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3602 std::ostringstream os(std::ios_base::binary);
3605 writeU16(os, TOCLIENT_HUDADD);
3607 writeU8(os, (u8)form->type);
3608 writeV2F1000(os, form->pos);
3609 os << serializeString(form->name);
3610 writeV2F1000(os, form->scale);
3611 os << serializeString(form->text);
3612 writeU32(os, form->number);
3613 writeU32(os, form->item);
3614 writeU32(os, form->dir);
3615 writeV2F1000(os, form->align);
3616 writeV2F1000(os, form->offset);
3619 std::string s = os.str();
3620 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3622 m_con.Send(peer_id, 0, data, true);
3625 void Server::SendHUDRemove(u16 peer_id, u32 id)
3627 std::ostringstream os(std::ios_base::binary);
3630 writeU16(os, TOCLIENT_HUDRM);
3634 std::string s = os.str();
3635 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3637 m_con.Send(peer_id, 0, data, true);
3640 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3642 std::ostringstream os(std::ios_base::binary);
3645 writeU16(os, TOCLIENT_HUDCHANGE);
3647 writeU8(os, (u8)stat);
3650 case HUD_STAT_SCALE:
3651 case HUD_STAT_ALIGN:
3652 case HUD_STAT_OFFSET:
3653 writeV2F1000(os, *(v2f *)value);
3657 os << serializeString(*(std::string *)value);
3659 case HUD_STAT_NUMBER:
3663 writeU32(os, *(u32 *)value);
3668 std::string s = os.str();
3669 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3671 m_con.Send(peer_id, 0, data, true);
3674 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3676 std::ostringstream os(std::ios_base::binary);
3679 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3680 writeU32(os, flags);
3684 std::string s = os.str();
3685 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3687 m_con.Send(peer_id, 0, data, true);
3690 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3692 std::ostringstream os(std::ios_base::binary);
3695 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3696 writeU16(os, param);
3697 os<<serializeString(value);
3700 std::string s = os.str();
3701 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3703 m_con.Send(peer_id, 0, data, true);
3706 void Server::BroadcastChatMessage(const std::wstring &message)
3708 for(std::map<u16, RemoteClient*>::iterator
3709 i = m_clients.begin();
3710 i != m_clients.end(); ++i)
3712 // Get client and check that it is valid
3713 RemoteClient *client = i->second;
3714 assert(client->peer_id == i->first);
3715 if(client->serialization_version == SER_FMT_VER_INVALID)
3718 SendChatMessage(client->peer_id, message);
3722 void Server::SendPlayerHP(u16 peer_id)
3724 DSTACK(__FUNCTION_NAME);
3725 PlayerSAO *playersao = getPlayerSAO(peer_id);
3727 playersao->m_hp_not_sent = false;
3728 SendHP(m_con, peer_id, playersao->getHP());
3731 void Server::SendMovePlayer(u16 peer_id)
3733 DSTACK(__FUNCTION_NAME);
3734 Player *player = m_env->getPlayer(peer_id);
3737 std::ostringstream os(std::ios_base::binary);
3738 writeU16(os, TOCLIENT_MOVE_PLAYER);
3739 writeV3F1000(os, player->getPosition());
3740 writeF1000(os, player->getPitch());
3741 writeF1000(os, player->getYaw());
3744 v3f pos = player->getPosition();
3745 f32 pitch = player->getPitch();
3746 f32 yaw = player->getYaw();
3747 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3748 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3755 std::string s = os.str();
3756 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3758 m_con.Send(peer_id, 0, data, true);
3761 void Server::SendPlayerPrivileges(u16 peer_id)
3763 Player *player = m_env->getPlayer(peer_id);
3765 if(player->peer_id == PEER_ID_INEXISTENT)
3768 std::set<std::string> privs;
3769 m_script->getAuth(player->getName(), NULL, &privs);
3771 std::ostringstream os(std::ios_base::binary);
3772 writeU16(os, TOCLIENT_PRIVILEGES);
3773 writeU16(os, privs.size());
3774 for(std::set<std::string>::const_iterator i = privs.begin();
3775 i != privs.end(); i++){
3776 os<<serializeString(*i);
3780 std::string s = os.str();
3781 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3783 m_con.Send(peer_id, 0, data, true);
3786 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3788 Player *player = m_env->getPlayer(peer_id);
3790 if(player->peer_id == PEER_ID_INEXISTENT)
3793 std::ostringstream os(std::ios_base::binary);
3794 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3795 os<<serializeLongString(player->inventory_formspec);
3798 std::string s = os.str();
3799 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3801 m_con.Send(peer_id, 0, data, true);
3804 s32 Server::playSound(const SimpleSoundSpec &spec,
3805 const ServerSoundParams ¶ms)
3807 // Find out initial position of sound
3808 bool pos_exists = false;
3809 v3f pos = params.getPos(m_env, &pos_exists);
3810 // If position is not found while it should be, cancel sound
3811 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3813 // Filter destination clients
3814 std::set<RemoteClient*> dst_clients;
3815 if(params.to_player != "")
3817 Player *player = m_env->getPlayer(params.to_player.c_str());
3819 infostream<<"Server::playSound: Player \""<<params.to_player
3820 <<"\" not found"<<std::endl;
3823 if(player->peer_id == PEER_ID_INEXISTENT){
3824 infostream<<"Server::playSound: Player \""<<params.to_player
3825 <<"\" not connected"<<std::endl;
3828 RemoteClient *client = getClient(player->peer_id);
3829 dst_clients.insert(client);
3833 for(std::map<u16, RemoteClient*>::iterator
3834 i = m_clients.begin(); i != m_clients.end(); ++i)
3836 RemoteClient *client = i->second;
3837 Player *player = m_env->getPlayer(client->peer_id);
3841 if(player->getPosition().getDistanceFrom(pos) >
3842 params.max_hear_distance)
3845 dst_clients.insert(client);
3848 if(dst_clients.size() == 0)
3851 s32 id = m_next_sound_id++;
3852 // The sound will exist as a reference in m_playing_sounds
3853 m_playing_sounds[id] = ServerPlayingSound();
3854 ServerPlayingSound &psound = m_playing_sounds[id];
3855 psound.params = params;
3856 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3857 i != dst_clients.end(); i++)
3858 psound.clients.insert((*i)->peer_id);
3860 std::ostringstream os(std::ios_base::binary);
3861 writeU16(os, TOCLIENT_PLAY_SOUND);
3863 os<<serializeString(spec.name);
3864 writeF1000(os, spec.gain * params.gain);
3865 writeU8(os, params.type);
3866 writeV3F1000(os, pos);
3867 writeU16(os, params.object);
3868 writeU8(os, params.loop);
3870 std::string s = os.str();
3871 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3873 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3874 i != dst_clients.end(); i++){
3876 m_con.Send((*i)->peer_id, 0, data, true);
3880 void Server::stopSound(s32 handle)
3882 // Get sound reference
3883 std::map<s32, ServerPlayingSound>::iterator i =
3884 m_playing_sounds.find(handle);
3885 if(i == m_playing_sounds.end())
3887 ServerPlayingSound &psound = i->second;
3889 std::ostringstream os(std::ios_base::binary);
3890 writeU16(os, TOCLIENT_STOP_SOUND);
3891 writeS32(os, handle);
3893 std::string s = os.str();
3894 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3896 for(std::set<u16>::iterator i = psound.clients.begin();
3897 i != psound.clients.end(); i++){
3899 m_con.Send(*i, 0, data, true);
3901 // Remove sound reference
3902 m_playing_sounds.erase(i);
3905 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3906 std::list<u16> *far_players, float far_d_nodes)
3908 float maxd = far_d_nodes*BS;
3909 v3f p_f = intToFloat(p, BS);
3913 SharedBuffer<u8> reply(replysize);
3914 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3915 writeS16(&reply[2], p.X);
3916 writeS16(&reply[4], p.Y);
3917 writeS16(&reply[6], p.Z);
3919 for(std::map<u16, RemoteClient*>::iterator
3920 i = m_clients.begin();
3921 i != m_clients.end(); ++i)
3923 // Get client and check that it is valid
3924 RemoteClient *client = i->second;
3925 assert(client->peer_id == i->first);
3926 if(client->serialization_version == SER_FMT_VER_INVALID)
3929 // Don't send if it's the same one
3930 if(client->peer_id == ignore_id)
3936 Player *player = m_env->getPlayer(client->peer_id);
3939 // If player is far away, only set modified blocks not sent
3940 v3f player_pos = player->getPosition();
3941 if(player_pos.getDistanceFrom(p_f) > maxd)
3943 far_players->push_back(client->peer_id);
3950 m_con.Send(client->peer_id, 0, reply, true);
3954 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3955 std::list<u16> *far_players, float far_d_nodes)
3957 float maxd = far_d_nodes*BS;
3958 v3f p_f = intToFloat(p, BS);
3960 for(std::map<u16, RemoteClient*>::iterator
3961 i = m_clients.begin();
3962 i != m_clients.end(); ++i)
3964 // Get client and check that it is valid
3965 RemoteClient *client = i->second;
3966 assert(client->peer_id == i->first);
3967 if(client->serialization_version == SER_FMT_VER_INVALID)
3970 // Don't send if it's the same one
3971 if(client->peer_id == ignore_id)
3977 Player *player = m_env->getPlayer(client->peer_id);
3980 // If player is far away, only set modified blocks not sent
3981 v3f player_pos = player->getPosition();
3982 if(player_pos.getDistanceFrom(p_f) > maxd)
3984 far_players->push_back(client->peer_id);
3991 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3992 SharedBuffer<u8> reply(replysize);
3993 writeU16(&reply[0], TOCLIENT_ADDNODE);
3994 writeS16(&reply[2], p.X);
3995 writeS16(&reply[4], p.Y);
3996 writeS16(&reply[6], p.Z);
3997 n.serialize(&reply[8], client->serialization_version);
4000 m_con.Send(client->peer_id, 0, reply, true);
4004 void Server::setBlockNotSent(v3s16 p)
4006 for(std::map<u16, RemoteClient*>::iterator
4007 i = m_clients.begin();
4008 i != m_clients.end(); ++i)
4010 RemoteClient *client = i->second;
4011 client->SetBlockNotSent(p);
4015 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4017 DSTACK(__FUNCTION_NAME);
4019 v3s16 p = block->getPos();
4023 bool completely_air = true;
4024 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4025 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4026 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4028 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4030 completely_air = false;
4031 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4036 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4038 infostream<<"[completely air] ";
4039 infostream<<std::endl;
4043 Create a packet with the block in the right format
4046 std::ostringstream os(std::ios_base::binary);
4047 block->serialize(os, ver, false);
4048 std::string s = os.str();
4049 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4051 u32 replysize = 8 + blockdata.getSize();
4052 SharedBuffer<u8> reply(replysize);
4053 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4054 writeS16(&reply[2], p.X);
4055 writeS16(&reply[4], p.Y);
4056 writeS16(&reply[6], p.Z);
4057 memcpy(&reply[8], *blockdata, blockdata.getSize());
4059 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4060 <<": \tpacket size: "<<replysize<<std::endl;*/
4065 m_con.Send(peer_id, 1, reply, true);
4068 void Server::SendBlocks(float dtime)
4070 DSTACK(__FUNCTION_NAME);
4072 JMutexAutoLock envlock(m_env_mutex);
4073 JMutexAutoLock conlock(m_con_mutex);
4075 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4077 std::vector<PrioritySortedBlockTransfer> queue;
4079 s32 total_sending = 0;
4082 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4084 for(std::map<u16, RemoteClient*>::iterator
4085 i = m_clients.begin();
4086 i != m_clients.end(); ++i)
4088 RemoteClient *client = i->second;
4089 assert(client->peer_id == i->first);
4091 // If definitions and textures have not been sent, don't
4092 // send MapBlocks either
4093 if(!client->definitions_sent)
4096 total_sending += client->SendingCount();
4098 if(client->serialization_version == SER_FMT_VER_INVALID)
4101 client->GetNextBlocks(this, dtime, queue);
4106 // Lowest priority number comes first.
4107 // Lowest is most important.
4108 std::sort(queue.begin(), queue.end());
4110 for(u32 i=0; i<queue.size(); i++)
4112 //TODO: Calculate limit dynamically
4113 if(total_sending >= g_settings->getS32
4114 ("max_simultaneous_block_sends_server_total"))
4117 PrioritySortedBlockTransfer q = queue[i];
4119 MapBlock *block = NULL;
4122 block = m_env->getMap().getBlockNoCreate(q.pos);
4124 catch(InvalidPositionException &e)
4129 RemoteClient *client = getClient(q.peer_id);
4131 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4133 client->SentBlock(q.pos);
4139 void Server::fillMediaCache()
4141 DSTACK(__FUNCTION_NAME);
4143 infostream<<"Server: Calculating media file checksums"<<std::endl;
4145 // Collect all media file paths
4146 std::list<std::string> paths;
4147 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4148 i != m_mods.end(); i++){
4149 const ModSpec &mod = *i;
4150 paths.push_back(mod.path + DIR_DELIM + "textures");
4151 paths.push_back(mod.path + DIR_DELIM + "sounds");
4152 paths.push_back(mod.path + DIR_DELIM + "media");
4153 paths.push_back(mod.path + DIR_DELIM + "models");
4155 std::string path_all = "textures";
4156 paths.push_back(path_all + DIR_DELIM + "all");
4158 // Collect media file information from paths into cache
4159 for(std::list<std::string>::iterator i = paths.begin();
4160 i != paths.end(); i++)
4162 std::string mediapath = *i;
4163 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4164 for(u32 j=0; j<dirlist.size(); j++){
4165 if(dirlist[j].dir) // Ignode dirs
4167 std::string filename = dirlist[j].name;
4168 // If name contains illegal characters, ignore the file
4169 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4170 infostream<<"Server: ignoring illegal file name: \""
4171 <<filename<<"\""<<std::endl;
4174 // If name is not in a supported format, ignore it
4175 const char *supported_ext[] = {
4176 ".png", ".jpg", ".bmp", ".tga",
4177 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4179 ".x", ".b3d", ".md2", ".obj",
4182 if(removeStringEnd(filename, supported_ext) == ""){
4183 infostream<<"Server: ignoring unsupported file extension: \""
4184 <<filename<<"\""<<std::endl;
4187 // Ok, attempt to load the file and add to cache
4188 std::string filepath = mediapath + DIR_DELIM + filename;
4190 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4191 if(fis.good() == false){
4192 errorstream<<"Server::fillMediaCache(): Could not open \""
4193 <<filename<<"\" for reading"<<std::endl;
4196 std::ostringstream tmp_os(std::ios_base::binary);
4200 fis.read(buf, 1024);
4201 std::streamsize len = fis.gcount();
4202 tmp_os.write(buf, len);
4211 errorstream<<"Server::fillMediaCache(): Failed to read \""
4212 <<filename<<"\""<<std::endl;
4215 if(tmp_os.str().length() == 0){
4216 errorstream<<"Server::fillMediaCache(): Empty file \""
4217 <<filepath<<"\""<<std::endl;
4222 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4224 unsigned char *digest = sha1.getDigest();
4225 std::string sha1_base64 = base64_encode(digest, 20);
4226 std::string sha1_hex = hex_encode((char*)digest, 20);
4230 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4231 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4236 struct SendableMediaAnnouncement
4239 std::string sha1_digest;
4241 SendableMediaAnnouncement(const std::string name_="",
4242 const std::string sha1_digest_=""):
4244 sha1_digest(sha1_digest_)
4248 void Server::sendMediaAnnouncement(u16 peer_id)
4250 DSTACK(__FUNCTION_NAME);
4252 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4255 std::list<SendableMediaAnnouncement> file_announcements;
4257 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4258 i != m_media.end(); i++){
4260 file_announcements.push_back(
4261 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4265 std::ostringstream os(std::ios_base::binary);
4273 u16 length of sha1_digest
4278 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4279 writeU16(os, file_announcements.size());
4281 for(std::list<SendableMediaAnnouncement>::iterator
4282 j = file_announcements.begin();
4283 j != file_announcements.end(); ++j){
4284 os<<serializeString(j->name);
4285 os<<serializeString(j->sha1_digest);
4287 os<<serializeString(g_settings->get("remote_media"));
4290 std::string s = os.str();
4291 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4294 m_con.Send(peer_id, 0, data, true);
4297 struct SendableMedia
4303 SendableMedia(const std::string &name_="", const std::string path_="",
4304 const std::string &data_=""):
4311 void Server::sendRequestedMedia(u16 peer_id,
4312 const std::list<MediaRequest> &tosend)
4314 DSTACK(__FUNCTION_NAME);
4316 verbosestream<<"Server::sendRequestedMedia(): "
4317 <<"Sending files to client"<<std::endl;
4321 // Put 5kB in one bunch (this is not accurate)
4322 u32 bytes_per_bunch = 5000;
4324 std::vector< std::list<SendableMedia> > file_bunches;
4325 file_bunches.push_back(std::list<SendableMedia>());
4327 u32 file_size_bunch_total = 0;
4329 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4330 i != tosend.end(); ++i)
4332 if(m_media.find(i->name) == m_media.end()){
4333 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4334 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4338 //TODO get path + name
4339 std::string tpath = m_media[(*i).name].path;
4342 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4343 if(fis.good() == false){
4344 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4345 <<tpath<<"\" for reading"<<std::endl;
4348 std::ostringstream tmp_os(std::ios_base::binary);
4352 fis.read(buf, 1024);
4353 std::streamsize len = fis.gcount();
4354 tmp_os.write(buf, len);
4355 file_size_bunch_total += len;
4364 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4365 <<(*i).name<<"\""<<std::endl;
4368 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4369 <<tname<<"\""<<std::endl;*/
4371 file_bunches[file_bunches.size()-1].push_back(
4372 SendableMedia((*i).name, tpath, tmp_os.str()));
4374 // Start next bunch if got enough data
4375 if(file_size_bunch_total >= bytes_per_bunch){
4376 file_bunches.push_back(std::list<SendableMedia>());
4377 file_size_bunch_total = 0;
4382 /* Create and send packets */
4384 u32 num_bunches = file_bunches.size();
4385 for(u32 i=0; i<num_bunches; i++)
4387 std::ostringstream os(std::ios_base::binary);
4391 u16 total number of texture bunches
4392 u16 index of this bunch
4393 u32 number of files in this bunch
4402 writeU16(os, TOCLIENT_MEDIA);
4403 writeU16(os, num_bunches);
4405 writeU32(os, file_bunches[i].size());
4407 for(std::list<SendableMedia>::iterator
4408 j = file_bunches[i].begin();
4409 j != file_bunches[i].end(); ++j){
4410 os<<serializeString(j->name);
4411 os<<serializeLongString(j->data);
4415 std::string s = os.str();
4416 verbosestream<<"Server::sendRequestedMedia(): bunch "
4417 <<i<<"/"<<num_bunches
4418 <<" files="<<file_bunches[i].size()
4419 <<" size=" <<s.size()<<std::endl;
4420 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4422 m_con.Send(peer_id, 0, data, true);
4426 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4428 if(m_detached_inventories.count(name) == 0){
4429 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4432 Inventory *inv = m_detached_inventories[name];
4434 std::ostringstream os(std::ios_base::binary);
4435 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4436 os<<serializeString(name);
4440 std::string s = os.str();
4441 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4443 m_con.Send(peer_id, 0, data, true);
4446 void Server::sendDetachedInventoryToAll(const std::string &name)
4448 DSTACK(__FUNCTION_NAME);
4450 for(std::map<u16, RemoteClient*>::iterator
4451 i = m_clients.begin();
4452 i != m_clients.end(); ++i){
4453 RemoteClient *client = i->second;
4454 sendDetachedInventory(name, client->peer_id);
4458 void Server::sendDetachedInventories(u16 peer_id)
4460 DSTACK(__FUNCTION_NAME);
4462 for(std::map<std::string, Inventory*>::iterator
4463 i = m_detached_inventories.begin();
4464 i != m_detached_inventories.end(); i++){
4465 const std::string &name = i->first;
4466 //Inventory *inv = i->second;
4467 sendDetachedInventory(name, peer_id);
4475 void Server::DiePlayer(u16 peer_id)
4477 DSTACK(__FUNCTION_NAME);
4479 PlayerSAO *playersao = getPlayerSAO(peer_id);
4482 infostream<<"Server::DiePlayer(): Player "
4483 <<playersao->getPlayer()->getName()
4484 <<" dies"<<std::endl;
4486 playersao->setHP(0);
4488 // Trigger scripted stuff
4489 m_script->on_dieplayer(playersao);
4491 SendPlayerHP(peer_id);
4492 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4495 void Server::RespawnPlayer(u16 peer_id)
4497 DSTACK(__FUNCTION_NAME);
4499 PlayerSAO *playersao = getPlayerSAO(peer_id);
4502 infostream<<"Server::RespawnPlayer(): Player "
4503 <<playersao->getPlayer()->getName()
4504 <<" respawns"<<std::endl;
4506 playersao->setHP(PLAYER_MAX_HP);
4508 bool repositioned = m_script->on_respawnplayer(playersao);
4510 v3f pos = findSpawnPos(m_env->getServerMap());
4511 playersao->setPos(pos);
4515 void Server::UpdateCrafting(u16 peer_id)
4517 DSTACK(__FUNCTION_NAME);
4519 Player* player = m_env->getPlayer(peer_id);
4522 // Get a preview for crafting
4524 getCraftingResult(&player->inventory, preview, false, this);
4526 // Put the new preview in
4527 InventoryList *plist = player->inventory.getList("craftpreview");
4529 assert(plist->getSize() >= 1);
4530 plist->changeItem(0, preview);
4533 RemoteClient* Server::getClient(u16 peer_id)
4535 DSTACK(__FUNCTION_NAME);
4536 //JMutexAutoLock lock(m_con_mutex);
4537 std::map<u16, RemoteClient*>::iterator n;
4538 n = m_clients.find(peer_id);
4539 // A client should exist for all peers
4540 assert(n != m_clients.end());
4544 std::wstring Server::getStatusString()
4546 std::wostringstream os(std::ios_base::binary);
4549 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4551 os<<L", uptime="<<m_uptime.get();
4552 // Information about clients
4553 std::map<u16, RemoteClient*>::iterator i;
4556 for(i = m_clients.begin(), first = true;
4557 i != m_clients.end(); ++i)
4559 // Get client and check that it is valid
4560 RemoteClient *client = i->second;
4561 assert(client->peer_id == i->first);
4562 if(client->serialization_version == SER_FMT_VER_INVALID)
4565 Player *player = m_env->getPlayer(client->peer_id);
4566 // Get name of player
4567 std::wstring name = L"unknown";
4569 name = narrow_to_wide(player->getName());
4570 // Add name to information string
4578 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4579 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4580 if(g_settings->get("motd") != "")
4581 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4585 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4587 std::set<std::string> privs;
4588 m_script->getAuth(name, NULL, &privs);
4592 bool Server::checkPriv(const std::string &name, const std::string &priv)
4594 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4595 return (privs.count(priv) != 0);
4598 void Server::reportPrivsModified(const std::string &name)
4601 for(std::map<u16, RemoteClient*>::iterator
4602 i = m_clients.begin();
4603 i != m_clients.end(); ++i){
4604 RemoteClient *client = i->second;
4605 Player *player = m_env->getPlayer(client->peer_id);
4606 reportPrivsModified(player->getName());
4609 Player *player = m_env->getPlayer(name.c_str());
4612 SendPlayerPrivileges(player->peer_id);
4613 PlayerSAO *sao = player->getPlayerSAO();
4616 sao->updatePrivileges(
4617 getPlayerEffectivePrivs(name),
4622 void Server::reportInventoryFormspecModified(const std::string &name)
4624 Player *player = m_env->getPlayer(name.c_str());
4627 SendPlayerInventoryFormspec(player->peer_id);
4630 // Saves g_settings to configpath given at initialization
4631 void Server::saveConfig()
4633 if(m_path_config != "")
4634 g_settings->updateConfigFile(m_path_config.c_str());
4637 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4639 Player *player = m_env->getPlayer(name);
4643 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4645 SendChatMessage(player->peer_id, msg);
4648 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4650 Player *player = m_env->getPlayer(playername);
4654 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4658 SendShowFormspecMessage(player->peer_id, formspec, formname);
4662 u32 Server::hudAdd(Player *player, HudElement *form) {
4666 u32 id = hud_get_free_id(player);
4667 if (id < player->hud.size())
4668 player->hud[id] = form;
4670 player->hud.push_back(form);
4672 SendHUDAdd(player->peer_id, id, form);
4676 bool Server::hudRemove(Player *player, u32 id) {
4677 if (!player || id >= player->hud.size() || !player->hud[id])
4680 delete player->hud[id];
4681 player->hud[id] = NULL;
4683 SendHUDRemove(player->peer_id, id);
4687 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4691 SendHUDChange(player->peer_id, id, stat, data);
4695 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4699 SendHUDSetFlags(player->peer_id, flags, mask);
4703 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4706 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4709 std::ostringstream os(std::ios::binary);
4710 writeS32(os, hotbar_itemcount);
4711 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4715 void Server::notifyPlayers(const std::wstring msg)
4717 BroadcastChatMessage(msg);
4720 void Server::spawnParticle(const char *playername, v3f pos,
4721 v3f velocity, v3f acceleration,
4722 float expirationtime, float size, bool
4723 collisiondetection, std::string texture)
4725 Player *player = m_env->getPlayer(playername);
4728 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4729 expirationtime, size, collisiondetection, texture);
4732 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4733 float expirationtime, float size,
4734 bool collisiondetection, std::string texture)
4736 SendSpawnParticleAll(pos, velocity, acceleration,
4737 expirationtime, size, collisiondetection, texture);
4740 u32 Server::addParticleSpawner(const char *playername,
4741 u16 amount, float spawntime,
4742 v3f minpos, v3f maxpos,
4743 v3f minvel, v3f maxvel,
4744 v3f minacc, v3f maxacc,
4745 float minexptime, float maxexptime,
4746 float minsize, float maxsize,
4747 bool collisiondetection, std::string texture)
4749 Player *player = m_env->getPlayer(playername);
4754 for(;;) // look for unused particlespawner id
4757 if (std::find(m_particlespawner_ids.begin(),
4758 m_particlespawner_ids.end(), id)
4759 == m_particlespawner_ids.end())
4761 m_particlespawner_ids.push_back(id);
4766 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4767 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4768 minexptime, maxexptime, minsize, maxsize,
4769 collisiondetection, texture, id);
4774 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4775 v3f minpos, v3f maxpos,
4776 v3f minvel, v3f maxvel,
4777 v3f minacc, v3f maxacc,
4778 float minexptime, float maxexptime,
4779 float minsize, float maxsize,
4780 bool collisiondetection, std::string texture)
4783 for(;;) // look for unused particlespawner id
4786 if (std::find(m_particlespawner_ids.begin(),
4787 m_particlespawner_ids.end(), id)
4788 == m_particlespawner_ids.end())
4790 m_particlespawner_ids.push_back(id);
4795 SendAddParticleSpawnerAll(amount, spawntime,
4796 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4797 minexptime, maxexptime, minsize, maxsize,
4798 collisiondetection, texture, id);
4803 void Server::deleteParticleSpawner(const char *playername, u32 id)
4805 Player *player = m_env->getPlayer(playername);
4809 m_particlespawner_ids.erase(
4810 std::remove(m_particlespawner_ids.begin(),
4811 m_particlespawner_ids.end(), id),
4812 m_particlespawner_ids.end());
4813 SendDeleteParticleSpawner(player->peer_id, id);
4816 void Server::deleteParticleSpawnerAll(u32 id)
4818 m_particlespawner_ids.erase(
4819 std::remove(m_particlespawner_ids.begin(),
4820 m_particlespawner_ids.end(), id),
4821 m_particlespawner_ids.end());
4822 SendDeleteParticleSpawnerAll(id);
4825 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4827 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4830 Inventory* Server::createDetachedInventory(const std::string &name)
4832 if(m_detached_inventories.count(name) > 0){
4833 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4834 delete m_detached_inventories[name];
4836 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4838 Inventory *inv = new Inventory(m_itemdef);
4840 m_detached_inventories[name] = inv;
4841 sendDetachedInventoryToAll(name);
4848 BoolScopeSet(bool *dst, bool val):
4851 m_orig_state = *m_dst;
4856 *m_dst = m_orig_state;
4863 // actions: time-reversed list
4864 // Return value: success/failure
4865 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4866 std::list<std::string> *log)
4868 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4869 ServerMap *map = (ServerMap*)(&m_env->getMap());
4870 // Disable rollback report sink while reverting
4871 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4873 // Fail if no actions to handle
4874 if(actions.empty()){
4875 log->push_back("Nothing to do.");
4882 for(std::list<RollbackAction>::const_iterator
4883 i = actions.begin();
4884 i != actions.end(); i++)
4886 const RollbackAction &action = *i;
4888 bool success = action.applyRevert(map, this, this);
4891 std::ostringstream os;
4892 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4893 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4895 log->push_back(os.str());
4897 std::ostringstream os;
4898 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4899 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4901 log->push_back(os.str());
4905 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4906 <<" failed"<<std::endl;
4908 // Call it done if less than half failed
4909 return num_failed <= num_tried/2;
4912 // IGameDef interface
4914 IItemDefManager* Server::getItemDefManager()
4918 INodeDefManager* Server::getNodeDefManager()
4922 ICraftDefManager* Server::getCraftDefManager()
4926 ITextureSource* Server::getTextureSource()
4930 IShaderSource* Server::getShaderSource()
4934 u16 Server::allocateUnknownNodeId(const std::string &name)
4936 return m_nodedef->allocateDummy(name);
4938 ISoundManager* Server::getSoundManager()
4940 return &dummySoundManager;
4942 MtEventManager* Server::getEventManager()
4946 IRollbackReportSink* Server::getRollbackReportSink()
4948 if(!m_enable_rollback_recording)
4950 if(!m_rollback_sink_enabled)
4955 IWritableItemDefManager* Server::getWritableItemDefManager()
4959 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4963 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4968 const ModSpec* Server::getModSpec(const std::string &modname)
4970 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4971 i != m_mods.end(); i++){
4972 const ModSpec &mod = *i;
4973 if(mod.name == modname)
4978 void Server::getModNames(std::list<std::string> &modlist)
4980 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4982 modlist.push_back(i->name);
4985 std::string Server::getBuiltinLuaPath()
4987 return porting::path_share + DIR_DELIM + "builtin";
4990 v3f findSpawnPos(ServerMap &map)
4992 //return v3f(50,50,50)*BS;
4997 nodepos = v2s16(0,0);
5002 s16 water_level = map.m_mgparams->water_level;
5004 // Try to find a good place a few times
5005 for(s32 i=0; i<1000; i++)
5008 // We're going to try to throw the player to this position
5009 v2s16 nodepos2d = v2s16(
5010 -range + (myrand() % (range * 2)),
5011 -range + (myrand() % (range * 2)));
5013 // Get ground height at point
5014 s16 groundheight = map.findGroundLevel(nodepos2d);
5015 if (groundheight <= water_level) // Don't go underwater
5017 if (groundheight > water_level + 6) // Don't go to high places
5020 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5021 bool is_good = false;
5023 for (s32 i = 0; i < 10; i++) {
5024 v3s16 blockpos = getNodeBlockPos(nodepos);
5025 map.emergeBlock(blockpos, true);
5026 content_t c = map.getNodeNoEx(nodepos).getContent();
5027 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5029 if (air_count >= 2){
5037 // Found a good place
5038 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5044 return intToFloat(nodepos, BS);
5047 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5049 RemotePlayer *player = NULL;
5050 bool newplayer = false;
5053 Try to get an existing player
5055 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5057 // If player is already connected, cancel
5058 if(player != NULL && player->peer_id != 0)
5060 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5065 If player with the wanted peer_id already exists, cancel.
5067 if(m_env->getPlayer(peer_id) != NULL)
5069 infostream<<"emergePlayer(): Player with wrong name but same"
5070 " peer_id already exists"<<std::endl;
5075 Create a new player if it doesn't exist yet
5080 player = new RemotePlayer(this);
5081 player->updateName(name);
5083 /* Set player position */
5084 infostream<<"Server: Finding spawn place for player \""
5085 <<name<<"\""<<std::endl;
5086 v3f pos = findSpawnPos(m_env->getServerMap());
5087 player->setPosition(pos);
5089 /* Add player to environment */
5090 m_env->addPlayer(player);
5094 Create a new player active object
5096 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5097 getPlayerEffectivePrivs(player->getName()),
5100 /* Clean up old HUD elements from previous sessions */
5101 player->hud.clear();
5103 /* Add object to environment */
5104 m_env->addActiveObject(playersao);
5108 m_script->on_newplayer(playersao);
5110 m_script->on_joinplayer(playersao);
5115 void Server::handlePeerChange(PeerChange &c)
5117 JMutexAutoLock envlock(m_env_mutex);
5118 JMutexAutoLock conlock(m_con_mutex);
5120 if(c.type == PEER_ADDED)
5127 std::map<u16, RemoteClient*>::iterator n;
5128 n = m_clients.find(c.peer_id);
5129 // The client shouldn't already exist
5130 assert(n == m_clients.end());
5133 RemoteClient *client = new RemoteClient();
5134 client->peer_id = c.peer_id;
5135 m_clients[client->peer_id] = client;
5138 else if(c.type == PEER_REMOVED)
5145 std::map<u16, RemoteClient*>::iterator n;
5146 n = m_clients.find(c.peer_id);
5147 // The client should exist
5148 assert(n != m_clients.end());
5151 Mark objects to be not known by the client
5153 RemoteClient *client = n->second;
5155 for(std::set<u16>::iterator
5156 i = client->m_known_objects.begin();
5157 i != client->m_known_objects.end(); ++i)
5161 ServerActiveObject* obj = m_env->getActiveObject(id);
5163 if(obj && obj->m_known_by_count > 0)
5164 obj->m_known_by_count--;
5168 Clear references to playing sounds
5170 for(std::map<s32, ServerPlayingSound>::iterator
5171 i = m_playing_sounds.begin();
5172 i != m_playing_sounds.end();)
5174 ServerPlayingSound &psound = i->second;
5175 psound.clients.erase(c.peer_id);
5176 if(psound.clients.size() == 0)
5177 m_playing_sounds.erase(i++);
5182 Player *player = m_env->getPlayer(c.peer_id);
5184 // Collect information about leaving in chat
5185 std::wstring message;
5189 std::wstring name = narrow_to_wide(player->getName());
5192 message += L" left the game.";
5194 message += L" (timed out)";
5198 /* Run scripts and remove from environment */
5202 PlayerSAO *playersao = player->getPlayerSAO();
5205 m_script->on_leaveplayer(playersao);
5207 playersao->disconnected();
5217 std::ostringstream os(std::ios_base::binary);
5218 for(std::map<u16, RemoteClient*>::iterator
5219 i = m_clients.begin();
5220 i != m_clients.end(); ++i)
5222 RemoteClient *client = i->second;
5223 assert(client->peer_id == i->first);
5224 if(client->serialization_version == SER_FMT_VER_INVALID)
5227 Player *player = m_env->getPlayer(client->peer_id);
5230 // Get name of player
5231 os<<player->getName()<<" ";
5234 actionstream<<player->getName()<<" "
5235 <<(c.timeout?"times out.":"leaves game.")
5236 <<" List of players: "
5237 <<os.str()<<std::endl;
5242 delete m_clients[c.peer_id];
5243 m_clients.erase(c.peer_id);
5245 // Send player info to all remaining clients
5246 //SendPlayerInfos();
5248 // Send leave chat message to all remaining clients
5249 if(message.length() != 0)
5250 BroadcastChatMessage(message);
5259 void Server::handlePeerChanges()
5261 while(m_peer_change_queue.size() > 0)
5263 PeerChange c = m_peer_change_queue.pop_front();
5265 verbosestream<<"Server: Handling peer change: "
5266 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5269 handlePeerChange(c);
5273 void dedicated_server_loop(Server &server, bool &kill)
5275 DSTACK(__FUNCTION_NAME);
5277 verbosestream<<"dedicated_server_loop()"<<std::endl;
5279 IntervalLimiter m_profiler_interval;
5283 float steplen = g_settings->getFloat("dedicated_server_step");
5284 // This is kind of a hack but can be done like this
5285 // because server.step() is very light
5287 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5288 sleep_ms((int)(steplen*1000.0));
5290 server.step(steplen);
5292 if(server.getShutdownRequested() || kill)
5294 infostream<<"Dedicated server quitting"<<std::endl;
5296 if(g_settings->getBool("server_announce") == true)
5297 ServerList::sendAnnounce("delete");
5305 float profiler_print_interval =
5306 g_settings->getFloat("profiler_print_interval");
5307 if(profiler_print_interval != 0)
5309 if(m_profiler_interval.step(steplen, profiler_print_interval))
5311 infostream<<"Profiler:"<<std::endl;
5312 g_profiler->print(infostream);
5313 g_profiler->clear();