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,
643 g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this),
644 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
646 m_rollback_sink_enabled(true),
647 m_enable_rollback_recording(false),
650 m_itemdef(createItemDefManager()),
651 m_nodedef(createNodeDefManager()),
652 m_craftdef(createCraftDefManager()),
653 m_event(new EventManager()),
655 m_time_of_day_send_timer(0),
657 m_shutdown_requested(false),
658 m_ignore_map_edit_events(false),
659 m_ignore_map_edit_events_peer_id(0)
661 m_liquid_transform_timer = 0.0;
662 m_liquid_transform_every = 1.0;
663 m_print_info_timer = 0.0;
664 m_masterserver_timer = 0.0;
665 m_objectdata_timer = 0.0;
666 m_emergethread_trigger_timer = 0.0;
667 m_savemap_timer = 0.0;
668 m_clients_number = 0;
672 m_step_dtime_mutex.Init();
676 throw ServerError("Supplied empty world path");
678 if(!gamespec.isValid())
679 throw ServerError("Supplied invalid gamespec");
681 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
682 if(m_simple_singleplayer_mode)
683 infostream<<" in simple singleplayer mode"<<std::endl;
685 infostream<<std::endl;
686 infostream<<"- world: "<<m_path_world<<std::endl;
687 infostream<<"- config: "<<m_path_config<<std::endl;
688 infostream<<"- game: "<<m_gamespec.path<<std::endl;
690 // Initialize default settings and override defaults with those provided
692 set_default_settings(g_settings);
693 Settings gamedefaults;
694 getGameMinetestConfig(gamespec.path, gamedefaults);
695 override_default_settings(g_settings, &gamedefaults);
697 // Create emerge manager
698 m_emerge = new EmergeManager(this);
700 // Create rollback manager
701 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
702 m_rollback = createRollbackManager(rollback_path, this);
704 // Create world if it doesn't exist
705 if(!initializeWorld(m_path_world, m_gamespec.id))
706 throw ServerError("Failed to initialize world");
708 ModConfiguration modconf(m_path_world);
709 m_mods = modconf.getMods();
710 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
711 // complain about mods with unsatisfied dependencies
712 if(!modconf.isConsistent())
714 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
715 it != unsatisfied_mods.end(); ++it)
718 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
719 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
720 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
721 errorstream << " \"" << *dep_it << "\"";
722 errorstream << std::endl;
726 Settings worldmt_settings;
727 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
728 worldmt_settings.readConfigFile(worldmt.c_str());
729 std::vector<std::string> names = worldmt_settings.getNames();
730 std::set<std::string> load_mod_names;
731 for(std::vector<std::string>::iterator it = names.begin();
732 it != names.end(); ++it)
734 std::string name = *it;
735 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
736 load_mod_names.insert(name.substr(9));
738 // complain about mods declared to be loaded, but not found
739 for(std::vector<ModSpec>::iterator it = m_mods.begin();
740 it != m_mods.end(); ++it)
741 load_mod_names.erase((*it).name);
742 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
743 it != unsatisfied_mods.end(); ++it)
744 load_mod_names.erase((*it).name);
745 if(!load_mod_names.empty())
747 errorstream << "The following mods could not be found:";
748 for(std::set<std::string>::iterator it = load_mod_names.begin();
749 it != load_mod_names.end(); ++it)
750 errorstream << " \"" << (*it) << "\"";
751 errorstream << std::endl;
754 // Path to builtin.lua
755 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
758 JMutexAutoLock envlock(m_env_mutex);
759 JMutexAutoLock conlock(m_con_mutex);
761 // Initialize scripting
763 infostream<<"Server: Initializing Lua"<<std::endl;
765 m_script = new ScriptApi(this);
768 // Load and run builtin.lua
769 infostream<<"Server: Loading builtin.lua [\""
770 <<builtinpath<<"\"]"<<std::endl;
771 bool success = m_script->loadMod(builtinpath, "__builtin");
773 errorstream<<"Server: Failed to load and run "
774 <<builtinpath<<std::endl;
775 throw ModError("Failed to load and run "+builtinpath);
778 infostream<<"Server: Loading mods: ";
779 for(std::vector<ModSpec>::iterator i = m_mods.begin();
780 i != m_mods.end(); i++){
781 const ModSpec &mod = *i;
782 infostream<<mod.name<<" ";
784 infostream<<std::endl;
785 // Load and run "mod" scripts
786 for(std::vector<ModSpec>::iterator i = m_mods.begin();
787 i != m_mods.end(); i++){
788 const ModSpec &mod = *i;
789 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
790 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
791 <<scriptpath<<"\"]"<<std::endl;
792 bool success = m_script->loadMod(scriptpath, mod.name);
794 errorstream<<"Server: Failed to load and run "
795 <<scriptpath<<std::endl;
796 throw ModError("Failed to load and run "+scriptpath);
800 // Read Textures and calculate sha1 sums
803 // Apply item aliases in the node definition manager
804 m_nodedef->updateAliases(m_itemdef);
806 // Initialize Environment
807 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
808 m_env = new ServerEnvironment(servermap, m_script, this, this);
810 // Run some callbacks after the MG params have been set up but before activation
811 MapgenParams *mgparams = servermap->getMapgenParams();
812 m_script->environment_OnMapgenInit(mgparams);
814 // Initialize mapgens
815 m_emerge->initMapgens(mgparams);
817 // Give environment reference to scripting api
818 m_script->initializeEnvironment(m_env);
820 // Register us to receive map edit events
821 servermap->addEventReceiver(this);
823 // If file exists, load environment metadata
824 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
826 infostream<<"Server: Loading environment metadata"<<std::endl;
827 m_env->loadMeta(m_path_world);
831 infostream<<"Server: Loading players"<<std::endl;
832 m_env->deSerializePlayers(m_path_world);
835 Add some test ActiveBlockModifiers to environment
837 add_legacy_abms(m_env, m_nodedef);
839 m_liquid_transform_every = g_settings->getFloat("liquid_update");
844 infostream<<"Server destructing"<<std::endl;
847 Send shutdown message
850 JMutexAutoLock conlock(m_con_mutex);
852 std::wstring line = L"*** Server shutting down";
855 Send the message to clients
857 for(std::map<u16, RemoteClient*>::iterator
858 i = m_clients.begin();
859 i != m_clients.end(); ++i)
861 // Get client and check that it is valid
862 RemoteClient *client = i->second;
863 assert(client->peer_id == i->first);
864 if(client->serialization_version == SER_FMT_VER_INVALID)
868 SendChatMessage(client->peer_id, line);
870 catch(con::PeerNotFoundException &e)
876 JMutexAutoLock envlock(m_env_mutex);
877 JMutexAutoLock conlock(m_con_mutex);
880 Execute script shutdown hooks
882 m_script->on_shutdown();
886 JMutexAutoLock envlock(m_env_mutex);
891 infostream<<"Server: Saving players"<<std::endl;
892 m_env->serializePlayers(m_path_world);
895 Save environment metadata
897 infostream<<"Server: Saving environment metadata"<<std::endl;
898 m_env->saveMeta(m_path_world);
906 //shutdown all emerge threads first!
913 JMutexAutoLock clientslock(m_con_mutex);
915 for(std::map<u16, RemoteClient*>::iterator
916 i = m_clients.begin();
917 i != m_clients.end(); ++i)
925 // Delete things in the reverse order of creation
933 // Deinitialize scripting
934 infostream<<"Server: Deinitializing scripting"<<std::endl;
937 // Delete detached inventories
939 for(std::map<std::string, Inventory*>::iterator
940 i = m_detached_inventories.begin();
941 i != m_detached_inventories.end(); i++){
947 void Server::start(unsigned short port)
949 DSTACK(__FUNCTION_NAME);
950 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
952 // Stop thread if already running
955 // Initialize connection
956 m_con.SetTimeoutMs(30);
960 m_thread.setRun(true);
963 // ASCII art for the win!
965 <<" .__ __ __ "<<std::endl
966 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
967 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
968 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
969 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
970 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
971 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
972 actionstream<<"Server for gameid=\""<<m_gamespec.id
973 <<"\" listening on port "<<port<<"."<<std::endl;
978 DSTACK(__FUNCTION_NAME);
980 infostream<<"Server: Stopping and waiting threads"<<std::endl;
982 // Stop threads (set run=false first so both start stopping)
983 m_thread.setRun(false);
984 //m_emergethread.setRun(false);
986 //m_emergethread.stop();
988 infostream<<"Server: Threads stopped"<<std::endl;
991 void Server::step(float dtime)
993 DSTACK(__FUNCTION_NAME);
998 JMutexAutoLock lock(m_step_dtime_mutex);
999 m_step_dtime += dtime;
1001 // Throw if fatal error occurred in thread
1002 std::string async_err = m_async_fatal_error.get();
1003 if(async_err != ""){
1004 throw ServerError(async_err);
1008 void Server::AsyncRunStep()
1010 DSTACK(__FUNCTION_NAME);
1012 g_profiler->add("Server::AsyncRunStep (num)", 1);
1016 JMutexAutoLock lock1(m_step_dtime_mutex);
1017 dtime = m_step_dtime;
1021 // Send blocks to clients
1028 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1030 //infostream<<"Server steps "<<dtime<<std::endl;
1031 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1034 JMutexAutoLock lock1(m_step_dtime_mutex);
1035 m_step_dtime -= dtime;
1042 m_uptime.set(m_uptime.get() + dtime);
1046 // Process connection's timeouts
1047 JMutexAutoLock lock2(m_con_mutex);
1048 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1049 m_con.RunTimeouts(dtime);
1053 // This has to be called so that the client list gets synced
1054 // with the peer list of the connection
1055 handlePeerChanges();
1059 Update time of day and overall game time
1062 JMutexAutoLock envlock(m_env_mutex);
1064 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1067 Send to clients at constant intervals
1070 m_time_of_day_send_timer -= dtime;
1071 if(m_time_of_day_send_timer < 0.0)
1073 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1075 //JMutexAutoLock envlock(m_env_mutex);
1076 JMutexAutoLock conlock(m_con_mutex);
1078 for(std::map<u16, RemoteClient*>::iterator
1079 i = m_clients.begin();
1080 i != m_clients.end(); ++i)
1082 RemoteClient *client = i->second;
1083 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1084 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1086 m_con.Send(client->peer_id, 0, data, true);
1092 JMutexAutoLock lock(m_env_mutex);
1094 ScopeProfiler sp(g_profiler, "SEnv step");
1095 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1099 const float map_timer_and_unload_dtime = 2.92;
1100 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1102 JMutexAutoLock lock(m_env_mutex);
1103 // Run Map's timers and unload unused data
1104 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1105 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1106 g_settings->getFloat("server_unload_unused_data_timeout"));
1117 JMutexAutoLock lock(m_env_mutex);
1118 JMutexAutoLock lock2(m_con_mutex);
1120 ScopeProfiler sp(g_profiler, "Server: handle players");
1122 for(std::map<u16, RemoteClient*>::iterator
1123 i = m_clients.begin();
1124 i != m_clients.end(); ++i)
1126 RemoteClient *client = i->second;
1127 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1128 if(playersao == NULL)
1132 Handle player HPs (die if hp=0)
1134 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
1136 if(playersao->getHP() == 0)
1137 DiePlayer(client->peer_id);
1139 SendPlayerHP(client->peer_id);
1143 Send player inventories if necessary
1145 if(playersao->m_moved){
1146 SendMovePlayer(client->peer_id);
1147 playersao->m_moved = false;
1149 if(playersao->m_inventory_not_sent){
1150 UpdateCrafting(client->peer_id);
1151 SendInventory(client->peer_id);
1156 /* Transform liquids */
1157 m_liquid_transform_timer += dtime;
1158 if(m_liquid_transform_timer >= m_liquid_transform_every)
1160 m_liquid_transform_timer -= m_liquid_transform_every;
1162 JMutexAutoLock lock(m_env_mutex);
1164 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1166 std::map<v3s16, MapBlock*> modified_blocks;
1167 m_env->getMap().transformLiquids(modified_blocks);
1172 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1173 ServerMap &map = ((ServerMap&)m_env->getMap());
1174 map.updateLighting(modified_blocks, lighting_modified_blocks);
1176 // Add blocks modified by lighting to modified_blocks
1177 for(core::map<v3s16, MapBlock*>::Iterator
1178 i = lighting_modified_blocks.getIterator();
1179 i.atEnd() == false; i++)
1181 MapBlock *block = i.getNode()->getValue();
1182 modified_blocks.insert(block->getPos(), block);
1186 Set the modified blocks unsent for all the clients
1189 JMutexAutoLock lock2(m_con_mutex);
1191 for(std::map<u16, RemoteClient*>::iterator
1192 i = m_clients.begin();
1193 i != m_clients.end(); ++i)
1195 RemoteClient *client = i->second;
1197 if(modified_blocks.size() > 0)
1199 // Remove block from sent history
1200 client->SetBlocksNotSent(modified_blocks);
1205 // Periodically print some info
1207 float &counter = m_print_info_timer;
1213 JMutexAutoLock lock2(m_con_mutex);
1214 m_clients_number = 0;
1215 if(m_clients.size() != 0)
1216 infostream<<"Players:"<<std::endl;
1217 for(std::map<u16, RemoteClient*>::iterator
1218 i = m_clients.begin();
1219 i != m_clients.end(); ++i)
1221 //u16 peer_id = i.getNode()->getKey();
1222 RemoteClient *client = i->second;
1223 Player *player = m_env->getPlayer(client->peer_id);
1226 infostream<<"* "<<player->getName()<<"\t";
1227 client->PrintInfo(infostream);
1235 // send masterserver announce
1237 float &counter = m_masterserver_timer;
1238 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1240 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
1247 //if(g_settings->getBool("enable_experimental"))
1251 Check added and deleted active objects
1254 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1255 JMutexAutoLock envlock(m_env_mutex);
1256 JMutexAutoLock conlock(m_con_mutex);
1258 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1260 // Radius inside which objects are active
1261 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1262 radius *= MAP_BLOCKSIZE;
1264 for(std::map<u16, RemoteClient*>::iterator
1265 i = m_clients.begin();
1266 i != m_clients.end(); ++i)
1268 RemoteClient *client = i->second;
1270 // If definitions and textures have not been sent, don't
1271 // send objects either
1272 if(!client->definitions_sent)
1275 Player *player = m_env->getPlayer(client->peer_id);
1278 // This can happen if the client timeouts somehow
1279 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1281 <<" has no associated player"<<std::endl;*/
1284 v3s16 pos = floatToInt(player->getPosition(), BS);
1286 std::set<u16> removed_objects;
1287 std::set<u16> added_objects;
1288 m_env->getRemovedActiveObjects(pos, radius,
1289 client->m_known_objects, removed_objects);
1290 m_env->getAddedActiveObjects(pos, radius,
1291 client->m_known_objects, added_objects);
1293 // Ignore if nothing happened
1294 if(removed_objects.size() == 0 && added_objects.size() == 0)
1296 //infostream<<"active objects: none changed"<<std::endl;
1300 std::string data_buffer;
1304 // Handle removed objects
1305 writeU16((u8*)buf, removed_objects.size());
1306 data_buffer.append(buf, 2);
1307 for(std::set<u16>::iterator
1308 i = removed_objects.begin();
1309 i != removed_objects.end(); ++i)
1313 ServerActiveObject* obj = m_env->getActiveObject(id);
1315 // Add to data buffer for sending
1316 writeU16((u8*)buf, id);
1317 data_buffer.append(buf, 2);
1319 // Remove from known objects
1320 client->m_known_objects.erase(id);
1322 if(obj && obj->m_known_by_count > 0)
1323 obj->m_known_by_count--;
1326 // Handle added objects
1327 writeU16((u8*)buf, added_objects.size());
1328 data_buffer.append(buf, 2);
1329 for(std::set<u16>::iterator
1330 i = added_objects.begin();
1331 i != added_objects.end(); ++i)
1335 ServerActiveObject* obj = m_env->getActiveObject(id);
1338 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1340 infostream<<"WARNING: "<<__FUNCTION_NAME
1341 <<": NULL object"<<std::endl;
1343 type = obj->getSendType();
1345 // Add to data buffer for sending
1346 writeU16((u8*)buf, id);
1347 data_buffer.append(buf, 2);
1348 writeU8((u8*)buf, type);
1349 data_buffer.append(buf, 1);
1352 data_buffer.append(serializeLongString(
1353 obj->getClientInitializationData(client->net_proto_version)));
1355 data_buffer.append(serializeLongString(""));
1357 // Add to known objects
1358 client->m_known_objects.insert(id);
1361 obj->m_known_by_count++;
1365 SharedBuffer<u8> reply(2 + data_buffer.size());
1366 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1367 memcpy((char*)&reply[2], data_buffer.c_str(),
1368 data_buffer.size());
1370 m_con.Send(client->peer_id, 0, reply, true);
1372 verbosestream<<"Server: Sent object remove/add: "
1373 <<removed_objects.size()<<" removed, "
1374 <<added_objects.size()<<" added, "
1375 <<"packet size is "<<reply.getSize()<<std::endl;
1380 Collect a list of all the objects known by the clients
1381 and report it back to the environment.
1384 core::map<u16, bool> all_known_objects;
1386 for(core::map<u16, RemoteClient*>::Iterator
1387 i = m_clients.getIterator();
1388 i.atEnd() == false; i++)
1390 RemoteClient *client = i.getNode()->getValue();
1391 // Go through all known objects of client
1392 for(core::map<u16, bool>::Iterator
1393 i = client->m_known_objects.getIterator();
1394 i.atEnd()==false; i++)
1396 u16 id = i.getNode()->getKey();
1397 all_known_objects[id] = true;
1401 m_env->setKnownActiveObjects(whatever);
1407 Send object messages
1410 JMutexAutoLock envlock(m_env_mutex);
1411 JMutexAutoLock conlock(m_con_mutex);
1413 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1416 // Value = data sent by object
1417 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1419 // Get active object messages from environment
1422 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1426 std::list<ActiveObjectMessage>* message_list = NULL;
1427 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1428 n = buffered_messages.find(aom.id);
1429 if(n == buffered_messages.end())
1431 message_list = new std::list<ActiveObjectMessage>;
1432 buffered_messages[aom.id] = message_list;
1436 message_list = n->second;
1438 message_list->push_back(aom);
1441 // Route data to every client
1442 for(std::map<u16, RemoteClient*>::iterator
1443 i = m_clients.begin();
1444 i != m_clients.end(); ++i)
1446 RemoteClient *client = i->second;
1447 std::string reliable_data;
1448 std::string unreliable_data;
1449 // Go through all objects in message buffer
1450 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1451 j = buffered_messages.begin();
1452 j != buffered_messages.end(); ++j)
1454 // If object is not known by client, skip it
1456 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1458 // Get message list of object
1459 std::list<ActiveObjectMessage>* list = j->second;
1460 // Go through every message
1461 for(std::list<ActiveObjectMessage>::iterator
1462 k = list->begin(); k != list->end(); ++k)
1464 // Compose the full new data with header
1465 ActiveObjectMessage aom = *k;
1466 std::string new_data;
1469 writeU16((u8*)&buf[0], aom.id);
1470 new_data.append(buf, 2);
1472 new_data += serializeString(aom.datastring);
1473 // Add data to buffer
1475 reliable_data += new_data;
1477 unreliable_data += new_data;
1481 reliable_data and unreliable_data are now ready.
1484 if(reliable_data.size() > 0)
1486 SharedBuffer<u8> reply(2 + reliable_data.size());
1487 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1488 memcpy((char*)&reply[2], reliable_data.c_str(),
1489 reliable_data.size());
1491 m_con.Send(client->peer_id, 0, reply, true);
1493 if(unreliable_data.size() > 0)
1495 SharedBuffer<u8> reply(2 + unreliable_data.size());
1496 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1497 memcpy((char*)&reply[2], unreliable_data.c_str(),
1498 unreliable_data.size());
1499 // Send as unreliable
1500 m_con.Send(client->peer_id, 0, reply, false);
1503 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1505 infostream<<"Server: Size of object message data: "
1506 <<"reliable: "<<reliable_data.size()
1507 <<", unreliable: "<<unreliable_data.size()
1512 // Clear buffered_messages
1513 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1514 i = buffered_messages.begin();
1515 i != buffered_messages.end(); ++i)
1521 } // enable_experimental
1524 Send queued-for-sending map edit events.
1527 // We will be accessing the environment and the connection
1528 JMutexAutoLock lock(m_env_mutex);
1529 JMutexAutoLock conlock(m_con_mutex);
1531 // Don't send too many at a time
1534 // Single change sending is disabled if queue size is not small
1535 bool disable_single_change_sending = false;
1536 if(m_unsent_map_edit_queue.size() >= 4)
1537 disable_single_change_sending = true;
1539 int event_count = m_unsent_map_edit_queue.size();
1541 // We'll log the amount of each
1544 while(m_unsent_map_edit_queue.size() != 0)
1546 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1548 // Players far away from the change are stored here.
1549 // Instead of sending the changes, MapBlocks are set not sent
1551 std::list<u16> far_players;
1553 if(event->type == MEET_ADDNODE)
1555 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1556 prof.add("MEET_ADDNODE", 1);
1557 if(disable_single_change_sending)
1558 sendAddNode(event->p, event->n, event->already_known_by_peer,
1561 sendAddNode(event->p, event->n, event->already_known_by_peer,
1564 else if(event->type == MEET_REMOVENODE)
1566 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1567 prof.add("MEET_REMOVENODE", 1);
1568 if(disable_single_change_sending)
1569 sendRemoveNode(event->p, event->already_known_by_peer,
1572 sendRemoveNode(event->p, event->already_known_by_peer,
1575 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1577 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1578 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1579 setBlockNotSent(event->p);
1581 else if(event->type == MEET_OTHER)
1583 infostream<<"Server: MEET_OTHER"<<std::endl;
1584 prof.add("MEET_OTHER", 1);
1585 for(std::set<v3s16>::iterator
1586 i = event->modified_blocks.begin();
1587 i != event->modified_blocks.end(); ++i)
1589 setBlockNotSent(*i);
1594 prof.add("unknown", 1);
1595 infostream<<"WARNING: Server: Unknown MapEditEvent "
1596 <<((u32)event->type)<<std::endl;
1600 Set blocks not sent to far players
1602 if(far_players.size() > 0)
1604 // Convert list format to that wanted by SetBlocksNotSent
1605 std::map<v3s16, MapBlock*> modified_blocks2;
1606 for(std::set<v3s16>::iterator
1607 i = event->modified_blocks.begin();
1608 i != event->modified_blocks.end(); ++i)
1610 modified_blocks2[*i] =
1611 m_env->getMap().getBlockNoCreateNoEx(*i);
1613 // Set blocks not sent
1614 for(std::list<u16>::iterator
1615 i = far_players.begin();
1616 i != far_players.end(); ++i)
1619 RemoteClient *client = getClient(peer_id);
1622 client->SetBlocksNotSent(modified_blocks2);
1628 /*// Don't send too many at a time
1630 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1634 if(event_count >= 5){
1635 infostream<<"Server: MapEditEvents:"<<std::endl;
1636 prof.print(infostream);
1637 } else if(event_count != 0){
1638 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1639 prof.print(verbosestream);
1645 Trigger emergethread (it somehow gets to a non-triggered but
1646 bysy state sometimes)
1649 float &counter = m_emergethread_trigger_timer;
1655 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1656 m_emerge->emergethread[i]->trigger();
1658 // Update m_enable_rollback_recording here too
1659 m_enable_rollback_recording =
1660 g_settings->getBool("enable_rollback_recording");
1664 // Save map, players and auth stuff
1666 float &counter = m_savemap_timer;
1668 if(counter >= g_settings->getFloat("server_map_save_interval"))
1671 JMutexAutoLock lock(m_env_mutex);
1673 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1676 if(m_banmanager.isModified())
1677 m_banmanager.save();
1679 // Save changed parts of map
1680 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1683 m_env->serializePlayers(m_path_world);
1685 // Save environment metadata
1686 m_env->saveMeta(m_path_world);
1691 void Server::Receive()
1693 DSTACK(__FUNCTION_NAME);
1694 SharedBuffer<u8> data;
1699 JMutexAutoLock conlock(m_con_mutex);
1700 datasize = m_con.Receive(peer_id, data);
1703 // This has to be called so that the client list gets synced
1704 // with the peer list of the connection
1705 handlePeerChanges();
1707 ProcessData(*data, datasize, peer_id);
1709 catch(con::InvalidIncomingDataException &e)
1711 infostream<<"Server::Receive(): "
1712 "InvalidIncomingDataException: what()="
1713 <<e.what()<<std::endl;
1715 catch(con::PeerNotFoundException &e)
1717 //NOTE: This is not needed anymore
1719 // The peer has been disconnected.
1720 // Find the associated player and remove it.
1722 /*JMutexAutoLock envlock(m_env_mutex);
1724 infostream<<"ServerThread: peer_id="<<peer_id
1725 <<" has apparently closed connection. "
1726 <<"Removing player."<<std::endl;
1728 m_env->removePlayer(peer_id);*/
1732 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1734 DSTACK(__FUNCTION_NAME);
1735 // Environment is locked first.
1736 JMutexAutoLock envlock(m_env_mutex);
1737 JMutexAutoLock conlock(m_con_mutex);
1739 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1742 Address address = m_con.GetPeerAddress(peer_id);
1743 std::string addr_s = address.serializeString();
1745 // drop player if is ip is banned
1746 if(m_banmanager.isIpBanned(addr_s)){
1747 infostream<<"Server: A banned client tried to connect from "
1748 <<addr_s<<"; banned name was "
1749 <<m_banmanager.getBanName(addr_s)<<std::endl;
1750 // This actually doesn't seem to transfer to the client
1751 SendAccessDenied(m_con, peer_id,
1752 L"Your ip is banned. Banned name was "
1753 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1754 m_con.DeletePeer(peer_id);
1758 catch(con::PeerNotFoundException &e)
1760 infostream<<"Server::ProcessData(): Cancelling: peer "
1761 <<peer_id<<" not found"<<std::endl;
1765 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1767 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1775 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1777 if(command == TOSERVER_INIT)
1779 // [0] u16 TOSERVER_INIT
1780 // [2] u8 SER_FMT_VER_HIGHEST
1781 // [3] u8[20] player_name
1782 // [23] u8[28] password <--- can be sent without this, from old versions
1784 if(datasize < 2+1+PLAYERNAME_SIZE)
1787 verbosestream<<"Server: Got TOSERVER_INIT from "
1788 <<peer_id<<std::endl;
1790 // First byte after command is maximum supported
1791 // serialization version
1792 u8 client_max = data[2];
1793 u8 our_max = SER_FMT_VER_HIGHEST;
1794 // Use the highest version supported by both
1795 u8 deployed = std::min(client_max, our_max);
1796 // If it's lower than the lowest supported, give up.
1797 if(deployed < SER_FMT_VER_LOWEST)
1798 deployed = SER_FMT_VER_INVALID;
1800 //peer->serialization_version = deployed;
1801 getClient(peer_id)->pending_serialization_version = deployed;
1803 if(deployed == SER_FMT_VER_INVALID)
1805 actionstream<<"Server: A mismatched client tried to connect from "
1806 <<addr_s<<std::endl;
1807 infostream<<"Server: Cannot negotiate "
1808 "serialization version with peer "
1809 <<peer_id<<std::endl;
1810 SendAccessDenied(m_con, peer_id, std::wstring(
1811 L"Your client's version is not supported.\n"
1812 L"Server version is ")
1813 + narrow_to_wide(VERSION_STRING) + L"."
1819 Read and check network protocol version
1822 u16 min_net_proto_version = 0;
1823 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1824 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1826 // Use same version as minimum and maximum if maximum version field
1827 // doesn't exist (backwards compatibility)
1828 u16 max_net_proto_version = min_net_proto_version;
1829 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1830 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1832 // Start with client's maximum version
1833 u16 net_proto_version = max_net_proto_version;
1835 // Figure out a working version if it is possible at all
1836 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1837 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1839 // If maximum is larger than our maximum, go with our maximum
1840 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1841 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1842 // Else go with client's maximum
1844 net_proto_version = max_net_proto_version;
1847 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1848 <<min_net_proto_version<<", max: "<<max_net_proto_version
1849 <<", chosen: "<<net_proto_version<<std::endl;
1851 getClient(peer_id)->net_proto_version = net_proto_version;
1853 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1854 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1856 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1858 SendAccessDenied(m_con, peer_id, std::wstring(
1859 L"Your client's version is not supported.\n"
1860 L"Server version is ")
1861 + narrow_to_wide(VERSION_STRING) + L",\n"
1862 + L"server's PROTOCOL_VERSION is "
1863 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1865 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1866 + L", client's PROTOCOL_VERSION is "
1867 + narrow_to_wide(itos(min_net_proto_version))
1869 + narrow_to_wide(itos(max_net_proto_version))
1874 if(g_settings->getBool("strict_protocol_version_checking"))
1876 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1878 actionstream<<"Server: A mismatched (strict) client tried to "
1879 <<"connect from "<<addr_s<<std::endl;
1880 SendAccessDenied(m_con, peer_id, std::wstring(
1881 L"Your client's version is not supported.\n"
1882 L"Server version is ")
1883 + narrow_to_wide(VERSION_STRING) + L",\n"
1884 + L"server's PROTOCOL_VERSION (strict) is "
1885 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1886 + L", client's PROTOCOL_VERSION is "
1887 + narrow_to_wide(itos(min_net_proto_version))
1889 + narrow_to_wide(itos(max_net_proto_version))
1900 char playername[PLAYERNAME_SIZE];
1901 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1903 playername[i] = data[3+i];
1905 playername[PLAYERNAME_SIZE-1] = 0;
1907 if(playername[0]=='\0')
1909 actionstream<<"Server: Player with an empty name "
1910 <<"tried to connect from "<<addr_s<<std::endl;
1911 SendAccessDenied(m_con, peer_id,
1916 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1918 actionstream<<"Server: Player with an invalid name "
1919 <<"tried to connect from "<<addr_s<<std::endl;
1920 SendAccessDenied(m_con, peer_id,
1921 L"Name contains unallowed characters");
1925 infostream<<"Server: New connection: \""<<playername<<"\" from "
1926 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1929 char given_password[PASSWORD_SIZE];
1930 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1932 // old version - assume blank password
1933 given_password[0] = 0;
1937 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1939 given_password[i] = data[23+i];
1941 given_password[PASSWORD_SIZE-1] = 0;
1944 if(!base64_is_valid(given_password)){
1945 infostream<<"Server: "<<playername
1946 <<" supplied invalid password hash"<<std::endl;
1947 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1951 std::string checkpwd; // Password hash to check against
1952 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1954 // If no authentication info exists for user, create it
1956 if(!isSingleplayer() &&
1957 g_settings->getBool("disallow_empty_password") &&
1958 std::string(given_password) == ""){
1959 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1960 L"disallowed. Set a password and try again.");
1963 std::wstring raw_default_password =
1964 narrow_to_wide(g_settings->get("default_password"));
1965 std::string initial_password =
1966 translatePassword(playername, raw_default_password);
1968 // If default_password is empty, allow any initial password
1969 if (raw_default_password.length() == 0)
1970 initial_password = given_password;
1972 m_script->createAuth(playername, initial_password);
1975 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1978 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1982 if(given_password != checkpwd){
1983 infostream<<"Server: peer_id="<<peer_id
1984 <<": supplied invalid password for "
1985 <<playername<<std::endl;
1986 SendAccessDenied(m_con, peer_id, L"Invalid password");
1990 // Do not allow multiple players in simple singleplayer mode.
1991 // This isn't a perfect way to do it, but will suffice for now.
1992 if(m_simple_singleplayer_mode && m_clients.size() > 1){
1993 infostream<<"Server: Not allowing another client to connect in"
1994 <<" simple singleplayer mode"<<std::endl;
1995 SendAccessDenied(m_con, peer_id,
1996 L"Running in simple singleplayer mode.");
2000 // Enforce user limit.
2001 // Don't enforce for users that have some admin right
2002 if(m_clients.size() >= g_settings->getU16("max_users") &&
2003 !checkPriv(playername, "server") &&
2004 !checkPriv(playername, "ban") &&
2005 !checkPriv(playername, "privs") &&
2006 !checkPriv(playername, "password") &&
2007 playername != g_settings->get("name"))
2009 actionstream<<"Server: "<<playername<<" tried to join, but there"
2010 <<" are already max_users="
2011 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2012 SendAccessDenied(m_con, peer_id, L"Too many users.");
2017 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2019 // If failed, cancel
2020 if(playersao == NULL)
2022 errorstream<<"Server: peer_id="<<peer_id
2023 <<": failed to emerge player"<<std::endl;
2028 Answer with a TOCLIENT_INIT
2031 SharedBuffer<u8> reply(2+1+6+8+4);
2032 writeU16(&reply[0], TOCLIENT_INIT);
2033 writeU8(&reply[2], deployed);
2034 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2035 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2036 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2039 m_con.Send(peer_id, 0, reply, true);
2043 Send complete position information
2045 SendMovePlayer(peer_id);
2050 if(command == TOSERVER_INIT2)
2052 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2053 <<peer_id<<std::endl;
2055 Player *player = m_env->getPlayer(peer_id);
2057 verbosestream<<"Server: TOSERVER_INIT2: "
2058 <<"Player not found; ignoring."<<std::endl;
2062 RemoteClient *client = getClient(peer_id);
2063 client->serialization_version =
2064 getClient(peer_id)->pending_serialization_version;
2067 Send some initialization data
2070 infostream<<"Server: Sending content to "
2071 <<getPlayerName(peer_id)<<std::endl;
2073 // Send player movement settings
2074 SendMovement(m_con, peer_id);
2076 // Send item definitions
2077 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2079 // Send node definitions
2080 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2082 // Send media announcement
2083 sendMediaAnnouncement(peer_id);
2086 SendPlayerPrivileges(peer_id);
2088 // Send inventory formspec
2089 SendPlayerInventoryFormspec(peer_id);
2092 UpdateCrafting(peer_id);
2093 SendInventory(peer_id);
2096 if(g_settings->getBool("enable_damage"))
2097 SendPlayerHP(peer_id);
2099 // Send detached inventories
2100 sendDetachedInventories(peer_id);
2102 // Show death screen if necessary
2104 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2108 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2109 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2110 m_con.Send(peer_id, 0, data, true);
2113 // Note things in chat if not in simple singleplayer mode
2114 if(!m_simple_singleplayer_mode)
2116 // Send information about server to player in chat
2117 SendChatMessage(peer_id, getStatusString());
2119 // Send information about joining in chat
2121 std::wstring name = L"unknown";
2122 Player *player = m_env->getPlayer(peer_id);
2124 name = narrow_to_wide(player->getName());
2126 std::wstring message;
2129 message += L" joined the game.";
2130 BroadcastChatMessage(message);
2134 // Warnings about protocol version can be issued here
2135 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2137 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2138 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2145 std::ostringstream os(std::ios_base::binary);
2146 for(std::map<u16, RemoteClient*>::iterator
2147 i = m_clients.begin();
2148 i != m_clients.end(); ++i)
2150 RemoteClient *client = i->second;
2151 assert(client->peer_id == i->first);
2152 if(client->serialization_version == SER_FMT_VER_INVALID)
2155 Player *player = m_env->getPlayer(client->peer_id);
2158 // Get name of player
2159 os<<player->getName()<<" ";
2162 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<" joins game. List of players: "
2163 <<os.str()<<std::endl;
2169 if(peer_ser_ver == SER_FMT_VER_INVALID)
2171 infostream<<"Server::ProcessData(): Cancelling: Peer"
2172 " serialization format invalid or not initialized."
2173 " Skipping incoming command="<<command<<std::endl;
2177 Player *player = m_env->getPlayer(peer_id);
2179 infostream<<"Server::ProcessData(): Cancelling: "
2180 "No player for peer_id="<<peer_id
2185 PlayerSAO *playersao = player->getPlayerSAO();
2186 if(playersao == NULL){
2187 infostream<<"Server::ProcessData(): Cancelling: "
2188 "No player object for peer_id="<<peer_id
2193 if(command == TOSERVER_PLAYERPOS)
2195 if(datasize < 2+12+12+4+4)
2199 v3s32 ps = readV3S32(&data[start+2]);
2200 v3s32 ss = readV3S32(&data[start+2+12]);
2201 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2202 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2204 if(datasize >= 2+12+12+4+4+4)
2205 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2206 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2207 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2208 pitch = wrapDegrees(pitch);
2209 yaw = wrapDegrees(yaw);
2211 player->setPosition(position);
2212 player->setSpeed(speed);
2213 player->setPitch(pitch);
2214 player->setYaw(yaw);
2215 player->keyPressed=keyPressed;
2216 player->control.up = (bool)(keyPressed&1);
2217 player->control.down = (bool)(keyPressed&2);
2218 player->control.left = (bool)(keyPressed&4);
2219 player->control.right = (bool)(keyPressed&8);
2220 player->control.jump = (bool)(keyPressed&16);
2221 player->control.aux1 = (bool)(keyPressed&32);
2222 player->control.sneak = (bool)(keyPressed&64);
2223 player->control.LMB = (bool)(keyPressed&128);
2224 player->control.RMB = (bool)(keyPressed&256);
2226 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2227 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2228 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2230 else if(command == TOSERVER_GOTBLOCKS)
2243 u16 count = data[2];
2244 for(u16 i=0; i<count; i++)
2246 if((s16)datasize < 2+1+(i+1)*6)
2247 throw con::InvalidIncomingDataException
2248 ("GOTBLOCKS length is too short");
2249 v3s16 p = readV3S16(&data[2+1+i*6]);
2250 /*infostream<<"Server: GOTBLOCKS ("
2251 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2252 RemoteClient *client = getClient(peer_id);
2253 client->GotBlock(p);
2256 else if(command == TOSERVER_DELETEDBLOCKS)
2269 u16 count = data[2];
2270 for(u16 i=0; i<count; i++)
2272 if((s16)datasize < 2+1+(i+1)*6)
2273 throw con::InvalidIncomingDataException
2274 ("DELETEDBLOCKS length is too short");
2275 v3s16 p = readV3S16(&data[2+1+i*6]);
2276 /*infostream<<"Server: DELETEDBLOCKS ("
2277 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2278 RemoteClient *client = getClient(peer_id);
2279 client->SetBlockNotSent(p);
2282 else if(command == TOSERVER_CLICK_OBJECT)
2284 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2287 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2289 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2292 else if(command == TOSERVER_GROUND_ACTION)
2294 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2298 else if(command == TOSERVER_RELEASE)
2300 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2303 else if(command == TOSERVER_SIGNTEXT)
2305 infostream<<"Server: SIGNTEXT not supported anymore"
2309 else if(command == TOSERVER_SIGNNODETEXT)
2311 infostream<<"Server: SIGNNODETEXT not supported anymore"
2315 else if(command == TOSERVER_INVENTORY_ACTION)
2317 // Strip command and create a stream
2318 std::string datastring((char*)&data[2], datasize-2);
2319 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2320 std::istringstream is(datastring, std::ios_base::binary);
2322 InventoryAction *a = InventoryAction::deSerialize(is);
2325 infostream<<"TOSERVER_INVENTORY_ACTION: "
2326 <<"InventoryAction::deSerialize() returned NULL"
2331 // If something goes wrong, this player is to blame
2332 RollbackScopeActor rollback_scope(m_rollback,
2333 std::string("player:")+player->getName());
2336 Note: Always set inventory not sent, to repair cases
2337 where the client made a bad prediction.
2341 Handle restrictions and special cases of the move action
2343 if(a->getType() == IACTION_MOVE)
2345 IMoveAction *ma = (IMoveAction*)a;
2347 ma->from_inv.applyCurrentPlayer(player->getName());
2348 ma->to_inv.applyCurrentPlayer(player->getName());
2350 setInventoryModified(ma->from_inv);
2351 setInventoryModified(ma->to_inv);
2353 bool from_inv_is_current_player =
2354 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2355 (ma->from_inv.name == player->getName());
2357 bool to_inv_is_current_player =
2358 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2359 (ma->to_inv.name == player->getName());
2362 Disable moving items out of craftpreview
2364 if(ma->from_list == "craftpreview")
2366 infostream<<"Ignoring IMoveAction from "
2367 <<(ma->from_inv.dump())<<":"<<ma->from_list
2368 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2369 <<" because src is "<<ma->from_list<<std::endl;
2375 Disable moving items into craftresult and craftpreview
2377 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2379 infostream<<"Ignoring IMoveAction from "
2380 <<(ma->from_inv.dump())<<":"<<ma->from_list
2381 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2382 <<" because dst is "<<ma->to_list<<std::endl;
2387 // Disallow moving items in elsewhere than player's inventory
2388 // if not allowed to interact
2389 if(!checkPriv(player->getName(), "interact") &&
2390 (!from_inv_is_current_player ||
2391 !to_inv_is_current_player))
2393 infostream<<"Cannot move outside of player's inventory: "
2394 <<"No interact privilege"<<std::endl;
2400 Handle restrictions and special cases of the drop action
2402 else if(a->getType() == IACTION_DROP)
2404 IDropAction *da = (IDropAction*)a;
2406 da->from_inv.applyCurrentPlayer(player->getName());
2408 setInventoryModified(da->from_inv);
2411 Disable dropping items out of craftpreview
2413 if(da->from_list == "craftpreview")
2415 infostream<<"Ignoring IDropAction from "
2416 <<(da->from_inv.dump())<<":"<<da->from_list
2417 <<" because src is "<<da->from_list<<std::endl;
2422 // Disallow dropping items if not allowed to interact
2423 if(!checkPriv(player->getName(), "interact"))
2430 Handle restrictions and special cases of the craft action
2432 else if(a->getType() == IACTION_CRAFT)
2434 ICraftAction *ca = (ICraftAction*)a;
2436 ca->craft_inv.applyCurrentPlayer(player->getName());
2438 setInventoryModified(ca->craft_inv);
2440 //bool craft_inv_is_current_player =
2441 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2442 // (ca->craft_inv.name == player->getName());
2444 // Disallow crafting if not allowed to interact
2445 if(!checkPriv(player->getName(), "interact"))
2447 infostream<<"Cannot craft: "
2448 <<"No interact privilege"<<std::endl;
2455 a->apply(this, playersao, this);
2459 else if(command == TOSERVER_CHAT_MESSAGE)
2467 std::string datastring((char*)&data[2], datasize-2);
2468 std::istringstream is(datastring, std::ios_base::binary);
2471 is.read((char*)buf, 2);
2472 u16 len = readU16(buf);
2474 std::wstring message;
2475 for(u16 i=0; i<len; i++)
2477 is.read((char*)buf, 2);
2478 message += (wchar_t)readU16(buf);
2481 // If something goes wrong, this player is to blame
2482 RollbackScopeActor rollback_scope(m_rollback,
2483 std::string("player:")+player->getName());
2485 // Get player name of this client
2486 std::wstring name = narrow_to_wide(player->getName());
2489 bool ate = m_script->on_chat_message(player->getName(),
2490 wide_to_narrow(message));
2491 // If script ate the message, don't proceed
2495 // Line to send to players
2497 // Whether to send to the player that sent the line
2498 bool send_to_sender = false;
2499 // Whether to send to other players
2500 bool send_to_others = false;
2502 // Commands are implemented in Lua, so only catch invalid
2503 // commands that were not "eaten" and send an error back
2504 if(message[0] == L'/')
2506 message = message.substr(1);
2507 send_to_sender = true;
2508 if(message.length() == 0)
2509 line += L"-!- Empty command";
2511 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2515 if(checkPriv(player->getName(), "shout")){
2520 send_to_others = true;
2522 line += L"-!- You don't have permission to shout.";
2523 send_to_sender = true;
2530 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2533 Send the message to clients
2535 for(std::map<u16, RemoteClient*>::iterator
2536 i = m_clients.begin();
2537 i != m_clients.end(); ++i)
2539 // Get client and check that it is valid
2540 RemoteClient *client = i->second;
2541 assert(client->peer_id == i->first);
2542 if(client->serialization_version == SER_FMT_VER_INVALID)
2546 bool sender_selected = (peer_id == client->peer_id);
2547 if(sender_selected == true && send_to_sender == false)
2549 if(sender_selected == false && send_to_others == false)
2552 SendChatMessage(client->peer_id, line);
2556 else if(command == TOSERVER_DAMAGE)
2558 std::string datastring((char*)&data[2], datasize-2);
2559 std::istringstream is(datastring, std::ios_base::binary);
2560 u8 damage = readU8(is);
2562 if(g_settings->getBool("enable_damage"))
2564 actionstream<<player->getName()<<" damaged by "
2565 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2568 playersao->setHP(playersao->getHP() - damage);
2570 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2573 if(playersao->m_hp_not_sent)
2574 SendPlayerHP(peer_id);
2577 else if(command == TOSERVER_PASSWORD)
2580 [0] u16 TOSERVER_PASSWORD
2581 [2] u8[28] old password
2582 [30] u8[28] new password
2585 if(datasize != 2+PASSWORD_SIZE*2)
2587 /*char password[PASSWORD_SIZE];
2588 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2589 password[i] = data[2+i];
2590 password[PASSWORD_SIZE-1] = 0;*/
2592 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2600 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2602 char c = data[2+PASSWORD_SIZE+i];
2608 if(!base64_is_valid(newpwd)){
2609 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2610 // Wrong old password supplied!!
2611 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2615 infostream<<"Server: Client requests a password change from "
2616 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2618 std::string playername = player->getName();
2620 std::string checkpwd;
2621 m_script->getAuth(playername, &checkpwd, NULL);
2623 if(oldpwd != checkpwd)
2625 infostream<<"Server: invalid old password"<<std::endl;
2626 // Wrong old password supplied!!
2627 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2631 bool success = m_script->setPassword(playername, newpwd);
2633 actionstream<<player->getName()<<" changes password"<<std::endl;
2634 SendChatMessage(peer_id, L"Password change successful.");
2636 actionstream<<player->getName()<<" tries to change password but "
2637 <<"it fails"<<std::endl;
2638 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2641 else if(command == TOSERVER_PLAYERITEM)
2646 u16 item = readU16(&data[2]);
2647 playersao->setWieldIndex(item);
2649 else if(command == TOSERVER_RESPAWN)
2651 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2654 RespawnPlayer(peer_id);
2656 actionstream<<player->getName()<<" respawns at "
2657 <<PP(player->getPosition()/BS)<<std::endl;
2659 // ActiveObject is added to environment in AsyncRunStep after
2660 // the previous addition has been succesfully removed
2662 else if(command == TOSERVER_REQUEST_MEDIA) {
2663 std::string datastring((char*)&data[2], datasize-2);
2664 std::istringstream is(datastring, std::ios_base::binary);
2666 std::list<MediaRequest> tosend;
2667 u16 numfiles = readU16(is);
2669 infostream<<"Sending "<<numfiles<<" files to "
2670 <<getPlayerName(peer_id)<<std::endl;
2671 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2673 for(int i = 0; i < numfiles; i++) {
2674 std::string name = deSerializeString(is);
2675 tosend.push_back(MediaRequest(name));
2676 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2680 sendRequestedMedia(peer_id, tosend);
2682 // Now the client should know about everything
2683 // (definitions and files)
2684 getClient(peer_id)->definitions_sent = true;
2686 else if(command == TOSERVER_RECEIVED_MEDIA) {
2687 getClient(peer_id)->definitions_sent = true;
2689 else if(command == TOSERVER_INTERACT)
2691 std::string datastring((char*)&data[2], datasize-2);
2692 std::istringstream is(datastring, std::ios_base::binary);
2698 [5] u32 length of the next item
2699 [9] serialized PointedThing
2701 0: start digging (from undersurface) or use
2702 1: stop digging (all parameters ignored)
2703 2: digging completed
2704 3: place block or item (to abovesurface)
2707 u8 action = readU8(is);
2708 u16 item_i = readU16(is);
2709 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2710 PointedThing pointed;
2711 pointed.deSerialize(tmp_is);
2713 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2714 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2718 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2719 <<" tried to interact, but is dead!"<<std::endl;
2723 v3f player_pos = playersao->getLastGoodPosition();
2725 // Update wielded item
2726 playersao->setWieldIndex(item_i);
2728 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2729 v3s16 p_under = pointed.node_undersurface;
2730 v3s16 p_above = pointed.node_abovesurface;
2732 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2733 ServerActiveObject *pointed_object = NULL;
2734 if(pointed.type == POINTEDTHING_OBJECT)
2736 pointed_object = m_env->getActiveObject(pointed.object_id);
2737 if(pointed_object == NULL)
2739 verbosestream<<"TOSERVER_INTERACT: "
2740 "pointed object is NULL"<<std::endl;
2746 v3f pointed_pos_under = player_pos;
2747 v3f pointed_pos_above = player_pos;
2748 if(pointed.type == POINTEDTHING_NODE)
2750 pointed_pos_under = intToFloat(p_under, BS);
2751 pointed_pos_above = intToFloat(p_above, BS);
2753 else if(pointed.type == POINTEDTHING_OBJECT)
2755 pointed_pos_under = pointed_object->getBasePosition();
2756 pointed_pos_above = pointed_pos_under;
2760 Check that target is reasonably close
2761 (only when digging or placing things)
2763 if(action == 0 || action == 2 || action == 3)
2765 float d = player_pos.getDistanceFrom(pointed_pos_under);
2766 float max_d = BS * 14; // Just some large enough value
2768 actionstream<<"Player "<<player->getName()
2769 <<" tried to access "<<pointed.dump()
2771 <<"d="<<d<<", max_d="<<max_d
2772 <<". ignoring."<<std::endl;
2773 // Re-send block to revert change on client-side
2774 RemoteClient *client = getClient(peer_id);
2775 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2776 client->SetBlockNotSent(blockpos);
2783 Make sure the player is allowed to do it
2785 if(!checkPriv(player->getName(), "interact"))
2787 actionstream<<player->getName()<<" attempted to interact with "
2788 <<pointed.dump()<<" without 'interact' privilege"
2790 // Re-send block to revert change on client-side
2791 RemoteClient *client = getClient(peer_id);
2792 // Digging completed -> under
2794 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2795 client->SetBlockNotSent(blockpos);
2797 // Placement -> above
2799 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2800 client->SetBlockNotSent(blockpos);
2806 If something goes wrong, this player is to blame
2808 RollbackScopeActor rollback_scope(m_rollback,
2809 std::string("player:")+player->getName());
2812 0: start digging or punch object
2816 if(pointed.type == POINTEDTHING_NODE)
2819 NOTE: This can be used in the future to check if
2820 somebody is cheating, by checking the timing.
2822 MapNode n(CONTENT_IGNORE);
2825 n = m_env->getMap().getNode(p_under);
2827 catch(InvalidPositionException &e)
2829 infostream<<"Server: Not punching: Node not found."
2830 <<" Adding block to emerge queue."
2832 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2834 if(n.getContent() != CONTENT_IGNORE)
2835 m_script->node_on_punch(p_under, n, playersao);
2837 playersao->noCheatDigStart(p_under);
2839 else if(pointed.type == POINTEDTHING_OBJECT)
2841 // Skip if object has been removed
2842 if(pointed_object->m_removed)
2845 actionstream<<player->getName()<<" punches object "
2846 <<pointed.object_id<<": "
2847 <<pointed_object->getDescription()<<std::endl;
2849 ItemStack punchitem = playersao->getWieldedItem();
2850 ToolCapabilities toolcap =
2851 punchitem.getToolCapabilities(m_itemdef);
2852 v3f dir = (pointed_object->getBasePosition() -
2853 (player->getPosition() + player->getEyeOffset())
2855 float time_from_last_punch =
2856 playersao->resetTimeFromLastPunch();
2857 pointed_object->punch(dir, &toolcap, playersao,
2858 time_from_last_punch);
2866 else if(action == 1)
2871 2: Digging completed
2873 else if(action == 2)
2875 // Only digging of nodes
2876 if(pointed.type == POINTEDTHING_NODE)
2878 MapNode n(CONTENT_IGNORE);
2881 n = m_env->getMap().getNode(p_under);
2883 catch(InvalidPositionException &e)
2885 infostream<<"Server: Not finishing digging: Node not found."
2886 <<" Adding block to emerge queue."
2888 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2891 /* Cheat prevention */
2892 bool is_valid_dig = true;
2893 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2895 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2896 float nocheat_t = playersao->getNoCheatDigTime();
2897 playersao->noCheatDigEnd();
2898 // If player didn't start digging this, ignore dig
2899 if(nocheat_p != p_under){
2900 infostream<<"Server: NoCheat: "<<player->getName()
2901 <<" started digging "
2902 <<PP(nocheat_p)<<" and completed digging "
2903 <<PP(p_under)<<"; not digging."<<std::endl;
2904 is_valid_dig = false;
2906 // Get player's wielded item
2907 ItemStack playeritem;
2908 InventoryList *mlist = playersao->getInventory()->getList("main");
2910 playeritem = mlist->getItem(playersao->getWieldIndex());
2911 ToolCapabilities playeritem_toolcap =
2912 playeritem.getToolCapabilities(m_itemdef);
2913 // Get diggability and expected digging time
2914 DigParams params = getDigParams(m_nodedef->get(n).groups,
2915 &playeritem_toolcap);
2916 // If can't dig, try hand
2917 if(!params.diggable){
2918 const ItemDefinition &hand = m_itemdef->get("");
2919 const ToolCapabilities *tp = hand.tool_capabilities;
2921 params = getDigParams(m_nodedef->get(n).groups, tp);
2923 // If can't dig, ignore dig
2924 if(!params.diggable){
2925 infostream<<"Server: NoCheat: "<<player->getName()
2926 <<" completed digging "<<PP(p_under)
2927 <<", which is not diggable with tool. not digging."
2929 is_valid_dig = false;
2931 // If time is considerably too short, ignore dig
2932 // Check time only for medium and slow timed digs
2933 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2934 infostream<<"Server: NoCheat: "<<player->getName()
2935 <<" completed digging "
2936 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2937 <<params.time<<"s; not digging."<<std::endl;
2938 is_valid_dig = false;
2942 /* Actually dig node */
2944 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2945 m_script->node_on_dig(p_under, n, playersao);
2947 // Send unusual result (that is, node not being removed)
2948 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2950 // Re-send block to revert change on client-side
2951 RemoteClient *client = getClient(peer_id);
2952 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2953 client->SetBlockNotSent(blockpos);
2959 3: place block or right-click object
2961 else if(action == 3)
2963 ItemStack item = playersao->getWieldedItem();
2965 // Reset build time counter
2966 if(pointed.type == POINTEDTHING_NODE &&
2967 item.getDefinition(m_itemdef).type == ITEM_NODE)
2968 getClient(peer_id)->m_time_from_building = 0.0;
2970 if(pointed.type == POINTEDTHING_OBJECT)
2972 // Right click object
2974 // Skip if object has been removed
2975 if(pointed_object->m_removed)
2978 actionstream<<player->getName()<<" right-clicks object "
2979 <<pointed.object_id<<": "
2980 <<pointed_object->getDescription()<<std::endl;
2983 pointed_object->rightClick(playersao);
2985 else if(m_script->item_OnPlace(
2986 item, playersao, pointed))
2988 // Placement was handled in lua
2990 // Apply returned ItemStack
2991 playersao->setWieldedItem(item);
2994 // If item has node placement prediction, always send the
2995 // blocks to make sure the client knows what exactly happened
2996 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2997 RemoteClient *client = getClient(peer_id);
2998 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2999 client->SetBlockNotSent(blockpos);
3000 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3001 if(blockpos2 != blockpos){
3002 client->SetBlockNotSent(blockpos2);
3010 else if(action == 4)
3012 ItemStack item = playersao->getWieldedItem();
3014 actionstream<<player->getName()<<" uses "<<item.name
3015 <<", pointing at "<<pointed.dump()<<std::endl;
3017 if(m_script->item_OnUse(
3018 item, playersao, pointed))
3020 // Apply returned ItemStack
3021 playersao->setWieldedItem(item);
3028 Catch invalid actions
3032 infostream<<"WARNING: Server: Invalid action "
3033 <<action<<std::endl;
3036 else if(command == TOSERVER_REMOVED_SOUNDS)
3038 std::string datastring((char*)&data[2], datasize-2);
3039 std::istringstream is(datastring, std::ios_base::binary);
3041 int num = readU16(is);
3042 for(int k=0; k<num; k++){
3043 s32 id = readS32(is);
3044 std::map<s32, ServerPlayingSound>::iterator i =
3045 m_playing_sounds.find(id);
3046 if(i == m_playing_sounds.end())
3048 ServerPlayingSound &psound = i->second;
3049 psound.clients.erase(peer_id);
3050 if(psound.clients.size() == 0)
3051 m_playing_sounds.erase(i++);
3054 else if(command == TOSERVER_NODEMETA_FIELDS)
3056 std::string datastring((char*)&data[2], datasize-2);
3057 std::istringstream is(datastring, std::ios_base::binary);
3059 v3s16 p = readV3S16(is);
3060 std::string formname = deSerializeString(is);
3061 int num = readU16(is);
3062 std::map<std::string, std::string> fields;
3063 for(int k=0; k<num; k++){
3064 std::string fieldname = deSerializeString(is);
3065 std::string fieldvalue = deSerializeLongString(is);
3066 fields[fieldname] = fieldvalue;
3069 // If something goes wrong, this player is to blame
3070 RollbackScopeActor rollback_scope(m_rollback,
3071 std::string("player:")+player->getName());
3073 // Check the target node for rollback data; leave others unnoticed
3074 RollbackNode rn_old(&m_env->getMap(), p, this);
3076 m_script->node_on_receive_fields(p, formname, fields,playersao);
3078 // Report rollback data
3079 RollbackNode rn_new(&m_env->getMap(), p, this);
3080 if(rollback() && rn_new != rn_old){
3081 RollbackAction action;
3082 action.setSetNode(p, rn_old, rn_new);
3083 rollback()->reportAction(action);
3086 else if(command == TOSERVER_INVENTORY_FIELDS)
3088 std::string datastring((char*)&data[2], datasize-2);
3089 std::istringstream is(datastring, std::ios_base::binary);
3091 std::string formname = deSerializeString(is);
3092 int num = readU16(is);
3093 std::map<std::string, std::string> fields;
3094 for(int k=0; k<num; k++){
3095 std::string fieldname = deSerializeString(is);
3096 std::string fieldvalue = deSerializeLongString(is);
3097 fields[fieldname] = fieldvalue;
3100 m_script->on_playerReceiveFields(playersao, formname, fields);
3104 infostream<<"Server::ProcessData(): Ignoring "
3105 "unknown command "<<command<<std::endl;
3109 catch(SendFailedException &e)
3111 errorstream<<"Server::ProcessData(): SendFailedException: "
3117 void Server::onMapEditEvent(MapEditEvent *event)
3119 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3120 if(m_ignore_map_edit_events)
3122 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3124 MapEditEvent *e = event->clone();
3125 m_unsent_map_edit_queue.push_back(e);
3128 Inventory* Server::getInventory(const InventoryLocation &loc)
3131 case InventoryLocation::UNDEFINED:
3134 case InventoryLocation::CURRENT_PLAYER:
3137 case InventoryLocation::PLAYER:
3139 Player *player = m_env->getPlayer(loc.name.c_str());
3142 PlayerSAO *playersao = player->getPlayerSAO();
3145 return playersao->getInventory();
3148 case InventoryLocation::NODEMETA:
3150 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3153 return meta->getInventory();
3156 case InventoryLocation::DETACHED:
3158 if(m_detached_inventories.count(loc.name) == 0)
3160 return m_detached_inventories[loc.name];
3168 void Server::setInventoryModified(const InventoryLocation &loc)
3171 case InventoryLocation::UNDEFINED:
3174 case InventoryLocation::PLAYER:
3176 Player *player = m_env->getPlayer(loc.name.c_str());
3179 PlayerSAO *playersao = player->getPlayerSAO();
3182 playersao->m_inventory_not_sent = true;
3183 playersao->m_wielded_item_not_sent = true;
3186 case InventoryLocation::NODEMETA:
3188 v3s16 blockpos = getNodeBlockPos(loc.p);
3190 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3192 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3194 setBlockNotSent(blockpos);
3197 case InventoryLocation::DETACHED:
3199 sendDetachedInventoryToAll(loc.name);
3207 //std::list<PlayerInfo> Server::getPlayerInfo()
3209 // DSTACK(__FUNCTION_NAME);
3210 // JMutexAutoLock envlock(m_env_mutex);
3211 // JMutexAutoLock conlock(m_con_mutex);
3213 // std::list<PlayerInfo> list;
3215 // std::list<Player*> players = m_env->getPlayers();
3217 // std::list<Player*>::iterator i;
3218 // for(i = players.begin();
3219 // i != players.end(); ++i)
3223 // Player *player = *i;
3226 // // Copy info from connection to info struct
3227 // info.id = player->peer_id;
3228 // info.address = m_con.GetPeerAddress(player->peer_id);
3229 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3231 // catch(con::PeerNotFoundException &e)
3233 // // Set dummy peer info
3235 // info.address = Address(0,0,0,0,0);
3236 // info.avg_rtt = 0.0;
3239 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3240 // info.position = player->getPosition();
3242 // list.push_back(info);
3249 void Server::peerAdded(con::Peer *peer)
3251 DSTACK(__FUNCTION_NAME);
3252 verbosestream<<"Server::peerAdded(): peer->id="
3253 <<peer->id<<std::endl;
3256 c.type = PEER_ADDED;
3257 c.peer_id = peer->id;
3259 m_peer_change_queue.push_back(c);
3262 void Server::deletingPeer(con::Peer *peer, bool timeout)
3264 DSTACK(__FUNCTION_NAME);
3265 verbosestream<<"Server::deletingPeer(): peer->id="
3266 <<peer->id<<", timeout="<<timeout<<std::endl;
3269 c.type = PEER_REMOVED;
3270 c.peer_id = peer->id;
3271 c.timeout = timeout;
3272 m_peer_change_queue.push_back(c);
3279 void Server::SendMovement(con::Connection &con, u16 peer_id)
3281 DSTACK(__FUNCTION_NAME);
3282 std::ostringstream os(std::ios_base::binary);
3284 writeU16(os, TOCLIENT_MOVEMENT);
3285 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3286 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3287 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3288 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3289 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3290 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3291 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3292 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3293 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3294 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3295 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3296 writeF1000(os, g_settings->getFloat("movement_gravity"));
3299 std::string s = os.str();
3300 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3302 con.Send(peer_id, 0, data, true);
3305 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3307 DSTACK(__FUNCTION_NAME);
3308 std::ostringstream os(std::ios_base::binary);
3310 writeU16(os, TOCLIENT_HP);
3314 std::string s = os.str();
3315 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3317 con.Send(peer_id, 0, data, true);
3320 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3321 const std::wstring &reason)
3323 DSTACK(__FUNCTION_NAME);
3324 std::ostringstream os(std::ios_base::binary);
3326 writeU16(os, TOCLIENT_ACCESS_DENIED);
3327 os<<serializeWideString(reason);
3330 std::string s = os.str();
3331 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3333 con.Send(peer_id, 0, data, true);
3336 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3337 bool set_camera_point_target, v3f camera_point_target)
3339 DSTACK(__FUNCTION_NAME);
3340 std::ostringstream os(std::ios_base::binary);
3342 writeU16(os, TOCLIENT_DEATHSCREEN);
3343 writeU8(os, set_camera_point_target);
3344 writeV3F1000(os, camera_point_target);
3347 std::string s = os.str();
3348 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3350 con.Send(peer_id, 0, data, true);
3353 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3354 IItemDefManager *itemdef, u16 protocol_version)
3356 DSTACK(__FUNCTION_NAME);
3357 std::ostringstream os(std::ios_base::binary);
3361 u32 length of the next item
3362 zlib-compressed serialized ItemDefManager
3364 writeU16(os, TOCLIENT_ITEMDEF);
3365 std::ostringstream tmp_os(std::ios::binary);
3366 itemdef->serialize(tmp_os, protocol_version);
3367 std::ostringstream tmp_os2(std::ios::binary);
3368 compressZlib(tmp_os.str(), tmp_os2);
3369 os<<serializeLongString(tmp_os2.str());
3372 std::string s = os.str();
3373 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3374 <<"): size="<<s.size()<<std::endl;
3375 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3377 con.Send(peer_id, 0, data, true);
3380 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3381 INodeDefManager *nodedef, u16 protocol_version)
3383 DSTACK(__FUNCTION_NAME);
3384 std::ostringstream os(std::ios_base::binary);
3388 u32 length of the next item
3389 zlib-compressed serialized NodeDefManager
3391 writeU16(os, TOCLIENT_NODEDEF);
3392 std::ostringstream tmp_os(std::ios::binary);
3393 nodedef->serialize(tmp_os, protocol_version);
3394 std::ostringstream tmp_os2(std::ios::binary);
3395 compressZlib(tmp_os.str(), tmp_os2);
3396 os<<serializeLongString(tmp_os2.str());
3399 std::string s = os.str();
3400 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3401 <<"): size="<<s.size()<<std::endl;
3402 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3404 con.Send(peer_id, 0, data, true);
3408 Non-static send methods
3411 void Server::SendInventory(u16 peer_id)
3413 DSTACK(__FUNCTION_NAME);
3415 PlayerSAO *playersao = getPlayerSAO(peer_id);
3418 playersao->m_inventory_not_sent = false;
3424 std::ostringstream os;
3425 playersao->getInventory()->serialize(os);
3427 std::string s = os.str();
3429 SharedBuffer<u8> data(s.size()+2);
3430 writeU16(&data[0], TOCLIENT_INVENTORY);
3431 memcpy(&data[2], s.c_str(), s.size());
3434 m_con.Send(peer_id, 0, data, true);
3437 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3439 DSTACK(__FUNCTION_NAME);
3441 std::ostringstream os(std::ios_base::binary);
3445 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3446 os.write((char*)buf, 2);
3449 writeU16(buf, message.size());
3450 os.write((char*)buf, 2);
3453 for(u32 i=0; i<message.size(); i++)
3457 os.write((char*)buf, 2);
3461 std::string s = os.str();
3462 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3464 m_con.Send(peer_id, 0, data, true);
3467 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3468 const std::string formname)
3470 DSTACK(__FUNCTION_NAME);
3472 std::ostringstream os(std::ios_base::binary);
3476 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3477 os.write((char*)buf, 2);
3478 os<<serializeLongString(formspec);
3479 os<<serializeString(formname);
3482 std::string s = os.str();
3483 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3485 m_con.Send(peer_id, 0, data, true);
3488 // Spawns a particle on peer with peer_id
3489 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3490 float expirationtime, float size, bool collisiondetection,
3491 std::string texture)
3493 DSTACK(__FUNCTION_NAME);
3495 std::ostringstream os(std::ios_base::binary);
3496 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3497 writeV3F1000(os, pos);
3498 writeV3F1000(os, velocity);
3499 writeV3F1000(os, acceleration);
3500 writeF1000(os, expirationtime);
3501 writeF1000(os, size);
3502 writeU8(os, collisiondetection);
3503 os<<serializeLongString(texture);
3506 std::string s = os.str();
3507 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3509 m_con.Send(peer_id, 0, data, true);
3512 // Spawns a particle on all peers
3513 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3514 float expirationtime, float size, bool collisiondetection,
3515 std::string texture)
3517 for(std::map<u16, RemoteClient*>::iterator
3518 i = m_clients.begin();
3519 i != m_clients.end(); i++)
3521 // Get client and check that it is valid
3522 RemoteClient *client = i->second;
3523 assert(client->peer_id == i->first);
3524 if(client->serialization_version == SER_FMT_VER_INVALID)
3527 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3528 expirationtime, size, collisiondetection, texture);
3532 // Adds a ParticleSpawner on peer with peer_id
3533 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3534 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3535 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3537 DSTACK(__FUNCTION_NAME);
3539 std::ostringstream os(std::ios_base::binary);
3540 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3542 writeU16(os, amount);
3543 writeF1000(os, spawntime);
3544 writeV3F1000(os, minpos);
3545 writeV3F1000(os, maxpos);
3546 writeV3F1000(os, minvel);
3547 writeV3F1000(os, maxvel);
3548 writeV3F1000(os, minacc);
3549 writeV3F1000(os, maxacc);
3550 writeF1000(os, minexptime);
3551 writeF1000(os, maxexptime);
3552 writeF1000(os, minsize);
3553 writeF1000(os, maxsize);
3554 writeU8(os, collisiondetection);
3555 os<<serializeLongString(texture);
3559 std::string s = os.str();
3560 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3562 m_con.Send(peer_id, 0, data, true);
3565 // Adds a ParticleSpawner on all peers
3566 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3567 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3568 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3570 for(std::map<u16, RemoteClient*>::iterator
3571 i = m_clients.begin();
3572 i != m_clients.end(); i++)
3574 // Get client and check that it is valid
3575 RemoteClient *client = i->second;
3576 assert(client->peer_id == i->first);
3577 if(client->serialization_version == SER_FMT_VER_INVALID)
3580 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3581 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3582 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3586 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3588 DSTACK(__FUNCTION_NAME);
3590 std::ostringstream os(std::ios_base::binary);
3591 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3596 std::string s = os.str();
3597 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3599 m_con.Send(peer_id, 0, data, true);
3602 void Server::SendDeleteParticleSpawnerAll(u32 id)
3604 for(std::map<u16, RemoteClient*>::iterator
3605 i = m_clients.begin();
3606 i != m_clients.end(); i++)
3608 // Get client and check that it is valid
3609 RemoteClient *client = i->second;
3610 assert(client->peer_id == i->first);
3611 if(client->serialization_version == SER_FMT_VER_INVALID)
3614 SendDeleteParticleSpawner(client->peer_id, id);
3618 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3620 std::ostringstream os(std::ios_base::binary);
3623 writeU16(os, TOCLIENT_HUDADD);
3625 writeU8(os, (u8)form->type);
3626 writeV2F1000(os, form->pos);
3627 os << serializeString(form->name);
3628 writeV2F1000(os, form->scale);
3629 os << serializeString(form->text);
3630 writeU32(os, form->number);
3631 writeU32(os, form->item);
3632 writeU32(os, form->dir);
3633 writeV2F1000(os, form->align);
3634 writeV2F1000(os, form->offset);
3637 std::string s = os.str();
3638 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3640 m_con.Send(peer_id, 0, data, true);
3643 void Server::SendHUDRemove(u16 peer_id, u32 id)
3645 std::ostringstream os(std::ios_base::binary);
3648 writeU16(os, TOCLIENT_HUDRM);
3652 std::string s = os.str();
3653 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3655 m_con.Send(peer_id, 0, data, true);
3658 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3660 std::ostringstream os(std::ios_base::binary);
3663 writeU16(os, TOCLIENT_HUDCHANGE);
3665 writeU8(os, (u8)stat);
3668 case HUD_STAT_SCALE:
3669 case HUD_STAT_ALIGN:
3670 case HUD_STAT_OFFSET:
3671 writeV2F1000(os, *(v2f *)value);
3675 os << serializeString(*(std::string *)value);
3677 case HUD_STAT_NUMBER:
3681 writeU32(os, *(u32 *)value);
3686 std::string s = os.str();
3687 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3689 m_con.Send(peer_id, 0, data, true);
3692 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3694 std::ostringstream os(std::ios_base::binary);
3697 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3698 writeU32(os, flags);
3702 std::string s = os.str();
3703 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3705 m_con.Send(peer_id, 0, data, true);
3708 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3710 std::ostringstream os(std::ios_base::binary);
3713 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3714 writeU16(os, param);
3715 os<<serializeString(value);
3718 std::string s = os.str();
3719 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3721 m_con.Send(peer_id, 0, data, true);
3724 void Server::BroadcastChatMessage(const std::wstring &message)
3726 for(std::map<u16, RemoteClient*>::iterator
3727 i = m_clients.begin();
3728 i != m_clients.end(); ++i)
3730 // Get client and check that it is valid
3731 RemoteClient *client = i->second;
3732 assert(client->peer_id == i->first);
3733 if(client->serialization_version == SER_FMT_VER_INVALID)
3736 SendChatMessage(client->peer_id, message);
3740 void Server::SendPlayerHP(u16 peer_id)
3742 DSTACK(__FUNCTION_NAME);
3743 PlayerSAO *playersao = getPlayerSAO(peer_id);
3745 playersao->m_hp_not_sent = false;
3746 SendHP(m_con, peer_id, playersao->getHP());
3749 void Server::SendMovePlayer(u16 peer_id)
3751 DSTACK(__FUNCTION_NAME);
3752 Player *player = m_env->getPlayer(peer_id);
3755 std::ostringstream os(std::ios_base::binary);
3756 writeU16(os, TOCLIENT_MOVE_PLAYER);
3757 writeV3F1000(os, player->getPosition());
3758 writeF1000(os, player->getPitch());
3759 writeF1000(os, player->getYaw());
3762 v3f pos = player->getPosition();
3763 f32 pitch = player->getPitch();
3764 f32 yaw = player->getYaw();
3765 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3766 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3773 std::string s = os.str();
3774 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3776 m_con.Send(peer_id, 0, data, true);
3779 void Server::SendPlayerPrivileges(u16 peer_id)
3781 Player *player = m_env->getPlayer(peer_id);
3783 if(player->peer_id == PEER_ID_INEXISTENT)
3786 std::set<std::string> privs;
3787 m_script->getAuth(player->getName(), NULL, &privs);
3789 std::ostringstream os(std::ios_base::binary);
3790 writeU16(os, TOCLIENT_PRIVILEGES);
3791 writeU16(os, privs.size());
3792 for(std::set<std::string>::const_iterator i = privs.begin();
3793 i != privs.end(); i++){
3794 os<<serializeString(*i);
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 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3806 Player *player = m_env->getPlayer(peer_id);
3808 if(player->peer_id == PEER_ID_INEXISTENT)
3811 std::ostringstream os(std::ios_base::binary);
3812 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3813 os<<serializeLongString(player->inventory_formspec);
3816 std::string s = os.str();
3817 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3819 m_con.Send(peer_id, 0, data, true);
3822 s32 Server::playSound(const SimpleSoundSpec &spec,
3823 const ServerSoundParams ¶ms)
3825 // Find out initial position of sound
3826 bool pos_exists = false;
3827 v3f pos = params.getPos(m_env, &pos_exists);
3828 // If position is not found while it should be, cancel sound
3829 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3831 // Filter destination clients
3832 std::set<RemoteClient*> dst_clients;
3833 if(params.to_player != "")
3835 Player *player = m_env->getPlayer(params.to_player.c_str());
3837 infostream<<"Server::playSound: Player \""<<params.to_player
3838 <<"\" not found"<<std::endl;
3841 if(player->peer_id == PEER_ID_INEXISTENT){
3842 infostream<<"Server::playSound: Player \""<<params.to_player
3843 <<"\" not connected"<<std::endl;
3846 RemoteClient *client = getClient(player->peer_id);
3847 dst_clients.insert(client);
3851 for(std::map<u16, RemoteClient*>::iterator
3852 i = m_clients.begin(); i != m_clients.end(); ++i)
3854 RemoteClient *client = i->second;
3855 Player *player = m_env->getPlayer(client->peer_id);
3859 if(player->getPosition().getDistanceFrom(pos) >
3860 params.max_hear_distance)
3863 dst_clients.insert(client);
3866 if(dst_clients.size() == 0)
3869 s32 id = m_next_sound_id++;
3870 // The sound will exist as a reference in m_playing_sounds
3871 m_playing_sounds[id] = ServerPlayingSound();
3872 ServerPlayingSound &psound = m_playing_sounds[id];
3873 psound.params = params;
3874 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3875 i != dst_clients.end(); i++)
3876 psound.clients.insert((*i)->peer_id);
3878 std::ostringstream os(std::ios_base::binary);
3879 writeU16(os, TOCLIENT_PLAY_SOUND);
3881 os<<serializeString(spec.name);
3882 writeF1000(os, spec.gain * params.gain);
3883 writeU8(os, params.type);
3884 writeV3F1000(os, pos);
3885 writeU16(os, params.object);
3886 writeU8(os, params.loop);
3888 std::string s = os.str();
3889 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3891 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3892 i != dst_clients.end(); i++){
3894 m_con.Send((*i)->peer_id, 0, data, true);
3898 void Server::stopSound(s32 handle)
3900 // Get sound reference
3901 std::map<s32, ServerPlayingSound>::iterator i =
3902 m_playing_sounds.find(handle);
3903 if(i == m_playing_sounds.end())
3905 ServerPlayingSound &psound = i->second;
3907 std::ostringstream os(std::ios_base::binary);
3908 writeU16(os, TOCLIENT_STOP_SOUND);
3909 writeS32(os, handle);
3911 std::string s = os.str();
3912 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3914 for(std::set<u16>::iterator i = psound.clients.begin();
3915 i != psound.clients.end(); i++){
3917 m_con.Send(*i, 0, data, true);
3919 // Remove sound reference
3920 m_playing_sounds.erase(i);
3923 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3924 std::list<u16> *far_players, float far_d_nodes)
3926 float maxd = far_d_nodes*BS;
3927 v3f p_f = intToFloat(p, BS);
3931 SharedBuffer<u8> reply(replysize);
3932 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3933 writeS16(&reply[2], p.X);
3934 writeS16(&reply[4], p.Y);
3935 writeS16(&reply[6], p.Z);
3937 for(std::map<u16, RemoteClient*>::iterator
3938 i = m_clients.begin();
3939 i != m_clients.end(); ++i)
3941 // Get client and check that it is valid
3942 RemoteClient *client = i->second;
3943 assert(client->peer_id == i->first);
3944 if(client->serialization_version == SER_FMT_VER_INVALID)
3947 // Don't send if it's the same one
3948 if(client->peer_id == ignore_id)
3954 Player *player = m_env->getPlayer(client->peer_id);
3957 // If player is far away, only set modified blocks not sent
3958 v3f player_pos = player->getPosition();
3959 if(player_pos.getDistanceFrom(p_f) > maxd)
3961 far_players->push_back(client->peer_id);
3968 m_con.Send(client->peer_id, 0, reply, true);
3972 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3973 std::list<u16> *far_players, float far_d_nodes)
3975 float maxd = far_d_nodes*BS;
3976 v3f p_f = intToFloat(p, BS);
3978 for(std::map<u16, RemoteClient*>::iterator
3979 i = m_clients.begin();
3980 i != m_clients.end(); ++i)
3982 // Get client and check that it is valid
3983 RemoteClient *client = i->second;
3984 assert(client->peer_id == i->first);
3985 if(client->serialization_version == SER_FMT_VER_INVALID)
3988 // Don't send if it's the same one
3989 if(client->peer_id == ignore_id)
3995 Player *player = m_env->getPlayer(client->peer_id);
3998 // If player is far away, only set modified blocks not sent
3999 v3f player_pos = player->getPosition();
4000 if(player_pos.getDistanceFrom(p_f) > maxd)
4002 far_players->push_back(client->peer_id);
4009 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4010 SharedBuffer<u8> reply(replysize);
4011 writeU16(&reply[0], TOCLIENT_ADDNODE);
4012 writeS16(&reply[2], p.X);
4013 writeS16(&reply[4], p.Y);
4014 writeS16(&reply[6], p.Z);
4015 n.serialize(&reply[8], client->serialization_version);
4018 m_con.Send(client->peer_id, 0, reply, true);
4022 void Server::setBlockNotSent(v3s16 p)
4024 for(std::map<u16, RemoteClient*>::iterator
4025 i = m_clients.begin();
4026 i != m_clients.end(); ++i)
4028 RemoteClient *client = i->second;
4029 client->SetBlockNotSent(p);
4033 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4035 DSTACK(__FUNCTION_NAME);
4037 v3s16 p = block->getPos();
4041 bool completely_air = true;
4042 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4043 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4044 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4046 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4048 completely_air = false;
4049 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4054 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4056 infostream<<"[completely air] ";
4057 infostream<<std::endl;
4061 Create a packet with the block in the right format
4064 std::ostringstream os(std::ios_base::binary);
4065 block->serialize(os, ver, false);
4066 std::string s = os.str();
4067 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4069 u32 replysize = 8 + blockdata.getSize();
4070 SharedBuffer<u8> reply(replysize);
4071 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4072 writeS16(&reply[2], p.X);
4073 writeS16(&reply[4], p.Y);
4074 writeS16(&reply[6], p.Z);
4075 memcpy(&reply[8], *blockdata, blockdata.getSize());
4077 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4078 <<": \tpacket size: "<<replysize<<std::endl;*/
4083 m_con.Send(peer_id, 1, reply, true);
4086 void Server::SendBlocks(float dtime)
4088 DSTACK(__FUNCTION_NAME);
4090 JMutexAutoLock envlock(m_env_mutex);
4091 JMutexAutoLock conlock(m_con_mutex);
4093 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4095 std::vector<PrioritySortedBlockTransfer> queue;
4097 s32 total_sending = 0;
4100 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4102 for(std::map<u16, RemoteClient*>::iterator
4103 i = m_clients.begin();
4104 i != m_clients.end(); ++i)
4106 RemoteClient *client = i->second;
4107 assert(client->peer_id == i->first);
4109 // If definitions and textures have not been sent, don't
4110 // send MapBlocks either
4111 if(!client->definitions_sent)
4114 total_sending += client->SendingCount();
4116 if(client->serialization_version == SER_FMT_VER_INVALID)
4119 client->GetNextBlocks(this, dtime, queue);
4124 // Lowest priority number comes first.
4125 // Lowest is most important.
4126 std::sort(queue.begin(), queue.end());
4128 for(u32 i=0; i<queue.size(); i++)
4130 //TODO: Calculate limit dynamically
4131 if(total_sending >= g_settings->getS32
4132 ("max_simultaneous_block_sends_server_total"))
4135 PrioritySortedBlockTransfer q = queue[i];
4137 MapBlock *block = NULL;
4140 block = m_env->getMap().getBlockNoCreate(q.pos);
4142 catch(InvalidPositionException &e)
4147 RemoteClient *client = getClient(q.peer_id);
4149 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4151 client->SentBlock(q.pos);
4157 void Server::fillMediaCache()
4159 DSTACK(__FUNCTION_NAME);
4161 infostream<<"Server: Calculating media file checksums"<<std::endl;
4163 // Collect all media file paths
4164 std::list<std::string> paths;
4165 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4166 i != m_mods.end(); i++){
4167 const ModSpec &mod = *i;
4168 paths.push_back(mod.path + DIR_DELIM + "textures");
4169 paths.push_back(mod.path + DIR_DELIM + "sounds");
4170 paths.push_back(mod.path + DIR_DELIM + "media");
4171 paths.push_back(mod.path + DIR_DELIM + "models");
4173 std::string path_all = "textures";
4174 paths.push_back(path_all + DIR_DELIM + "all");
4176 // Collect media file information from paths into cache
4177 for(std::list<std::string>::iterator i = paths.begin();
4178 i != paths.end(); i++)
4180 std::string mediapath = *i;
4181 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4182 for(u32 j=0; j<dirlist.size(); j++){
4183 if(dirlist[j].dir) // Ignode dirs
4185 std::string filename = dirlist[j].name;
4186 // If name contains illegal characters, ignore the file
4187 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4188 infostream<<"Server: ignoring illegal file name: \""
4189 <<filename<<"\""<<std::endl;
4192 // If name is not in a supported format, ignore it
4193 const char *supported_ext[] = {
4194 ".png", ".jpg", ".bmp", ".tga",
4195 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4197 ".x", ".b3d", ".md2", ".obj",
4200 if(removeStringEnd(filename, supported_ext) == ""){
4201 infostream<<"Server: ignoring unsupported file extension: \""
4202 <<filename<<"\""<<std::endl;
4205 // Ok, attempt to load the file and add to cache
4206 std::string filepath = mediapath + DIR_DELIM + filename;
4208 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4209 if(fis.good() == false){
4210 errorstream<<"Server::fillMediaCache(): Could not open \""
4211 <<filename<<"\" for reading"<<std::endl;
4214 std::ostringstream tmp_os(std::ios_base::binary);
4218 fis.read(buf, 1024);
4219 std::streamsize len = fis.gcount();
4220 tmp_os.write(buf, len);
4229 errorstream<<"Server::fillMediaCache(): Failed to read \""
4230 <<filename<<"\""<<std::endl;
4233 if(tmp_os.str().length() == 0){
4234 errorstream<<"Server::fillMediaCache(): Empty file \""
4235 <<filepath<<"\""<<std::endl;
4240 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4242 unsigned char *digest = sha1.getDigest();
4243 std::string sha1_base64 = base64_encode(digest, 20);
4244 std::string sha1_hex = hex_encode((char*)digest, 20);
4248 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4249 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4254 struct SendableMediaAnnouncement
4257 std::string sha1_digest;
4259 SendableMediaAnnouncement(const std::string name_="",
4260 const std::string sha1_digest_=""):
4262 sha1_digest(sha1_digest_)
4266 void Server::sendMediaAnnouncement(u16 peer_id)
4268 DSTACK(__FUNCTION_NAME);
4270 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4273 std::list<SendableMediaAnnouncement> file_announcements;
4275 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4276 i != m_media.end(); i++){
4278 file_announcements.push_back(
4279 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4283 std::ostringstream os(std::ios_base::binary);
4291 u16 length of sha1_digest
4296 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4297 writeU16(os, file_announcements.size());
4299 for(std::list<SendableMediaAnnouncement>::iterator
4300 j = file_announcements.begin();
4301 j != file_announcements.end(); ++j){
4302 os<<serializeString(j->name);
4303 os<<serializeString(j->sha1_digest);
4305 os<<serializeString(g_settings->get("remote_media"));
4308 std::string s = os.str();
4309 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4312 m_con.Send(peer_id, 0, data, true);
4315 struct SendableMedia
4321 SendableMedia(const std::string &name_="", const std::string path_="",
4322 const std::string &data_=""):
4329 void Server::sendRequestedMedia(u16 peer_id,
4330 const std::list<MediaRequest> &tosend)
4332 DSTACK(__FUNCTION_NAME);
4334 verbosestream<<"Server::sendRequestedMedia(): "
4335 <<"Sending files to client"<<std::endl;
4339 // Put 5kB in one bunch (this is not accurate)
4340 u32 bytes_per_bunch = 5000;
4342 std::vector< std::list<SendableMedia> > file_bunches;
4343 file_bunches.push_back(std::list<SendableMedia>());
4345 u32 file_size_bunch_total = 0;
4347 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4348 i != tosend.end(); ++i)
4350 if(m_media.find(i->name) == m_media.end()){
4351 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4352 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4356 //TODO get path + name
4357 std::string tpath = m_media[(*i).name].path;
4360 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4361 if(fis.good() == false){
4362 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4363 <<tpath<<"\" for reading"<<std::endl;
4366 std::ostringstream tmp_os(std::ios_base::binary);
4370 fis.read(buf, 1024);
4371 std::streamsize len = fis.gcount();
4372 tmp_os.write(buf, len);
4373 file_size_bunch_total += len;
4382 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4383 <<(*i).name<<"\""<<std::endl;
4386 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4387 <<tname<<"\""<<std::endl;*/
4389 file_bunches[file_bunches.size()-1].push_back(
4390 SendableMedia((*i).name, tpath, tmp_os.str()));
4392 // Start next bunch if got enough data
4393 if(file_size_bunch_total >= bytes_per_bunch){
4394 file_bunches.push_back(std::list<SendableMedia>());
4395 file_size_bunch_total = 0;
4400 /* Create and send packets */
4402 u32 num_bunches = file_bunches.size();
4403 for(u32 i=0; i<num_bunches; i++)
4405 std::ostringstream os(std::ios_base::binary);
4409 u16 total number of texture bunches
4410 u16 index of this bunch
4411 u32 number of files in this bunch
4420 writeU16(os, TOCLIENT_MEDIA);
4421 writeU16(os, num_bunches);
4423 writeU32(os, file_bunches[i].size());
4425 for(std::list<SendableMedia>::iterator
4426 j = file_bunches[i].begin();
4427 j != file_bunches[i].end(); ++j){
4428 os<<serializeString(j->name);
4429 os<<serializeLongString(j->data);
4433 std::string s = os.str();
4434 verbosestream<<"Server::sendRequestedMedia(): bunch "
4435 <<i<<"/"<<num_bunches
4436 <<" files="<<file_bunches[i].size()
4437 <<" size=" <<s.size()<<std::endl;
4438 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4440 m_con.Send(peer_id, 0, data, true);
4444 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4446 if(m_detached_inventories.count(name) == 0){
4447 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4450 Inventory *inv = m_detached_inventories[name];
4452 std::ostringstream os(std::ios_base::binary);
4453 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4454 os<<serializeString(name);
4458 std::string s = os.str();
4459 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4461 m_con.Send(peer_id, 0, data, true);
4464 void Server::sendDetachedInventoryToAll(const std::string &name)
4466 DSTACK(__FUNCTION_NAME);
4468 for(std::map<u16, RemoteClient*>::iterator
4469 i = m_clients.begin();
4470 i != m_clients.end(); ++i){
4471 RemoteClient *client = i->second;
4472 sendDetachedInventory(name, client->peer_id);
4476 void Server::sendDetachedInventories(u16 peer_id)
4478 DSTACK(__FUNCTION_NAME);
4480 for(std::map<std::string, Inventory*>::iterator
4481 i = m_detached_inventories.begin();
4482 i != m_detached_inventories.end(); i++){
4483 const std::string &name = i->first;
4484 //Inventory *inv = i->second;
4485 sendDetachedInventory(name, peer_id);
4493 void Server::DiePlayer(u16 peer_id)
4495 DSTACK(__FUNCTION_NAME);
4497 PlayerSAO *playersao = getPlayerSAO(peer_id);
4500 infostream<<"Server::DiePlayer(): Player "
4501 <<playersao->getPlayer()->getName()
4502 <<" dies"<<std::endl;
4504 playersao->setHP(0);
4506 // Trigger scripted stuff
4507 m_script->on_dieplayer(playersao);
4509 SendPlayerHP(peer_id);
4510 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4513 void Server::RespawnPlayer(u16 peer_id)
4515 DSTACK(__FUNCTION_NAME);
4517 PlayerSAO *playersao = getPlayerSAO(peer_id);
4520 infostream<<"Server::RespawnPlayer(): Player "
4521 <<playersao->getPlayer()->getName()
4522 <<" respawns"<<std::endl;
4524 playersao->setHP(PLAYER_MAX_HP);
4526 bool repositioned = m_script->on_respawnplayer(playersao);
4528 v3f pos = findSpawnPos(m_env->getServerMap());
4529 playersao->setPos(pos);
4533 void Server::UpdateCrafting(u16 peer_id)
4535 DSTACK(__FUNCTION_NAME);
4537 Player* player = m_env->getPlayer(peer_id);
4540 // Get a preview for crafting
4542 getCraftingResult(&player->inventory, preview, false, this);
4544 // Put the new preview in
4545 InventoryList *plist = player->inventory.getList("craftpreview");
4547 assert(plist->getSize() >= 1);
4548 plist->changeItem(0, preview);
4551 RemoteClient* Server::getClient(u16 peer_id)
4553 DSTACK(__FUNCTION_NAME);
4554 //JMutexAutoLock lock(m_con_mutex);
4555 std::map<u16, RemoteClient*>::iterator n;
4556 n = m_clients.find(peer_id);
4557 // A client should exist for all peers
4558 assert(n != m_clients.end());
4562 std::wstring Server::getStatusString()
4564 std::wostringstream os(std::ios_base::binary);
4567 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4569 os<<L", uptime="<<m_uptime.get();
4570 // Information about clients
4571 std::map<u16, RemoteClient*>::iterator i;
4574 for(i = m_clients.begin(), first = true;
4575 i != m_clients.end(); ++i)
4577 // Get client and check that it is valid
4578 RemoteClient *client = i->second;
4579 assert(client->peer_id == i->first);
4580 if(client->serialization_version == SER_FMT_VER_INVALID)
4583 Player *player = m_env->getPlayer(client->peer_id);
4584 // Get name of player
4585 std::wstring name = L"unknown";
4587 name = narrow_to_wide(player->getName());
4588 // Add name to information string
4596 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4597 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4598 if(g_settings->get("motd") != "")
4599 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4603 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4605 std::set<std::string> privs;
4606 m_script->getAuth(name, NULL, &privs);
4610 bool Server::checkPriv(const std::string &name, const std::string &priv)
4612 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4613 return (privs.count(priv) != 0);
4616 void Server::reportPrivsModified(const std::string &name)
4619 for(std::map<u16, RemoteClient*>::iterator
4620 i = m_clients.begin();
4621 i != m_clients.end(); ++i){
4622 RemoteClient *client = i->second;
4623 Player *player = m_env->getPlayer(client->peer_id);
4624 reportPrivsModified(player->getName());
4627 Player *player = m_env->getPlayer(name.c_str());
4630 SendPlayerPrivileges(player->peer_id);
4631 PlayerSAO *sao = player->getPlayerSAO();
4634 sao->updatePrivileges(
4635 getPlayerEffectivePrivs(name),
4640 void Server::reportInventoryFormspecModified(const std::string &name)
4642 Player *player = m_env->getPlayer(name.c_str());
4645 SendPlayerInventoryFormspec(player->peer_id);
4648 // Saves g_settings to configpath given at initialization
4649 void Server::saveConfig()
4651 if(m_path_config != "")
4652 g_settings->updateConfigFile(m_path_config.c_str());
4655 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4657 Player *player = m_env->getPlayer(name);
4661 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4663 SendChatMessage(player->peer_id, msg);
4666 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4668 Player *player = m_env->getPlayer(playername);
4672 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4676 SendShowFormspecMessage(player->peer_id, formspec, formname);
4680 u32 Server::hudAdd(Player *player, HudElement *form) {
4684 u32 id = hud_get_free_id(player);
4685 if (id < player->hud.size())
4686 player->hud[id] = form;
4688 player->hud.push_back(form);
4690 SendHUDAdd(player->peer_id, id, form);
4694 bool Server::hudRemove(Player *player, u32 id) {
4695 if (!player || id >= player->hud.size() || !player->hud[id])
4698 delete player->hud[id];
4699 player->hud[id] = NULL;
4701 SendHUDRemove(player->peer_id, id);
4705 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4709 SendHUDChange(player->peer_id, id, stat, data);
4713 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4717 SendHUDSetFlags(player->peer_id, flags, mask);
4721 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4724 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4727 std::ostringstream os(std::ios::binary);
4728 writeS32(os, hotbar_itemcount);
4729 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4733 void Server::notifyPlayers(const std::wstring msg)
4735 BroadcastChatMessage(msg);
4738 void Server::spawnParticle(const char *playername, v3f pos,
4739 v3f velocity, v3f acceleration,
4740 float expirationtime, float size, bool
4741 collisiondetection, std::string texture)
4743 Player *player = m_env->getPlayer(playername);
4746 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4747 expirationtime, size, collisiondetection, texture);
4750 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4751 float expirationtime, float size,
4752 bool collisiondetection, std::string texture)
4754 SendSpawnParticleAll(pos, velocity, acceleration,
4755 expirationtime, size, collisiondetection, texture);
4758 u32 Server::addParticleSpawner(const char *playername,
4759 u16 amount, float spawntime,
4760 v3f minpos, v3f maxpos,
4761 v3f minvel, v3f maxvel,
4762 v3f minacc, v3f maxacc,
4763 float minexptime, float maxexptime,
4764 float minsize, float maxsize,
4765 bool collisiondetection, std::string texture)
4767 Player *player = m_env->getPlayer(playername);
4772 for(;;) // look for unused particlespawner id
4775 if (std::find(m_particlespawner_ids.begin(),
4776 m_particlespawner_ids.end(), id)
4777 == m_particlespawner_ids.end())
4779 m_particlespawner_ids.push_back(id);
4784 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4785 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4786 minexptime, maxexptime, minsize, maxsize,
4787 collisiondetection, texture, id);
4792 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4793 v3f minpos, v3f maxpos,
4794 v3f minvel, v3f maxvel,
4795 v3f minacc, v3f maxacc,
4796 float minexptime, float maxexptime,
4797 float minsize, float maxsize,
4798 bool collisiondetection, std::string texture)
4801 for(;;) // look for unused particlespawner id
4804 if (std::find(m_particlespawner_ids.begin(),
4805 m_particlespawner_ids.end(), id)
4806 == m_particlespawner_ids.end())
4808 m_particlespawner_ids.push_back(id);
4813 SendAddParticleSpawnerAll(amount, spawntime,
4814 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4815 minexptime, maxexptime, minsize, maxsize,
4816 collisiondetection, texture, id);
4821 void Server::deleteParticleSpawner(const char *playername, u32 id)
4823 Player *player = m_env->getPlayer(playername);
4827 m_particlespawner_ids.erase(
4828 std::remove(m_particlespawner_ids.begin(),
4829 m_particlespawner_ids.end(), id),
4830 m_particlespawner_ids.end());
4831 SendDeleteParticleSpawner(player->peer_id, id);
4834 void Server::deleteParticleSpawnerAll(u32 id)
4836 m_particlespawner_ids.erase(
4837 std::remove(m_particlespawner_ids.begin(),
4838 m_particlespawner_ids.end(), id),
4839 m_particlespawner_ids.end());
4840 SendDeleteParticleSpawnerAll(id);
4843 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4845 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4848 Inventory* Server::createDetachedInventory(const std::string &name)
4850 if(m_detached_inventories.count(name) > 0){
4851 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4852 delete m_detached_inventories[name];
4854 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4856 Inventory *inv = new Inventory(m_itemdef);
4858 m_detached_inventories[name] = inv;
4859 sendDetachedInventoryToAll(name);
4866 BoolScopeSet(bool *dst, bool val):
4869 m_orig_state = *m_dst;
4874 *m_dst = m_orig_state;
4881 // actions: time-reversed list
4882 // Return value: success/failure
4883 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4884 std::list<std::string> *log)
4886 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4887 ServerMap *map = (ServerMap*)(&m_env->getMap());
4888 // Disable rollback report sink while reverting
4889 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4891 // Fail if no actions to handle
4892 if(actions.empty()){
4893 log->push_back("Nothing to do.");
4900 for(std::list<RollbackAction>::const_iterator
4901 i = actions.begin();
4902 i != actions.end(); i++)
4904 const RollbackAction &action = *i;
4906 bool success = action.applyRevert(map, this, this);
4909 std::ostringstream os;
4910 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4911 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4913 log->push_back(os.str());
4915 std::ostringstream os;
4916 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4917 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4919 log->push_back(os.str());
4923 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4924 <<" failed"<<std::endl;
4926 // Call it done if less than half failed
4927 return num_failed <= num_tried/2;
4930 // IGameDef interface
4932 IItemDefManager* Server::getItemDefManager()
4936 INodeDefManager* Server::getNodeDefManager()
4940 ICraftDefManager* Server::getCraftDefManager()
4944 ITextureSource* Server::getTextureSource()
4948 IShaderSource* Server::getShaderSource()
4952 u16 Server::allocateUnknownNodeId(const std::string &name)
4954 return m_nodedef->allocateDummy(name);
4956 ISoundManager* Server::getSoundManager()
4958 return &dummySoundManager;
4960 MtEventManager* Server::getEventManager()
4964 IRollbackReportSink* Server::getRollbackReportSink()
4966 if(!m_enable_rollback_recording)
4968 if(!m_rollback_sink_enabled)
4973 IWritableItemDefManager* Server::getWritableItemDefManager()
4977 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4981 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4986 const ModSpec* Server::getModSpec(const std::string &modname)
4988 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4989 i != m_mods.end(); i++){
4990 const ModSpec &mod = *i;
4991 if(mod.name == modname)
4996 void Server::getModNames(std::list<std::string> &modlist)
4998 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
5000 modlist.push_back(i->name);
5003 std::string Server::getBuiltinLuaPath()
5005 return porting::path_share + DIR_DELIM + "builtin";
5008 v3f findSpawnPos(ServerMap &map)
5010 //return v3f(50,50,50)*BS;
5015 nodepos = v2s16(0,0);
5020 s16 water_level = map.m_mgparams->water_level;
5022 // Try to find a good place a few times
5023 for(s32 i=0; i<1000; i++)
5026 // We're going to try to throw the player to this position
5027 v2s16 nodepos2d = v2s16(
5028 -range + (myrand() % (range * 2)),
5029 -range + (myrand() % (range * 2)));
5031 // Get ground height at point
5032 s16 groundheight = map.findGroundLevel(nodepos2d);
5033 if (groundheight <= water_level) // Don't go underwater
5035 if (groundheight > water_level + 6) // Don't go to high places
5038 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5039 bool is_good = false;
5041 for (s32 i = 0; i < 10; i++) {
5042 v3s16 blockpos = getNodeBlockPos(nodepos);
5043 map.emergeBlock(blockpos, true);
5044 content_t c = map.getNodeNoEx(nodepos).getContent();
5045 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5047 if (air_count >= 2){
5055 // Found a good place
5056 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5062 return intToFloat(nodepos, BS);
5065 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5067 RemotePlayer *player = NULL;
5068 bool newplayer = false;
5071 Try to get an existing player
5073 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5075 // If player is already connected, cancel
5076 if(player != NULL && player->peer_id != 0)
5078 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5083 If player with the wanted peer_id already exists, cancel.
5085 if(m_env->getPlayer(peer_id) != NULL)
5087 infostream<<"emergePlayer(): Player with wrong name but same"
5088 " peer_id already exists"<<std::endl;
5093 Create a new player if it doesn't exist yet
5098 player = new RemotePlayer(this);
5099 player->updateName(name);
5101 /* Set player position */
5102 infostream<<"Server: Finding spawn place for player \""
5103 <<name<<"\""<<std::endl;
5104 v3f pos = findSpawnPos(m_env->getServerMap());
5105 player->setPosition(pos);
5107 /* Add player to environment */
5108 m_env->addPlayer(player);
5112 Create a new player active object
5114 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5115 getPlayerEffectivePrivs(player->getName()),
5118 /* Clean up old HUD elements from previous sessions */
5119 player->hud.clear();
5121 /* Add object to environment */
5122 m_env->addActiveObject(playersao);
5126 m_script->on_newplayer(playersao);
5128 m_script->on_joinplayer(playersao);
5133 void Server::handlePeerChange(PeerChange &c)
5135 JMutexAutoLock envlock(m_env_mutex);
5136 JMutexAutoLock conlock(m_con_mutex);
5138 if(c.type == PEER_ADDED)
5145 std::map<u16, RemoteClient*>::iterator n;
5146 n = m_clients.find(c.peer_id);
5147 // The client shouldn't already exist
5148 assert(n == m_clients.end());
5151 RemoteClient *client = new RemoteClient();
5152 client->peer_id = c.peer_id;
5153 m_clients[client->peer_id] = client;
5156 else if(c.type == PEER_REMOVED)
5163 std::map<u16, RemoteClient*>::iterator n;
5164 n = m_clients.find(c.peer_id);
5165 // The client should exist
5166 assert(n != m_clients.end());
5169 Mark objects to be not known by the client
5171 RemoteClient *client = n->second;
5173 for(std::set<u16>::iterator
5174 i = client->m_known_objects.begin();
5175 i != client->m_known_objects.end(); ++i)
5179 ServerActiveObject* obj = m_env->getActiveObject(id);
5181 if(obj && obj->m_known_by_count > 0)
5182 obj->m_known_by_count--;
5186 Clear references to playing sounds
5188 for(std::map<s32, ServerPlayingSound>::iterator
5189 i = m_playing_sounds.begin();
5190 i != m_playing_sounds.end();)
5192 ServerPlayingSound &psound = i->second;
5193 psound.clients.erase(c.peer_id);
5194 if(psound.clients.size() == 0)
5195 m_playing_sounds.erase(i++);
5200 Player *player = m_env->getPlayer(c.peer_id);
5202 // Collect information about leaving in chat
5203 std::wstring message;
5207 std::wstring name = narrow_to_wide(player->getName());
5210 message += L" left the game.";
5212 message += L" (timed out)";
5216 /* Run scripts and remove from environment */
5220 PlayerSAO *playersao = player->getPlayerSAO();
5223 m_script->on_leaveplayer(playersao);
5225 playersao->disconnected();
5235 std::ostringstream os(std::ios_base::binary);
5236 for(std::map<u16, RemoteClient*>::iterator
5237 i = m_clients.begin();
5238 i != m_clients.end(); ++i)
5240 RemoteClient *client = i->second;
5241 assert(client->peer_id == i->first);
5242 if(client->serialization_version == SER_FMT_VER_INVALID)
5245 Player *player = m_env->getPlayer(client->peer_id);
5248 // Get name of player
5249 os<<player->getName()<<" ";
5252 actionstream<<player->getName()<<" "
5253 <<(c.timeout?"times out.":"leaves game.")
5254 <<" List of players: "
5255 <<os.str()<<std::endl;
5260 delete m_clients[c.peer_id];
5261 m_clients.erase(c.peer_id);
5263 // Send player info to all remaining clients
5264 //SendPlayerInfos();
5266 // Send leave chat message to all remaining clients
5267 if(message.length() != 0)
5268 BroadcastChatMessage(message);
5277 void Server::handlePeerChanges()
5279 while(m_peer_change_queue.size() > 0)
5281 PeerChange c = m_peer_change_queue.pop_front();
5283 verbosestream<<"Server: Handling peer change: "
5284 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5287 handlePeerChange(c);
5291 void dedicated_server_loop(Server &server, bool &kill)
5293 DSTACK(__FUNCTION_NAME);
5295 verbosestream<<"dedicated_server_loop()"<<std::endl;
5297 IntervalLimiter m_profiler_interval;
5301 float steplen = g_settings->getFloat("dedicated_server_step");
5302 // This is kind of a hack but can be done like this
5303 // because server.step() is very light
5305 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5306 sleep_ms((int)(steplen*1000.0));
5308 server.step(steplen);
5310 if(server.getShutdownRequested() || kill)
5312 infostream<<"Dedicated server quitting"<<std::endl;
5314 if(g_settings->getBool("server_announce") == true)
5315 ServerList::sendAnnounce("delete");
5323 float profiler_print_interval =
5324 g_settings->getFloat("profiler_print_interval");
5325 if(profiler_print_interval != 0)
5327 if(m_profiler_interval.step(steplen, profiler_print_interval))
5329 infostream<<"Profiler:"<<std::endl;
5330 g_profiler->print(infostream);
5331 g_profiler->clear();