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, m_mods);
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 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1927 actionstream<<"Server: Player with an invalid name "
1928 <<"tried to connect from "<<addr_s<<std::endl;
1929 SendAccessDenied(m_con, peer_id,
1930 L"Name is not allowed");
1934 infostream<<"Server: New connection: \""<<playername<<"\" from "
1935 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1938 char given_password[PASSWORD_SIZE];
1939 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1941 // old version - assume blank password
1942 given_password[0] = 0;
1946 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1948 given_password[i] = data[23+i];
1950 given_password[PASSWORD_SIZE-1] = 0;
1953 if(!base64_is_valid(given_password)){
1954 infostream<<"Server: "<<playername
1955 <<" supplied invalid password hash"<<std::endl;
1956 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1960 std::string checkpwd; // Password hash to check against
1961 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1963 // If no authentication info exists for user, create it
1965 if(!isSingleplayer() &&
1966 g_settings->getBool("disallow_empty_password") &&
1967 std::string(given_password) == ""){
1968 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1969 L"disallowed. Set a password and try again.");
1972 std::wstring raw_default_password =
1973 narrow_to_wide(g_settings->get("default_password"));
1974 std::string initial_password =
1975 translatePassword(playername, raw_default_password);
1977 // If default_password is empty, allow any initial password
1978 if (raw_default_password.length() == 0)
1979 initial_password = given_password;
1981 m_script->createAuth(playername, initial_password);
1984 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1987 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1991 if(given_password != checkpwd){
1992 infostream<<"Server: peer_id="<<peer_id
1993 <<": supplied invalid password for "
1994 <<playername<<std::endl;
1995 SendAccessDenied(m_con, peer_id, L"Invalid password");
1999 // Do not allow multiple players in simple singleplayer mode.
2000 // This isn't a perfect way to do it, but will suffice for now.
2001 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2002 infostream<<"Server: Not allowing another client to connect in"
2003 <<" simple singleplayer mode"<<std::endl;
2004 SendAccessDenied(m_con, peer_id,
2005 L"Running in simple singleplayer mode.");
2009 // Enforce user limit.
2010 // Don't enforce for users that have some admin right
2011 if(m_clients.size() >= g_settings->getU16("max_users") &&
2012 !checkPriv(playername, "server") &&
2013 !checkPriv(playername, "ban") &&
2014 !checkPriv(playername, "privs") &&
2015 !checkPriv(playername, "password") &&
2016 playername != g_settings->get("name"))
2018 actionstream<<"Server: "<<playername<<" tried to join, but there"
2019 <<" are already max_users="
2020 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2021 SendAccessDenied(m_con, peer_id, L"Too many users.");
2026 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2028 // If failed, cancel
2029 if(playersao == NULL)
2031 errorstream<<"Server: peer_id="<<peer_id
2032 <<": failed to emerge player"<<std::endl;
2037 Answer with a TOCLIENT_INIT
2040 SharedBuffer<u8> reply(2+1+6+8+4);
2041 writeU16(&reply[0], TOCLIENT_INIT);
2042 writeU8(&reply[2], deployed);
2043 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2044 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2045 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2048 m_con.Send(peer_id, 0, reply, true);
2052 Send complete position information
2054 SendMovePlayer(peer_id);
2059 if(command == TOSERVER_INIT2)
2061 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2062 <<peer_id<<std::endl;
2064 Player *player = m_env->getPlayer(peer_id);
2066 verbosestream<<"Server: TOSERVER_INIT2: "
2067 <<"Player not found; ignoring."<<std::endl;
2071 RemoteClient *client = getClient(peer_id);
2072 client->serialization_version =
2073 getClient(peer_id)->pending_serialization_version;
2076 Send some initialization data
2079 infostream<<"Server: Sending content to "
2080 <<getPlayerName(peer_id)<<std::endl;
2082 // Send player movement settings
2083 SendMovement(m_con, peer_id);
2085 // Send item definitions
2086 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2088 // Send node definitions
2089 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2091 // Send media announcement
2092 sendMediaAnnouncement(peer_id);
2095 SendPlayerPrivileges(peer_id);
2097 // Send inventory formspec
2098 SendPlayerInventoryFormspec(peer_id);
2101 UpdateCrafting(peer_id);
2102 SendInventory(peer_id);
2105 if(g_settings->getBool("enable_damage"))
2106 SendPlayerHP(peer_id);
2108 // Send detached inventories
2109 sendDetachedInventories(peer_id);
2111 // Show death screen if necessary
2113 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2117 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2118 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2119 m_con.Send(peer_id, 0, data, true);
2122 // Note things in chat if not in simple singleplayer mode
2123 if(!m_simple_singleplayer_mode)
2125 // Send information about server to player in chat
2126 SendChatMessage(peer_id, getStatusString());
2128 // Send information about joining in chat
2130 std::wstring name = L"unknown";
2131 Player *player = m_env->getPlayer(peer_id);
2133 name = narrow_to_wide(player->getName());
2135 std::wstring message;
2138 message += L" joined the game.";
2139 BroadcastChatMessage(message);
2143 // Warnings about protocol version can be issued here
2144 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2146 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2147 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2154 std::ostringstream os(std::ios_base::binary);
2155 for(std::map<u16, RemoteClient*>::iterator
2156 i = m_clients.begin();
2157 i != m_clients.end(); ++i)
2159 RemoteClient *client = i->second;
2160 assert(client->peer_id == i->first);
2161 if(client->serialization_version == SER_FMT_VER_INVALID)
2164 Player *player = m_env->getPlayer(client->peer_id);
2167 // Get name of player
2168 os<<player->getName()<<" ";
2171 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<" joins game. List of players: "
2172 <<os.str()<<std::endl;
2178 if(peer_ser_ver == SER_FMT_VER_INVALID)
2180 infostream<<"Server::ProcessData(): Cancelling: Peer"
2181 " serialization format invalid or not initialized."
2182 " Skipping incoming command="<<command<<std::endl;
2186 Player *player = m_env->getPlayer(peer_id);
2188 infostream<<"Server::ProcessData(): Cancelling: "
2189 "No player for peer_id="<<peer_id
2194 PlayerSAO *playersao = player->getPlayerSAO();
2195 if(playersao == NULL){
2196 infostream<<"Server::ProcessData(): Cancelling: "
2197 "No player object for peer_id="<<peer_id
2202 if(command == TOSERVER_PLAYERPOS)
2204 if(datasize < 2+12+12+4+4)
2208 v3s32 ps = readV3S32(&data[start+2]);
2209 v3s32 ss = readV3S32(&data[start+2+12]);
2210 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2211 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2213 if(datasize >= 2+12+12+4+4+4)
2214 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2215 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2216 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2217 pitch = wrapDegrees(pitch);
2218 yaw = wrapDegrees(yaw);
2220 player->setPosition(position);
2221 player->setSpeed(speed);
2222 player->setPitch(pitch);
2223 player->setYaw(yaw);
2224 player->keyPressed=keyPressed;
2225 player->control.up = (bool)(keyPressed&1);
2226 player->control.down = (bool)(keyPressed&2);
2227 player->control.left = (bool)(keyPressed&4);
2228 player->control.right = (bool)(keyPressed&8);
2229 player->control.jump = (bool)(keyPressed&16);
2230 player->control.aux1 = (bool)(keyPressed&32);
2231 player->control.sneak = (bool)(keyPressed&64);
2232 player->control.LMB = (bool)(keyPressed&128);
2233 player->control.RMB = (bool)(keyPressed&256);
2235 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2236 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2237 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2239 else if(command == TOSERVER_GOTBLOCKS)
2252 u16 count = data[2];
2253 for(u16 i=0; i<count; i++)
2255 if((s16)datasize < 2+1+(i+1)*6)
2256 throw con::InvalidIncomingDataException
2257 ("GOTBLOCKS length is too short");
2258 v3s16 p = readV3S16(&data[2+1+i*6]);
2259 /*infostream<<"Server: GOTBLOCKS ("
2260 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2261 RemoteClient *client = getClient(peer_id);
2262 client->GotBlock(p);
2265 else if(command == TOSERVER_DELETEDBLOCKS)
2278 u16 count = data[2];
2279 for(u16 i=0; i<count; i++)
2281 if((s16)datasize < 2+1+(i+1)*6)
2282 throw con::InvalidIncomingDataException
2283 ("DELETEDBLOCKS length is too short");
2284 v3s16 p = readV3S16(&data[2+1+i*6]);
2285 /*infostream<<"Server: DELETEDBLOCKS ("
2286 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2287 RemoteClient *client = getClient(peer_id);
2288 client->SetBlockNotSent(p);
2291 else if(command == TOSERVER_CLICK_OBJECT)
2293 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2296 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2298 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2301 else if(command == TOSERVER_GROUND_ACTION)
2303 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2307 else if(command == TOSERVER_RELEASE)
2309 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2312 else if(command == TOSERVER_SIGNTEXT)
2314 infostream<<"Server: SIGNTEXT not supported anymore"
2318 else if(command == TOSERVER_SIGNNODETEXT)
2320 infostream<<"Server: SIGNNODETEXT not supported anymore"
2324 else if(command == TOSERVER_INVENTORY_ACTION)
2326 // Strip command and create a stream
2327 std::string datastring((char*)&data[2], datasize-2);
2328 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2329 std::istringstream is(datastring, std::ios_base::binary);
2331 InventoryAction *a = InventoryAction::deSerialize(is);
2334 infostream<<"TOSERVER_INVENTORY_ACTION: "
2335 <<"InventoryAction::deSerialize() returned NULL"
2340 // If something goes wrong, this player is to blame
2341 RollbackScopeActor rollback_scope(m_rollback,
2342 std::string("player:")+player->getName());
2345 Note: Always set inventory not sent, to repair cases
2346 where the client made a bad prediction.
2350 Handle restrictions and special cases of the move action
2352 if(a->getType() == IACTION_MOVE)
2354 IMoveAction *ma = (IMoveAction*)a;
2356 ma->from_inv.applyCurrentPlayer(player->getName());
2357 ma->to_inv.applyCurrentPlayer(player->getName());
2359 setInventoryModified(ma->from_inv);
2360 setInventoryModified(ma->to_inv);
2362 bool from_inv_is_current_player =
2363 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2364 (ma->from_inv.name == player->getName());
2366 bool to_inv_is_current_player =
2367 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2368 (ma->to_inv.name == player->getName());
2371 Disable moving items out of craftpreview
2373 if(ma->from_list == "craftpreview")
2375 infostream<<"Ignoring IMoveAction from "
2376 <<(ma->from_inv.dump())<<":"<<ma->from_list
2377 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2378 <<" because src is "<<ma->from_list<<std::endl;
2384 Disable moving items into craftresult and craftpreview
2386 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2388 infostream<<"Ignoring IMoveAction from "
2389 <<(ma->from_inv.dump())<<":"<<ma->from_list
2390 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2391 <<" because dst is "<<ma->to_list<<std::endl;
2396 // Disallow moving items in elsewhere than player's inventory
2397 // if not allowed to interact
2398 if(!checkPriv(player->getName(), "interact") &&
2399 (!from_inv_is_current_player ||
2400 !to_inv_is_current_player))
2402 infostream<<"Cannot move outside of player's inventory: "
2403 <<"No interact privilege"<<std::endl;
2409 Handle restrictions and special cases of the drop action
2411 else if(a->getType() == IACTION_DROP)
2413 IDropAction *da = (IDropAction*)a;
2415 da->from_inv.applyCurrentPlayer(player->getName());
2417 setInventoryModified(da->from_inv);
2420 Disable dropping items out of craftpreview
2422 if(da->from_list == "craftpreview")
2424 infostream<<"Ignoring IDropAction from "
2425 <<(da->from_inv.dump())<<":"<<da->from_list
2426 <<" because src is "<<da->from_list<<std::endl;
2431 // Disallow dropping items if not allowed to interact
2432 if(!checkPriv(player->getName(), "interact"))
2439 Handle restrictions and special cases of the craft action
2441 else if(a->getType() == IACTION_CRAFT)
2443 ICraftAction *ca = (ICraftAction*)a;
2445 ca->craft_inv.applyCurrentPlayer(player->getName());
2447 setInventoryModified(ca->craft_inv);
2449 //bool craft_inv_is_current_player =
2450 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2451 // (ca->craft_inv.name == player->getName());
2453 // Disallow crafting if not allowed to interact
2454 if(!checkPriv(player->getName(), "interact"))
2456 infostream<<"Cannot craft: "
2457 <<"No interact privilege"<<std::endl;
2464 a->apply(this, playersao, this);
2468 else if(command == TOSERVER_CHAT_MESSAGE)
2476 std::string datastring((char*)&data[2], datasize-2);
2477 std::istringstream is(datastring, std::ios_base::binary);
2480 is.read((char*)buf, 2);
2481 u16 len = readU16(buf);
2483 std::wstring message;
2484 for(u16 i=0; i<len; i++)
2486 is.read((char*)buf, 2);
2487 message += (wchar_t)readU16(buf);
2490 // If something goes wrong, this player is to blame
2491 RollbackScopeActor rollback_scope(m_rollback,
2492 std::string("player:")+player->getName());
2494 // Get player name of this client
2495 std::wstring name = narrow_to_wide(player->getName());
2498 bool ate = m_script->on_chat_message(player->getName(),
2499 wide_to_narrow(message));
2500 // If script ate the message, don't proceed
2504 // Line to send to players
2506 // Whether to send to the player that sent the line
2507 bool send_to_sender = false;
2508 // Whether to send to other players
2509 bool send_to_others = false;
2511 // Commands are implemented in Lua, so only catch invalid
2512 // commands that were not "eaten" and send an error back
2513 if(message[0] == L'/')
2515 message = message.substr(1);
2516 send_to_sender = true;
2517 if(message.length() == 0)
2518 line += L"-!- Empty command";
2520 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2524 if(checkPriv(player->getName(), "shout")){
2529 send_to_others = true;
2531 line += L"-!- You don't have permission to shout.";
2532 send_to_sender = true;
2539 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2542 Send the message to clients
2544 for(std::map<u16, RemoteClient*>::iterator
2545 i = m_clients.begin();
2546 i != m_clients.end(); ++i)
2548 // Get client and check that it is valid
2549 RemoteClient *client = i->second;
2550 assert(client->peer_id == i->first);
2551 if(client->serialization_version == SER_FMT_VER_INVALID)
2555 bool sender_selected = (peer_id == client->peer_id);
2556 if(sender_selected == true && send_to_sender == false)
2558 if(sender_selected == false && send_to_others == false)
2561 SendChatMessage(client->peer_id, line);
2565 else if(command == TOSERVER_DAMAGE)
2567 std::string datastring((char*)&data[2], datasize-2);
2568 std::istringstream is(datastring, std::ios_base::binary);
2569 u8 damage = readU8(is);
2571 if(g_settings->getBool("enable_damage"))
2573 actionstream<<player->getName()<<" damaged by "
2574 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2577 playersao->setHP(playersao->getHP() - damage);
2579 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2582 if(playersao->m_hp_not_sent)
2583 SendPlayerHP(peer_id);
2586 else if(command == TOSERVER_PASSWORD)
2589 [0] u16 TOSERVER_PASSWORD
2590 [2] u8[28] old password
2591 [30] u8[28] new password
2594 if(datasize != 2+PASSWORD_SIZE*2)
2596 /*char password[PASSWORD_SIZE];
2597 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2598 password[i] = data[2+i];
2599 password[PASSWORD_SIZE-1] = 0;*/
2601 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2609 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2611 char c = data[2+PASSWORD_SIZE+i];
2617 if(!base64_is_valid(newpwd)){
2618 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2619 // Wrong old password supplied!!
2620 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2624 infostream<<"Server: Client requests a password change from "
2625 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2627 std::string playername = player->getName();
2629 std::string checkpwd;
2630 m_script->getAuth(playername, &checkpwd, NULL);
2632 if(oldpwd != checkpwd)
2634 infostream<<"Server: invalid old password"<<std::endl;
2635 // Wrong old password supplied!!
2636 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2640 bool success = m_script->setPassword(playername, newpwd);
2642 actionstream<<player->getName()<<" changes password"<<std::endl;
2643 SendChatMessage(peer_id, L"Password change successful.");
2645 actionstream<<player->getName()<<" tries to change password but "
2646 <<"it fails"<<std::endl;
2647 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2650 else if(command == TOSERVER_PLAYERITEM)
2655 u16 item = readU16(&data[2]);
2656 playersao->setWieldIndex(item);
2658 else if(command == TOSERVER_RESPAWN)
2660 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2663 RespawnPlayer(peer_id);
2665 actionstream<<player->getName()<<" respawns at "
2666 <<PP(player->getPosition()/BS)<<std::endl;
2668 // ActiveObject is added to environment in AsyncRunStep after
2669 // the previous addition has been succesfully removed
2671 else if(command == TOSERVER_REQUEST_MEDIA) {
2672 std::string datastring((char*)&data[2], datasize-2);
2673 std::istringstream is(datastring, std::ios_base::binary);
2675 std::list<MediaRequest> tosend;
2676 u16 numfiles = readU16(is);
2678 infostream<<"Sending "<<numfiles<<" files to "
2679 <<getPlayerName(peer_id)<<std::endl;
2680 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2682 for(int i = 0; i < numfiles; i++) {
2683 std::string name = deSerializeString(is);
2684 tosend.push_back(MediaRequest(name));
2685 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2689 sendRequestedMedia(peer_id, tosend);
2691 // Now the client should know about everything
2692 // (definitions and files)
2693 getClient(peer_id)->definitions_sent = true;
2695 else if(command == TOSERVER_RECEIVED_MEDIA) {
2696 getClient(peer_id)->definitions_sent = true;
2698 else if(command == TOSERVER_INTERACT)
2700 std::string datastring((char*)&data[2], datasize-2);
2701 std::istringstream is(datastring, std::ios_base::binary);
2707 [5] u32 length of the next item
2708 [9] serialized PointedThing
2710 0: start digging (from undersurface) or use
2711 1: stop digging (all parameters ignored)
2712 2: digging completed
2713 3: place block or item (to abovesurface)
2716 u8 action = readU8(is);
2717 u16 item_i = readU16(is);
2718 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2719 PointedThing pointed;
2720 pointed.deSerialize(tmp_is);
2722 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2723 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2727 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2728 <<" tried to interact, but is dead!"<<std::endl;
2732 v3f player_pos = playersao->getLastGoodPosition();
2734 // Update wielded item
2735 playersao->setWieldIndex(item_i);
2737 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2738 v3s16 p_under = pointed.node_undersurface;
2739 v3s16 p_above = pointed.node_abovesurface;
2741 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2742 ServerActiveObject *pointed_object = NULL;
2743 if(pointed.type == POINTEDTHING_OBJECT)
2745 pointed_object = m_env->getActiveObject(pointed.object_id);
2746 if(pointed_object == NULL)
2748 verbosestream<<"TOSERVER_INTERACT: "
2749 "pointed object is NULL"<<std::endl;
2755 v3f pointed_pos_under = player_pos;
2756 v3f pointed_pos_above = player_pos;
2757 if(pointed.type == POINTEDTHING_NODE)
2759 pointed_pos_under = intToFloat(p_under, BS);
2760 pointed_pos_above = intToFloat(p_above, BS);
2762 else if(pointed.type == POINTEDTHING_OBJECT)
2764 pointed_pos_under = pointed_object->getBasePosition();
2765 pointed_pos_above = pointed_pos_under;
2769 Check that target is reasonably close
2770 (only when digging or placing things)
2772 if(action == 0 || action == 2 || action == 3)
2774 float d = player_pos.getDistanceFrom(pointed_pos_under);
2775 float max_d = BS * 14; // Just some large enough value
2777 actionstream<<"Player "<<player->getName()
2778 <<" tried to access "<<pointed.dump()
2780 <<"d="<<d<<", max_d="<<max_d
2781 <<". ignoring."<<std::endl;
2782 // Re-send block to revert change on client-side
2783 RemoteClient *client = getClient(peer_id);
2784 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2785 client->SetBlockNotSent(blockpos);
2792 Make sure the player is allowed to do it
2794 if(!checkPriv(player->getName(), "interact"))
2796 actionstream<<player->getName()<<" attempted to interact with "
2797 <<pointed.dump()<<" without 'interact' privilege"
2799 // Re-send block to revert change on client-side
2800 RemoteClient *client = getClient(peer_id);
2801 // Digging completed -> under
2803 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2804 client->SetBlockNotSent(blockpos);
2806 // Placement -> above
2808 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2809 client->SetBlockNotSent(blockpos);
2815 If something goes wrong, this player is to blame
2817 RollbackScopeActor rollback_scope(m_rollback,
2818 std::string("player:")+player->getName());
2821 0: start digging or punch object
2825 if(pointed.type == POINTEDTHING_NODE)
2828 NOTE: This can be used in the future to check if
2829 somebody is cheating, by checking the timing.
2831 MapNode n(CONTENT_IGNORE);
2834 n = m_env->getMap().getNode(p_under);
2836 catch(InvalidPositionException &e)
2838 infostream<<"Server: Not punching: Node not found."
2839 <<" Adding block to emerge queue."
2841 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2843 if(n.getContent() != CONTENT_IGNORE)
2844 m_script->node_on_punch(p_under, n, playersao);
2846 playersao->noCheatDigStart(p_under);
2848 else if(pointed.type == POINTEDTHING_OBJECT)
2850 // Skip if object has been removed
2851 if(pointed_object->m_removed)
2854 actionstream<<player->getName()<<" punches object "
2855 <<pointed.object_id<<": "
2856 <<pointed_object->getDescription()<<std::endl;
2858 ItemStack punchitem = playersao->getWieldedItem();
2859 ToolCapabilities toolcap =
2860 punchitem.getToolCapabilities(m_itemdef);
2861 v3f dir = (pointed_object->getBasePosition() -
2862 (player->getPosition() + player->getEyeOffset())
2864 float time_from_last_punch =
2865 playersao->resetTimeFromLastPunch();
2866 pointed_object->punch(dir, &toolcap, playersao,
2867 time_from_last_punch);
2875 else if(action == 1)
2880 2: Digging completed
2882 else if(action == 2)
2884 // Only digging of nodes
2885 if(pointed.type == POINTEDTHING_NODE)
2887 MapNode n(CONTENT_IGNORE);
2890 n = m_env->getMap().getNode(p_under);
2892 catch(InvalidPositionException &e)
2894 infostream<<"Server: Not finishing digging: Node not found."
2895 <<" Adding block to emerge queue."
2897 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2900 /* Cheat prevention */
2901 bool is_valid_dig = true;
2902 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2904 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2905 float nocheat_t = playersao->getNoCheatDigTime();
2906 playersao->noCheatDigEnd();
2907 // If player didn't start digging this, ignore dig
2908 if(nocheat_p != p_under){
2909 infostream<<"Server: NoCheat: "<<player->getName()
2910 <<" started digging "
2911 <<PP(nocheat_p)<<" and completed digging "
2912 <<PP(p_under)<<"; not digging."<<std::endl;
2913 is_valid_dig = false;
2915 // Get player's wielded item
2916 ItemStack playeritem;
2917 InventoryList *mlist = playersao->getInventory()->getList("main");
2919 playeritem = mlist->getItem(playersao->getWieldIndex());
2920 ToolCapabilities playeritem_toolcap =
2921 playeritem.getToolCapabilities(m_itemdef);
2922 // Get diggability and expected digging time
2923 DigParams params = getDigParams(m_nodedef->get(n).groups,
2924 &playeritem_toolcap);
2925 // If can't dig, try hand
2926 if(!params.diggable){
2927 const ItemDefinition &hand = m_itemdef->get("");
2928 const ToolCapabilities *tp = hand.tool_capabilities;
2930 params = getDigParams(m_nodedef->get(n).groups, tp);
2932 // If can't dig, ignore dig
2933 if(!params.diggable){
2934 infostream<<"Server: NoCheat: "<<player->getName()
2935 <<" completed digging "<<PP(p_under)
2936 <<", which is not diggable with tool. not digging."
2938 is_valid_dig = false;
2940 // If time is considerably too short, ignore dig
2941 // Check time only for medium and slow timed digs
2942 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2943 infostream<<"Server: NoCheat: "<<player->getName()
2944 <<" completed digging "
2945 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2946 <<params.time<<"s; not digging."<<std::endl;
2947 is_valid_dig = false;
2951 /* Actually dig node */
2953 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2954 m_script->node_on_dig(p_under, n, playersao);
2956 // Send unusual result (that is, node not being removed)
2957 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2959 // Re-send block to revert change on client-side
2960 RemoteClient *client = getClient(peer_id);
2961 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2962 client->SetBlockNotSent(blockpos);
2968 3: place block or right-click object
2970 else if(action == 3)
2972 ItemStack item = playersao->getWieldedItem();
2974 // Reset build time counter
2975 if(pointed.type == POINTEDTHING_NODE &&
2976 item.getDefinition(m_itemdef).type == ITEM_NODE)
2977 getClient(peer_id)->m_time_from_building = 0.0;
2979 if(pointed.type == POINTEDTHING_OBJECT)
2981 // Right click object
2983 // Skip if object has been removed
2984 if(pointed_object->m_removed)
2987 actionstream<<player->getName()<<" right-clicks object "
2988 <<pointed.object_id<<": "
2989 <<pointed_object->getDescription()<<std::endl;
2992 pointed_object->rightClick(playersao);
2994 else if(m_script->item_OnPlace(
2995 item, playersao, pointed))
2997 // Placement was handled in lua
2999 // Apply returned ItemStack
3000 playersao->setWieldedItem(item);
3003 // If item has node placement prediction, always send the
3004 // blocks to make sure the client knows what exactly happened
3005 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3006 RemoteClient *client = getClient(peer_id);
3007 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3008 client->SetBlockNotSent(blockpos);
3009 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3010 if(blockpos2 != blockpos){
3011 client->SetBlockNotSent(blockpos2);
3019 else if(action == 4)
3021 ItemStack item = playersao->getWieldedItem();
3023 actionstream<<player->getName()<<" uses "<<item.name
3024 <<", pointing at "<<pointed.dump()<<std::endl;
3026 if(m_script->item_OnUse(
3027 item, playersao, pointed))
3029 // Apply returned ItemStack
3030 playersao->setWieldedItem(item);
3037 Catch invalid actions
3041 infostream<<"WARNING: Server: Invalid action "
3042 <<action<<std::endl;
3045 else if(command == TOSERVER_REMOVED_SOUNDS)
3047 std::string datastring((char*)&data[2], datasize-2);
3048 std::istringstream is(datastring, std::ios_base::binary);
3050 int num = readU16(is);
3051 for(int k=0; k<num; k++){
3052 s32 id = readS32(is);
3053 std::map<s32, ServerPlayingSound>::iterator i =
3054 m_playing_sounds.find(id);
3055 if(i == m_playing_sounds.end())
3057 ServerPlayingSound &psound = i->second;
3058 psound.clients.erase(peer_id);
3059 if(psound.clients.size() == 0)
3060 m_playing_sounds.erase(i++);
3063 else if(command == TOSERVER_NODEMETA_FIELDS)
3065 std::string datastring((char*)&data[2], datasize-2);
3066 std::istringstream is(datastring, std::ios_base::binary);
3068 v3s16 p = readV3S16(is);
3069 std::string formname = deSerializeString(is);
3070 int num = readU16(is);
3071 std::map<std::string, std::string> fields;
3072 for(int k=0; k<num; k++){
3073 std::string fieldname = deSerializeString(is);
3074 std::string fieldvalue = deSerializeLongString(is);
3075 fields[fieldname] = fieldvalue;
3078 // If something goes wrong, this player is to blame
3079 RollbackScopeActor rollback_scope(m_rollback,
3080 std::string("player:")+player->getName());
3082 // Check the target node for rollback data; leave others unnoticed
3083 RollbackNode rn_old(&m_env->getMap(), p, this);
3085 m_script->node_on_receive_fields(p, formname, fields,playersao);
3087 // Report rollback data
3088 RollbackNode rn_new(&m_env->getMap(), p, this);
3089 if(rollback() && rn_new != rn_old){
3090 RollbackAction action;
3091 action.setSetNode(p, rn_old, rn_new);
3092 rollback()->reportAction(action);
3095 else if(command == TOSERVER_INVENTORY_FIELDS)
3097 std::string datastring((char*)&data[2], datasize-2);
3098 std::istringstream is(datastring, std::ios_base::binary);
3100 std::string formname = deSerializeString(is);
3101 int num = readU16(is);
3102 std::map<std::string, std::string> fields;
3103 for(int k=0; k<num; k++){
3104 std::string fieldname = deSerializeString(is);
3105 std::string fieldvalue = deSerializeLongString(is);
3106 fields[fieldname] = fieldvalue;
3109 m_script->on_playerReceiveFields(playersao, formname, fields);
3113 infostream<<"Server::ProcessData(): Ignoring "
3114 "unknown command "<<command<<std::endl;
3118 catch(SendFailedException &e)
3120 errorstream<<"Server::ProcessData(): SendFailedException: "
3126 void Server::onMapEditEvent(MapEditEvent *event)
3128 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3129 if(m_ignore_map_edit_events)
3131 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3133 MapEditEvent *e = event->clone();
3134 m_unsent_map_edit_queue.push_back(e);
3137 Inventory* Server::getInventory(const InventoryLocation &loc)
3140 case InventoryLocation::UNDEFINED:
3143 case InventoryLocation::CURRENT_PLAYER:
3146 case InventoryLocation::PLAYER:
3148 Player *player = m_env->getPlayer(loc.name.c_str());
3151 PlayerSAO *playersao = player->getPlayerSAO();
3154 return playersao->getInventory();
3157 case InventoryLocation::NODEMETA:
3159 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3162 return meta->getInventory();
3165 case InventoryLocation::DETACHED:
3167 if(m_detached_inventories.count(loc.name) == 0)
3169 return m_detached_inventories[loc.name];
3177 void Server::setInventoryModified(const InventoryLocation &loc)
3180 case InventoryLocation::UNDEFINED:
3183 case InventoryLocation::PLAYER:
3185 Player *player = m_env->getPlayer(loc.name.c_str());
3188 PlayerSAO *playersao = player->getPlayerSAO();
3191 playersao->m_inventory_not_sent = true;
3192 playersao->m_wielded_item_not_sent = true;
3195 case InventoryLocation::NODEMETA:
3197 v3s16 blockpos = getNodeBlockPos(loc.p);
3199 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3201 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3203 setBlockNotSent(blockpos);
3206 case InventoryLocation::DETACHED:
3208 sendDetachedInventoryToAll(loc.name);
3216 //std::list<PlayerInfo> Server::getPlayerInfo()
3218 // DSTACK(__FUNCTION_NAME);
3219 // JMutexAutoLock envlock(m_env_mutex);
3220 // JMutexAutoLock conlock(m_con_mutex);
3222 // std::list<PlayerInfo> list;
3224 // std::list<Player*> players = m_env->getPlayers();
3226 // std::list<Player*>::iterator i;
3227 // for(i = players.begin();
3228 // i != players.end(); ++i)
3232 // Player *player = *i;
3235 // // Copy info from connection to info struct
3236 // info.id = player->peer_id;
3237 // info.address = m_con.GetPeerAddress(player->peer_id);
3238 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3240 // catch(con::PeerNotFoundException &e)
3242 // // Set dummy peer info
3244 // info.address = Address(0,0,0,0,0);
3245 // info.avg_rtt = 0.0;
3248 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3249 // info.position = player->getPosition();
3251 // list.push_back(info);
3258 void Server::peerAdded(con::Peer *peer)
3260 DSTACK(__FUNCTION_NAME);
3261 verbosestream<<"Server::peerAdded(): peer->id="
3262 <<peer->id<<std::endl;
3265 c.type = PEER_ADDED;
3266 c.peer_id = peer->id;
3268 m_peer_change_queue.push_back(c);
3271 void Server::deletingPeer(con::Peer *peer, bool timeout)
3273 DSTACK(__FUNCTION_NAME);
3274 verbosestream<<"Server::deletingPeer(): peer->id="
3275 <<peer->id<<", timeout="<<timeout<<std::endl;
3278 c.type = PEER_REMOVED;
3279 c.peer_id = peer->id;
3280 c.timeout = timeout;
3281 m_peer_change_queue.push_back(c);
3288 void Server::SendMovement(con::Connection &con, u16 peer_id)
3290 DSTACK(__FUNCTION_NAME);
3291 std::ostringstream os(std::ios_base::binary);
3293 writeU16(os, TOCLIENT_MOVEMENT);
3294 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3295 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3296 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3297 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3298 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3299 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3300 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3301 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3302 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3303 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3304 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3305 writeF1000(os, g_settings->getFloat("movement_gravity"));
3308 std::string s = os.str();
3309 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3311 con.Send(peer_id, 0, data, true);
3314 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3316 DSTACK(__FUNCTION_NAME);
3317 std::ostringstream os(std::ios_base::binary);
3319 writeU16(os, TOCLIENT_HP);
3323 std::string s = os.str();
3324 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3326 con.Send(peer_id, 0, data, true);
3329 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3330 const std::wstring &reason)
3332 DSTACK(__FUNCTION_NAME);
3333 std::ostringstream os(std::ios_base::binary);
3335 writeU16(os, TOCLIENT_ACCESS_DENIED);
3336 os<<serializeWideString(reason);
3339 std::string s = os.str();
3340 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3342 con.Send(peer_id, 0, data, true);
3345 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3346 bool set_camera_point_target, v3f camera_point_target)
3348 DSTACK(__FUNCTION_NAME);
3349 std::ostringstream os(std::ios_base::binary);
3351 writeU16(os, TOCLIENT_DEATHSCREEN);
3352 writeU8(os, set_camera_point_target);
3353 writeV3F1000(os, camera_point_target);
3356 std::string s = os.str();
3357 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3359 con.Send(peer_id, 0, data, true);
3362 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3363 IItemDefManager *itemdef, u16 protocol_version)
3365 DSTACK(__FUNCTION_NAME);
3366 std::ostringstream os(std::ios_base::binary);
3370 u32 length of the next item
3371 zlib-compressed serialized ItemDefManager
3373 writeU16(os, TOCLIENT_ITEMDEF);
3374 std::ostringstream tmp_os(std::ios::binary);
3375 itemdef->serialize(tmp_os, protocol_version);
3376 std::ostringstream tmp_os2(std::ios::binary);
3377 compressZlib(tmp_os.str(), tmp_os2);
3378 os<<serializeLongString(tmp_os2.str());
3381 std::string s = os.str();
3382 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3383 <<"): size="<<s.size()<<std::endl;
3384 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3386 con.Send(peer_id, 0, data, true);
3389 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3390 INodeDefManager *nodedef, u16 protocol_version)
3392 DSTACK(__FUNCTION_NAME);
3393 std::ostringstream os(std::ios_base::binary);
3397 u32 length of the next item
3398 zlib-compressed serialized NodeDefManager
3400 writeU16(os, TOCLIENT_NODEDEF);
3401 std::ostringstream tmp_os(std::ios::binary);
3402 nodedef->serialize(tmp_os, protocol_version);
3403 std::ostringstream tmp_os2(std::ios::binary);
3404 compressZlib(tmp_os.str(), tmp_os2);
3405 os<<serializeLongString(tmp_os2.str());
3408 std::string s = os.str();
3409 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3410 <<"): size="<<s.size()<<std::endl;
3411 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3413 con.Send(peer_id, 0, data, true);
3417 Non-static send methods
3420 void Server::SendInventory(u16 peer_id)
3422 DSTACK(__FUNCTION_NAME);
3424 PlayerSAO *playersao = getPlayerSAO(peer_id);
3427 playersao->m_inventory_not_sent = false;
3433 std::ostringstream os;
3434 playersao->getInventory()->serialize(os);
3436 std::string s = os.str();
3438 SharedBuffer<u8> data(s.size()+2);
3439 writeU16(&data[0], TOCLIENT_INVENTORY);
3440 memcpy(&data[2], s.c_str(), s.size());
3443 m_con.Send(peer_id, 0, data, true);
3446 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3448 DSTACK(__FUNCTION_NAME);
3450 std::ostringstream os(std::ios_base::binary);
3454 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3455 os.write((char*)buf, 2);
3458 writeU16(buf, message.size());
3459 os.write((char*)buf, 2);
3462 for(u32 i=0; i<message.size(); i++)
3466 os.write((char*)buf, 2);
3470 std::string s = os.str();
3471 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3473 m_con.Send(peer_id, 0, data, true);
3476 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3477 const std::string formname)
3479 DSTACK(__FUNCTION_NAME);
3481 std::ostringstream os(std::ios_base::binary);
3485 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3486 os.write((char*)buf, 2);
3487 os<<serializeLongString(formspec);
3488 os<<serializeString(formname);
3491 std::string s = os.str();
3492 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3494 m_con.Send(peer_id, 0, data, true);
3497 // Spawns a particle on peer with peer_id
3498 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3499 float expirationtime, float size, bool collisiondetection,
3500 std::string texture)
3502 DSTACK(__FUNCTION_NAME);
3504 std::ostringstream os(std::ios_base::binary);
3505 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3506 writeV3F1000(os, pos);
3507 writeV3F1000(os, velocity);
3508 writeV3F1000(os, acceleration);
3509 writeF1000(os, expirationtime);
3510 writeF1000(os, size);
3511 writeU8(os, collisiondetection);
3512 os<<serializeLongString(texture);
3515 std::string s = os.str();
3516 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3518 m_con.Send(peer_id, 0, data, true);
3521 // Spawns a particle on all peers
3522 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3523 float expirationtime, float size, bool collisiondetection,
3524 std::string texture)
3526 for(std::map<u16, RemoteClient*>::iterator
3527 i = m_clients.begin();
3528 i != m_clients.end(); i++)
3530 // Get client and check that it is valid
3531 RemoteClient *client = i->second;
3532 assert(client->peer_id == i->first);
3533 if(client->serialization_version == SER_FMT_VER_INVALID)
3536 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3537 expirationtime, size, collisiondetection, texture);
3541 // Adds a ParticleSpawner on peer with peer_id
3542 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3543 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3544 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3546 DSTACK(__FUNCTION_NAME);
3548 std::ostringstream os(std::ios_base::binary);
3549 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3551 writeU16(os, amount);
3552 writeF1000(os, spawntime);
3553 writeV3F1000(os, minpos);
3554 writeV3F1000(os, maxpos);
3555 writeV3F1000(os, minvel);
3556 writeV3F1000(os, maxvel);
3557 writeV3F1000(os, minacc);
3558 writeV3F1000(os, maxacc);
3559 writeF1000(os, minexptime);
3560 writeF1000(os, maxexptime);
3561 writeF1000(os, minsize);
3562 writeF1000(os, maxsize);
3563 writeU8(os, collisiondetection);
3564 os<<serializeLongString(texture);
3568 std::string s = os.str();
3569 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3571 m_con.Send(peer_id, 0, data, true);
3574 // Adds a ParticleSpawner on all peers
3575 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3576 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3577 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3579 for(std::map<u16, RemoteClient*>::iterator
3580 i = m_clients.begin();
3581 i != m_clients.end(); i++)
3583 // Get client and check that it is valid
3584 RemoteClient *client = i->second;
3585 assert(client->peer_id == i->first);
3586 if(client->serialization_version == SER_FMT_VER_INVALID)
3589 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3590 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3591 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3595 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3597 DSTACK(__FUNCTION_NAME);
3599 std::ostringstream os(std::ios_base::binary);
3600 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3605 std::string s = os.str();
3606 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3608 m_con.Send(peer_id, 0, data, true);
3611 void Server::SendDeleteParticleSpawnerAll(u32 id)
3613 for(std::map<u16, RemoteClient*>::iterator
3614 i = m_clients.begin();
3615 i != m_clients.end(); i++)
3617 // Get client and check that it is valid
3618 RemoteClient *client = i->second;
3619 assert(client->peer_id == i->first);
3620 if(client->serialization_version == SER_FMT_VER_INVALID)
3623 SendDeleteParticleSpawner(client->peer_id, id);
3627 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3629 std::ostringstream os(std::ios_base::binary);
3632 writeU16(os, TOCLIENT_HUDADD);
3634 writeU8(os, (u8)form->type);
3635 writeV2F1000(os, form->pos);
3636 os << serializeString(form->name);
3637 writeV2F1000(os, form->scale);
3638 os << serializeString(form->text);
3639 writeU32(os, form->number);
3640 writeU32(os, form->item);
3641 writeU32(os, form->dir);
3642 writeV2F1000(os, form->align);
3643 writeV2F1000(os, form->offset);
3646 std::string s = os.str();
3647 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3649 m_con.Send(peer_id, 0, data, true);
3652 void Server::SendHUDRemove(u16 peer_id, u32 id)
3654 std::ostringstream os(std::ios_base::binary);
3657 writeU16(os, TOCLIENT_HUDRM);
3661 std::string s = os.str();
3662 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3664 m_con.Send(peer_id, 0, data, true);
3667 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3669 std::ostringstream os(std::ios_base::binary);
3672 writeU16(os, TOCLIENT_HUDCHANGE);
3674 writeU8(os, (u8)stat);
3677 case HUD_STAT_SCALE:
3678 case HUD_STAT_ALIGN:
3679 case HUD_STAT_OFFSET:
3680 writeV2F1000(os, *(v2f *)value);
3684 os << serializeString(*(std::string *)value);
3686 case HUD_STAT_NUMBER:
3690 writeU32(os, *(u32 *)value);
3695 std::string s = os.str();
3696 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3698 m_con.Send(peer_id, 0, data, true);
3701 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3703 std::ostringstream os(std::ios_base::binary);
3706 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3707 writeU32(os, flags);
3711 std::string s = os.str();
3712 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3714 m_con.Send(peer_id, 0, data, true);
3717 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3719 std::ostringstream os(std::ios_base::binary);
3722 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3723 writeU16(os, param);
3724 os<<serializeString(value);
3727 std::string s = os.str();
3728 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3730 m_con.Send(peer_id, 0, data, true);
3733 void Server::BroadcastChatMessage(const std::wstring &message)
3735 for(std::map<u16, RemoteClient*>::iterator
3736 i = m_clients.begin();
3737 i != m_clients.end(); ++i)
3739 // Get client and check that it is valid
3740 RemoteClient *client = i->second;
3741 assert(client->peer_id == i->first);
3742 if(client->serialization_version == SER_FMT_VER_INVALID)
3745 SendChatMessage(client->peer_id, message);
3749 void Server::SendPlayerHP(u16 peer_id)
3751 DSTACK(__FUNCTION_NAME);
3752 PlayerSAO *playersao = getPlayerSAO(peer_id);
3754 playersao->m_hp_not_sent = false;
3755 SendHP(m_con, peer_id, playersao->getHP());
3758 void Server::SendMovePlayer(u16 peer_id)
3760 DSTACK(__FUNCTION_NAME);
3761 Player *player = m_env->getPlayer(peer_id);
3764 std::ostringstream os(std::ios_base::binary);
3765 writeU16(os, TOCLIENT_MOVE_PLAYER);
3766 writeV3F1000(os, player->getPosition());
3767 writeF1000(os, player->getPitch());
3768 writeF1000(os, player->getYaw());
3771 v3f pos = player->getPosition();
3772 f32 pitch = player->getPitch();
3773 f32 yaw = player->getYaw();
3774 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3775 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3782 std::string s = os.str();
3783 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3785 m_con.Send(peer_id, 0, data, true);
3788 void Server::SendPlayerPrivileges(u16 peer_id)
3790 Player *player = m_env->getPlayer(peer_id);
3792 if(player->peer_id == PEER_ID_INEXISTENT)
3795 std::set<std::string> privs;
3796 m_script->getAuth(player->getName(), NULL, &privs);
3798 std::ostringstream os(std::ios_base::binary);
3799 writeU16(os, TOCLIENT_PRIVILEGES);
3800 writeU16(os, privs.size());
3801 for(std::set<std::string>::const_iterator i = privs.begin();
3802 i != privs.end(); i++){
3803 os<<serializeString(*i);
3807 std::string s = os.str();
3808 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3810 m_con.Send(peer_id, 0, data, true);
3813 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3815 Player *player = m_env->getPlayer(peer_id);
3817 if(player->peer_id == PEER_ID_INEXISTENT)
3820 std::ostringstream os(std::ios_base::binary);
3821 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3822 os<<serializeLongString(player->inventory_formspec);
3825 std::string s = os.str();
3826 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3828 m_con.Send(peer_id, 0, data, true);
3831 s32 Server::playSound(const SimpleSoundSpec &spec,
3832 const ServerSoundParams ¶ms)
3834 // Find out initial position of sound
3835 bool pos_exists = false;
3836 v3f pos = params.getPos(m_env, &pos_exists);
3837 // If position is not found while it should be, cancel sound
3838 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3840 // Filter destination clients
3841 std::set<RemoteClient*> dst_clients;
3842 if(params.to_player != "")
3844 Player *player = m_env->getPlayer(params.to_player.c_str());
3846 infostream<<"Server::playSound: Player \""<<params.to_player
3847 <<"\" not found"<<std::endl;
3850 if(player->peer_id == PEER_ID_INEXISTENT){
3851 infostream<<"Server::playSound: Player \""<<params.to_player
3852 <<"\" not connected"<<std::endl;
3855 RemoteClient *client = getClient(player->peer_id);
3856 dst_clients.insert(client);
3860 for(std::map<u16, RemoteClient*>::iterator
3861 i = m_clients.begin(); i != m_clients.end(); ++i)
3863 RemoteClient *client = i->second;
3864 Player *player = m_env->getPlayer(client->peer_id);
3868 if(player->getPosition().getDistanceFrom(pos) >
3869 params.max_hear_distance)
3872 dst_clients.insert(client);
3875 if(dst_clients.size() == 0)
3878 s32 id = m_next_sound_id++;
3879 // The sound will exist as a reference in m_playing_sounds
3880 m_playing_sounds[id] = ServerPlayingSound();
3881 ServerPlayingSound &psound = m_playing_sounds[id];
3882 psound.params = params;
3883 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3884 i != dst_clients.end(); i++)
3885 psound.clients.insert((*i)->peer_id);
3887 std::ostringstream os(std::ios_base::binary);
3888 writeU16(os, TOCLIENT_PLAY_SOUND);
3890 os<<serializeString(spec.name);
3891 writeF1000(os, spec.gain * params.gain);
3892 writeU8(os, params.type);
3893 writeV3F1000(os, pos);
3894 writeU16(os, params.object);
3895 writeU8(os, params.loop);
3897 std::string s = os.str();
3898 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3900 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3901 i != dst_clients.end(); i++){
3903 m_con.Send((*i)->peer_id, 0, data, true);
3907 void Server::stopSound(s32 handle)
3909 // Get sound reference
3910 std::map<s32, ServerPlayingSound>::iterator i =
3911 m_playing_sounds.find(handle);
3912 if(i == m_playing_sounds.end())
3914 ServerPlayingSound &psound = i->second;
3916 std::ostringstream os(std::ios_base::binary);
3917 writeU16(os, TOCLIENT_STOP_SOUND);
3918 writeS32(os, handle);
3920 std::string s = os.str();
3921 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3923 for(std::set<u16>::iterator i = psound.clients.begin();
3924 i != psound.clients.end(); i++){
3926 m_con.Send(*i, 0, data, true);
3928 // Remove sound reference
3929 m_playing_sounds.erase(i);
3932 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3933 std::list<u16> *far_players, float far_d_nodes)
3935 float maxd = far_d_nodes*BS;
3936 v3f p_f = intToFloat(p, BS);
3940 SharedBuffer<u8> reply(replysize);
3941 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3942 writeS16(&reply[2], p.X);
3943 writeS16(&reply[4], p.Y);
3944 writeS16(&reply[6], p.Z);
3946 for(std::map<u16, RemoteClient*>::iterator
3947 i = m_clients.begin();
3948 i != m_clients.end(); ++i)
3950 // Get client and check that it is valid
3951 RemoteClient *client = i->second;
3952 assert(client->peer_id == i->first);
3953 if(client->serialization_version == SER_FMT_VER_INVALID)
3956 // Don't send if it's the same one
3957 if(client->peer_id == ignore_id)
3963 Player *player = m_env->getPlayer(client->peer_id);
3966 // If player is far away, only set modified blocks not sent
3967 v3f player_pos = player->getPosition();
3968 if(player_pos.getDistanceFrom(p_f) > maxd)
3970 far_players->push_back(client->peer_id);
3977 m_con.Send(client->peer_id, 0, reply, true);
3981 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3982 std::list<u16> *far_players, float far_d_nodes)
3984 float maxd = far_d_nodes*BS;
3985 v3f p_f = intToFloat(p, BS);
3987 for(std::map<u16, RemoteClient*>::iterator
3988 i = m_clients.begin();
3989 i != m_clients.end(); ++i)
3991 // Get client and check that it is valid
3992 RemoteClient *client = i->second;
3993 assert(client->peer_id == i->first);
3994 if(client->serialization_version == SER_FMT_VER_INVALID)
3997 // Don't send if it's the same one
3998 if(client->peer_id == ignore_id)
4004 Player *player = m_env->getPlayer(client->peer_id);
4007 // If player is far away, only set modified blocks not sent
4008 v3f player_pos = player->getPosition();
4009 if(player_pos.getDistanceFrom(p_f) > maxd)
4011 far_players->push_back(client->peer_id);
4018 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4019 SharedBuffer<u8> reply(replysize);
4020 writeU16(&reply[0], TOCLIENT_ADDNODE);
4021 writeS16(&reply[2], p.X);
4022 writeS16(&reply[4], p.Y);
4023 writeS16(&reply[6], p.Z);
4024 n.serialize(&reply[8], client->serialization_version);
4027 m_con.Send(client->peer_id, 0, reply, true);
4031 void Server::setBlockNotSent(v3s16 p)
4033 for(std::map<u16, RemoteClient*>::iterator
4034 i = m_clients.begin();
4035 i != m_clients.end(); ++i)
4037 RemoteClient *client = i->second;
4038 client->SetBlockNotSent(p);
4042 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4044 DSTACK(__FUNCTION_NAME);
4046 v3s16 p = block->getPos();
4050 bool completely_air = true;
4051 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4052 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4053 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4055 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4057 completely_air = false;
4058 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4063 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4065 infostream<<"[completely air] ";
4066 infostream<<std::endl;
4070 Create a packet with the block in the right format
4073 std::ostringstream os(std::ios_base::binary);
4074 block->serialize(os, ver, false);
4075 std::string s = os.str();
4076 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4078 u32 replysize = 8 + blockdata.getSize();
4079 SharedBuffer<u8> reply(replysize);
4080 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4081 writeS16(&reply[2], p.X);
4082 writeS16(&reply[4], p.Y);
4083 writeS16(&reply[6], p.Z);
4084 memcpy(&reply[8], *blockdata, blockdata.getSize());
4086 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4087 <<": \tpacket size: "<<replysize<<std::endl;*/
4092 m_con.Send(peer_id, 1, reply, true);
4095 void Server::SendBlocks(float dtime)
4097 DSTACK(__FUNCTION_NAME);
4099 JMutexAutoLock envlock(m_env_mutex);
4100 JMutexAutoLock conlock(m_con_mutex);
4102 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4104 std::vector<PrioritySortedBlockTransfer> queue;
4106 s32 total_sending = 0;
4109 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4111 for(std::map<u16, RemoteClient*>::iterator
4112 i = m_clients.begin();
4113 i != m_clients.end(); ++i)
4115 RemoteClient *client = i->second;
4116 assert(client->peer_id == i->first);
4118 // If definitions and textures have not been sent, don't
4119 // send MapBlocks either
4120 if(!client->definitions_sent)
4123 total_sending += client->SendingCount();
4125 if(client->serialization_version == SER_FMT_VER_INVALID)
4128 client->GetNextBlocks(this, dtime, queue);
4133 // Lowest priority number comes first.
4134 // Lowest is most important.
4135 std::sort(queue.begin(), queue.end());
4137 for(u32 i=0; i<queue.size(); i++)
4139 //TODO: Calculate limit dynamically
4140 if(total_sending >= g_settings->getS32
4141 ("max_simultaneous_block_sends_server_total"))
4144 PrioritySortedBlockTransfer q = queue[i];
4146 MapBlock *block = NULL;
4149 block = m_env->getMap().getBlockNoCreate(q.pos);
4151 catch(InvalidPositionException &e)
4156 RemoteClient *client = getClient(q.peer_id);
4158 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4160 client->SentBlock(q.pos);
4166 void Server::fillMediaCache()
4168 DSTACK(__FUNCTION_NAME);
4170 infostream<<"Server: Calculating media file checksums"<<std::endl;
4172 // Collect all media file paths
4173 std::list<std::string> paths;
4174 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4175 i != m_mods.end(); i++){
4176 const ModSpec &mod = *i;
4177 paths.push_back(mod.path + DIR_DELIM + "textures");
4178 paths.push_back(mod.path + DIR_DELIM + "sounds");
4179 paths.push_back(mod.path + DIR_DELIM + "media");
4180 paths.push_back(mod.path + DIR_DELIM + "models");
4182 std::string path_all = "textures";
4183 paths.push_back(path_all + DIR_DELIM + "all");
4185 // Collect media file information from paths into cache
4186 for(std::list<std::string>::iterator i = paths.begin();
4187 i != paths.end(); i++)
4189 std::string mediapath = *i;
4190 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4191 for(u32 j=0; j<dirlist.size(); j++){
4192 if(dirlist[j].dir) // Ignode dirs
4194 std::string filename = dirlist[j].name;
4195 // If name contains illegal characters, ignore the file
4196 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4197 infostream<<"Server: ignoring illegal file name: \""
4198 <<filename<<"\""<<std::endl;
4201 // If name is not in a supported format, ignore it
4202 const char *supported_ext[] = {
4203 ".png", ".jpg", ".bmp", ".tga",
4204 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4206 ".x", ".b3d", ".md2", ".obj",
4209 if(removeStringEnd(filename, supported_ext) == ""){
4210 infostream<<"Server: ignoring unsupported file extension: \""
4211 <<filename<<"\""<<std::endl;
4214 // Ok, attempt to load the file and add to cache
4215 std::string filepath = mediapath + DIR_DELIM + filename;
4217 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4218 if(fis.good() == false){
4219 errorstream<<"Server::fillMediaCache(): Could not open \""
4220 <<filename<<"\" for reading"<<std::endl;
4223 std::ostringstream tmp_os(std::ios_base::binary);
4227 fis.read(buf, 1024);
4228 std::streamsize len = fis.gcount();
4229 tmp_os.write(buf, len);
4238 errorstream<<"Server::fillMediaCache(): Failed to read \""
4239 <<filename<<"\""<<std::endl;
4242 if(tmp_os.str().length() == 0){
4243 errorstream<<"Server::fillMediaCache(): Empty file \""
4244 <<filepath<<"\""<<std::endl;
4249 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4251 unsigned char *digest = sha1.getDigest();
4252 std::string sha1_base64 = base64_encode(digest, 20);
4253 std::string sha1_hex = hex_encode((char*)digest, 20);
4257 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4258 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4263 struct SendableMediaAnnouncement
4266 std::string sha1_digest;
4268 SendableMediaAnnouncement(const std::string name_="",
4269 const std::string sha1_digest_=""):
4271 sha1_digest(sha1_digest_)
4275 void Server::sendMediaAnnouncement(u16 peer_id)
4277 DSTACK(__FUNCTION_NAME);
4279 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4282 std::list<SendableMediaAnnouncement> file_announcements;
4284 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4285 i != m_media.end(); i++){
4287 file_announcements.push_back(
4288 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4292 std::ostringstream os(std::ios_base::binary);
4300 u16 length of sha1_digest
4305 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4306 writeU16(os, file_announcements.size());
4308 for(std::list<SendableMediaAnnouncement>::iterator
4309 j = file_announcements.begin();
4310 j != file_announcements.end(); ++j){
4311 os<<serializeString(j->name);
4312 os<<serializeString(j->sha1_digest);
4314 os<<serializeString(g_settings->get("remote_media"));
4317 std::string s = os.str();
4318 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4321 m_con.Send(peer_id, 0, data, true);
4324 struct SendableMedia
4330 SendableMedia(const std::string &name_="", const std::string path_="",
4331 const std::string &data_=""):
4338 void Server::sendRequestedMedia(u16 peer_id,
4339 const std::list<MediaRequest> &tosend)
4341 DSTACK(__FUNCTION_NAME);
4343 verbosestream<<"Server::sendRequestedMedia(): "
4344 <<"Sending files to client"<<std::endl;
4348 // Put 5kB in one bunch (this is not accurate)
4349 u32 bytes_per_bunch = 5000;
4351 std::vector< std::list<SendableMedia> > file_bunches;
4352 file_bunches.push_back(std::list<SendableMedia>());
4354 u32 file_size_bunch_total = 0;
4356 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4357 i != tosend.end(); ++i)
4359 if(m_media.find(i->name) == m_media.end()){
4360 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4361 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4365 //TODO get path + name
4366 std::string tpath = m_media[(*i).name].path;
4369 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4370 if(fis.good() == false){
4371 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4372 <<tpath<<"\" for reading"<<std::endl;
4375 std::ostringstream tmp_os(std::ios_base::binary);
4379 fis.read(buf, 1024);
4380 std::streamsize len = fis.gcount();
4381 tmp_os.write(buf, len);
4382 file_size_bunch_total += len;
4391 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4392 <<(*i).name<<"\""<<std::endl;
4395 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4396 <<tname<<"\""<<std::endl;*/
4398 file_bunches[file_bunches.size()-1].push_back(
4399 SendableMedia((*i).name, tpath, tmp_os.str()));
4401 // Start next bunch if got enough data
4402 if(file_size_bunch_total >= bytes_per_bunch){
4403 file_bunches.push_back(std::list<SendableMedia>());
4404 file_size_bunch_total = 0;
4409 /* Create and send packets */
4411 u32 num_bunches = file_bunches.size();
4412 for(u32 i=0; i<num_bunches; i++)
4414 std::ostringstream os(std::ios_base::binary);
4418 u16 total number of texture bunches
4419 u16 index of this bunch
4420 u32 number of files in this bunch
4429 writeU16(os, TOCLIENT_MEDIA);
4430 writeU16(os, num_bunches);
4432 writeU32(os, file_bunches[i].size());
4434 for(std::list<SendableMedia>::iterator
4435 j = file_bunches[i].begin();
4436 j != file_bunches[i].end(); ++j){
4437 os<<serializeString(j->name);
4438 os<<serializeLongString(j->data);
4442 std::string s = os.str();
4443 verbosestream<<"Server::sendRequestedMedia(): bunch "
4444 <<i<<"/"<<num_bunches
4445 <<" files="<<file_bunches[i].size()
4446 <<" size=" <<s.size()<<std::endl;
4447 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4449 m_con.Send(peer_id, 0, data, true);
4453 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4455 if(m_detached_inventories.count(name) == 0){
4456 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4459 Inventory *inv = m_detached_inventories[name];
4461 std::ostringstream os(std::ios_base::binary);
4462 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4463 os<<serializeString(name);
4467 std::string s = os.str();
4468 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4470 m_con.Send(peer_id, 0, data, true);
4473 void Server::sendDetachedInventoryToAll(const std::string &name)
4475 DSTACK(__FUNCTION_NAME);
4477 for(std::map<u16, RemoteClient*>::iterator
4478 i = m_clients.begin();
4479 i != m_clients.end(); ++i){
4480 RemoteClient *client = i->second;
4481 sendDetachedInventory(name, client->peer_id);
4485 void Server::sendDetachedInventories(u16 peer_id)
4487 DSTACK(__FUNCTION_NAME);
4489 for(std::map<std::string, Inventory*>::iterator
4490 i = m_detached_inventories.begin();
4491 i != m_detached_inventories.end(); i++){
4492 const std::string &name = i->first;
4493 //Inventory *inv = i->second;
4494 sendDetachedInventory(name, peer_id);
4502 void Server::DiePlayer(u16 peer_id)
4504 DSTACK(__FUNCTION_NAME);
4506 PlayerSAO *playersao = getPlayerSAO(peer_id);
4509 infostream<<"Server::DiePlayer(): Player "
4510 <<playersao->getPlayer()->getName()
4511 <<" dies"<<std::endl;
4513 playersao->setHP(0);
4515 // Trigger scripted stuff
4516 m_script->on_dieplayer(playersao);
4518 SendPlayerHP(peer_id);
4519 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4522 void Server::RespawnPlayer(u16 peer_id)
4524 DSTACK(__FUNCTION_NAME);
4526 PlayerSAO *playersao = getPlayerSAO(peer_id);
4529 infostream<<"Server::RespawnPlayer(): Player "
4530 <<playersao->getPlayer()->getName()
4531 <<" respawns"<<std::endl;
4533 playersao->setHP(PLAYER_MAX_HP);
4535 bool repositioned = m_script->on_respawnplayer(playersao);
4537 v3f pos = findSpawnPos(m_env->getServerMap());
4538 playersao->setPos(pos);
4542 void Server::UpdateCrafting(u16 peer_id)
4544 DSTACK(__FUNCTION_NAME);
4546 Player* player = m_env->getPlayer(peer_id);
4549 // Get a preview for crafting
4551 getCraftingResult(&player->inventory, preview, false, this);
4553 // Put the new preview in
4554 InventoryList *plist = player->inventory.getList("craftpreview");
4556 assert(plist->getSize() >= 1);
4557 plist->changeItem(0, preview);
4560 RemoteClient* Server::getClient(u16 peer_id)
4562 DSTACK(__FUNCTION_NAME);
4563 //JMutexAutoLock lock(m_con_mutex);
4564 std::map<u16, RemoteClient*>::iterator n;
4565 n = m_clients.find(peer_id);
4566 // A client should exist for all peers
4567 assert(n != m_clients.end());
4571 std::wstring Server::getStatusString()
4573 std::wostringstream os(std::ios_base::binary);
4576 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4578 os<<L", uptime="<<m_uptime.get();
4579 // Information about clients
4580 std::map<u16, RemoteClient*>::iterator i;
4583 for(i = m_clients.begin(), first = true;
4584 i != m_clients.end(); ++i)
4586 // Get client and check that it is valid
4587 RemoteClient *client = i->second;
4588 assert(client->peer_id == i->first);
4589 if(client->serialization_version == SER_FMT_VER_INVALID)
4592 Player *player = m_env->getPlayer(client->peer_id);
4593 // Get name of player
4594 std::wstring name = L"unknown";
4596 name = narrow_to_wide(player->getName());
4597 // Add name to information string
4605 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4606 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4607 if(g_settings->get("motd") != "")
4608 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4612 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4614 std::set<std::string> privs;
4615 m_script->getAuth(name, NULL, &privs);
4619 bool Server::checkPriv(const std::string &name, const std::string &priv)
4621 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4622 return (privs.count(priv) != 0);
4625 void Server::reportPrivsModified(const std::string &name)
4628 for(std::map<u16, RemoteClient*>::iterator
4629 i = m_clients.begin();
4630 i != m_clients.end(); ++i){
4631 RemoteClient *client = i->second;
4632 Player *player = m_env->getPlayer(client->peer_id);
4633 reportPrivsModified(player->getName());
4636 Player *player = m_env->getPlayer(name.c_str());
4639 SendPlayerPrivileges(player->peer_id);
4640 PlayerSAO *sao = player->getPlayerSAO();
4643 sao->updatePrivileges(
4644 getPlayerEffectivePrivs(name),
4649 void Server::reportInventoryFormspecModified(const std::string &name)
4651 Player *player = m_env->getPlayer(name.c_str());
4654 SendPlayerInventoryFormspec(player->peer_id);
4657 // Saves g_settings to configpath given at initialization
4658 void Server::saveConfig()
4660 if(m_path_config != "")
4661 g_settings->updateConfigFile(m_path_config.c_str());
4664 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4666 Player *player = m_env->getPlayer(name);
4670 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4672 SendChatMessage(player->peer_id, msg);
4675 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4677 Player *player = m_env->getPlayer(playername);
4681 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4685 SendShowFormspecMessage(player->peer_id, formspec, formname);
4689 u32 Server::hudAdd(Player *player, HudElement *form) {
4693 u32 id = hud_get_free_id(player);
4694 if (id < player->hud.size())
4695 player->hud[id] = form;
4697 player->hud.push_back(form);
4699 SendHUDAdd(player->peer_id, id, form);
4703 bool Server::hudRemove(Player *player, u32 id) {
4704 if (!player || id >= player->hud.size() || !player->hud[id])
4707 delete player->hud[id];
4708 player->hud[id] = NULL;
4710 SendHUDRemove(player->peer_id, id);
4714 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4718 SendHUDChange(player->peer_id, id, stat, data);
4722 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4726 SendHUDSetFlags(player->peer_id, flags, mask);
4730 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4733 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4736 std::ostringstream os(std::ios::binary);
4737 writeS32(os, hotbar_itemcount);
4738 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4742 void Server::notifyPlayers(const std::wstring msg)
4744 BroadcastChatMessage(msg);
4747 void Server::spawnParticle(const char *playername, v3f pos,
4748 v3f velocity, v3f acceleration,
4749 float expirationtime, float size, bool
4750 collisiondetection, std::string texture)
4752 Player *player = m_env->getPlayer(playername);
4755 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4756 expirationtime, size, collisiondetection, texture);
4759 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4760 float expirationtime, float size,
4761 bool collisiondetection, std::string texture)
4763 SendSpawnParticleAll(pos, velocity, acceleration,
4764 expirationtime, size, collisiondetection, texture);
4767 u32 Server::addParticleSpawner(const char *playername,
4768 u16 amount, float spawntime,
4769 v3f minpos, v3f maxpos,
4770 v3f minvel, v3f maxvel,
4771 v3f minacc, v3f maxacc,
4772 float minexptime, float maxexptime,
4773 float minsize, float maxsize,
4774 bool collisiondetection, std::string texture)
4776 Player *player = m_env->getPlayer(playername);
4781 for(;;) // look for unused particlespawner id
4784 if (std::find(m_particlespawner_ids.begin(),
4785 m_particlespawner_ids.end(), id)
4786 == m_particlespawner_ids.end())
4788 m_particlespawner_ids.push_back(id);
4793 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4794 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4795 minexptime, maxexptime, minsize, maxsize,
4796 collisiondetection, texture, id);
4801 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4802 v3f minpos, v3f maxpos,
4803 v3f minvel, v3f maxvel,
4804 v3f minacc, v3f maxacc,
4805 float minexptime, float maxexptime,
4806 float minsize, float maxsize,
4807 bool collisiondetection, std::string texture)
4810 for(;;) // look for unused particlespawner id
4813 if (std::find(m_particlespawner_ids.begin(),
4814 m_particlespawner_ids.end(), id)
4815 == m_particlespawner_ids.end())
4817 m_particlespawner_ids.push_back(id);
4822 SendAddParticleSpawnerAll(amount, spawntime,
4823 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4824 minexptime, maxexptime, minsize, maxsize,
4825 collisiondetection, texture, id);
4830 void Server::deleteParticleSpawner(const char *playername, u32 id)
4832 Player *player = m_env->getPlayer(playername);
4836 m_particlespawner_ids.erase(
4837 std::remove(m_particlespawner_ids.begin(),
4838 m_particlespawner_ids.end(), id),
4839 m_particlespawner_ids.end());
4840 SendDeleteParticleSpawner(player->peer_id, id);
4843 void Server::deleteParticleSpawnerAll(u32 id)
4845 m_particlespawner_ids.erase(
4846 std::remove(m_particlespawner_ids.begin(),
4847 m_particlespawner_ids.end(), id),
4848 m_particlespawner_ids.end());
4849 SendDeleteParticleSpawnerAll(id);
4852 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4854 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4857 Inventory* Server::createDetachedInventory(const std::string &name)
4859 if(m_detached_inventories.count(name) > 0){
4860 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4861 delete m_detached_inventories[name];
4863 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4865 Inventory *inv = new Inventory(m_itemdef);
4867 m_detached_inventories[name] = inv;
4868 sendDetachedInventoryToAll(name);
4875 BoolScopeSet(bool *dst, bool val):
4878 m_orig_state = *m_dst;
4883 *m_dst = m_orig_state;
4890 // actions: time-reversed list
4891 // Return value: success/failure
4892 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4893 std::list<std::string> *log)
4895 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4896 ServerMap *map = (ServerMap*)(&m_env->getMap());
4897 // Disable rollback report sink while reverting
4898 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4900 // Fail if no actions to handle
4901 if(actions.empty()){
4902 log->push_back("Nothing to do.");
4909 for(std::list<RollbackAction>::const_iterator
4910 i = actions.begin();
4911 i != actions.end(); i++)
4913 const RollbackAction &action = *i;
4915 bool success = action.applyRevert(map, this, this);
4918 std::ostringstream os;
4919 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4920 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4922 log->push_back(os.str());
4924 std::ostringstream os;
4925 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4926 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4928 log->push_back(os.str());
4932 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4933 <<" failed"<<std::endl;
4935 // Call it done if less than half failed
4936 return num_failed <= num_tried/2;
4939 // IGameDef interface
4941 IItemDefManager* Server::getItemDefManager()
4945 INodeDefManager* Server::getNodeDefManager()
4949 ICraftDefManager* Server::getCraftDefManager()
4953 ITextureSource* Server::getTextureSource()
4957 IShaderSource* Server::getShaderSource()
4961 u16 Server::allocateUnknownNodeId(const std::string &name)
4963 return m_nodedef->allocateDummy(name);
4965 ISoundManager* Server::getSoundManager()
4967 return &dummySoundManager;
4969 MtEventManager* Server::getEventManager()
4973 IRollbackReportSink* Server::getRollbackReportSink()
4975 if(!m_enable_rollback_recording)
4977 if(!m_rollback_sink_enabled)
4982 IWritableItemDefManager* Server::getWritableItemDefManager()
4986 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4990 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4995 const ModSpec* Server::getModSpec(const std::string &modname)
4997 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4998 i != m_mods.end(); i++){
4999 const ModSpec &mod = *i;
5000 if(mod.name == modname)
5005 void Server::getModNames(std::list<std::string> &modlist)
5007 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
5009 modlist.push_back(i->name);
5012 std::string Server::getBuiltinLuaPath()
5014 return porting::path_share + DIR_DELIM + "builtin";
5017 v3f findSpawnPos(ServerMap &map)
5019 //return v3f(50,50,50)*BS;
5024 nodepos = v2s16(0,0);
5029 s16 water_level = map.m_mgparams->water_level;
5031 // Try to find a good place a few times
5032 for(s32 i=0; i<1000; i++)
5035 // We're going to try to throw the player to this position
5036 v2s16 nodepos2d = v2s16(
5037 -range + (myrand() % (range * 2)),
5038 -range + (myrand() % (range * 2)));
5040 // Get ground height at point
5041 s16 groundheight = map.findGroundLevel(nodepos2d);
5042 if (groundheight <= water_level) // Don't go underwater
5044 if (groundheight > water_level + 6) // Don't go to high places
5047 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5048 bool is_good = false;
5050 for (s32 i = 0; i < 10; i++) {
5051 v3s16 blockpos = getNodeBlockPos(nodepos);
5052 map.emergeBlock(blockpos, true);
5053 content_t c = map.getNodeNoEx(nodepos).getContent();
5054 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5056 if (air_count >= 2){
5064 // Found a good place
5065 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5071 return intToFloat(nodepos, BS);
5074 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5076 RemotePlayer *player = NULL;
5077 bool newplayer = false;
5080 Try to get an existing player
5082 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5084 // If player is already connected, cancel
5085 if(player != NULL && player->peer_id != 0)
5087 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5092 If player with the wanted peer_id already exists, cancel.
5094 if(m_env->getPlayer(peer_id) != NULL)
5096 infostream<<"emergePlayer(): Player with wrong name but same"
5097 " peer_id already exists"<<std::endl;
5102 Create a new player if it doesn't exist yet
5107 player = new RemotePlayer(this);
5108 player->updateName(name);
5110 /* Set player position */
5111 infostream<<"Server: Finding spawn place for player \""
5112 <<name<<"\""<<std::endl;
5113 v3f pos = findSpawnPos(m_env->getServerMap());
5114 player->setPosition(pos);
5116 /* Add player to environment */
5117 m_env->addPlayer(player);
5121 Create a new player active object
5123 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5124 getPlayerEffectivePrivs(player->getName()),
5127 /* Clean up old HUD elements from previous sessions */
5128 player->hud.clear();
5130 /* Add object to environment */
5131 m_env->addActiveObject(playersao);
5135 m_script->on_newplayer(playersao);
5137 m_script->on_joinplayer(playersao);
5142 void Server::handlePeerChange(PeerChange &c)
5144 JMutexAutoLock envlock(m_env_mutex);
5145 JMutexAutoLock conlock(m_con_mutex);
5147 if(c.type == PEER_ADDED)
5154 std::map<u16, RemoteClient*>::iterator n;
5155 n = m_clients.find(c.peer_id);
5156 // The client shouldn't already exist
5157 assert(n == m_clients.end());
5160 RemoteClient *client = new RemoteClient();
5161 client->peer_id = c.peer_id;
5162 m_clients[client->peer_id] = client;
5165 else if(c.type == PEER_REMOVED)
5172 std::map<u16, RemoteClient*>::iterator n;
5173 n = m_clients.find(c.peer_id);
5174 // The client should exist
5175 assert(n != m_clients.end());
5178 Mark objects to be not known by the client
5180 RemoteClient *client = n->second;
5182 for(std::set<u16>::iterator
5183 i = client->m_known_objects.begin();
5184 i != client->m_known_objects.end(); ++i)
5188 ServerActiveObject* obj = m_env->getActiveObject(id);
5190 if(obj && obj->m_known_by_count > 0)
5191 obj->m_known_by_count--;
5195 Clear references to playing sounds
5197 for(std::map<s32, ServerPlayingSound>::iterator
5198 i = m_playing_sounds.begin();
5199 i != m_playing_sounds.end();)
5201 ServerPlayingSound &psound = i->second;
5202 psound.clients.erase(c.peer_id);
5203 if(psound.clients.size() == 0)
5204 m_playing_sounds.erase(i++);
5209 Player *player = m_env->getPlayer(c.peer_id);
5211 // Collect information about leaving in chat
5212 std::wstring message;
5216 std::wstring name = narrow_to_wide(player->getName());
5219 message += L" left the game.";
5221 message += L" (timed out)";
5225 /* Run scripts and remove from environment */
5229 PlayerSAO *playersao = player->getPlayerSAO();
5232 m_script->on_leaveplayer(playersao);
5234 playersao->disconnected();
5244 std::ostringstream os(std::ios_base::binary);
5245 for(std::map<u16, RemoteClient*>::iterator
5246 i = m_clients.begin();
5247 i != m_clients.end(); ++i)
5249 RemoteClient *client = i->second;
5250 assert(client->peer_id == i->first);
5251 if(client->serialization_version == SER_FMT_VER_INVALID)
5254 Player *player = m_env->getPlayer(client->peer_id);
5257 // Get name of player
5258 os<<player->getName()<<" ";
5261 actionstream<<player->getName()<<" "
5262 <<(c.timeout?"times out.":"leaves game.")
5263 <<" List of players: "
5264 <<os.str()<<std::endl;
5269 delete m_clients[c.peer_id];
5270 m_clients.erase(c.peer_id);
5272 // Send player info to all remaining clients
5273 //SendPlayerInfos();
5275 // Send leave chat message to all remaining clients
5276 if(message.length() != 0)
5277 BroadcastChatMessage(message);
5286 void Server::handlePeerChanges()
5288 while(m_peer_change_queue.size() > 0)
5290 PeerChange c = m_peer_change_queue.pop_front();
5292 verbosestream<<"Server: Handling peer change: "
5293 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5296 handlePeerChange(c);
5300 void dedicated_server_loop(Server &server, bool &kill)
5302 DSTACK(__FUNCTION_NAME);
5304 verbosestream<<"dedicated_server_loop()"<<std::endl;
5306 IntervalLimiter m_profiler_interval;
5310 float steplen = g_settings->getFloat("dedicated_server_step");
5311 // This is kind of a hack but can be done like this
5312 // because server.step() is very light
5314 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5315 sleep_ms((int)(steplen*1000.0));
5317 server.step(steplen);
5319 if(server.getShutdownRequested() || kill)
5321 infostream<<"Dedicated server quitting"<<std::endl;
5323 if(g_settings->getBool("server_announce") == true)
5324 ServerList::sendAnnounce("delete");
5332 float profiler_print_interval =
5333 g_settings->getFloat("profiler_print_interval");
5334 if(profiler_print_interval != 0)
5336 if(m_profiler_interval.step(steplen, profiler_print_interval))
5338 infostream<<"Profiler:"<<std::endl;
5339 g_profiler->print(infostream);
5340 g_profiler->clear();