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 breath if changed
1145 if(playersao->m_breath_not_sent){
1146 SendPlayerBreath(client->peer_id);
1150 Send player inventories if necessary
1152 if(playersao->m_moved){
1153 SendMovePlayer(client->peer_id);
1154 playersao->m_moved = false;
1156 if(playersao->m_inventory_not_sent){
1157 UpdateCrafting(client->peer_id);
1158 SendInventory(client->peer_id);
1163 /* Transform liquids */
1164 m_liquid_transform_timer += dtime;
1165 if(m_liquid_transform_timer >= m_liquid_transform_every)
1167 m_liquid_transform_timer -= m_liquid_transform_every;
1169 JMutexAutoLock lock(m_env_mutex);
1171 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1173 std::map<v3s16, MapBlock*> modified_blocks;
1174 m_env->getMap().transformLiquids(modified_blocks);
1179 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1180 ServerMap &map = ((ServerMap&)m_env->getMap());
1181 map.updateLighting(modified_blocks, lighting_modified_blocks);
1183 // Add blocks modified by lighting to modified_blocks
1184 for(core::map<v3s16, MapBlock*>::Iterator
1185 i = lighting_modified_blocks.getIterator();
1186 i.atEnd() == false; i++)
1188 MapBlock *block = i.getNode()->getValue();
1189 modified_blocks.insert(block->getPos(), block);
1193 Set the modified blocks unsent for all the clients
1196 JMutexAutoLock lock2(m_con_mutex);
1198 for(std::map<u16, RemoteClient*>::iterator
1199 i = m_clients.begin();
1200 i != m_clients.end(); ++i)
1202 RemoteClient *client = i->second;
1204 if(modified_blocks.size() > 0)
1206 // Remove block from sent history
1207 client->SetBlocksNotSent(modified_blocks);
1212 // Periodically print some info
1214 float &counter = m_print_info_timer;
1220 JMutexAutoLock lock2(m_con_mutex);
1221 m_clients_number = 0;
1222 if(m_clients.size() != 0)
1223 infostream<<"Players:"<<std::endl;
1224 for(std::map<u16, RemoteClient*>::iterator
1225 i = m_clients.begin();
1226 i != m_clients.end(); ++i)
1228 //u16 peer_id = i.getNode()->getKey();
1229 RemoteClient *client = i->second;
1230 Player *player = m_env->getPlayer(client->peer_id);
1233 infostream<<"* "<<player->getName()<<"\t";
1234 client->PrintInfo(infostream);
1242 // send masterserver announce
1244 float &counter = m_masterserver_timer;
1245 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1247 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id, m_mods);
1254 //if(g_settings->getBool("enable_experimental"))
1258 Check added and deleted active objects
1261 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1262 JMutexAutoLock envlock(m_env_mutex);
1263 JMutexAutoLock conlock(m_con_mutex);
1265 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1267 // Radius inside which objects are active
1268 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1269 radius *= MAP_BLOCKSIZE;
1271 for(std::map<u16, RemoteClient*>::iterator
1272 i = m_clients.begin();
1273 i != m_clients.end(); ++i)
1275 RemoteClient *client = i->second;
1277 // If definitions and textures have not been sent, don't
1278 // send objects either
1279 if(!client->definitions_sent)
1282 Player *player = m_env->getPlayer(client->peer_id);
1285 // This can happen if the client timeouts somehow
1286 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1288 <<" has no associated player"<<std::endl;*/
1291 v3s16 pos = floatToInt(player->getPosition(), BS);
1293 std::set<u16> removed_objects;
1294 std::set<u16> added_objects;
1295 m_env->getRemovedActiveObjects(pos, radius,
1296 client->m_known_objects, removed_objects);
1297 m_env->getAddedActiveObjects(pos, radius,
1298 client->m_known_objects, added_objects);
1300 // Ignore if nothing happened
1301 if(removed_objects.size() == 0 && added_objects.size() == 0)
1303 //infostream<<"active objects: none changed"<<std::endl;
1307 std::string data_buffer;
1311 // Handle removed objects
1312 writeU16((u8*)buf, removed_objects.size());
1313 data_buffer.append(buf, 2);
1314 for(std::set<u16>::iterator
1315 i = removed_objects.begin();
1316 i != removed_objects.end(); ++i)
1320 ServerActiveObject* obj = m_env->getActiveObject(id);
1322 // Add to data buffer for sending
1323 writeU16((u8*)buf, id);
1324 data_buffer.append(buf, 2);
1326 // Remove from known objects
1327 client->m_known_objects.erase(id);
1329 if(obj && obj->m_known_by_count > 0)
1330 obj->m_known_by_count--;
1333 // Handle added objects
1334 writeU16((u8*)buf, added_objects.size());
1335 data_buffer.append(buf, 2);
1336 for(std::set<u16>::iterator
1337 i = added_objects.begin();
1338 i != added_objects.end(); ++i)
1342 ServerActiveObject* obj = m_env->getActiveObject(id);
1345 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1347 infostream<<"WARNING: "<<__FUNCTION_NAME
1348 <<": NULL object"<<std::endl;
1350 type = obj->getSendType();
1352 // Add to data buffer for sending
1353 writeU16((u8*)buf, id);
1354 data_buffer.append(buf, 2);
1355 writeU8((u8*)buf, type);
1356 data_buffer.append(buf, 1);
1359 data_buffer.append(serializeLongString(
1360 obj->getClientInitializationData(client->net_proto_version)));
1362 data_buffer.append(serializeLongString(""));
1364 // Add to known objects
1365 client->m_known_objects.insert(id);
1368 obj->m_known_by_count++;
1372 SharedBuffer<u8> reply(2 + data_buffer.size());
1373 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1374 memcpy((char*)&reply[2], data_buffer.c_str(),
1375 data_buffer.size());
1377 m_con.Send(client->peer_id, 0, reply, true);
1379 verbosestream<<"Server: Sent object remove/add: "
1380 <<removed_objects.size()<<" removed, "
1381 <<added_objects.size()<<" added, "
1382 <<"packet size is "<<reply.getSize()<<std::endl;
1387 Collect a list of all the objects known by the clients
1388 and report it back to the environment.
1391 core::map<u16, bool> all_known_objects;
1393 for(core::map<u16, RemoteClient*>::Iterator
1394 i = m_clients.getIterator();
1395 i.atEnd() == false; i++)
1397 RemoteClient *client = i.getNode()->getValue();
1398 // Go through all known objects of client
1399 for(core::map<u16, bool>::Iterator
1400 i = client->m_known_objects.getIterator();
1401 i.atEnd()==false; i++)
1403 u16 id = i.getNode()->getKey();
1404 all_known_objects[id] = true;
1408 m_env->setKnownActiveObjects(whatever);
1414 Send object messages
1417 JMutexAutoLock envlock(m_env_mutex);
1418 JMutexAutoLock conlock(m_con_mutex);
1420 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1423 // Value = data sent by object
1424 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1426 // Get active object messages from environment
1429 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1433 std::list<ActiveObjectMessage>* message_list = NULL;
1434 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1435 n = buffered_messages.find(aom.id);
1436 if(n == buffered_messages.end())
1438 message_list = new std::list<ActiveObjectMessage>;
1439 buffered_messages[aom.id] = message_list;
1443 message_list = n->second;
1445 message_list->push_back(aom);
1448 // Route data to every client
1449 for(std::map<u16, RemoteClient*>::iterator
1450 i = m_clients.begin();
1451 i != m_clients.end(); ++i)
1453 RemoteClient *client = i->second;
1454 std::string reliable_data;
1455 std::string unreliable_data;
1456 // Go through all objects in message buffer
1457 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1458 j = buffered_messages.begin();
1459 j != buffered_messages.end(); ++j)
1461 // If object is not known by client, skip it
1463 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1465 // Get message list of object
1466 std::list<ActiveObjectMessage>* list = j->second;
1467 // Go through every message
1468 for(std::list<ActiveObjectMessage>::iterator
1469 k = list->begin(); k != list->end(); ++k)
1471 // Compose the full new data with header
1472 ActiveObjectMessage aom = *k;
1473 std::string new_data;
1476 writeU16((u8*)&buf[0], aom.id);
1477 new_data.append(buf, 2);
1479 new_data += serializeString(aom.datastring);
1480 // Add data to buffer
1482 reliable_data += new_data;
1484 unreliable_data += new_data;
1488 reliable_data and unreliable_data are now ready.
1491 if(reliable_data.size() > 0)
1493 SharedBuffer<u8> reply(2 + reliable_data.size());
1494 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1495 memcpy((char*)&reply[2], reliable_data.c_str(),
1496 reliable_data.size());
1498 m_con.Send(client->peer_id, 0, reply, true);
1500 if(unreliable_data.size() > 0)
1502 SharedBuffer<u8> reply(2 + unreliable_data.size());
1503 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1504 memcpy((char*)&reply[2], unreliable_data.c_str(),
1505 unreliable_data.size());
1506 // Send as unreliable
1507 m_con.Send(client->peer_id, 0, reply, false);
1510 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1512 infostream<<"Server: Size of object message data: "
1513 <<"reliable: "<<reliable_data.size()
1514 <<", unreliable: "<<unreliable_data.size()
1519 // Clear buffered_messages
1520 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1521 i = buffered_messages.begin();
1522 i != buffered_messages.end(); ++i)
1528 } // enable_experimental
1531 Send queued-for-sending map edit events.
1534 // We will be accessing the environment and the connection
1535 JMutexAutoLock lock(m_env_mutex);
1536 JMutexAutoLock conlock(m_con_mutex);
1538 // Don't send too many at a time
1541 // Single change sending is disabled if queue size is not small
1542 bool disable_single_change_sending = false;
1543 if(m_unsent_map_edit_queue.size() >= 4)
1544 disable_single_change_sending = true;
1546 int event_count = m_unsent_map_edit_queue.size();
1548 // We'll log the amount of each
1551 while(m_unsent_map_edit_queue.size() != 0)
1553 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1555 // Players far away from the change are stored here.
1556 // Instead of sending the changes, MapBlocks are set not sent
1558 std::list<u16> far_players;
1560 if(event->type == MEET_ADDNODE)
1562 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1563 prof.add("MEET_ADDNODE", 1);
1564 if(disable_single_change_sending)
1565 sendAddNode(event->p, event->n, event->already_known_by_peer,
1568 sendAddNode(event->p, event->n, event->already_known_by_peer,
1571 else if(event->type == MEET_REMOVENODE)
1573 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1574 prof.add("MEET_REMOVENODE", 1);
1575 if(disable_single_change_sending)
1576 sendRemoveNode(event->p, event->already_known_by_peer,
1579 sendRemoveNode(event->p, event->already_known_by_peer,
1582 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1584 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1585 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1586 setBlockNotSent(event->p);
1588 else if(event->type == MEET_OTHER)
1590 infostream<<"Server: MEET_OTHER"<<std::endl;
1591 prof.add("MEET_OTHER", 1);
1592 for(std::set<v3s16>::iterator
1593 i = event->modified_blocks.begin();
1594 i != event->modified_blocks.end(); ++i)
1596 setBlockNotSent(*i);
1601 prof.add("unknown", 1);
1602 infostream<<"WARNING: Server: Unknown MapEditEvent "
1603 <<((u32)event->type)<<std::endl;
1607 Set blocks not sent to far players
1609 if(far_players.size() > 0)
1611 // Convert list format to that wanted by SetBlocksNotSent
1612 std::map<v3s16, MapBlock*> modified_blocks2;
1613 for(std::set<v3s16>::iterator
1614 i = event->modified_blocks.begin();
1615 i != event->modified_blocks.end(); ++i)
1617 modified_blocks2[*i] =
1618 m_env->getMap().getBlockNoCreateNoEx(*i);
1620 // Set blocks not sent
1621 for(std::list<u16>::iterator
1622 i = far_players.begin();
1623 i != far_players.end(); ++i)
1626 RemoteClient *client = getClient(peer_id);
1629 client->SetBlocksNotSent(modified_blocks2);
1635 /*// Don't send too many at a time
1637 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1641 if(event_count >= 5){
1642 infostream<<"Server: MapEditEvents:"<<std::endl;
1643 prof.print(infostream);
1644 } else if(event_count != 0){
1645 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1646 prof.print(verbosestream);
1652 Trigger emergethread (it somehow gets to a non-triggered but
1653 bysy state sometimes)
1656 float &counter = m_emergethread_trigger_timer;
1662 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1663 m_emerge->emergethread[i]->trigger();
1665 // Update m_enable_rollback_recording here too
1666 m_enable_rollback_recording =
1667 g_settings->getBool("enable_rollback_recording");
1671 // Save map, players and auth stuff
1673 float &counter = m_savemap_timer;
1675 if(counter >= g_settings->getFloat("server_map_save_interval"))
1678 JMutexAutoLock lock(m_env_mutex);
1680 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1683 if(m_banmanager.isModified())
1684 m_banmanager.save();
1686 // Save changed parts of map
1687 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1690 m_env->serializePlayers(m_path_world);
1692 // Save environment metadata
1693 m_env->saveMeta(m_path_world);
1698 void Server::Receive()
1700 DSTACK(__FUNCTION_NAME);
1701 SharedBuffer<u8> data;
1706 JMutexAutoLock conlock(m_con_mutex);
1707 datasize = m_con.Receive(peer_id, data);
1710 // This has to be called so that the client list gets synced
1711 // with the peer list of the connection
1712 handlePeerChanges();
1714 ProcessData(*data, datasize, peer_id);
1716 catch(con::InvalidIncomingDataException &e)
1718 infostream<<"Server::Receive(): "
1719 "InvalidIncomingDataException: what()="
1720 <<e.what()<<std::endl;
1722 catch(con::PeerNotFoundException &e)
1724 //NOTE: This is not needed anymore
1726 // The peer has been disconnected.
1727 // Find the associated player and remove it.
1729 /*JMutexAutoLock envlock(m_env_mutex);
1731 infostream<<"ServerThread: peer_id="<<peer_id
1732 <<" has apparently closed connection. "
1733 <<"Removing player."<<std::endl;
1735 m_env->removePlayer(peer_id);*/
1739 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1741 DSTACK(__FUNCTION_NAME);
1742 // Environment is locked first.
1743 JMutexAutoLock envlock(m_env_mutex);
1744 JMutexAutoLock conlock(m_con_mutex);
1746 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1750 Address address = m_con.GetPeerAddress(peer_id);
1751 addr_s = address.serializeString();
1753 // drop player if is ip is banned
1754 if(m_banmanager.isIpBanned(addr_s)){
1755 infostream<<"Server: A banned client tried to connect from "
1756 <<addr_s<<"; banned name was "
1757 <<m_banmanager.getBanName(addr_s)<<std::endl;
1758 // This actually doesn't seem to transfer to the client
1759 SendAccessDenied(m_con, peer_id,
1760 L"Your ip is banned. Banned name was "
1761 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1762 m_con.DeletePeer(peer_id);
1766 catch(con::PeerNotFoundException &e)
1768 infostream<<"Server::ProcessData(): Cancelling: peer "
1769 <<peer_id<<" not found"<<std::endl;
1773 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1781 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1783 if(command == TOSERVER_INIT)
1785 // [0] u16 TOSERVER_INIT
1786 // [2] u8 SER_FMT_VER_HIGHEST
1787 // [3] u8[20] player_name
1788 // [23] u8[28] password <--- can be sent without this, from old versions
1790 if(datasize < 2+1+PLAYERNAME_SIZE)
1793 verbosestream<<"Server: Got TOSERVER_INIT from "
1794 <<peer_id<<std::endl;
1796 // First byte after command is maximum supported
1797 // serialization version
1798 u8 client_max = data[2];
1799 u8 our_max = SER_FMT_VER_HIGHEST;
1800 // Use the highest version supported by both
1801 u8 deployed = std::min(client_max, our_max);
1802 // If it's lower than the lowest supported, give up.
1803 if(deployed < SER_FMT_VER_LOWEST)
1804 deployed = SER_FMT_VER_INVALID;
1806 //peer->serialization_version = deployed;
1807 getClient(peer_id)->pending_serialization_version = deployed;
1809 if(deployed == SER_FMT_VER_INVALID)
1811 actionstream<<"Server: A mismatched client tried to connect from "
1812 <<addr_s<<std::endl;
1813 infostream<<"Server: Cannot negotiate "
1814 "serialization version with peer "
1815 <<peer_id<<std::endl;
1816 SendAccessDenied(m_con, peer_id, std::wstring(
1817 L"Your client's version is not supported.\n"
1818 L"Server version is ")
1819 + narrow_to_wide(VERSION_STRING) + L"."
1825 Read and check network protocol version
1828 u16 min_net_proto_version = 0;
1829 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1830 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1832 // Use same version as minimum and maximum if maximum version field
1833 // doesn't exist (backwards compatibility)
1834 u16 max_net_proto_version = min_net_proto_version;
1835 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1836 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1838 // Start with client's maximum version
1839 u16 net_proto_version = max_net_proto_version;
1841 // Figure out a working version if it is possible at all
1842 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1843 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1845 // If maximum is larger than our maximum, go with our maximum
1846 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1847 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1848 // Else go with client's maximum
1850 net_proto_version = max_net_proto_version;
1853 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1854 <<min_net_proto_version<<", max: "<<max_net_proto_version
1855 <<", chosen: "<<net_proto_version<<std::endl;
1857 getClient(peer_id)->net_proto_version = net_proto_version;
1859 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1860 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1862 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1864 SendAccessDenied(m_con, peer_id, std::wstring(
1865 L"Your client's version is not supported.\n"
1866 L"Server version is ")
1867 + narrow_to_wide(VERSION_STRING) + L",\n"
1868 + L"server's PROTOCOL_VERSION is "
1869 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1871 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1872 + L", client's PROTOCOL_VERSION is "
1873 + narrow_to_wide(itos(min_net_proto_version))
1875 + narrow_to_wide(itos(max_net_proto_version))
1880 if(g_settings->getBool("strict_protocol_version_checking"))
1882 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1884 actionstream<<"Server: A mismatched (strict) client tried to "
1885 <<"connect from "<<addr_s<<std::endl;
1886 SendAccessDenied(m_con, peer_id, std::wstring(
1887 L"Your client's version is not supported.\n"
1888 L"Server version is ")
1889 + narrow_to_wide(VERSION_STRING) + L",\n"
1890 + L"server's PROTOCOL_VERSION (strict) is "
1891 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1892 + L", client's PROTOCOL_VERSION is "
1893 + narrow_to_wide(itos(min_net_proto_version))
1895 + narrow_to_wide(itos(max_net_proto_version))
1906 char playername[PLAYERNAME_SIZE];
1907 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1909 playername[i] = data[3+i];
1911 playername[PLAYERNAME_SIZE-1] = 0;
1913 if(playername[0]=='\0')
1915 actionstream<<"Server: Player with an empty name "
1916 <<"tried to connect from "<<addr_s<<std::endl;
1917 SendAccessDenied(m_con, peer_id,
1922 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1924 actionstream<<"Server: Player with an invalid name "
1925 <<"tried to connect from "<<addr_s<<std::endl;
1926 SendAccessDenied(m_con, peer_id,
1927 L"Name contains unallowed characters");
1931 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1933 actionstream<<"Server: Player with an invalid name "
1934 <<"tried to connect from "<<addr_s<<std::endl;
1935 SendAccessDenied(m_con, peer_id,
1936 L"Name is not allowed");
1940 infostream<<"Server: New connection: \""<<playername<<"\" from "
1941 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1944 char given_password[PASSWORD_SIZE];
1945 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1947 // old version - assume blank password
1948 given_password[0] = 0;
1952 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1954 given_password[i] = data[23+i];
1956 given_password[PASSWORD_SIZE-1] = 0;
1959 if(!base64_is_valid(given_password)){
1960 infostream<<"Server: "<<playername
1961 <<" supplied invalid password hash"<<std::endl;
1962 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1966 std::string checkpwd; // Password hash to check against
1967 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1969 // If no authentication info exists for user, create it
1971 if(!isSingleplayer() &&
1972 g_settings->getBool("disallow_empty_password") &&
1973 std::string(given_password) == ""){
1974 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1975 L"disallowed. Set a password and try again.");
1978 std::wstring raw_default_password =
1979 narrow_to_wide(g_settings->get("default_password"));
1980 std::string initial_password =
1981 translatePassword(playername, raw_default_password);
1983 // If default_password is empty, allow any initial password
1984 if (raw_default_password.length() == 0)
1985 initial_password = given_password;
1987 m_script->createAuth(playername, initial_password);
1990 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1993 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1997 if(given_password != checkpwd){
1998 infostream<<"Server: peer_id="<<peer_id
1999 <<": supplied invalid password for "
2000 <<playername<<std::endl;
2001 SendAccessDenied(m_con, peer_id, L"Invalid password");
2005 // Do not allow multiple players in simple singleplayer mode.
2006 // This isn't a perfect way to do it, but will suffice for now.
2007 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2008 infostream<<"Server: Not allowing another client to connect in"
2009 <<" simple singleplayer mode"<<std::endl;
2010 SendAccessDenied(m_con, peer_id,
2011 L"Running in simple singleplayer mode.");
2015 // Enforce user limit.
2016 // Don't enforce for users that have some admin right
2017 if(m_clients.size() >= g_settings->getU16("max_users") &&
2018 !checkPriv(playername, "server") &&
2019 !checkPriv(playername, "ban") &&
2020 !checkPriv(playername, "privs") &&
2021 !checkPriv(playername, "password") &&
2022 playername != g_settings->get("name"))
2024 actionstream<<"Server: "<<playername<<" tried to join, but there"
2025 <<" are already max_users="
2026 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2027 SendAccessDenied(m_con, peer_id, L"Too many users.");
2032 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2034 // If failed, cancel
2035 if(playersao == NULL)
2037 errorstream<<"Server: peer_id="<<peer_id
2038 <<": failed to emerge player"<<std::endl;
2043 Answer with a TOCLIENT_INIT
2046 SharedBuffer<u8> reply(2+1+6+8+4);
2047 writeU16(&reply[0], TOCLIENT_INIT);
2048 writeU8(&reply[2], deployed);
2049 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2050 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2051 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2054 m_con.Send(peer_id, 0, reply, true);
2058 Send complete position information
2060 SendMovePlayer(peer_id);
2065 if(command == TOSERVER_INIT2)
2067 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2068 <<peer_id<<std::endl;
2070 Player *player = m_env->getPlayer(peer_id);
2072 verbosestream<<"Server: TOSERVER_INIT2: "
2073 <<"Player not found; ignoring."<<std::endl;
2077 RemoteClient *client = getClient(peer_id);
2078 client->serialization_version =
2079 getClient(peer_id)->pending_serialization_version;
2082 Send some initialization data
2085 infostream<<"Server: Sending content to "
2086 <<getPlayerName(peer_id)<<std::endl;
2088 // Send player movement settings
2089 SendMovement(m_con, peer_id);
2091 // Send item definitions
2092 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2094 // Send node definitions
2095 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2097 // Send media announcement
2098 sendMediaAnnouncement(peer_id);
2101 SendPlayerPrivileges(peer_id);
2103 // Send inventory formspec
2104 SendPlayerInventoryFormspec(peer_id);
2107 UpdateCrafting(peer_id);
2108 SendInventory(peer_id);
2111 if(g_settings->getBool("enable_damage"))
2112 SendPlayerHP(peer_id);
2115 SendPlayerBreath(peer_id);
2117 // Send detached inventories
2118 sendDetachedInventories(peer_id);
2120 // Show death screen if necessary
2122 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2126 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2127 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2128 m_con.Send(peer_id, 0, data, true);
2131 // Note things in chat if not in simple singleplayer mode
2132 if(!m_simple_singleplayer_mode)
2134 // Send information about server to player in chat
2135 SendChatMessage(peer_id, getStatusString());
2137 // Send information about joining in chat
2139 std::wstring name = L"unknown";
2140 Player *player = m_env->getPlayer(peer_id);
2142 name = narrow_to_wide(player->getName());
2144 std::wstring message;
2147 message += L" joined the game.";
2148 BroadcastChatMessage(message);
2152 // Warnings about protocol version can be issued here
2153 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2155 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2156 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2163 std::ostringstream os(std::ios_base::binary);
2164 for(std::map<u16, RemoteClient*>::iterator
2165 i = m_clients.begin();
2166 i != m_clients.end(); ++i)
2168 RemoteClient *client = i->second;
2169 assert(client->peer_id == i->first);
2170 if(client->serialization_version == SER_FMT_VER_INVALID)
2173 Player *player = m_env->getPlayer(client->peer_id);
2176 // Get name of player
2177 os<<player->getName()<<" ";
2180 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. List of players: "
2181 <<os.str()<<std::endl;
2187 if(peer_ser_ver == SER_FMT_VER_INVALID)
2189 infostream<<"Server::ProcessData(): Cancelling: Peer"
2190 " serialization format invalid or not initialized."
2191 " Skipping incoming command="<<command<<std::endl;
2195 Player *player = m_env->getPlayer(peer_id);
2197 infostream<<"Server::ProcessData(): Cancelling: "
2198 "No player for peer_id="<<peer_id
2203 PlayerSAO *playersao = player->getPlayerSAO();
2204 if(playersao == NULL){
2205 infostream<<"Server::ProcessData(): Cancelling: "
2206 "No player object for peer_id="<<peer_id
2211 if(command == TOSERVER_PLAYERPOS)
2213 if(datasize < 2+12+12+4+4)
2217 v3s32 ps = readV3S32(&data[start+2]);
2218 v3s32 ss = readV3S32(&data[start+2+12]);
2219 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2220 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2222 if(datasize >= 2+12+12+4+4+4)
2223 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2224 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2225 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2226 pitch = wrapDegrees(pitch);
2227 yaw = wrapDegrees(yaw);
2229 player->setPosition(position);
2230 player->setSpeed(speed);
2231 player->setPitch(pitch);
2232 player->setYaw(yaw);
2233 player->keyPressed=keyPressed;
2234 player->control.up = (bool)(keyPressed&1);
2235 player->control.down = (bool)(keyPressed&2);
2236 player->control.left = (bool)(keyPressed&4);
2237 player->control.right = (bool)(keyPressed&8);
2238 player->control.jump = (bool)(keyPressed&16);
2239 player->control.aux1 = (bool)(keyPressed&32);
2240 player->control.sneak = (bool)(keyPressed&64);
2241 player->control.LMB = (bool)(keyPressed&128);
2242 player->control.RMB = (bool)(keyPressed&256);
2244 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2245 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2246 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2248 else if(command == TOSERVER_GOTBLOCKS)
2261 u16 count = data[2];
2262 for(u16 i=0; i<count; i++)
2264 if((s16)datasize < 2+1+(i+1)*6)
2265 throw con::InvalidIncomingDataException
2266 ("GOTBLOCKS length is too short");
2267 v3s16 p = readV3S16(&data[2+1+i*6]);
2268 /*infostream<<"Server: GOTBLOCKS ("
2269 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2270 RemoteClient *client = getClient(peer_id);
2271 client->GotBlock(p);
2274 else if(command == TOSERVER_DELETEDBLOCKS)
2287 u16 count = data[2];
2288 for(u16 i=0; i<count; i++)
2290 if((s16)datasize < 2+1+(i+1)*6)
2291 throw con::InvalidIncomingDataException
2292 ("DELETEDBLOCKS length is too short");
2293 v3s16 p = readV3S16(&data[2+1+i*6]);
2294 /*infostream<<"Server: DELETEDBLOCKS ("
2295 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2296 RemoteClient *client = getClient(peer_id);
2297 client->SetBlockNotSent(p);
2300 else if(command == TOSERVER_CLICK_OBJECT)
2302 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2305 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2307 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2310 else if(command == TOSERVER_GROUND_ACTION)
2312 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2316 else if(command == TOSERVER_RELEASE)
2318 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2321 else if(command == TOSERVER_SIGNTEXT)
2323 infostream<<"Server: SIGNTEXT not supported anymore"
2327 else if(command == TOSERVER_SIGNNODETEXT)
2329 infostream<<"Server: SIGNNODETEXT not supported anymore"
2333 else if(command == TOSERVER_INVENTORY_ACTION)
2335 // Strip command and create a stream
2336 std::string datastring((char*)&data[2], datasize-2);
2337 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2338 std::istringstream is(datastring, std::ios_base::binary);
2340 InventoryAction *a = InventoryAction::deSerialize(is);
2343 infostream<<"TOSERVER_INVENTORY_ACTION: "
2344 <<"InventoryAction::deSerialize() returned NULL"
2349 // If something goes wrong, this player is to blame
2350 RollbackScopeActor rollback_scope(m_rollback,
2351 std::string("player:")+player->getName());
2354 Note: Always set inventory not sent, to repair cases
2355 where the client made a bad prediction.
2359 Handle restrictions and special cases of the move action
2361 if(a->getType() == IACTION_MOVE)
2363 IMoveAction *ma = (IMoveAction*)a;
2365 ma->from_inv.applyCurrentPlayer(player->getName());
2366 ma->to_inv.applyCurrentPlayer(player->getName());
2368 setInventoryModified(ma->from_inv);
2369 setInventoryModified(ma->to_inv);
2371 bool from_inv_is_current_player =
2372 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2373 (ma->from_inv.name == player->getName());
2375 bool to_inv_is_current_player =
2376 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2377 (ma->to_inv.name == player->getName());
2380 Disable moving items out of craftpreview
2382 if(ma->from_list == "craftpreview")
2384 infostream<<"Ignoring IMoveAction from "
2385 <<(ma->from_inv.dump())<<":"<<ma->from_list
2386 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2387 <<" because src is "<<ma->from_list<<std::endl;
2393 Disable moving items into craftresult and craftpreview
2395 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2397 infostream<<"Ignoring IMoveAction from "
2398 <<(ma->from_inv.dump())<<":"<<ma->from_list
2399 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2400 <<" because dst is "<<ma->to_list<<std::endl;
2405 // Disallow moving items in elsewhere than player's inventory
2406 // if not allowed to interact
2407 if(!checkPriv(player->getName(), "interact") &&
2408 (!from_inv_is_current_player ||
2409 !to_inv_is_current_player))
2411 infostream<<"Cannot move outside of player's inventory: "
2412 <<"No interact privilege"<<std::endl;
2418 Handle restrictions and special cases of the drop action
2420 else if(a->getType() == IACTION_DROP)
2422 IDropAction *da = (IDropAction*)a;
2424 da->from_inv.applyCurrentPlayer(player->getName());
2426 setInventoryModified(da->from_inv);
2429 Disable dropping items out of craftpreview
2431 if(da->from_list == "craftpreview")
2433 infostream<<"Ignoring IDropAction from "
2434 <<(da->from_inv.dump())<<":"<<da->from_list
2435 <<" because src is "<<da->from_list<<std::endl;
2440 // Disallow dropping items if not allowed to interact
2441 if(!checkPriv(player->getName(), "interact"))
2448 Handle restrictions and special cases of the craft action
2450 else if(a->getType() == IACTION_CRAFT)
2452 ICraftAction *ca = (ICraftAction*)a;
2454 ca->craft_inv.applyCurrentPlayer(player->getName());
2456 setInventoryModified(ca->craft_inv);
2458 //bool craft_inv_is_current_player =
2459 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2460 // (ca->craft_inv.name == player->getName());
2462 // Disallow crafting if not allowed to interact
2463 if(!checkPriv(player->getName(), "interact"))
2465 infostream<<"Cannot craft: "
2466 <<"No interact privilege"<<std::endl;
2473 a->apply(this, playersao, this);
2477 else if(command == TOSERVER_CHAT_MESSAGE)
2485 std::string datastring((char*)&data[2], datasize-2);
2486 std::istringstream is(datastring, std::ios_base::binary);
2489 is.read((char*)buf, 2);
2490 u16 len = readU16(buf);
2492 std::wstring message;
2493 for(u16 i=0; i<len; i++)
2495 is.read((char*)buf, 2);
2496 message += (wchar_t)readU16(buf);
2499 // If something goes wrong, this player is to blame
2500 RollbackScopeActor rollback_scope(m_rollback,
2501 std::string("player:")+player->getName());
2503 // Get player name of this client
2504 std::wstring name = narrow_to_wide(player->getName());
2507 bool ate = m_script->on_chat_message(player->getName(),
2508 wide_to_narrow(message));
2509 // If script ate the message, don't proceed
2513 // Line to send to players
2515 // Whether to send to the player that sent the line
2516 bool send_to_sender = false;
2517 // Whether to send to other players
2518 bool send_to_others = false;
2520 // Commands are implemented in Lua, so only catch invalid
2521 // commands that were not "eaten" and send an error back
2522 if(message[0] == L'/')
2524 message = message.substr(1);
2525 send_to_sender = true;
2526 if(message.length() == 0)
2527 line += L"-!- Empty command";
2529 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2533 if(checkPriv(player->getName(), "shout")){
2538 send_to_others = true;
2540 line += L"-!- You don't have permission to shout.";
2541 send_to_sender = true;
2548 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2551 Send the message to clients
2553 for(std::map<u16, RemoteClient*>::iterator
2554 i = m_clients.begin();
2555 i != m_clients.end(); ++i)
2557 // Get client and check that it is valid
2558 RemoteClient *client = i->second;
2559 assert(client->peer_id == i->first);
2560 if(client->serialization_version == SER_FMT_VER_INVALID)
2564 bool sender_selected = (peer_id == client->peer_id);
2565 if(sender_selected == true && send_to_sender == false)
2567 if(sender_selected == false && send_to_others == false)
2570 SendChatMessage(client->peer_id, line);
2574 else if(command == TOSERVER_DAMAGE)
2576 std::string datastring((char*)&data[2], datasize-2);
2577 std::istringstream is(datastring, std::ios_base::binary);
2578 u8 damage = readU8(is);
2580 if(g_settings->getBool("enable_damage"))
2582 actionstream<<player->getName()<<" damaged by "
2583 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2586 playersao->setHP(playersao->getHP() - damage);
2588 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2591 if(playersao->m_hp_not_sent)
2592 SendPlayerHP(peer_id);
2595 else if(command == TOSERVER_BREATH)
2597 std::string datastring((char*)&data[2], datasize-2);
2598 std::istringstream is(datastring, std::ios_base::binary);
2599 u16 breath = readU16(is);
2600 playersao->setBreath(breath);
2602 else if(command == TOSERVER_PASSWORD)
2605 [0] u16 TOSERVER_PASSWORD
2606 [2] u8[28] old password
2607 [30] u8[28] new password
2610 if(datasize != 2+PASSWORD_SIZE*2)
2612 /*char password[PASSWORD_SIZE];
2613 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2614 password[i] = data[2+i];
2615 password[PASSWORD_SIZE-1] = 0;*/
2617 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2625 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2627 char c = data[2+PASSWORD_SIZE+i];
2633 if(!base64_is_valid(newpwd)){
2634 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2635 // Wrong old password supplied!!
2636 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2640 infostream<<"Server: Client requests a password change from "
2641 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2643 std::string playername = player->getName();
2645 std::string checkpwd;
2646 m_script->getAuth(playername, &checkpwd, NULL);
2648 if(oldpwd != checkpwd)
2650 infostream<<"Server: invalid old password"<<std::endl;
2651 // Wrong old password supplied!!
2652 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2656 bool success = m_script->setPassword(playername, newpwd);
2658 actionstream<<player->getName()<<" changes password"<<std::endl;
2659 SendChatMessage(peer_id, L"Password change successful.");
2661 actionstream<<player->getName()<<" tries to change password but "
2662 <<"it fails"<<std::endl;
2663 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2666 else if(command == TOSERVER_PLAYERITEM)
2671 u16 item = readU16(&data[2]);
2672 playersao->setWieldIndex(item);
2674 else if(command == TOSERVER_RESPAWN)
2676 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2679 RespawnPlayer(peer_id);
2681 actionstream<<player->getName()<<" respawns at "
2682 <<PP(player->getPosition()/BS)<<std::endl;
2684 // ActiveObject is added to environment in AsyncRunStep after
2685 // the previous addition has been succesfully removed
2687 else if(command == TOSERVER_REQUEST_MEDIA) {
2688 std::string datastring((char*)&data[2], datasize-2);
2689 std::istringstream is(datastring, std::ios_base::binary);
2691 std::list<MediaRequest> tosend;
2692 u16 numfiles = readU16(is);
2694 infostream<<"Sending "<<numfiles<<" files to "
2695 <<getPlayerName(peer_id)<<std::endl;
2696 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2698 for(int i = 0; i < numfiles; i++) {
2699 std::string name = deSerializeString(is);
2700 tosend.push_back(MediaRequest(name));
2701 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2705 sendRequestedMedia(peer_id, tosend);
2707 // Now the client should know about everything
2708 // (definitions and files)
2709 getClient(peer_id)->definitions_sent = true;
2711 else if(command == TOSERVER_RECEIVED_MEDIA) {
2712 getClient(peer_id)->definitions_sent = true;
2714 else if(command == TOSERVER_INTERACT)
2716 std::string datastring((char*)&data[2], datasize-2);
2717 std::istringstream is(datastring, std::ios_base::binary);
2723 [5] u32 length of the next item
2724 [9] serialized PointedThing
2726 0: start digging (from undersurface) or use
2727 1: stop digging (all parameters ignored)
2728 2: digging completed
2729 3: place block or item (to abovesurface)
2732 u8 action = readU8(is);
2733 u16 item_i = readU16(is);
2734 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2735 PointedThing pointed;
2736 pointed.deSerialize(tmp_is);
2738 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2739 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2743 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2744 <<" tried to interact, but is dead!"<<std::endl;
2748 v3f player_pos = playersao->getLastGoodPosition();
2750 // Update wielded item
2751 playersao->setWieldIndex(item_i);
2753 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2754 v3s16 p_under = pointed.node_undersurface;
2755 v3s16 p_above = pointed.node_abovesurface;
2757 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2758 ServerActiveObject *pointed_object = NULL;
2759 if(pointed.type == POINTEDTHING_OBJECT)
2761 pointed_object = m_env->getActiveObject(pointed.object_id);
2762 if(pointed_object == NULL)
2764 verbosestream<<"TOSERVER_INTERACT: "
2765 "pointed object is NULL"<<std::endl;
2771 v3f pointed_pos_under = player_pos;
2772 v3f pointed_pos_above = player_pos;
2773 if(pointed.type == POINTEDTHING_NODE)
2775 pointed_pos_under = intToFloat(p_under, BS);
2776 pointed_pos_above = intToFloat(p_above, BS);
2778 else if(pointed.type == POINTEDTHING_OBJECT)
2780 pointed_pos_under = pointed_object->getBasePosition();
2781 pointed_pos_above = pointed_pos_under;
2785 Check that target is reasonably close
2786 (only when digging or placing things)
2788 if(action == 0 || action == 2 || action == 3)
2790 float d = player_pos.getDistanceFrom(pointed_pos_under);
2791 float max_d = BS * 14; // Just some large enough value
2793 actionstream<<"Player "<<player->getName()
2794 <<" tried to access "<<pointed.dump()
2796 <<"d="<<d<<", max_d="<<max_d
2797 <<". ignoring."<<std::endl;
2798 // Re-send block to revert change on client-side
2799 RemoteClient *client = getClient(peer_id);
2800 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2801 client->SetBlockNotSent(blockpos);
2808 Make sure the player is allowed to do it
2810 if(!checkPriv(player->getName(), "interact"))
2812 actionstream<<player->getName()<<" attempted to interact with "
2813 <<pointed.dump()<<" without 'interact' privilege"
2815 // Re-send block to revert change on client-side
2816 RemoteClient *client = getClient(peer_id);
2817 // Digging completed -> under
2819 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2820 client->SetBlockNotSent(blockpos);
2822 // Placement -> above
2824 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2825 client->SetBlockNotSent(blockpos);
2831 If something goes wrong, this player is to blame
2833 RollbackScopeActor rollback_scope(m_rollback,
2834 std::string("player:")+player->getName());
2837 0: start digging or punch object
2841 if(pointed.type == POINTEDTHING_NODE)
2844 NOTE: This can be used in the future to check if
2845 somebody is cheating, by checking the timing.
2847 MapNode n(CONTENT_IGNORE);
2850 n = m_env->getMap().getNode(p_under);
2852 catch(InvalidPositionException &e)
2854 infostream<<"Server: Not punching: Node not found."
2855 <<" Adding block to emerge queue."
2857 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2859 if(n.getContent() != CONTENT_IGNORE)
2860 m_script->node_on_punch(p_under, n, playersao);
2862 playersao->noCheatDigStart(p_under);
2864 else if(pointed.type == POINTEDTHING_OBJECT)
2866 // Skip if object has been removed
2867 if(pointed_object->m_removed)
2870 actionstream<<player->getName()<<" punches object "
2871 <<pointed.object_id<<": "
2872 <<pointed_object->getDescription()<<std::endl;
2874 ItemStack punchitem = playersao->getWieldedItem();
2875 ToolCapabilities toolcap =
2876 punchitem.getToolCapabilities(m_itemdef);
2877 v3f dir = (pointed_object->getBasePosition() -
2878 (player->getPosition() + player->getEyeOffset())
2880 float time_from_last_punch =
2881 playersao->resetTimeFromLastPunch();
2882 pointed_object->punch(dir, &toolcap, playersao,
2883 time_from_last_punch);
2891 else if(action == 1)
2896 2: Digging completed
2898 else if(action == 2)
2900 // Only digging of nodes
2901 if(pointed.type == POINTEDTHING_NODE)
2903 MapNode n(CONTENT_IGNORE);
2906 n = m_env->getMap().getNode(p_under);
2908 catch(InvalidPositionException &e)
2910 infostream<<"Server: Not finishing digging: Node not found."
2911 <<" Adding block to emerge queue."
2913 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2916 /* Cheat prevention */
2917 bool is_valid_dig = true;
2918 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2920 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2921 float nocheat_t = playersao->getNoCheatDigTime();
2922 playersao->noCheatDigEnd();
2923 // If player didn't start digging this, ignore dig
2924 if(nocheat_p != p_under){
2925 infostream<<"Server: NoCheat: "<<player->getName()
2926 <<" started digging "
2927 <<PP(nocheat_p)<<" and completed digging "
2928 <<PP(p_under)<<"; not digging."<<std::endl;
2929 is_valid_dig = false;
2931 // Get player's wielded item
2932 ItemStack playeritem;
2933 InventoryList *mlist = playersao->getInventory()->getList("main");
2935 playeritem = mlist->getItem(playersao->getWieldIndex());
2936 ToolCapabilities playeritem_toolcap =
2937 playeritem.getToolCapabilities(m_itemdef);
2938 // Get diggability and expected digging time
2939 DigParams params = getDigParams(m_nodedef->get(n).groups,
2940 &playeritem_toolcap);
2941 // If can't dig, try hand
2942 if(!params.diggable){
2943 const ItemDefinition &hand = m_itemdef->get("");
2944 const ToolCapabilities *tp = hand.tool_capabilities;
2946 params = getDigParams(m_nodedef->get(n).groups, tp);
2948 // If can't dig, ignore dig
2949 if(!params.diggable){
2950 infostream<<"Server: NoCheat: "<<player->getName()
2951 <<" completed digging "<<PP(p_under)
2952 <<", which is not diggable with tool. not digging."
2954 is_valid_dig = false;
2956 // If time is considerably too short, ignore dig
2957 // Check time only for medium and slow timed digs
2958 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2959 infostream<<"Server: NoCheat: "<<player->getName()
2960 <<" completed digging "
2961 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2962 <<params.time<<"s; not digging."<<std::endl;
2963 is_valid_dig = false;
2967 /* Actually dig node */
2969 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2970 m_script->node_on_dig(p_under, n, playersao);
2972 // Send unusual result (that is, node not being removed)
2973 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2975 // Re-send block to revert change on client-side
2976 RemoteClient *client = getClient(peer_id);
2977 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2978 client->SetBlockNotSent(blockpos);
2984 3: place block or right-click object
2986 else if(action == 3)
2988 ItemStack item = playersao->getWieldedItem();
2990 // Reset build time counter
2991 if(pointed.type == POINTEDTHING_NODE &&
2992 item.getDefinition(m_itemdef).type == ITEM_NODE)
2993 getClient(peer_id)->m_time_from_building = 0.0;
2995 if(pointed.type == POINTEDTHING_OBJECT)
2997 // Right click object
2999 // Skip if object has been removed
3000 if(pointed_object->m_removed)
3003 actionstream<<player->getName()<<" right-clicks object "
3004 <<pointed.object_id<<": "
3005 <<pointed_object->getDescription()<<std::endl;
3008 pointed_object->rightClick(playersao);
3010 else if(m_script->item_OnPlace(
3011 item, playersao, pointed))
3013 // Placement was handled in lua
3015 // Apply returned ItemStack
3016 playersao->setWieldedItem(item);
3019 // If item has node placement prediction, always send the
3020 // blocks to make sure the client knows what exactly happened
3021 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3022 RemoteClient *client = getClient(peer_id);
3023 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3024 client->SetBlockNotSent(blockpos);
3025 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3026 if(blockpos2 != blockpos){
3027 client->SetBlockNotSent(blockpos2);
3035 else if(action == 4)
3037 ItemStack item = playersao->getWieldedItem();
3039 actionstream<<player->getName()<<" uses "<<item.name
3040 <<", pointing at "<<pointed.dump()<<std::endl;
3042 if(m_script->item_OnUse(
3043 item, playersao, pointed))
3045 // Apply returned ItemStack
3046 playersao->setWieldedItem(item);
3053 Catch invalid actions
3057 infostream<<"WARNING: Server: Invalid action "
3058 <<action<<std::endl;
3061 else if(command == TOSERVER_REMOVED_SOUNDS)
3063 std::string datastring((char*)&data[2], datasize-2);
3064 std::istringstream is(datastring, std::ios_base::binary);
3066 int num = readU16(is);
3067 for(int k=0; k<num; k++){
3068 s32 id = readS32(is);
3069 std::map<s32, ServerPlayingSound>::iterator i =
3070 m_playing_sounds.find(id);
3071 if(i == m_playing_sounds.end())
3073 ServerPlayingSound &psound = i->second;
3074 psound.clients.erase(peer_id);
3075 if(psound.clients.size() == 0)
3076 m_playing_sounds.erase(i++);
3079 else if(command == TOSERVER_NODEMETA_FIELDS)
3081 std::string datastring((char*)&data[2], datasize-2);
3082 std::istringstream is(datastring, std::ios_base::binary);
3084 v3s16 p = readV3S16(is);
3085 std::string formname = deSerializeString(is);
3086 int num = readU16(is);
3087 std::map<std::string, std::string> fields;
3088 for(int k=0; k<num; k++){
3089 std::string fieldname = deSerializeString(is);
3090 std::string fieldvalue = deSerializeLongString(is);
3091 fields[fieldname] = fieldvalue;
3094 // If something goes wrong, this player is to blame
3095 RollbackScopeActor rollback_scope(m_rollback,
3096 std::string("player:")+player->getName());
3098 // Check the target node for rollback data; leave others unnoticed
3099 RollbackNode rn_old(&m_env->getMap(), p, this);
3101 m_script->node_on_receive_fields(p, formname, fields,playersao);
3103 // Report rollback data
3104 RollbackNode rn_new(&m_env->getMap(), p, this);
3105 if(rollback() && rn_new != rn_old){
3106 RollbackAction action;
3107 action.setSetNode(p, rn_old, rn_new);
3108 rollback()->reportAction(action);
3111 else if(command == TOSERVER_INVENTORY_FIELDS)
3113 std::string datastring((char*)&data[2], datasize-2);
3114 std::istringstream is(datastring, std::ios_base::binary);
3116 std::string formname = deSerializeString(is);
3117 int num = readU16(is);
3118 std::map<std::string, std::string> fields;
3119 for(int k=0; k<num; k++){
3120 std::string fieldname = deSerializeString(is);
3121 std::string fieldvalue = deSerializeLongString(is);
3122 fields[fieldname] = fieldvalue;
3125 m_script->on_playerReceiveFields(playersao, formname, fields);
3129 infostream<<"Server::ProcessData(): Ignoring "
3130 "unknown command "<<command<<std::endl;
3134 catch(SendFailedException &e)
3136 errorstream<<"Server::ProcessData(): SendFailedException: "
3142 void Server::onMapEditEvent(MapEditEvent *event)
3144 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3145 if(m_ignore_map_edit_events)
3147 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3149 MapEditEvent *e = event->clone();
3150 m_unsent_map_edit_queue.push_back(e);
3153 Inventory* Server::getInventory(const InventoryLocation &loc)
3156 case InventoryLocation::UNDEFINED:
3159 case InventoryLocation::CURRENT_PLAYER:
3162 case InventoryLocation::PLAYER:
3164 Player *player = m_env->getPlayer(loc.name.c_str());
3167 PlayerSAO *playersao = player->getPlayerSAO();
3170 return playersao->getInventory();
3173 case InventoryLocation::NODEMETA:
3175 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3178 return meta->getInventory();
3181 case InventoryLocation::DETACHED:
3183 if(m_detached_inventories.count(loc.name) == 0)
3185 return m_detached_inventories[loc.name];
3193 void Server::setInventoryModified(const InventoryLocation &loc)
3196 case InventoryLocation::UNDEFINED:
3199 case InventoryLocation::PLAYER:
3201 Player *player = m_env->getPlayer(loc.name.c_str());
3204 PlayerSAO *playersao = player->getPlayerSAO();
3207 playersao->m_inventory_not_sent = true;
3208 playersao->m_wielded_item_not_sent = true;
3211 case InventoryLocation::NODEMETA:
3213 v3s16 blockpos = getNodeBlockPos(loc.p);
3215 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3217 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3219 setBlockNotSent(blockpos);
3222 case InventoryLocation::DETACHED:
3224 sendDetachedInventoryToAll(loc.name);
3232 //std::list<PlayerInfo> Server::getPlayerInfo()
3234 // DSTACK(__FUNCTION_NAME);
3235 // JMutexAutoLock envlock(m_env_mutex);
3236 // JMutexAutoLock conlock(m_con_mutex);
3238 // std::list<PlayerInfo> list;
3240 // std::list<Player*> players = m_env->getPlayers();
3242 // std::list<Player*>::iterator i;
3243 // for(i = players.begin();
3244 // i != players.end(); ++i)
3248 // Player *player = *i;
3251 // // Copy info from connection to info struct
3252 // info.id = player->peer_id;
3253 // info.address = m_con.GetPeerAddress(player->peer_id);
3254 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3256 // catch(con::PeerNotFoundException &e)
3258 // // Set dummy peer info
3260 // info.address = Address(0,0,0,0,0);
3261 // info.avg_rtt = 0.0;
3264 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3265 // info.position = player->getPosition();
3267 // list.push_back(info);
3274 void Server::peerAdded(con::Peer *peer)
3276 DSTACK(__FUNCTION_NAME);
3277 verbosestream<<"Server::peerAdded(): peer->id="
3278 <<peer->id<<std::endl;
3281 c.type = PEER_ADDED;
3282 c.peer_id = peer->id;
3284 m_peer_change_queue.push_back(c);
3287 void Server::deletingPeer(con::Peer *peer, bool timeout)
3289 DSTACK(__FUNCTION_NAME);
3290 verbosestream<<"Server::deletingPeer(): peer->id="
3291 <<peer->id<<", timeout="<<timeout<<std::endl;
3294 c.type = PEER_REMOVED;
3295 c.peer_id = peer->id;
3296 c.timeout = timeout;
3297 m_peer_change_queue.push_back(c);
3304 void Server::SendMovement(con::Connection &con, u16 peer_id)
3306 DSTACK(__FUNCTION_NAME);
3307 std::ostringstream os(std::ios_base::binary);
3309 writeU16(os, TOCLIENT_MOVEMENT);
3310 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3311 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3312 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3313 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3314 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3315 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3316 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3317 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3318 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3319 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3320 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3321 writeF1000(os, g_settings->getFloat("movement_gravity"));
3324 std::string s = os.str();
3325 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3327 con.Send(peer_id, 0, data, true);
3330 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3332 DSTACK(__FUNCTION_NAME);
3333 std::ostringstream os(std::ios_base::binary);
3335 writeU16(os, TOCLIENT_HP);
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::SendBreath(con::Connection &con, u16 peer_id, u16 breath)
3347 DSTACK(__FUNCTION_NAME);
3348 std::ostringstream os(std::ios_base::binary);
3350 writeU16(os, TOCLIENT_BREATH);
3351 writeU16(os, breath);
3354 std::string s = os.str();
3355 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3357 con.Send(peer_id, 0, data, true);
3360 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3361 const std::wstring &reason)
3363 DSTACK(__FUNCTION_NAME);
3364 std::ostringstream os(std::ios_base::binary);
3366 writeU16(os, TOCLIENT_ACCESS_DENIED);
3367 os<<serializeWideString(reason);
3370 std::string s = os.str();
3371 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3373 con.Send(peer_id, 0, data, true);
3376 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3377 bool set_camera_point_target, v3f camera_point_target)
3379 DSTACK(__FUNCTION_NAME);
3380 std::ostringstream os(std::ios_base::binary);
3382 writeU16(os, TOCLIENT_DEATHSCREEN);
3383 writeU8(os, set_camera_point_target);
3384 writeV3F1000(os, camera_point_target);
3387 std::string s = os.str();
3388 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3390 con.Send(peer_id, 0, data, true);
3393 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3394 IItemDefManager *itemdef, u16 protocol_version)
3396 DSTACK(__FUNCTION_NAME);
3397 std::ostringstream os(std::ios_base::binary);
3401 u32 length of the next item
3402 zlib-compressed serialized ItemDefManager
3404 writeU16(os, TOCLIENT_ITEMDEF);
3405 std::ostringstream tmp_os(std::ios::binary);
3406 itemdef->serialize(tmp_os, protocol_version);
3407 std::ostringstream tmp_os2(std::ios::binary);
3408 compressZlib(tmp_os.str(), tmp_os2);
3409 os<<serializeLongString(tmp_os2.str());
3412 std::string s = os.str();
3413 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3414 <<"): size="<<s.size()<<std::endl;
3415 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3417 con.Send(peer_id, 0, data, true);
3420 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3421 INodeDefManager *nodedef, u16 protocol_version)
3423 DSTACK(__FUNCTION_NAME);
3424 std::ostringstream os(std::ios_base::binary);
3428 u32 length of the next item
3429 zlib-compressed serialized NodeDefManager
3431 writeU16(os, TOCLIENT_NODEDEF);
3432 std::ostringstream tmp_os(std::ios::binary);
3433 nodedef->serialize(tmp_os, protocol_version);
3434 std::ostringstream tmp_os2(std::ios::binary);
3435 compressZlib(tmp_os.str(), tmp_os2);
3436 os<<serializeLongString(tmp_os2.str());
3439 std::string s = os.str();
3440 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3441 <<"): size="<<s.size()<<std::endl;
3442 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3444 con.Send(peer_id, 0, data, true);
3448 Non-static send methods
3451 void Server::SendInventory(u16 peer_id)
3453 DSTACK(__FUNCTION_NAME);
3455 PlayerSAO *playersao = getPlayerSAO(peer_id);
3458 playersao->m_inventory_not_sent = false;
3464 std::ostringstream os;
3465 playersao->getInventory()->serialize(os);
3467 std::string s = os.str();
3469 SharedBuffer<u8> data(s.size()+2);
3470 writeU16(&data[0], TOCLIENT_INVENTORY);
3471 memcpy(&data[2], s.c_str(), s.size());
3474 m_con.Send(peer_id, 0, data, true);
3477 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3479 DSTACK(__FUNCTION_NAME);
3481 std::ostringstream os(std::ios_base::binary);
3485 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3486 os.write((char*)buf, 2);
3489 writeU16(buf, message.size());
3490 os.write((char*)buf, 2);
3493 for(u32 i=0; i<message.size(); i++)
3497 os.write((char*)buf, 2);
3501 std::string s = os.str();
3502 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504 m_con.Send(peer_id, 0, data, true);
3507 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3508 const std::string formname)
3510 DSTACK(__FUNCTION_NAME);
3512 std::ostringstream os(std::ios_base::binary);
3516 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3517 os.write((char*)buf, 2);
3518 os<<serializeLongString(formspec);
3519 os<<serializeString(formname);
3522 std::string s = os.str();
3523 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3525 m_con.Send(peer_id, 0, data, true);
3528 // Spawns a particle on peer with peer_id
3529 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3530 float expirationtime, float size, bool collisiondetection,
3531 std::string texture)
3533 DSTACK(__FUNCTION_NAME);
3535 std::ostringstream os(std::ios_base::binary);
3536 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3537 writeV3F1000(os, pos);
3538 writeV3F1000(os, velocity);
3539 writeV3F1000(os, acceleration);
3540 writeF1000(os, expirationtime);
3541 writeF1000(os, size);
3542 writeU8(os, collisiondetection);
3543 os<<serializeLongString(texture);
3546 std::string s = os.str();
3547 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3549 m_con.Send(peer_id, 0, data, true);
3552 // Spawns a particle on all peers
3553 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3554 float expirationtime, float size, bool collisiondetection,
3555 std::string texture)
3557 for(std::map<u16, RemoteClient*>::iterator
3558 i = m_clients.begin();
3559 i != m_clients.end(); i++)
3561 // Get client and check that it is valid
3562 RemoteClient *client = i->second;
3563 assert(client->peer_id == i->first);
3564 if(client->serialization_version == SER_FMT_VER_INVALID)
3567 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3568 expirationtime, size, collisiondetection, texture);
3572 // Adds a ParticleSpawner on peer with peer_id
3573 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3574 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3575 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3577 DSTACK(__FUNCTION_NAME);
3579 std::ostringstream os(std::ios_base::binary);
3580 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3582 writeU16(os, amount);
3583 writeF1000(os, spawntime);
3584 writeV3F1000(os, minpos);
3585 writeV3F1000(os, maxpos);
3586 writeV3F1000(os, minvel);
3587 writeV3F1000(os, maxvel);
3588 writeV3F1000(os, minacc);
3589 writeV3F1000(os, maxacc);
3590 writeF1000(os, minexptime);
3591 writeF1000(os, maxexptime);
3592 writeF1000(os, minsize);
3593 writeF1000(os, maxsize);
3594 writeU8(os, collisiondetection);
3595 os<<serializeLongString(texture);
3599 std::string s = os.str();
3600 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3602 m_con.Send(peer_id, 0, data, true);
3605 // Adds a ParticleSpawner on all peers
3606 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3607 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3608 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3610 for(std::map<u16, RemoteClient*>::iterator
3611 i = m_clients.begin();
3612 i != m_clients.end(); i++)
3614 // Get client and check that it is valid
3615 RemoteClient *client = i->second;
3616 assert(client->peer_id == i->first);
3617 if(client->serialization_version == SER_FMT_VER_INVALID)
3620 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3621 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3622 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3626 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3628 DSTACK(__FUNCTION_NAME);
3630 std::ostringstream os(std::ios_base::binary);
3631 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3636 std::string s = os.str();
3637 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3639 m_con.Send(peer_id, 0, data, true);
3642 void Server::SendDeleteParticleSpawnerAll(u32 id)
3644 for(std::map<u16, RemoteClient*>::iterator
3645 i = m_clients.begin();
3646 i != m_clients.end(); i++)
3648 // Get client and check that it is valid
3649 RemoteClient *client = i->second;
3650 assert(client->peer_id == i->first);
3651 if(client->serialization_version == SER_FMT_VER_INVALID)
3654 SendDeleteParticleSpawner(client->peer_id, id);
3658 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3660 std::ostringstream os(std::ios_base::binary);
3663 writeU16(os, TOCLIENT_HUDADD);
3665 writeU8(os, (u8)form->type);
3666 writeV2F1000(os, form->pos);
3667 os << serializeString(form->name);
3668 writeV2F1000(os, form->scale);
3669 os << serializeString(form->text);
3670 writeU32(os, form->number);
3671 writeU32(os, form->item);
3672 writeU32(os, form->dir);
3673 writeV2F1000(os, form->align);
3674 writeV2F1000(os, form->offset);
3677 std::string s = os.str();
3678 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3680 m_con.Send(peer_id, 0, data, true);
3683 void Server::SendHUDRemove(u16 peer_id, u32 id)
3685 std::ostringstream os(std::ios_base::binary);
3688 writeU16(os, TOCLIENT_HUDRM);
3692 std::string s = os.str();
3693 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3695 m_con.Send(peer_id, 0, data, true);
3698 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3700 std::ostringstream os(std::ios_base::binary);
3703 writeU16(os, TOCLIENT_HUDCHANGE);
3705 writeU8(os, (u8)stat);
3708 case HUD_STAT_SCALE:
3709 case HUD_STAT_ALIGN:
3710 case HUD_STAT_OFFSET:
3711 writeV2F1000(os, *(v2f *)value);
3715 os << serializeString(*(std::string *)value);
3717 case HUD_STAT_NUMBER:
3721 writeU32(os, *(u32 *)value);
3726 std::string s = os.str();
3727 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3729 m_con.Send(peer_id, 0, data, true);
3732 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3734 std::ostringstream os(std::ios_base::binary);
3737 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3738 writeU32(os, flags);
3742 std::string s = os.str();
3743 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3745 m_con.Send(peer_id, 0, data, true);
3748 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3750 std::ostringstream os(std::ios_base::binary);
3753 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3754 writeU16(os, param);
3755 os<<serializeString(value);
3758 std::string s = os.str();
3759 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3761 m_con.Send(peer_id, 0, data, true);
3764 void Server::BroadcastChatMessage(const std::wstring &message)
3766 for(std::map<u16, RemoteClient*>::iterator
3767 i = m_clients.begin();
3768 i != m_clients.end(); ++i)
3770 // Get client and check that it is valid
3771 RemoteClient *client = i->second;
3772 assert(client->peer_id == i->first);
3773 if(client->serialization_version == SER_FMT_VER_INVALID)
3776 SendChatMessage(client->peer_id, message);
3780 void Server::SendPlayerHP(u16 peer_id)
3782 DSTACK(__FUNCTION_NAME);
3783 PlayerSAO *playersao = getPlayerSAO(peer_id);
3785 playersao->m_hp_not_sent = false;
3786 SendHP(m_con, peer_id, playersao->getHP());
3789 void Server::SendPlayerBreath(u16 peer_id)
3791 DSTACK(__FUNCTION_NAME);
3792 PlayerSAO *playersao = getPlayerSAO(peer_id);
3794 playersao->m_breath_not_sent = false;
3795 SendBreath(m_con, peer_id, playersao->getBreath());
3798 void Server::SendMovePlayer(u16 peer_id)
3800 DSTACK(__FUNCTION_NAME);
3801 Player *player = m_env->getPlayer(peer_id);
3804 std::ostringstream os(std::ios_base::binary);
3805 writeU16(os, TOCLIENT_MOVE_PLAYER);
3806 writeV3F1000(os, player->getPosition());
3807 writeF1000(os, player->getPitch());
3808 writeF1000(os, player->getYaw());
3811 v3f pos = player->getPosition();
3812 f32 pitch = player->getPitch();
3813 f32 yaw = player->getYaw();
3814 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3815 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3822 std::string s = os.str();
3823 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3825 m_con.Send(peer_id, 0, data, true);
3828 void Server::SendPlayerPrivileges(u16 peer_id)
3830 Player *player = m_env->getPlayer(peer_id);
3832 if(player->peer_id == PEER_ID_INEXISTENT)
3835 std::set<std::string> privs;
3836 m_script->getAuth(player->getName(), NULL, &privs);
3838 std::ostringstream os(std::ios_base::binary);
3839 writeU16(os, TOCLIENT_PRIVILEGES);
3840 writeU16(os, privs.size());
3841 for(std::set<std::string>::const_iterator i = privs.begin();
3842 i != privs.end(); i++){
3843 os<<serializeString(*i);
3847 std::string s = os.str();
3848 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3850 m_con.Send(peer_id, 0, data, true);
3853 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3855 Player *player = m_env->getPlayer(peer_id);
3857 if(player->peer_id == PEER_ID_INEXISTENT)
3860 std::ostringstream os(std::ios_base::binary);
3861 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3862 os<<serializeLongString(player->inventory_formspec);
3865 std::string s = os.str();
3866 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3868 m_con.Send(peer_id, 0, data, true);
3871 s32 Server::playSound(const SimpleSoundSpec &spec,
3872 const ServerSoundParams ¶ms)
3874 // Find out initial position of sound
3875 bool pos_exists = false;
3876 v3f pos = params.getPos(m_env, &pos_exists);
3877 // If position is not found while it should be, cancel sound
3878 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3880 // Filter destination clients
3881 std::set<RemoteClient*> dst_clients;
3882 if(params.to_player != "")
3884 Player *player = m_env->getPlayer(params.to_player.c_str());
3886 infostream<<"Server::playSound: Player \""<<params.to_player
3887 <<"\" not found"<<std::endl;
3890 if(player->peer_id == PEER_ID_INEXISTENT){
3891 infostream<<"Server::playSound: Player \""<<params.to_player
3892 <<"\" not connected"<<std::endl;
3895 RemoteClient *client = getClient(player->peer_id);
3896 dst_clients.insert(client);
3900 for(std::map<u16, RemoteClient*>::iterator
3901 i = m_clients.begin(); i != m_clients.end(); ++i)
3903 RemoteClient *client = i->second;
3904 Player *player = m_env->getPlayer(client->peer_id);
3908 if(player->getPosition().getDistanceFrom(pos) >
3909 params.max_hear_distance)
3912 dst_clients.insert(client);
3915 if(dst_clients.size() == 0)
3918 s32 id = m_next_sound_id++;
3919 // The sound will exist as a reference in m_playing_sounds
3920 m_playing_sounds[id] = ServerPlayingSound();
3921 ServerPlayingSound &psound = m_playing_sounds[id];
3922 psound.params = params;
3923 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3924 i != dst_clients.end(); i++)
3925 psound.clients.insert((*i)->peer_id);
3927 std::ostringstream os(std::ios_base::binary);
3928 writeU16(os, TOCLIENT_PLAY_SOUND);
3930 os<<serializeString(spec.name);
3931 writeF1000(os, spec.gain * params.gain);
3932 writeU8(os, params.type);
3933 writeV3F1000(os, pos);
3934 writeU16(os, params.object);
3935 writeU8(os, params.loop);
3937 std::string s = os.str();
3938 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3940 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3941 i != dst_clients.end(); i++){
3943 m_con.Send((*i)->peer_id, 0, data, true);
3947 void Server::stopSound(s32 handle)
3949 // Get sound reference
3950 std::map<s32, ServerPlayingSound>::iterator i =
3951 m_playing_sounds.find(handle);
3952 if(i == m_playing_sounds.end())
3954 ServerPlayingSound &psound = i->second;
3956 std::ostringstream os(std::ios_base::binary);
3957 writeU16(os, TOCLIENT_STOP_SOUND);
3958 writeS32(os, handle);
3960 std::string s = os.str();
3961 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3963 for(std::set<u16>::iterator i = psound.clients.begin();
3964 i != psound.clients.end(); i++){
3966 m_con.Send(*i, 0, data, true);
3968 // Remove sound reference
3969 m_playing_sounds.erase(i);
3972 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3973 std::list<u16> *far_players, float far_d_nodes)
3975 float maxd = far_d_nodes*BS;
3976 v3f p_f = intToFloat(p, BS);
3980 SharedBuffer<u8> reply(replysize);
3981 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3982 writeS16(&reply[2], p.X);
3983 writeS16(&reply[4], p.Y);
3984 writeS16(&reply[6], p.Z);
3986 for(std::map<u16, RemoteClient*>::iterator
3987 i = m_clients.begin();
3988 i != m_clients.end(); ++i)
3990 // Get client and check that it is valid
3991 RemoteClient *client = i->second;
3992 assert(client->peer_id == i->first);
3993 if(client->serialization_version == SER_FMT_VER_INVALID)
3996 // Don't send if it's the same one
3997 if(client->peer_id == ignore_id)
4003 Player *player = m_env->getPlayer(client->peer_id);
4006 // If player is far away, only set modified blocks not sent
4007 v3f player_pos = player->getPosition();
4008 if(player_pos.getDistanceFrom(p_f) > maxd)
4010 far_players->push_back(client->peer_id);
4017 m_con.Send(client->peer_id, 0, reply, true);
4021 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4022 std::list<u16> *far_players, float far_d_nodes)
4024 float maxd = far_d_nodes*BS;
4025 v3f p_f = intToFloat(p, BS);
4027 for(std::map<u16, RemoteClient*>::iterator
4028 i = m_clients.begin();
4029 i != m_clients.end(); ++i)
4031 // Get client and check that it is valid
4032 RemoteClient *client = i->second;
4033 assert(client->peer_id == i->first);
4034 if(client->serialization_version == SER_FMT_VER_INVALID)
4037 // Don't send if it's the same one
4038 if(client->peer_id == ignore_id)
4044 Player *player = m_env->getPlayer(client->peer_id);
4047 // If player is far away, only set modified blocks not sent
4048 v3f player_pos = player->getPosition();
4049 if(player_pos.getDistanceFrom(p_f) > maxd)
4051 far_players->push_back(client->peer_id);
4058 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4059 SharedBuffer<u8> reply(replysize);
4060 writeU16(&reply[0], TOCLIENT_ADDNODE);
4061 writeS16(&reply[2], p.X);
4062 writeS16(&reply[4], p.Y);
4063 writeS16(&reply[6], p.Z);
4064 n.serialize(&reply[8], client->serialization_version);
4067 m_con.Send(client->peer_id, 0, reply, true);
4071 void Server::setBlockNotSent(v3s16 p)
4073 for(std::map<u16, RemoteClient*>::iterator
4074 i = m_clients.begin();
4075 i != m_clients.end(); ++i)
4077 RemoteClient *client = i->second;
4078 client->SetBlockNotSent(p);
4082 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4084 DSTACK(__FUNCTION_NAME);
4086 v3s16 p = block->getPos();
4090 bool completely_air = true;
4091 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4092 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4093 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4095 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4097 completely_air = false;
4098 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4103 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4105 infostream<<"[completely air] ";
4106 infostream<<std::endl;
4110 Create a packet with the block in the right format
4113 std::ostringstream os(std::ios_base::binary);
4114 block->serialize(os, ver, false);
4115 std::string s = os.str();
4116 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4118 u32 replysize = 8 + blockdata.getSize();
4119 SharedBuffer<u8> reply(replysize);
4120 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4121 writeS16(&reply[2], p.X);
4122 writeS16(&reply[4], p.Y);
4123 writeS16(&reply[6], p.Z);
4124 memcpy(&reply[8], *blockdata, blockdata.getSize());
4126 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4127 <<": \tpacket size: "<<replysize<<std::endl;*/
4132 m_con.Send(peer_id, 1, reply, true);
4135 void Server::SendBlocks(float dtime)
4137 DSTACK(__FUNCTION_NAME);
4139 JMutexAutoLock envlock(m_env_mutex);
4140 JMutexAutoLock conlock(m_con_mutex);
4142 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4144 std::vector<PrioritySortedBlockTransfer> queue;
4146 s32 total_sending = 0;
4149 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4151 for(std::map<u16, RemoteClient*>::iterator
4152 i = m_clients.begin();
4153 i != m_clients.end(); ++i)
4155 RemoteClient *client = i->second;
4156 assert(client->peer_id == i->first);
4158 // If definitions and textures have not been sent, don't
4159 // send MapBlocks either
4160 if(!client->definitions_sent)
4163 total_sending += client->SendingCount();
4165 if(client->serialization_version == SER_FMT_VER_INVALID)
4168 client->GetNextBlocks(this, dtime, queue);
4173 // Lowest priority number comes first.
4174 // Lowest is most important.
4175 std::sort(queue.begin(), queue.end());
4177 for(u32 i=0; i<queue.size(); i++)
4179 //TODO: Calculate limit dynamically
4180 if(total_sending >= g_settings->getS32
4181 ("max_simultaneous_block_sends_server_total"))
4184 PrioritySortedBlockTransfer q = queue[i];
4186 MapBlock *block = NULL;
4189 block = m_env->getMap().getBlockNoCreate(q.pos);
4191 catch(InvalidPositionException &e)
4196 RemoteClient *client = getClient(q.peer_id);
4198 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4200 client->SentBlock(q.pos);
4206 void Server::fillMediaCache()
4208 DSTACK(__FUNCTION_NAME);
4210 infostream<<"Server: Calculating media file checksums"<<std::endl;
4212 // Collect all media file paths
4213 std::list<std::string> paths;
4214 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4215 i != m_mods.end(); i++){
4216 const ModSpec &mod = *i;
4217 paths.push_back(mod.path + DIR_DELIM + "textures");
4218 paths.push_back(mod.path + DIR_DELIM + "sounds");
4219 paths.push_back(mod.path + DIR_DELIM + "media");
4220 paths.push_back(mod.path + DIR_DELIM + "models");
4222 std::string path_all = "textures";
4223 paths.push_back(path_all + DIR_DELIM + "all");
4225 // Collect media file information from paths into cache
4226 for(std::list<std::string>::iterator i = paths.begin();
4227 i != paths.end(); i++)
4229 std::string mediapath = *i;
4230 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4231 for(u32 j=0; j<dirlist.size(); j++){
4232 if(dirlist[j].dir) // Ignode dirs
4234 std::string filename = dirlist[j].name;
4235 // If name contains illegal characters, ignore the file
4236 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4237 infostream<<"Server: ignoring illegal file name: \""
4238 <<filename<<"\""<<std::endl;
4241 // If name is not in a supported format, ignore it
4242 const char *supported_ext[] = {
4243 ".png", ".jpg", ".bmp", ".tga",
4244 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4246 ".x", ".b3d", ".md2", ".obj",
4249 if(removeStringEnd(filename, supported_ext) == ""){
4250 infostream<<"Server: ignoring unsupported file extension: \""
4251 <<filename<<"\""<<std::endl;
4254 // Ok, attempt to load the file and add to cache
4255 std::string filepath = mediapath + DIR_DELIM + filename;
4257 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4258 if(fis.good() == false){
4259 errorstream<<"Server::fillMediaCache(): Could not open \""
4260 <<filename<<"\" for reading"<<std::endl;
4263 std::ostringstream tmp_os(std::ios_base::binary);
4267 fis.read(buf, 1024);
4268 std::streamsize len = fis.gcount();
4269 tmp_os.write(buf, len);
4278 errorstream<<"Server::fillMediaCache(): Failed to read \""
4279 <<filename<<"\""<<std::endl;
4282 if(tmp_os.str().length() == 0){
4283 errorstream<<"Server::fillMediaCache(): Empty file \""
4284 <<filepath<<"\""<<std::endl;
4289 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4291 unsigned char *digest = sha1.getDigest();
4292 std::string sha1_base64 = base64_encode(digest, 20);
4293 std::string sha1_hex = hex_encode((char*)digest, 20);
4297 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4298 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4303 struct SendableMediaAnnouncement
4306 std::string sha1_digest;
4308 SendableMediaAnnouncement(const std::string name_="",
4309 const std::string sha1_digest_=""):
4311 sha1_digest(sha1_digest_)
4315 void Server::sendMediaAnnouncement(u16 peer_id)
4317 DSTACK(__FUNCTION_NAME);
4319 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4322 std::list<SendableMediaAnnouncement> file_announcements;
4324 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4325 i != m_media.end(); i++){
4327 file_announcements.push_back(
4328 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4332 std::ostringstream os(std::ios_base::binary);
4340 u16 length of sha1_digest
4345 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4346 writeU16(os, file_announcements.size());
4348 for(std::list<SendableMediaAnnouncement>::iterator
4349 j = file_announcements.begin();
4350 j != file_announcements.end(); ++j){
4351 os<<serializeString(j->name);
4352 os<<serializeString(j->sha1_digest);
4354 os<<serializeString(g_settings->get("remote_media"));
4357 std::string s = os.str();
4358 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4361 m_con.Send(peer_id, 0, data, true);
4364 struct SendableMedia
4370 SendableMedia(const std::string &name_="", const std::string path_="",
4371 const std::string &data_=""):
4378 void Server::sendRequestedMedia(u16 peer_id,
4379 const std::list<MediaRequest> &tosend)
4381 DSTACK(__FUNCTION_NAME);
4383 verbosestream<<"Server::sendRequestedMedia(): "
4384 <<"Sending files to client"<<std::endl;
4388 // Put 5kB in one bunch (this is not accurate)
4389 u32 bytes_per_bunch = 5000;
4391 std::vector< std::list<SendableMedia> > file_bunches;
4392 file_bunches.push_back(std::list<SendableMedia>());
4394 u32 file_size_bunch_total = 0;
4396 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4397 i != tosend.end(); ++i)
4399 if(m_media.find(i->name) == m_media.end()){
4400 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4401 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4405 //TODO get path + name
4406 std::string tpath = m_media[(*i).name].path;
4409 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4410 if(fis.good() == false){
4411 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4412 <<tpath<<"\" for reading"<<std::endl;
4415 std::ostringstream tmp_os(std::ios_base::binary);
4419 fis.read(buf, 1024);
4420 std::streamsize len = fis.gcount();
4421 tmp_os.write(buf, len);
4422 file_size_bunch_total += len;
4431 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4432 <<(*i).name<<"\""<<std::endl;
4435 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4436 <<tname<<"\""<<std::endl;*/
4438 file_bunches[file_bunches.size()-1].push_back(
4439 SendableMedia((*i).name, tpath, tmp_os.str()));
4441 // Start next bunch if got enough data
4442 if(file_size_bunch_total >= bytes_per_bunch){
4443 file_bunches.push_back(std::list<SendableMedia>());
4444 file_size_bunch_total = 0;
4449 /* Create and send packets */
4451 u32 num_bunches = file_bunches.size();
4452 for(u32 i=0; i<num_bunches; i++)
4454 std::ostringstream os(std::ios_base::binary);
4458 u16 total number of texture bunches
4459 u16 index of this bunch
4460 u32 number of files in this bunch
4469 writeU16(os, TOCLIENT_MEDIA);
4470 writeU16(os, num_bunches);
4472 writeU32(os, file_bunches[i].size());
4474 for(std::list<SendableMedia>::iterator
4475 j = file_bunches[i].begin();
4476 j != file_bunches[i].end(); ++j){
4477 os<<serializeString(j->name);
4478 os<<serializeLongString(j->data);
4482 std::string s = os.str();
4483 verbosestream<<"Server::sendRequestedMedia(): bunch "
4484 <<i<<"/"<<num_bunches
4485 <<" files="<<file_bunches[i].size()
4486 <<" size=" <<s.size()<<std::endl;
4487 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4489 m_con.Send(peer_id, 0, data, true);
4493 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4495 if(m_detached_inventories.count(name) == 0){
4496 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4499 Inventory *inv = m_detached_inventories[name];
4501 std::ostringstream os(std::ios_base::binary);
4502 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4503 os<<serializeString(name);
4507 std::string s = os.str();
4508 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4510 m_con.Send(peer_id, 0, data, true);
4513 void Server::sendDetachedInventoryToAll(const std::string &name)
4515 DSTACK(__FUNCTION_NAME);
4517 for(std::map<u16, RemoteClient*>::iterator
4518 i = m_clients.begin();
4519 i != m_clients.end(); ++i){
4520 RemoteClient *client = i->second;
4521 sendDetachedInventory(name, client->peer_id);
4525 void Server::sendDetachedInventories(u16 peer_id)
4527 DSTACK(__FUNCTION_NAME);
4529 for(std::map<std::string, Inventory*>::iterator
4530 i = m_detached_inventories.begin();
4531 i != m_detached_inventories.end(); i++){
4532 const std::string &name = i->first;
4533 //Inventory *inv = i->second;
4534 sendDetachedInventory(name, peer_id);
4542 void Server::DiePlayer(u16 peer_id)
4544 DSTACK(__FUNCTION_NAME);
4546 PlayerSAO *playersao = getPlayerSAO(peer_id);
4549 infostream<<"Server::DiePlayer(): Player "
4550 <<playersao->getPlayer()->getName()
4551 <<" dies"<<std::endl;
4553 playersao->setHP(0);
4555 // Trigger scripted stuff
4556 m_script->on_dieplayer(playersao);
4558 SendPlayerHP(peer_id);
4559 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4562 void Server::RespawnPlayer(u16 peer_id)
4564 DSTACK(__FUNCTION_NAME);
4566 PlayerSAO *playersao = getPlayerSAO(peer_id);
4569 infostream<<"Server::RespawnPlayer(): Player "
4570 <<playersao->getPlayer()->getName()
4571 <<" respawns"<<std::endl;
4573 playersao->setHP(PLAYER_MAX_HP);
4575 bool repositioned = m_script->on_respawnplayer(playersao);
4577 v3f pos = findSpawnPos(m_env->getServerMap());
4578 playersao->setPos(pos);
4582 void Server::UpdateCrafting(u16 peer_id)
4584 DSTACK(__FUNCTION_NAME);
4586 Player* player = m_env->getPlayer(peer_id);
4589 // Get a preview for crafting
4591 getCraftingResult(&player->inventory, preview, false, this);
4593 // Put the new preview in
4594 InventoryList *plist = player->inventory.getList("craftpreview");
4596 assert(plist->getSize() >= 1);
4597 plist->changeItem(0, preview);
4600 RemoteClient* Server::getClient(u16 peer_id)
4602 DSTACK(__FUNCTION_NAME);
4603 //JMutexAutoLock lock(m_con_mutex);
4604 std::map<u16, RemoteClient*>::iterator n;
4605 n = m_clients.find(peer_id);
4606 // A client should exist for all peers
4607 assert(n != m_clients.end());
4611 std::wstring Server::getStatusString()
4613 std::wostringstream os(std::ios_base::binary);
4616 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4618 os<<L", uptime="<<m_uptime.get();
4619 // Information about clients
4620 std::map<u16, RemoteClient*>::iterator i;
4623 for(i = m_clients.begin(), first = true;
4624 i != m_clients.end(); ++i)
4626 // Get client and check that it is valid
4627 RemoteClient *client = i->second;
4628 assert(client->peer_id == i->first);
4629 if(client->serialization_version == SER_FMT_VER_INVALID)
4632 Player *player = m_env->getPlayer(client->peer_id);
4633 // Get name of player
4634 std::wstring name = L"unknown";
4636 name = narrow_to_wide(player->getName());
4637 // Add name to information string
4645 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4646 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4647 if(g_settings->get("motd") != "")
4648 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4652 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4654 std::set<std::string> privs;
4655 m_script->getAuth(name, NULL, &privs);
4659 bool Server::checkPriv(const std::string &name, const std::string &priv)
4661 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4662 return (privs.count(priv) != 0);
4665 void Server::reportPrivsModified(const std::string &name)
4668 for(std::map<u16, RemoteClient*>::iterator
4669 i = m_clients.begin();
4670 i != m_clients.end(); ++i){
4671 RemoteClient *client = i->second;
4672 Player *player = m_env->getPlayer(client->peer_id);
4673 reportPrivsModified(player->getName());
4676 Player *player = m_env->getPlayer(name.c_str());
4679 SendPlayerPrivileges(player->peer_id);
4680 PlayerSAO *sao = player->getPlayerSAO();
4683 sao->updatePrivileges(
4684 getPlayerEffectivePrivs(name),
4689 void Server::reportInventoryFormspecModified(const std::string &name)
4691 Player *player = m_env->getPlayer(name.c_str());
4694 SendPlayerInventoryFormspec(player->peer_id);
4697 // Saves g_settings to configpath given at initialization
4698 void Server::saveConfig()
4700 if(m_path_config != "")
4701 g_settings->updateConfigFile(m_path_config.c_str());
4704 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4706 Player *player = m_env->getPlayer(name);
4710 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4712 SendChatMessage(player->peer_id, msg);
4715 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4717 Player *player = m_env->getPlayer(playername);
4721 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4725 SendShowFormspecMessage(player->peer_id, formspec, formname);
4729 u32 Server::hudAdd(Player *player, HudElement *form) {
4733 u32 id = hud_get_free_id(player);
4734 if (id < player->hud.size())
4735 player->hud[id] = form;
4737 player->hud.push_back(form);
4739 SendHUDAdd(player->peer_id, id, form);
4743 bool Server::hudRemove(Player *player, u32 id) {
4744 if (!player || id >= player->hud.size() || !player->hud[id])
4747 delete player->hud[id];
4748 player->hud[id] = NULL;
4750 SendHUDRemove(player->peer_id, id);
4754 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4758 SendHUDChange(player->peer_id, id, stat, data);
4762 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4766 SendHUDSetFlags(player->peer_id, flags, mask);
4770 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4773 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4776 std::ostringstream os(std::ios::binary);
4777 writeS32(os, hotbar_itemcount);
4778 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4782 void Server::notifyPlayers(const std::wstring msg)
4784 BroadcastChatMessage(msg);
4787 void Server::spawnParticle(const char *playername, v3f pos,
4788 v3f velocity, v3f acceleration,
4789 float expirationtime, float size, bool
4790 collisiondetection, std::string texture)
4792 Player *player = m_env->getPlayer(playername);
4795 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4796 expirationtime, size, collisiondetection, texture);
4799 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4800 float expirationtime, float size,
4801 bool collisiondetection, std::string texture)
4803 SendSpawnParticleAll(pos, velocity, acceleration,
4804 expirationtime, size, collisiondetection, texture);
4807 u32 Server::addParticleSpawner(const char *playername,
4808 u16 amount, float spawntime,
4809 v3f minpos, v3f maxpos,
4810 v3f minvel, v3f maxvel,
4811 v3f minacc, v3f maxacc,
4812 float minexptime, float maxexptime,
4813 float minsize, float maxsize,
4814 bool collisiondetection, std::string texture)
4816 Player *player = m_env->getPlayer(playername);
4821 for(;;) // look for unused particlespawner id
4824 if (std::find(m_particlespawner_ids.begin(),
4825 m_particlespawner_ids.end(), id)
4826 == m_particlespawner_ids.end())
4828 m_particlespawner_ids.push_back(id);
4833 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4834 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4835 minexptime, maxexptime, minsize, maxsize,
4836 collisiondetection, texture, id);
4841 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4842 v3f minpos, v3f maxpos,
4843 v3f minvel, v3f maxvel,
4844 v3f minacc, v3f maxacc,
4845 float minexptime, float maxexptime,
4846 float minsize, float maxsize,
4847 bool collisiondetection, std::string texture)
4850 for(;;) // look for unused particlespawner id
4853 if (std::find(m_particlespawner_ids.begin(),
4854 m_particlespawner_ids.end(), id)
4855 == m_particlespawner_ids.end())
4857 m_particlespawner_ids.push_back(id);
4862 SendAddParticleSpawnerAll(amount, spawntime,
4863 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4864 minexptime, maxexptime, minsize, maxsize,
4865 collisiondetection, texture, id);
4870 void Server::deleteParticleSpawner(const char *playername, u32 id)
4872 Player *player = m_env->getPlayer(playername);
4876 m_particlespawner_ids.erase(
4877 std::remove(m_particlespawner_ids.begin(),
4878 m_particlespawner_ids.end(), id),
4879 m_particlespawner_ids.end());
4880 SendDeleteParticleSpawner(player->peer_id, id);
4883 void Server::deleteParticleSpawnerAll(u32 id)
4885 m_particlespawner_ids.erase(
4886 std::remove(m_particlespawner_ids.begin(),
4887 m_particlespawner_ids.end(), id),
4888 m_particlespawner_ids.end());
4889 SendDeleteParticleSpawnerAll(id);
4892 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4894 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4897 Inventory* Server::createDetachedInventory(const std::string &name)
4899 if(m_detached_inventories.count(name) > 0){
4900 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4901 delete m_detached_inventories[name];
4903 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4905 Inventory *inv = new Inventory(m_itemdef);
4907 m_detached_inventories[name] = inv;
4908 sendDetachedInventoryToAll(name);
4915 BoolScopeSet(bool *dst, bool val):
4918 m_orig_state = *m_dst;
4923 *m_dst = m_orig_state;
4930 // actions: time-reversed list
4931 // Return value: success/failure
4932 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4933 std::list<std::string> *log)
4935 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4936 ServerMap *map = (ServerMap*)(&m_env->getMap());
4937 // Disable rollback report sink while reverting
4938 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4940 // Fail if no actions to handle
4941 if(actions.empty()){
4942 log->push_back("Nothing to do.");
4949 for(std::list<RollbackAction>::const_iterator
4950 i = actions.begin();
4951 i != actions.end(); i++)
4953 const RollbackAction &action = *i;
4955 bool success = action.applyRevert(map, this, this);
4958 std::ostringstream os;
4959 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4960 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4962 log->push_back(os.str());
4964 std::ostringstream os;
4965 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4966 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4968 log->push_back(os.str());
4972 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4973 <<" failed"<<std::endl;
4975 // Call it done if less than half failed
4976 return num_failed <= num_tried/2;
4979 // IGameDef interface
4981 IItemDefManager* Server::getItemDefManager()
4985 INodeDefManager* Server::getNodeDefManager()
4989 ICraftDefManager* Server::getCraftDefManager()
4993 ITextureSource* Server::getTextureSource()
4997 IShaderSource* Server::getShaderSource()
5001 u16 Server::allocateUnknownNodeId(const std::string &name)
5003 return m_nodedef->allocateDummy(name);
5005 ISoundManager* Server::getSoundManager()
5007 return &dummySoundManager;
5009 MtEventManager* Server::getEventManager()
5013 IRollbackReportSink* Server::getRollbackReportSink()
5015 if(!m_enable_rollback_recording)
5017 if(!m_rollback_sink_enabled)
5022 IWritableItemDefManager* Server::getWritableItemDefManager()
5026 IWritableNodeDefManager* Server::getWritableNodeDefManager()
5030 IWritableCraftDefManager* Server::getWritableCraftDefManager()
5035 const ModSpec* Server::getModSpec(const std::string &modname)
5037 for(std::vector<ModSpec>::iterator i = m_mods.begin();
5038 i != m_mods.end(); i++){
5039 const ModSpec &mod = *i;
5040 if(mod.name == modname)
5045 void Server::getModNames(std::list<std::string> &modlist)
5047 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
5049 modlist.push_back(i->name);
5052 std::string Server::getBuiltinLuaPath()
5054 return porting::path_share + DIR_DELIM + "builtin";
5057 v3f findSpawnPos(ServerMap &map)
5059 //return v3f(50,50,50)*BS;
5064 nodepos = v2s16(0,0);
5069 s16 water_level = map.m_mgparams->water_level;
5071 // Try to find a good place a few times
5072 for(s32 i=0; i<1000; i++)
5075 // We're going to try to throw the player to this position
5076 v2s16 nodepos2d = v2s16(
5077 -range + (myrand() % (range * 2)),
5078 -range + (myrand() % (range * 2)));
5080 // Get ground height at point
5081 s16 groundheight = map.findGroundLevel(nodepos2d);
5082 if (groundheight <= water_level) // Don't go underwater
5084 if (groundheight > water_level + 6) // Don't go to high places
5087 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5088 bool is_good = false;
5090 for (s32 i = 0; i < 10; i++) {
5091 v3s16 blockpos = getNodeBlockPos(nodepos);
5092 map.emergeBlock(blockpos, true);
5093 content_t c = map.getNodeNoEx(nodepos).getContent();
5094 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5096 if (air_count >= 2){
5104 // Found a good place
5105 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5111 return intToFloat(nodepos, BS);
5114 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5116 RemotePlayer *player = NULL;
5117 bool newplayer = false;
5120 Try to get an existing player
5122 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5124 // If player is already connected, cancel
5125 if(player != NULL && player->peer_id != 0)
5127 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5132 If player with the wanted peer_id already exists, cancel.
5134 if(m_env->getPlayer(peer_id) != NULL)
5136 infostream<<"emergePlayer(): Player with wrong name but same"
5137 " peer_id already exists"<<std::endl;
5142 Create a new player if it doesn't exist yet
5147 player = new RemotePlayer(this);
5148 player->updateName(name);
5150 /* Set player position */
5151 infostream<<"Server: Finding spawn place for player \""
5152 <<name<<"\""<<std::endl;
5153 v3f pos = findSpawnPos(m_env->getServerMap());
5154 player->setPosition(pos);
5156 /* Add player to environment */
5157 m_env->addPlayer(player);
5161 Create a new player active object
5163 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5164 getPlayerEffectivePrivs(player->getName()),
5167 /* Clean up old HUD elements from previous sessions */
5168 player->hud.clear();
5170 /* Add object to environment */
5171 m_env->addActiveObject(playersao);
5175 m_script->on_newplayer(playersao);
5177 m_script->on_joinplayer(playersao);
5182 void Server::handlePeerChange(PeerChange &c)
5184 JMutexAutoLock envlock(m_env_mutex);
5185 JMutexAutoLock conlock(m_con_mutex);
5187 if(c.type == PEER_ADDED)
5194 std::map<u16, RemoteClient*>::iterator n;
5195 n = m_clients.find(c.peer_id);
5196 // The client shouldn't already exist
5197 assert(n == m_clients.end());
5200 RemoteClient *client = new RemoteClient();
5201 client->peer_id = c.peer_id;
5202 m_clients[client->peer_id] = client;
5205 else if(c.type == PEER_REMOVED)
5212 std::map<u16, RemoteClient*>::iterator n;
5213 n = m_clients.find(c.peer_id);
5214 // The client should exist
5215 assert(n != m_clients.end());
5218 Mark objects to be not known by the client
5220 RemoteClient *client = n->second;
5222 for(std::set<u16>::iterator
5223 i = client->m_known_objects.begin();
5224 i != client->m_known_objects.end(); ++i)
5228 ServerActiveObject* obj = m_env->getActiveObject(id);
5230 if(obj && obj->m_known_by_count > 0)
5231 obj->m_known_by_count--;
5235 Clear references to playing sounds
5237 for(std::map<s32, ServerPlayingSound>::iterator
5238 i = m_playing_sounds.begin();
5239 i != m_playing_sounds.end();)
5241 ServerPlayingSound &psound = i->second;
5242 psound.clients.erase(c.peer_id);
5243 if(psound.clients.size() == 0)
5244 m_playing_sounds.erase(i++);
5249 Player *player = m_env->getPlayer(c.peer_id);
5251 // Collect information about leaving in chat
5252 std::wstring message;
5256 std::wstring name = narrow_to_wide(player->getName());
5259 message += L" left the game.";
5261 message += L" (timed out)";
5265 /* Run scripts and remove from environment */
5269 PlayerSAO *playersao = player->getPlayerSAO();
5272 m_script->on_leaveplayer(playersao);
5274 playersao->disconnected();
5284 std::ostringstream os(std::ios_base::binary);
5285 for(std::map<u16, RemoteClient*>::iterator
5286 i = m_clients.begin();
5287 i != m_clients.end(); ++i)
5289 RemoteClient *client = i->second;
5290 assert(client->peer_id == i->first);
5291 if(client->serialization_version == SER_FMT_VER_INVALID)
5294 Player *player = m_env->getPlayer(client->peer_id);
5297 // Get name of player
5298 os<<player->getName()<<" ";
5301 actionstream<<player->getName()<<" "
5302 <<(c.timeout?"times out.":"leaves game.")
5303 <<" List of players: "
5304 <<os.str()<<std::endl;
5309 delete m_clients[c.peer_id];
5310 m_clients.erase(c.peer_id);
5312 // Send player info to all remaining clients
5313 //SendPlayerInfos();
5315 // Send leave chat message to all remaining clients
5316 if(message.length() != 0)
5317 BroadcastChatMessage(message);
5326 void Server::handlePeerChanges()
5328 while(m_peer_change_queue.size() > 0)
5330 PeerChange c = m_peer_change_queue.pop_front();
5332 verbosestream<<"Server: Handling peer change: "
5333 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5336 handlePeerChange(c);
5340 void dedicated_server_loop(Server &server, bool &kill)
5342 DSTACK(__FUNCTION_NAME);
5344 verbosestream<<"dedicated_server_loop()"<<std::endl;
5346 IntervalLimiter m_profiler_interval;
5350 float steplen = g_settings->getFloat("dedicated_server_step");
5351 // This is kind of a hack but can be done like this
5352 // because server.step() is very light
5354 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5355 sleep_ms((int)(steplen*1000.0));
5357 server.step(steplen);
5359 if(server.getShutdownRequested() || kill)
5361 infostream<<"Dedicated server quitting"<<std::endl;
5363 if(g_settings->getBool("server_announce") == true)
5364 ServerList::sendAnnounce("delete");
5372 float profiler_print_interval =
5373 g_settings->getFloat("profiler_print_interval");
5374 if(profiler_print_interval != 0)
5376 if(m_profiler_interval.step(steplen, profiler_print_interval))
5378 infostream<<"Profiler:"<<std::endl;
5379 g_profiler->print(infostream);
5380 g_profiler->clear();