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 "environment.h"
28 #include "jthread/jmutexautolock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "scripting_game.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
56 #include "sound.h" // dummySoundManager
57 #include "event_manager.h"
59 #include "serverlist.h"
60 #include "util/string.h"
61 #include "util/pointedthing.h"
62 #include "util/mathconstants.h"
64 #include "util/serialize.h"
65 #include "util/thread.h"
66 #include "defaultsettings.h"
68 class ClientNotFoundException : public BaseException
71 ClientNotFoundException(const char *s):
76 class ServerThread : public JThread
82 ServerThread(Server *server):
91 void * ServerThread::Thread()
93 log_register_thread("ServerThread");
95 DSTACK(__FUNCTION_NAME);
96 BEGIN_DEBUG_EXCEPTION_HANDLER
98 m_server->AsyncRunStep(true);
102 while(!StopRequested())
105 //TimeTaker timer("AsyncRunStep() + Receive()");
107 m_server->AsyncRunStep();
112 catch(con::NoIncomingDataException &e)
115 catch(con::PeerNotFoundException &e)
117 infostream<<"Server: PeerNotFoundException"<<std::endl;
119 catch(ClientNotFoundException &e)
122 catch(con::ConnectionBindFailed &e)
124 m_server->setAsyncFatalError(e.what());
128 m_server->setAsyncFatalError(e.what());
132 END_DEBUG_EXCEPTION_HANDLER(errorstream)
137 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
139 if(pos_exists) *pos_exists = false;
144 if(pos_exists) *pos_exists = true;
149 ServerActiveObject *sao = env->getActiveObject(object);
152 if(pos_exists) *pos_exists = true;
153 return sao->getBasePosition(); }
165 const std::string &path_world,
166 const SubgameSpec &gamespec,
167 bool simple_singleplayer_mode
169 m_path_world(path_world),
170 m_gamespec(gamespec),
171 m_simple_singleplayer_mode(simple_singleplayer_mode),
172 m_async_fatal_error(""),
177 g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"),
181 m_rollback_sink_enabled(true),
182 m_enable_rollback_recording(false),
185 m_itemdef(createItemDefManager()),
186 m_nodedef(createNodeDefManager()),
187 m_craftdef(createCraftDefManager()),
188 m_event(new EventManager()),
190 m_time_of_day_send_timer(0),
193 m_shutdown_requested(false),
194 m_ignore_map_edit_events(false),
195 m_ignore_map_edit_events_peer_id(0)
198 m_liquid_transform_timer = 0.0;
199 m_liquid_transform_every = 1.0;
200 m_print_info_timer = 0.0;
201 m_masterserver_timer = 0.0;
202 m_objectdata_timer = 0.0;
203 m_emergethread_trigger_timer = 0.0;
204 m_savemap_timer = 0.0;
207 m_lag = g_settings->getFloat("dedicated_server_step");
210 throw ServerError("Supplied empty world path");
212 if(!gamespec.isValid())
213 throw ServerError("Supplied invalid gamespec");
215 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
216 if(m_simple_singleplayer_mode)
217 infostream<<" in simple singleplayer mode"<<std::endl;
219 infostream<<std::endl;
220 infostream<<"- world: "<<m_path_world<<std::endl;
221 infostream<<"- game: "<<m_gamespec.path<<std::endl;
223 // Initialize default settings and override defaults with those provided
225 set_default_settings(g_settings);
226 Settings gamedefaults;
227 getGameMinetestConfig(gamespec.path, gamedefaults);
228 override_default_settings(g_settings, &gamedefaults);
230 // Create server thread
231 m_thread = new ServerThread(this);
233 // Create emerge manager
234 m_emerge = new EmergeManager(this);
236 // Create world if it doesn't exist
237 if(!initializeWorld(m_path_world, m_gamespec.id))
238 throw ServerError("Failed to initialize world");
240 // Create ban manager
241 std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
242 m_banmanager = new BanManager(ban_path);
244 // Create rollback manager
245 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
246 m_rollback = createRollbackManager(rollback_path, this);
248 ModConfiguration modconf(m_path_world);
249 m_mods = modconf.getMods();
250 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
251 // complain about mods with unsatisfied dependencies
252 if(!modconf.isConsistent())
254 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
255 it != unsatisfied_mods.end(); ++it)
258 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
259 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
260 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
261 errorstream << " \"" << *dep_it << "\"";
262 errorstream << std::endl;
266 Settings worldmt_settings;
267 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
268 worldmt_settings.readConfigFile(worldmt.c_str());
269 std::vector<std::string> names = worldmt_settings.getNames();
270 std::set<std::string> load_mod_names;
271 for(std::vector<std::string>::iterator it = names.begin();
272 it != names.end(); ++it)
274 std::string name = *it;
275 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
276 load_mod_names.insert(name.substr(9));
278 // complain about mods declared to be loaded, but not found
279 for(std::vector<ModSpec>::iterator it = m_mods.begin();
280 it != m_mods.end(); ++it)
281 load_mod_names.erase((*it).name);
282 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
283 it != unsatisfied_mods.end(); ++it)
284 load_mod_names.erase((*it).name);
285 if(!load_mod_names.empty())
287 errorstream << "The following mods could not be found:";
288 for(std::set<std::string>::iterator it = load_mod_names.begin();
289 it != load_mod_names.end(); ++it)
290 errorstream << " \"" << (*it) << "\"";
291 errorstream << std::endl;
294 // Path to builtin.lua
295 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
298 JMutexAutoLock envlock(m_env_mutex);
300 // Initialize scripting
301 infostream<<"Server: Initializing Lua"<<std::endl;
303 m_script = new GameScripting(this);
306 // Load and run builtin.lua
307 infostream<<"Server: Loading builtin.lua [\""
308 <<builtinpath<<"\"]"<<std::endl;
309 bool success = m_script->loadMod(builtinpath, "__builtin");
311 errorstream<<"Server: Failed to load and run "
312 <<builtinpath<<std::endl;
313 throw ModError("Failed to load and run "+builtinpath);
316 infostream<<"Server: Loading mods: ";
317 for(std::vector<ModSpec>::iterator i = m_mods.begin();
318 i != m_mods.end(); i++){
319 const ModSpec &mod = *i;
320 infostream<<mod.name<<" ";
322 infostream<<std::endl;
323 // Load and run "mod" scripts
324 for(std::vector<ModSpec>::iterator i = m_mods.begin();
325 i != m_mods.end(); i++){
326 const ModSpec &mod = *i;
327 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
328 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
329 <<scriptpath<<"\"]"<<std::endl;
330 bool success = m_script->loadMod(scriptpath, mod.name);
332 errorstream<<"Server: Failed to load and run "
333 <<scriptpath<<std::endl;
334 throw ModError("Failed to load and run "+scriptpath);
338 // Read Textures and calculate sha1 sums
341 // Apply item aliases in the node definition manager
342 m_nodedef->updateAliases(m_itemdef);
344 // Initialize Environment
345 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
346 m_env = new ServerEnvironment(servermap, m_script, this, m_emerge);
348 m_clients.setEnv(m_env);
350 // Run some callbacks after the MG params have been set up but before activation
351 MapgenParams *mgparams = servermap->getMapgenParams();
352 m_script->environment_OnMapgenInit(mgparams);
354 // Initialize mapgens
355 m_emerge->initMapgens(mgparams);
356 servermap->setMapgenParams(m_emerge->params);
358 // Give environment reference to scripting api
359 m_script->initializeEnvironment(m_env);
361 // Register us to receive map edit events
362 servermap->addEventReceiver(this);
364 // If file exists, load environment metadata
365 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
367 infostream<<"Server: Loading environment metadata"<<std::endl;
368 m_env->loadMeta(m_path_world);
372 infostream<<"Server: Loading players"<<std::endl;
373 m_env->deSerializePlayers(m_path_world);
376 Add some test ActiveBlockModifiers to environment
378 add_legacy_abms(m_env, m_nodedef);
380 m_liquid_transform_every = g_settings->getFloat("liquid_update");
385 infostream<<"Server destructing"<<std::endl;
388 Send shutdown message
391 std::wstring line = L"*** Server shutting down";
392 SendChatMessage(PEER_ID_INEXISTENT, line);
396 JMutexAutoLock envlock(m_env_mutex);
399 Execute script shutdown hooks
401 m_script->on_shutdown();
405 JMutexAutoLock envlock(m_env_mutex);
410 infostream<<"Server: Saving players"<<std::endl;
411 m_env->serializePlayers(m_path_world);
414 Save environment metadata
416 infostream<<"Server: Saving environment metadata"<<std::endl;
417 m_env->saveMeta(m_path_world);
426 // stop all emerge threads before deleting players that may have
427 // requested blocks to be emerged
428 m_emerge->stopThreads();
430 // Delete things in the reverse order of creation
433 // N.B. the EmergeManager should be deleted after the Environment since Map
434 // depends on EmergeManager to write its current params to the map meta
443 // Deinitialize scripting
444 infostream<<"Server: Deinitializing scripting"<<std::endl;
447 // Delete detached inventories
449 for(std::map<std::string, Inventory*>::iterator
450 i = m_detached_inventories.begin();
451 i != m_detached_inventories.end(); i++){
457 void Server::start(unsigned short port)
459 DSTACK(__FUNCTION_NAME);
460 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
462 // Stop thread if already running
465 // Initialize connection
466 m_con.SetTimeoutMs(30);
472 // ASCII art for the win!
474 <<" .__ __ __ "<<std::endl
475 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
476 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
477 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
478 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
479 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
480 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
481 actionstream<<"Server for gameid=\""<<m_gamespec.id
482 <<"\" listening on port "<<port<<"."<<std::endl;
487 DSTACK(__FUNCTION_NAME);
489 infostream<<"Server: Stopping and waiting threads"<<std::endl;
491 // Stop threads (set run=false first so both start stopping)
493 //m_emergethread.setRun(false);
495 //m_emergethread.stop();
497 infostream<<"Server: Threads stopped"<<std::endl;
500 void Server::step(float dtime)
502 DSTACK(__FUNCTION_NAME);
507 JMutexAutoLock lock(m_step_dtime_mutex);
508 m_step_dtime += dtime;
510 // Throw if fatal error occurred in thread
511 std::string async_err = m_async_fatal_error.get();
513 throw ServerError(async_err);
517 void Server::AsyncRunStep(bool initial_step)
519 DSTACK(__FUNCTION_NAME);
521 g_profiler->add("Server::AsyncRunStep (num)", 1);
525 JMutexAutoLock lock1(m_step_dtime_mutex);
526 dtime = m_step_dtime;
530 // Send blocks to clients
534 if((dtime < 0.001) && (initial_step == false))
537 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
539 //infostream<<"Server steps "<<dtime<<std::endl;
540 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
543 JMutexAutoLock lock1(m_step_dtime_mutex);
544 m_step_dtime -= dtime;
551 m_uptime.set(m_uptime.get() + dtime);
557 Update time of day and overall game time
560 JMutexAutoLock envlock(m_env_mutex);
562 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
565 Send to clients at constant intervals
568 m_time_of_day_send_timer -= dtime;
569 if(m_time_of_day_send_timer < 0.0)
571 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
572 u16 time = m_env->getTimeOfDay();
573 float time_speed = g_settings->getFloat("time_speed");
574 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
579 JMutexAutoLock lock(m_env_mutex);
580 // Figure out and report maximum lag to environment
581 float max_lag = m_env->getMaxLagEstimate();
582 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
584 if(dtime > 0.1 && dtime > max_lag * 2.0)
585 infostream<<"Server: Maximum lag peaked to "<<dtime
589 m_env->reportMaxLagEstimate(max_lag);
591 ScopeProfiler sp(g_profiler, "SEnv step");
592 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
596 const float map_timer_and_unload_dtime = 2.92;
597 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
599 JMutexAutoLock lock(m_env_mutex);
600 // Run Map's timers and unload unused data
601 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
602 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
603 g_settings->getFloat("server_unload_unused_data_timeout"));
614 JMutexAutoLock lock(m_env_mutex);
616 std::list<u16> clientids = m_clients.getClientIDs();
618 ScopeProfiler sp(g_profiler, "Server: handle players");
620 for(std::list<u16>::iterator
621 i = clientids.begin();
622 i != clientids.end(); ++i)
624 PlayerSAO *playersao = getPlayerSAO(*i);
625 if(playersao == NULL)
629 Handle player HPs (die if hp=0)
631 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
633 if(playersao->getHP() == 0)
640 Send player breath if changed
642 if(playersao->m_breath_not_sent){
643 SendPlayerBreath(*i);
647 Send player inventories if necessary
649 if(playersao->m_moved){
651 playersao->m_moved = false;
653 if(playersao->m_inventory_not_sent){
660 /* Transform liquids */
661 m_liquid_transform_timer += dtime;
662 if(m_liquid_transform_timer >= m_liquid_transform_every)
664 m_liquid_transform_timer -= m_liquid_transform_every;
666 JMutexAutoLock lock(m_env_mutex);
668 ScopeProfiler sp(g_profiler, "Server: liquid transform");
670 std::map<v3s16, MapBlock*> modified_blocks;
671 m_env->getMap().transformLiquids(modified_blocks);
676 core::map<v3s16, MapBlock*> lighting_modified_blocks;
677 ServerMap &map = ((ServerMap&)m_env->getMap());
678 map.updateLighting(modified_blocks, lighting_modified_blocks);
680 // Add blocks modified by lighting to modified_blocks
681 for(core::map<v3s16, MapBlock*>::Iterator
682 i = lighting_modified_blocks.getIterator();
683 i.atEnd() == false; i++)
685 MapBlock *block = i.getNode()->getValue();
686 modified_blocks.insert(block->getPos(), block);
690 Set the modified blocks unsent for all the clients
692 if(modified_blocks.size() > 0)
694 SetBlocksNotSent(modified_blocks);
697 m_clients.step(dtime);
699 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
701 // send masterserver announce
703 float &counter = m_masterserver_timer;
704 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
705 g_settings->getBool("server_announce") == true)
707 ServerList::sendAnnounce(!counter ? "start" : "update",
708 m_clients.getPlayerNames(),
710 m_env->getGameTime(),
721 Check added and deleted active objects
724 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
725 JMutexAutoLock envlock(m_env_mutex);
728 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
729 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
731 // Radius inside which objects are active
732 s16 radius = g_settings->getS16("active_object_send_range_blocks");
733 radius *= MAP_BLOCKSIZE;
735 for(std::map<u16, RemoteClient*>::iterator
737 i != clients.end(); ++i)
739 RemoteClient *client = i->second;
741 // If definitions and textures have not been sent, don't
742 // send objects either
743 if (client->getState() < DefinitionsSent)
746 Player *player = m_env->getPlayer(client->peer_id);
749 // This can happen if the client timeouts somehow
750 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
752 <<" has no associated player"<<std::endl;*/
755 v3s16 pos = floatToInt(player->getPosition(), BS);
757 std::set<u16> removed_objects;
758 std::set<u16> added_objects;
759 m_env->getRemovedActiveObjects(pos, radius,
760 client->m_known_objects, removed_objects);
761 m_env->getAddedActiveObjects(pos, radius,
762 client->m_known_objects, added_objects);
764 // Ignore if nothing happened
765 if(removed_objects.size() == 0 && added_objects.size() == 0)
767 //infostream<<"active objects: none changed"<<std::endl;
771 std::string data_buffer;
775 // Handle removed objects
776 writeU16((u8*)buf, removed_objects.size());
777 data_buffer.append(buf, 2);
778 for(std::set<u16>::iterator
779 i = removed_objects.begin();
780 i != removed_objects.end(); ++i)
784 ServerActiveObject* obj = m_env->getActiveObject(id);
786 // Add to data buffer for sending
787 writeU16((u8*)buf, id);
788 data_buffer.append(buf, 2);
790 // Remove from known objects
791 client->m_known_objects.erase(id);
793 if(obj && obj->m_known_by_count > 0)
794 obj->m_known_by_count--;
797 // Handle added objects
798 writeU16((u8*)buf, added_objects.size());
799 data_buffer.append(buf, 2);
800 for(std::set<u16>::iterator
801 i = added_objects.begin();
802 i != added_objects.end(); ++i)
806 ServerActiveObject* obj = m_env->getActiveObject(id);
809 u8 type = ACTIVEOBJECT_TYPE_INVALID;
811 infostream<<"WARNING: "<<__FUNCTION_NAME
812 <<": NULL object"<<std::endl;
814 type = obj->getSendType();
816 // Add to data buffer for sending
817 writeU16((u8*)buf, id);
818 data_buffer.append(buf, 2);
819 writeU8((u8*)buf, type);
820 data_buffer.append(buf, 1);
823 data_buffer.append(serializeLongString(
824 obj->getClientInitializationData(client->net_proto_version)));
826 data_buffer.append(serializeLongString(""));
828 // Add to known objects
829 client->m_known_objects.insert(id);
832 obj->m_known_by_count++;
836 SharedBuffer<u8> reply(2 + data_buffer.size());
837 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
838 memcpy((char*)&reply[2], data_buffer.c_str(),
841 m_clients.send(client->peer_id, 0, reply, true);
843 verbosestream<<"Server: Sent object remove/add: "
844 <<removed_objects.size()<<" removed, "
845 <<added_objects.size()<<" added, "
846 <<"packet size is "<<reply.getSize()<<std::endl;
851 Collect a list of all the objects known by the clients
852 and report it back to the environment.
855 core::map<u16, bool> all_known_objects;
857 for(core::map<u16, RemoteClient*>::Iterator
858 i = m_clients.getIterator();
859 i.atEnd() == false; i++)
861 RemoteClient *client = i.getNode()->getValue();
862 // Go through all known objects of client
863 for(core::map<u16, bool>::Iterator
864 i = client->m_known_objects.getIterator();
865 i.atEnd()==false; i++)
867 u16 id = i.getNode()->getKey();
868 all_known_objects[id] = true;
872 m_env->setKnownActiveObjects(whatever);
881 JMutexAutoLock envlock(m_env_mutex);
882 ScopeProfiler sp(g_profiler, "Server: sending object messages");
885 // Value = data sent by object
886 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
888 // Get active object messages from environment
891 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
895 std::list<ActiveObjectMessage>* message_list = NULL;
896 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
897 n = buffered_messages.find(aom.id);
898 if(n == buffered_messages.end())
900 message_list = new std::list<ActiveObjectMessage>;
901 buffered_messages[aom.id] = message_list;
905 message_list = n->second;
907 message_list->push_back(aom);
911 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
912 // Route data to every client
913 for(std::map<u16, RemoteClient*>::iterator
915 i != clients.end(); ++i)
917 RemoteClient *client = i->second;
918 std::string reliable_data;
919 std::string unreliable_data;
920 // Go through all objects in message buffer
921 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
922 j = buffered_messages.begin();
923 j != buffered_messages.end(); ++j)
925 // If object is not known by client, skip it
927 if(client->m_known_objects.find(id) == client->m_known_objects.end())
929 // Get message list of object
930 std::list<ActiveObjectMessage>* list = j->second;
931 // Go through every message
932 for(std::list<ActiveObjectMessage>::iterator
933 k = list->begin(); k != list->end(); ++k)
935 // Compose the full new data with header
936 ActiveObjectMessage aom = *k;
937 std::string new_data;
940 writeU16((u8*)&buf[0], aom.id);
941 new_data.append(buf, 2);
943 new_data += serializeString(aom.datastring);
944 // Add data to buffer
946 reliable_data += new_data;
948 unreliable_data += new_data;
952 reliable_data and unreliable_data are now ready.
955 if(reliable_data.size() > 0)
957 SharedBuffer<u8> reply(2 + reliable_data.size());
958 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
959 memcpy((char*)&reply[2], reliable_data.c_str(),
960 reliable_data.size());
962 m_clients.send(client->peer_id, 0, reply, true);
964 if(unreliable_data.size() > 0)
966 SharedBuffer<u8> reply(2 + unreliable_data.size());
967 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
968 memcpy((char*)&reply[2], unreliable_data.c_str(),
969 unreliable_data.size());
970 // Send as unreliable
971 m_clients.send(client->peer_id, 1, reply, false);
974 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
976 infostream<<"Server: Size of object message data: "
977 <<"reliable: "<<reliable_data.size()
978 <<", unreliable: "<<unreliable_data.size()
984 // Clear buffered_messages
985 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
986 i = buffered_messages.begin();
987 i != buffered_messages.end(); ++i)
994 Send queued-for-sending map edit events.
997 // We will be accessing the environment
998 JMutexAutoLock lock(m_env_mutex);
1000 // Don't send too many at a time
1003 // Single change sending is disabled if queue size is not small
1004 bool disable_single_change_sending = false;
1005 if(m_unsent_map_edit_queue.size() >= 4)
1006 disable_single_change_sending = true;
1008 int event_count = m_unsent_map_edit_queue.size();
1010 // We'll log the amount of each
1013 while(m_unsent_map_edit_queue.size() != 0)
1015 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1017 // Players far away from the change are stored here.
1018 // Instead of sending the changes, MapBlocks are set not sent
1020 std::list<u16> far_players;
1022 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
1024 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1025 prof.add("MEET_ADDNODE", 1);
1026 if(disable_single_change_sending)
1027 sendAddNode(event->p, event->n, event->already_known_by_peer,
1028 &far_players, 5, event->type == MEET_ADDNODE);
1030 sendAddNode(event->p, event->n, event->already_known_by_peer,
1031 &far_players, 30, event->type == MEET_ADDNODE);
1033 else if(event->type == MEET_REMOVENODE)
1035 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1036 prof.add("MEET_REMOVENODE", 1);
1037 if(disable_single_change_sending)
1038 sendRemoveNode(event->p, event->already_known_by_peer,
1041 sendRemoveNode(event->p, event->already_known_by_peer,
1044 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1046 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1047 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1048 setBlockNotSent(event->p);
1050 else if(event->type == MEET_OTHER)
1052 infostream<<"Server: MEET_OTHER"<<std::endl;
1053 prof.add("MEET_OTHER", 1);
1054 for(std::set<v3s16>::iterator
1055 i = event->modified_blocks.begin();
1056 i != event->modified_blocks.end(); ++i)
1058 setBlockNotSent(*i);
1063 prof.add("unknown", 1);
1064 infostream<<"WARNING: Server: Unknown MapEditEvent "
1065 <<((u32)event->type)<<std::endl;
1069 Set blocks not sent to far players
1071 if(far_players.size() > 0)
1073 // Convert list format to that wanted by SetBlocksNotSent
1074 std::map<v3s16, MapBlock*> modified_blocks2;
1075 for(std::set<v3s16>::iterator
1076 i = event->modified_blocks.begin();
1077 i != event->modified_blocks.end(); ++i)
1079 modified_blocks2[*i] =
1080 m_env->getMap().getBlockNoCreateNoEx(*i);
1082 // Set blocks not sent
1083 for(std::list<u16>::iterator
1084 i = far_players.begin();
1085 i != far_players.end(); ++i)
1088 RemoteClient *client = getClient(peer_id);
1091 client->SetBlocksNotSent(modified_blocks2);
1097 /*// Don't send too many at a time
1099 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1103 if(event_count >= 5){
1104 infostream<<"Server: MapEditEvents:"<<std::endl;
1105 prof.print(infostream);
1106 } else if(event_count != 0){
1107 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1108 prof.print(verbosestream);
1114 Trigger emergethread (it somehow gets to a non-triggered but
1115 bysy state sometimes)
1118 float &counter = m_emergethread_trigger_timer;
1124 m_emerge->startThreads();
1126 // Update m_enable_rollback_recording here too
1127 m_enable_rollback_recording =
1128 g_settings->getBool("enable_rollback_recording");
1132 // Save map, players and auth stuff
1134 float &counter = m_savemap_timer;
1136 if(counter >= g_settings->getFloat("server_map_save_interval"))
1139 JMutexAutoLock lock(m_env_mutex);
1141 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1144 if(m_banmanager->isModified())
1145 m_banmanager->save();
1147 // Save changed parts of map
1148 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1151 m_env->serializePlayers(m_path_world);
1153 // Save environment metadata
1154 m_env->saveMeta(m_path_world);
1159 void Server::Receive()
1161 DSTACK(__FUNCTION_NAME);
1162 SharedBuffer<u8> data;
1166 datasize = m_con.Receive(peer_id,data);
1167 ProcessData(*data, datasize, peer_id);
1169 catch(con::InvalidIncomingDataException &e)
1171 infostream<<"Server::Receive(): "
1172 "InvalidIncomingDataException: what()="
1173 <<e.what()<<std::endl;
1175 catch(con::PeerNotFoundException &e)
1177 //NOTE: This is not needed anymore
1179 // The peer has been disconnected.
1180 // Find the associated player and remove it.
1182 /*JMutexAutoLock envlock(m_env_mutex);
1184 infostream<<"ServerThread: peer_id="<<peer_id
1185 <<" has apparently closed connection. "
1186 <<"Removing player."<<std::endl;
1188 m_env->removePlayer(peer_id);*/
1192 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1194 DSTACK(__FUNCTION_NAME);
1195 // Environment is locked first.
1196 JMutexAutoLock envlock(m_env_mutex);
1198 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1202 Address address = getPeerAddress(peer_id);
1203 addr_s = address.serializeString();
1205 // drop player if is ip is banned
1206 if(m_banmanager->isIpBanned(addr_s)){
1207 std::string ban_name = m_banmanager->getBanName(addr_s);
1208 infostream<<"Server: A banned client tried to connect from "
1209 <<addr_s<<"; banned name was "
1210 <<ban_name<<std::endl;
1211 // This actually doesn't seem to transfer to the client
1212 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1213 +narrow_to_wide(ban_name));
1217 catch(con::PeerNotFoundException &e)
1219 errorstream<<"Server::ProcessData(): Cancelling: peer "
1220 <<peer_id<<" not found"<<std::endl;
1230 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1232 if(command == TOSERVER_INIT)
1234 // [0] u16 TOSERVER_INIT
1235 // [2] u8 SER_FMT_VER_HIGHEST_READ
1236 // [3] u8[20] player_name
1237 // [23] u8[28] password <--- can be sent without this, from old versions
1239 if(datasize < 2+1+PLAYERNAME_SIZE)
1242 RemoteClient* client = getClient(peer_id,Created);
1244 // If net_proto_version is set, this client has already been handled
1245 if(client->getState() > Created)
1247 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1248 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1252 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1253 <<peer_id<<")"<<std::endl;
1255 // Do not allow multiple players in simple singleplayer mode.
1256 // This isn't a perfect way to do it, but will suffice for now
1257 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1258 infostream<<"Server: Not allowing another client ("<<addr_s
1259 <<") to connect in simple singleplayer mode"<<std::endl;
1260 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1264 // First byte after command is maximum supported
1265 // serialization version
1266 u8 client_max = data[2];
1267 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1268 // Use the highest version supported by both
1269 u8 deployed = std::min(client_max, our_max);
1270 // If it's lower than the lowest supported, give up.
1271 if(deployed < SER_FMT_VER_LOWEST)
1272 deployed = SER_FMT_VER_INVALID;
1274 if(deployed == SER_FMT_VER_INVALID)
1276 actionstream<<"Server: A mismatched client tried to connect from "
1277 <<addr_s<<std::endl;
1278 infostream<<"Server: Cannot negotiate serialization version with "
1279 <<addr_s<<std::endl;
1280 DenyAccess(peer_id, std::wstring(
1281 L"Your client's version is not supported.\n"
1282 L"Server version is ")
1283 + narrow_to_wide(minetest_version_simple) + L"."
1288 client->setPendingSerializationVersion(deployed);
1291 Read and check network protocol version
1294 u16 min_net_proto_version = 0;
1295 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1296 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1298 // Use same version as minimum and maximum if maximum version field
1299 // doesn't exist (backwards compatibility)
1300 u16 max_net_proto_version = min_net_proto_version;
1301 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1302 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1304 // Start with client's maximum version
1305 u16 net_proto_version = max_net_proto_version;
1307 // Figure out a working version if it is possible at all
1308 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1309 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1311 // If maximum is larger than our maximum, go with our maximum
1312 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1313 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1314 // Else go with client's maximum
1316 net_proto_version = max_net_proto_version;
1319 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1320 <<min_net_proto_version<<", max: "<<max_net_proto_version
1321 <<", chosen: "<<net_proto_version<<std::endl;
1323 client->net_proto_version = net_proto_version;
1325 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1326 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1328 actionstream<<"Server: A mismatched client tried to connect from "
1329 <<addr_s<<std::endl;
1330 DenyAccess(peer_id, std::wstring(
1331 L"Your client's version is not supported.\n"
1332 L"Server version is ")
1333 + narrow_to_wide(minetest_version_simple) + L",\n"
1334 + L"server's PROTOCOL_VERSION is "
1335 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1337 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1338 + L", client's PROTOCOL_VERSION is "
1339 + narrow_to_wide(itos(min_net_proto_version))
1341 + narrow_to_wide(itos(max_net_proto_version))
1346 if(g_settings->getBool("strict_protocol_version_checking"))
1348 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1350 actionstream<<"Server: A mismatched (strict) client tried to "
1351 <<"connect from "<<addr_s<<std::endl;
1352 DenyAccess(peer_id, std::wstring(
1353 L"Your client's version is not supported.\n"
1354 L"Server version is ")
1355 + narrow_to_wide(minetest_version_simple) + L",\n"
1356 + L"server's PROTOCOL_VERSION (strict) is "
1357 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1358 + L", client's PROTOCOL_VERSION is "
1359 + narrow_to_wide(itos(min_net_proto_version))
1361 + narrow_to_wide(itos(max_net_proto_version))
1372 char playername[PLAYERNAME_SIZE];
1373 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1375 playername[i] = data[3+i];
1377 playername[PLAYERNAME_SIZE-1] = 0;
1379 if(playername[0]=='\0')
1381 actionstream<<"Server: Player with an empty name "
1382 <<"tried to connect from "<<addr_s<<std::endl;
1383 DenyAccess(peer_id, L"Empty name");
1387 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1389 actionstream<<"Server: Player with an invalid name "
1390 <<"tried to connect from "<<addr_s<<std::endl;
1391 DenyAccess(peer_id, L"Name contains unallowed characters");
1395 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1397 actionstream<<"Server: Player with the name \"singleplayer\" "
1398 <<"tried to connect from "<<addr_s<<std::endl;
1399 DenyAccess(peer_id, L"Name is not allowed");
1405 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1407 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1408 <<"tried to connect from "<<addr_s<<" "
1409 <<"but it was disallowed for the following reason: "
1410 <<reason<<std::endl;
1411 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1416 infostream<<"Server: New connection: \""<<playername<<"\" from "
1417 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1420 char given_password[PASSWORD_SIZE];
1421 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1423 // old version - assume blank password
1424 given_password[0] = 0;
1428 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1430 given_password[i] = data[23+i];
1432 given_password[PASSWORD_SIZE-1] = 0;
1435 if(!base64_is_valid(given_password)){
1436 actionstream<<"Server: "<<playername
1437 <<" supplied invalid password hash"<<std::endl;
1438 DenyAccess(peer_id, L"Invalid password hash");
1442 // Enforce user limit.
1443 // Don't enforce for users that have some admin right
1444 if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") &&
1445 !checkPriv(playername, "server") &&
1446 !checkPriv(playername, "ban") &&
1447 !checkPriv(playername, "privs") &&
1448 !checkPriv(playername, "password") &&
1449 playername != g_settings->get("name"))
1451 actionstream<<"Server: "<<playername<<" tried to join, but there"
1452 <<" are already max_users="
1453 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1454 DenyAccess(peer_id, L"Too many users.");
1458 std::string checkpwd; // Password hash to check against
1459 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1461 // If no authentication info exists for user, create it
1463 if(!isSingleplayer() &&
1464 g_settings->getBool("disallow_empty_password") &&
1465 std::string(given_password) == ""){
1466 actionstream<<"Server: "<<playername
1467 <<" supplied empty password"<<std::endl;
1468 DenyAccess(peer_id, L"Empty passwords are "
1469 L"disallowed. Set a password and try again.");
1472 std::wstring raw_default_password =
1473 narrow_to_wide(g_settings->get("default_password"));
1474 std::string initial_password =
1475 translatePassword(playername, raw_default_password);
1477 // If default_password is empty, allow any initial password
1478 if (raw_default_password.length() == 0)
1479 initial_password = given_password;
1481 m_script->createAuth(playername, initial_password);
1484 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1487 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1488 <<" (auth handler does not work?)"<<std::endl;
1489 DenyAccess(peer_id, L"Not allowed to login");
1493 if(given_password != checkpwd){
1494 actionstream<<"Server: "<<playername<<" supplied wrong password"
1496 DenyAccess(peer_id, L"Wrong password");
1500 RemotePlayer *player =
1501 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1503 if(player && player->peer_id != 0){
1504 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1505 <<" (player allocated to an another client)"<<std::endl;
1506 DenyAccess(peer_id, L"Another client is connected with this "
1507 L"name. If your client closed unexpectedly, try again in "
1511 m_clients.setPlayerName(peer_id,playername);
1514 Answer with a TOCLIENT_INIT
1517 SharedBuffer<u8> reply(2+1+6+8+4);
1518 writeU16(&reply[0], TOCLIENT_INIT);
1519 writeU8(&reply[2], deployed);
1520 //send dummy pos for legacy reasons only
1521 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1522 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1523 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1526 m_clients.send(peer_id, 0, reply, true);
1527 m_clients.event(peer_id, Init);
1533 if(command == TOSERVER_INIT2)
1536 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1537 <<peer_id<<std::endl;
1539 m_clients.event(peer_id, GotInit2);
1540 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1543 Send some initialization data
1546 infostream<<"Server: Sending content to "
1547 <<getPlayerName(peer_id)<<std::endl;
1549 // Send player movement settings
1550 SendMovement(peer_id);
1552 // Send item definitions
1553 SendItemDef(peer_id, m_itemdef, protocol_version);
1555 // Send node definitions
1556 SendNodeDef(peer_id, m_nodedef, protocol_version);
1558 m_clients.event(peer_id, SetDefinitionsSent);
1560 // Send media announcement
1561 sendMediaAnnouncement(peer_id);
1563 // Send detached inventories
1564 sendDetachedInventories(peer_id);
1567 u16 time = m_env->getTimeOfDay();
1568 float time_speed = g_settings->getFloat("time_speed");
1569 SendTimeOfDay(peer_id, time, time_speed);
1571 // Warnings about protocol version can be issued here
1572 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1574 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1575 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1581 u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version;
1583 if(peer_ser_ver == SER_FMT_VER_INVALID)
1585 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1586 " serialization format invalid or not initialized."
1587 " Skipping incoming command="<<command<<std::endl;
1591 /* Handle commands relate to client startup */
1592 if(command == TOSERVER_REQUEST_MEDIA) {
1593 std::string datastring((char*)&data[2], datasize-2);
1594 std::istringstream is(datastring, std::ios_base::binary);
1596 std::list<std::string> tosend;
1597 u16 numfiles = readU16(is);
1599 infostream<<"Sending "<<numfiles<<" files to "
1600 <<getPlayerName(peer_id)<<std::endl;
1601 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1603 for(int i = 0; i < numfiles; i++) {
1604 std::string name = deSerializeString(is);
1605 tosend.push_back(name);
1606 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1610 sendRequestedMedia(peer_id, tosend);
1613 else if(command == TOSERVER_RECEIVED_MEDIA) {
1614 std::string playername = "";
1615 PlayerSAO *playersao = NULL;
1617 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,DefinitionsSent);
1618 if (client != NULL) {
1619 playername = client->getName();
1620 playersao = emergePlayer(playername.c_str(), peer_id);
1624 RemotePlayer *player =
1625 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1627 // If failed, cancel
1628 if((playersao == NULL) || (player == NULL))
1630 if(player && player->peer_id != 0){
1631 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1632 <<" (player allocated to an another client)"<<std::endl;
1633 DenyAccess(peer_id, L"Another client is connected with this "
1634 L"name. If your client closed unexpectedly, try again in "
1637 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1639 DenyAccess(peer_id, L"Could not allocate player.");
1645 Send complete position information
1647 SendMovePlayer(peer_id);
1650 SendPlayerPrivileges(peer_id);
1652 // Send inventory formspec
1653 SendPlayerInventoryFormspec(peer_id);
1656 UpdateCrafting(peer_id);
1657 SendInventory(peer_id);
1660 if(g_settings->getBool("enable_damage"))
1661 SendPlayerHP(peer_id);
1664 SendPlayerBreath(peer_id);
1666 // Show death screen if necessary
1668 SendDeathscreen(peer_id, false, v3f(0,0,0));
1670 // Note things in chat if not in simple singleplayer mode
1671 if(!m_simple_singleplayer_mode)
1673 // Send information about server to player in chat
1674 SendChatMessage(peer_id, getStatusString());
1676 // Send information about joining in chat
1678 std::wstring name = L"unknown";
1679 Player *player = m_env->getPlayer(peer_id);
1681 name = narrow_to_wide(player->getName());
1683 std::wstring message;
1686 message += L" joined the game.";
1687 SendChatMessage(PEER_ID_INEXISTENT,message);
1691 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. " << std::endl;
1696 std::vector<std::string> names = m_clients.getPlayerNames();
1698 actionstream<<player->getName()<<" ["<<addr_s<<"] "
1699 <<"joins game. List of players: ";
1701 for (std::vector<std::string>::iterator i = names.begin();
1702 i != names.end(); i++)
1704 actionstream << *i << " ";
1707 actionstream<<std::endl;
1710 m_clients.event(peer_id,SetMediaSent);
1711 m_script->on_joinplayer(playersao);
1714 else if(command == TOSERVER_GOTBLOCKS)
1727 u16 count = data[2];
1728 for(u16 i=0; i<count; i++)
1730 if((s16)datasize < 2+1+(i+1)*6)
1731 throw con::InvalidIncomingDataException
1732 ("GOTBLOCKS length is too short");
1733 v3s16 p = readV3S16(&data[2+1+i*6]);
1734 /*infostream<<"Server: GOTBLOCKS ("
1735 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1736 RemoteClient *client = getClient(peer_id);
1737 client->GotBlock(p);
1742 if (m_clients.getClientState(peer_id) < Active)
1744 if (command == TOSERVER_PLAYERPOS) return;
1746 errorstream<<"Got packet command: " << command << " for peer id "
1747 << peer_id << " but client isn't active yet. Dropping packet "
1752 Player *player = m_env->getPlayer(peer_id);
1754 errorstream<<"Server::ProcessData(): Cancelling: "
1755 "No player for peer_id="<<peer_id
1760 PlayerSAO *playersao = player->getPlayerSAO();
1761 if(playersao == NULL){
1762 errorstream<<"Server::ProcessData(): Cancelling: "
1763 "No player object for peer_id="<<peer_id
1768 if(command == TOSERVER_PLAYERPOS)
1770 if(datasize < 2+12+12+4+4)
1774 v3s32 ps = readV3S32(&data[start+2]);
1775 v3s32 ss = readV3S32(&data[start+2+12]);
1776 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1777 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1779 if(datasize >= 2+12+12+4+4+4)
1780 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
1781 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1782 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1783 pitch = wrapDegrees(pitch);
1784 yaw = wrapDegrees(yaw);
1786 player->setPosition(position);
1787 player->setSpeed(speed);
1788 player->setPitch(pitch);
1789 player->setYaw(yaw);
1790 player->keyPressed=keyPressed;
1791 player->control.up = (bool)(keyPressed&1);
1792 player->control.down = (bool)(keyPressed&2);
1793 player->control.left = (bool)(keyPressed&4);
1794 player->control.right = (bool)(keyPressed&8);
1795 player->control.jump = (bool)(keyPressed&16);
1796 player->control.aux1 = (bool)(keyPressed&32);
1797 player->control.sneak = (bool)(keyPressed&64);
1798 player->control.LMB = (bool)(keyPressed&128);
1799 player->control.RMB = (bool)(keyPressed&256);
1801 bool cheated = playersao->checkMovementCheat();
1804 m_script->on_cheat(playersao, "moved_too_fast");
1807 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1808 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1809 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1811 else if(command == TOSERVER_DELETEDBLOCKS)
1824 u16 count = data[2];
1825 for(u16 i=0; i<count; i++)
1827 if((s16)datasize < 2+1+(i+1)*6)
1828 throw con::InvalidIncomingDataException
1829 ("DELETEDBLOCKS length is too short");
1830 v3s16 p = readV3S16(&data[2+1+i*6]);
1831 /*infostream<<"Server: DELETEDBLOCKS ("
1832 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1833 RemoteClient *client = getClient(peer_id);
1834 client->SetBlockNotSent(p);
1837 else if(command == TOSERVER_CLICK_OBJECT)
1839 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
1842 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
1844 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
1847 else if(command == TOSERVER_GROUND_ACTION)
1849 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
1853 else if(command == TOSERVER_RELEASE)
1855 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
1858 else if(command == TOSERVER_SIGNTEXT)
1860 infostream<<"Server: SIGNTEXT not supported anymore"
1864 else if(command == TOSERVER_SIGNNODETEXT)
1866 infostream<<"Server: SIGNNODETEXT not supported anymore"
1870 else if(command == TOSERVER_INVENTORY_ACTION)
1872 // Strip command and create a stream
1873 std::string datastring((char*)&data[2], datasize-2);
1874 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
1875 std::istringstream is(datastring, std::ios_base::binary);
1877 InventoryAction *a = InventoryAction::deSerialize(is);
1880 infostream<<"TOSERVER_INVENTORY_ACTION: "
1881 <<"InventoryAction::deSerialize() returned NULL"
1886 // If something goes wrong, this player is to blame
1887 RollbackScopeActor rollback_scope(m_rollback,
1888 std::string("player:")+player->getName());
1891 Note: Always set inventory not sent, to repair cases
1892 where the client made a bad prediction.
1896 Handle restrictions and special cases of the move action
1898 if(a->getType() == IACTION_MOVE)
1900 IMoveAction *ma = (IMoveAction*)a;
1902 ma->from_inv.applyCurrentPlayer(player->getName());
1903 ma->to_inv.applyCurrentPlayer(player->getName());
1905 setInventoryModified(ma->from_inv);
1906 setInventoryModified(ma->to_inv);
1908 bool from_inv_is_current_player =
1909 (ma->from_inv.type == InventoryLocation::PLAYER) &&
1910 (ma->from_inv.name == player->getName());
1912 bool to_inv_is_current_player =
1913 (ma->to_inv.type == InventoryLocation::PLAYER) &&
1914 (ma->to_inv.name == player->getName());
1917 Disable moving items out of craftpreview
1919 if(ma->from_list == "craftpreview")
1921 infostream<<"Ignoring IMoveAction from "
1922 <<(ma->from_inv.dump())<<":"<<ma->from_list
1923 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1924 <<" because src is "<<ma->from_list<<std::endl;
1930 Disable moving items into craftresult and craftpreview
1932 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
1934 infostream<<"Ignoring IMoveAction from "
1935 <<(ma->from_inv.dump())<<":"<<ma->from_list
1936 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1937 <<" because dst is "<<ma->to_list<<std::endl;
1942 // Disallow moving items in elsewhere than player's inventory
1943 // if not allowed to interact
1944 if(!checkPriv(player->getName(), "interact") &&
1945 (!from_inv_is_current_player ||
1946 !to_inv_is_current_player))
1948 infostream<<"Cannot move outside of player's inventory: "
1949 <<"No interact privilege"<<std::endl;
1955 Handle restrictions and special cases of the drop action
1957 else if(a->getType() == IACTION_DROP)
1959 IDropAction *da = (IDropAction*)a;
1961 da->from_inv.applyCurrentPlayer(player->getName());
1963 setInventoryModified(da->from_inv);
1966 Disable dropping items out of craftpreview
1968 if(da->from_list == "craftpreview")
1970 infostream<<"Ignoring IDropAction from "
1971 <<(da->from_inv.dump())<<":"<<da->from_list
1972 <<" because src is "<<da->from_list<<std::endl;
1977 // Disallow dropping items if not allowed to interact
1978 if(!checkPriv(player->getName(), "interact"))
1985 Handle restrictions and special cases of the craft action
1987 else if(a->getType() == IACTION_CRAFT)
1989 ICraftAction *ca = (ICraftAction*)a;
1991 ca->craft_inv.applyCurrentPlayer(player->getName());
1993 setInventoryModified(ca->craft_inv);
1995 //bool craft_inv_is_current_player =
1996 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
1997 // (ca->craft_inv.name == player->getName());
1999 // Disallow crafting if not allowed to interact
2000 if(!checkPriv(player->getName(), "interact"))
2002 infostream<<"Cannot craft: "
2003 <<"No interact privilege"<<std::endl;
2010 a->apply(this, playersao, this);
2014 else if(command == TOSERVER_CHAT_MESSAGE)
2022 std::string datastring((char*)&data[2], datasize-2);
2023 std::istringstream is(datastring, std::ios_base::binary);
2026 is.read((char*)buf, 2);
2027 u16 len = readU16(buf);
2029 std::wstring message;
2030 for(u16 i=0; i<len; i++)
2032 is.read((char*)buf, 2);
2033 message += (wchar_t)readU16(buf);
2036 // If something goes wrong, this player is to blame
2037 RollbackScopeActor rollback_scope(m_rollback,
2038 std::string("player:")+player->getName());
2040 // Get player name of this client
2041 std::wstring name = narrow_to_wide(player->getName());
2044 bool ate = m_script->on_chat_message(player->getName(),
2045 wide_to_narrow(message));
2046 // If script ate the message, don't proceed
2050 // Line to send to players
2052 // Whether to send to the player that sent the line
2053 bool send_to_sender_only = false;
2055 // Commands are implemented in Lua, so only catch invalid
2056 // commands that were not "eaten" and send an error back
2057 if(message[0] == L'/')
2059 message = message.substr(1);
2060 send_to_sender_only = true;
2061 if(message.length() == 0)
2062 line += L"-!- Empty command";
2064 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2068 if(checkPriv(player->getName(), "shout")){
2074 line += L"-!- You don't have permission to shout.";
2075 send_to_sender_only = true;
2082 Send the message to sender
2084 if (send_to_sender_only)
2086 SendChatMessage(peer_id, line);
2089 Send the message to others
2093 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2095 std::list<u16> clients = m_clients.getClientIDs();
2097 for(std::list<u16>::iterator
2098 i = clients.begin();
2099 i != clients.end(); ++i)
2102 SendChatMessage(*i, line);
2107 else if(command == TOSERVER_DAMAGE)
2109 std::string datastring((char*)&data[2], datasize-2);
2110 std::istringstream is(datastring, std::ios_base::binary);
2111 u8 damage = readU8(is);
2113 if(g_settings->getBool("enable_damage"))
2115 actionstream<<player->getName()<<" damaged by "
2116 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2119 playersao->setHP(playersao->getHP() - damage);
2121 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2124 if(playersao->m_hp_not_sent)
2125 SendPlayerHP(peer_id);
2128 else if(command == TOSERVER_BREATH)
2130 std::string datastring((char*)&data[2], datasize-2);
2131 std::istringstream is(datastring, std::ios_base::binary);
2132 u16 breath = readU16(is);
2133 playersao->setBreath(breath);
2135 else if(command == TOSERVER_PASSWORD)
2138 [0] u16 TOSERVER_PASSWORD
2139 [2] u8[28] old password
2140 [30] u8[28] new password
2143 if(datasize != 2+PASSWORD_SIZE*2)
2145 /*char password[PASSWORD_SIZE];
2146 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2147 password[i] = data[2+i];
2148 password[PASSWORD_SIZE-1] = 0;*/
2150 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2158 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2160 char c = data[2+PASSWORD_SIZE+i];
2166 if(!base64_is_valid(newpwd)){
2167 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2168 // Wrong old password supplied!!
2169 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2173 infostream<<"Server: Client requests a password change from "
2174 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2176 std::string playername = player->getName();
2178 std::string checkpwd;
2179 m_script->getAuth(playername, &checkpwd, NULL);
2181 if(oldpwd != checkpwd)
2183 infostream<<"Server: invalid old password"<<std::endl;
2184 // Wrong old password supplied!!
2185 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2189 bool success = m_script->setPassword(playername, newpwd);
2191 actionstream<<player->getName()<<" changes password"<<std::endl;
2192 SendChatMessage(peer_id, L"Password change successful.");
2194 actionstream<<player->getName()<<" tries to change password but "
2195 <<"it fails"<<std::endl;
2196 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2199 else if(command == TOSERVER_PLAYERITEM)
2204 u16 item = readU16(&data[2]);
2205 playersao->setWieldIndex(item);
2207 else if(command == TOSERVER_RESPAWN)
2209 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2212 RespawnPlayer(peer_id);
2214 actionstream<<player->getName()<<" respawns at "
2215 <<PP(player->getPosition()/BS)<<std::endl;
2217 // ActiveObject is added to environment in AsyncRunStep after
2218 // the previous addition has been succesfully removed
2220 else if(command == TOSERVER_INTERACT)
2222 std::string datastring((char*)&data[2], datasize-2);
2223 std::istringstream is(datastring, std::ios_base::binary);
2229 [5] u32 length of the next item
2230 [9] serialized PointedThing
2232 0: start digging (from undersurface) or use
2233 1: stop digging (all parameters ignored)
2234 2: digging completed
2235 3: place block or item (to abovesurface)
2238 u8 action = readU8(is);
2239 u16 item_i = readU16(is);
2240 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2241 PointedThing pointed;
2242 pointed.deSerialize(tmp_is);
2244 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2245 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2249 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2250 <<" tried to interact, but is dead!"<<std::endl;
2254 v3f player_pos = playersao->getLastGoodPosition();
2256 // Update wielded item
2257 playersao->setWieldIndex(item_i);
2259 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2260 v3s16 p_under = pointed.node_undersurface;
2261 v3s16 p_above = pointed.node_abovesurface;
2263 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2264 ServerActiveObject *pointed_object = NULL;
2265 if(pointed.type == POINTEDTHING_OBJECT)
2267 pointed_object = m_env->getActiveObject(pointed.object_id);
2268 if(pointed_object == NULL)
2270 verbosestream<<"TOSERVER_INTERACT: "
2271 "pointed object is NULL"<<std::endl;
2277 v3f pointed_pos_under = player_pos;
2278 v3f pointed_pos_above = player_pos;
2279 if(pointed.type == POINTEDTHING_NODE)
2281 pointed_pos_under = intToFloat(p_under, BS);
2282 pointed_pos_above = intToFloat(p_above, BS);
2284 else if(pointed.type == POINTEDTHING_OBJECT)
2286 pointed_pos_under = pointed_object->getBasePosition();
2287 pointed_pos_above = pointed_pos_under;
2291 Check that target is reasonably close
2292 (only when digging or placing things)
2294 if(action == 0 || action == 2 || action == 3)
2296 float d = player_pos.getDistanceFrom(pointed_pos_under);
2297 float max_d = BS * 14; // Just some large enough value
2299 actionstream<<"Player "<<player->getName()
2300 <<" tried to access "<<pointed.dump()
2302 <<"d="<<d<<", max_d="<<max_d
2303 <<". ignoring."<<std::endl;
2304 // Re-send block to revert change on client-side
2305 RemoteClient *client = getClient(peer_id);
2306 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2307 client->SetBlockNotSent(blockpos);
2309 m_script->on_cheat(playersao, "interacted_too_far");
2316 Make sure the player is allowed to do it
2318 if(!checkPriv(player->getName(), "interact"))
2320 actionstream<<player->getName()<<" attempted to interact with "
2321 <<pointed.dump()<<" without 'interact' privilege"
2323 // Re-send block to revert change on client-side
2324 RemoteClient *client = getClient(peer_id);
2325 // Digging completed -> under
2327 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2328 client->SetBlockNotSent(blockpos);
2330 // Placement -> above
2332 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2333 client->SetBlockNotSent(blockpos);
2339 If something goes wrong, this player is to blame
2341 RollbackScopeActor rollback_scope(m_rollback,
2342 std::string("player:")+player->getName());
2345 0: start digging or punch object
2349 if(pointed.type == POINTEDTHING_NODE)
2352 NOTE: This can be used in the future to check if
2353 somebody is cheating, by checking the timing.
2355 MapNode n(CONTENT_IGNORE);
2358 n = m_env->getMap().getNode(p_under);
2360 catch(InvalidPositionException &e)
2362 infostream<<"Server: Not punching: Node not found."
2363 <<" Adding block to emerge queue."
2365 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2367 if(n.getContent() != CONTENT_IGNORE)
2368 m_script->node_on_punch(p_under, n, playersao, pointed);
2370 playersao->noCheatDigStart(p_under);
2372 else if(pointed.type == POINTEDTHING_OBJECT)
2374 // Skip if object has been removed
2375 if(pointed_object->m_removed)
2378 actionstream<<player->getName()<<" punches object "
2379 <<pointed.object_id<<": "
2380 <<pointed_object->getDescription()<<std::endl;
2382 ItemStack punchitem = playersao->getWieldedItem();
2383 ToolCapabilities toolcap =
2384 punchitem.getToolCapabilities(m_itemdef);
2385 v3f dir = (pointed_object->getBasePosition() -
2386 (player->getPosition() + player->getEyeOffset())
2388 float time_from_last_punch =
2389 playersao->resetTimeFromLastPunch();
2390 pointed_object->punch(dir, &toolcap, playersao,
2391 time_from_last_punch);
2399 else if(action == 1)
2404 2: Digging completed
2406 else if(action == 2)
2408 // Only digging of nodes
2409 if(pointed.type == POINTEDTHING_NODE)
2411 MapNode n(CONTENT_IGNORE);
2414 n = m_env->getMap().getNode(p_under);
2416 catch(InvalidPositionException &e)
2418 infostream<<"Server: Not finishing digging: Node not found."
2419 <<" Adding block to emerge queue."
2421 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2424 /* Cheat prevention */
2425 bool is_valid_dig = true;
2426 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2428 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2429 float nocheat_t = playersao->getNoCheatDigTime();
2430 playersao->noCheatDigEnd();
2431 // If player didn't start digging this, ignore dig
2432 if(nocheat_p != p_under){
2433 infostream<<"Server: NoCheat: "<<player->getName()
2434 <<" started digging "
2435 <<PP(nocheat_p)<<" and completed digging "
2436 <<PP(p_under)<<"; not digging."<<std::endl;
2437 is_valid_dig = false;
2439 m_script->on_cheat(playersao, "finished_unknown_dig");
2441 // Get player's wielded item
2442 ItemStack playeritem;
2443 InventoryList *mlist = playersao->getInventory()->getList("main");
2445 playeritem = mlist->getItem(playersao->getWieldIndex());
2446 ToolCapabilities playeritem_toolcap =
2447 playeritem.getToolCapabilities(m_itemdef);
2448 // Get diggability and expected digging time
2449 DigParams params = getDigParams(m_nodedef->get(n).groups,
2450 &playeritem_toolcap);
2451 // If can't dig, try hand
2452 if(!params.diggable){
2453 const ItemDefinition &hand = m_itemdef->get("");
2454 const ToolCapabilities *tp = hand.tool_capabilities;
2456 params = getDigParams(m_nodedef->get(n).groups, tp);
2458 // If can't dig, ignore dig
2459 if(!params.diggable){
2460 infostream<<"Server: NoCheat: "<<player->getName()
2461 <<" completed digging "<<PP(p_under)
2462 <<", which is not diggable with tool. not digging."
2464 is_valid_dig = false;
2466 m_script->on_cheat(playersao, "dug_unbreakable");
2468 // Check digging time
2469 // If already invalidated, we don't have to
2471 // Well not our problem then
2473 // Clean and long dig
2474 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2475 // All is good, but grab time from pool; don't care if
2476 // it's actually available
2477 playersao->getDigPool().grab(params.time);
2479 // Short or laggy dig
2480 // Try getting the time from pool
2481 else if(playersao->getDigPool().grab(params.time)){
2486 infostream<<"Server: NoCheat: "<<player->getName()
2487 <<" completed digging "<<PP(p_under)
2488 <<"too fast; not digging."<<std::endl;
2489 is_valid_dig = false;
2491 m_script->on_cheat(playersao, "dug_too_fast");
2495 /* Actually dig node */
2497 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2498 m_script->node_on_dig(p_under, n, playersao);
2500 // Send unusual result (that is, node not being removed)
2501 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2503 // Re-send block to revert change on client-side
2504 RemoteClient *client = getClient(peer_id);
2505 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2506 client->SetBlockNotSent(blockpos);
2512 3: place block or right-click object
2514 else if(action == 3)
2516 ItemStack item = playersao->getWieldedItem();
2518 // Reset build time counter
2519 if(pointed.type == POINTEDTHING_NODE &&
2520 item.getDefinition(m_itemdef).type == ITEM_NODE)
2521 getClient(peer_id)->m_time_from_building = 0.0;
2523 if(pointed.type == POINTEDTHING_OBJECT)
2525 // Right click object
2527 // Skip if object has been removed
2528 if(pointed_object->m_removed)
2531 actionstream<<player->getName()<<" right-clicks object "
2532 <<pointed.object_id<<": "
2533 <<pointed_object->getDescription()<<std::endl;
2536 pointed_object->rightClick(playersao);
2538 else if(m_script->item_OnPlace(
2539 item, playersao, pointed))
2541 // Placement was handled in lua
2543 // Apply returned ItemStack
2544 playersao->setWieldedItem(item);
2547 // If item has node placement prediction, always send the
2548 // blocks to make sure the client knows what exactly happened
2549 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2550 RemoteClient *client = getClient(peer_id);
2551 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2552 client->SetBlockNotSent(blockpos);
2553 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2554 if(blockpos2 != blockpos){
2555 client->SetBlockNotSent(blockpos2);
2563 else if(action == 4)
2565 ItemStack item = playersao->getWieldedItem();
2567 actionstream<<player->getName()<<" uses "<<item.name
2568 <<", pointing at "<<pointed.dump()<<std::endl;
2570 if(m_script->item_OnUse(
2571 item, playersao, pointed))
2573 // Apply returned ItemStack
2574 playersao->setWieldedItem(item);
2581 Catch invalid actions
2585 infostream<<"WARNING: Server: Invalid action "
2586 <<action<<std::endl;
2589 else if(command == TOSERVER_REMOVED_SOUNDS)
2591 std::string datastring((char*)&data[2], datasize-2);
2592 std::istringstream is(datastring, std::ios_base::binary);
2594 int num = readU16(is);
2595 for(int k=0; k<num; k++){
2596 s32 id = readS32(is);
2597 std::map<s32, ServerPlayingSound>::iterator i =
2598 m_playing_sounds.find(id);
2599 if(i == m_playing_sounds.end())
2601 ServerPlayingSound &psound = i->second;
2602 psound.clients.erase(peer_id);
2603 if(psound.clients.size() == 0)
2604 m_playing_sounds.erase(i++);
2607 else if(command == TOSERVER_NODEMETA_FIELDS)
2609 std::string datastring((char*)&data[2], datasize-2);
2610 std::istringstream is(datastring, std::ios_base::binary);
2612 v3s16 p = readV3S16(is);
2613 std::string formname = deSerializeString(is);
2614 int num = readU16(is);
2615 std::map<std::string, std::string> fields;
2616 for(int k=0; k<num; k++){
2617 std::string fieldname = deSerializeString(is);
2618 std::string fieldvalue = deSerializeLongString(is);
2619 fields[fieldname] = fieldvalue;
2622 // If something goes wrong, this player is to blame
2623 RollbackScopeActor rollback_scope(m_rollback,
2624 std::string("player:")+player->getName());
2626 // Check the target node for rollback data; leave others unnoticed
2627 RollbackNode rn_old(&m_env->getMap(), p, this);
2629 m_script->node_on_receive_fields(p, formname, fields,playersao);
2631 // Report rollback data
2632 RollbackNode rn_new(&m_env->getMap(), p, this);
2633 if(rollback() && rn_new != rn_old){
2634 RollbackAction action;
2635 action.setSetNode(p, rn_old, rn_new);
2636 rollback()->reportAction(action);
2639 else if(command == TOSERVER_INVENTORY_FIELDS)
2641 std::string datastring((char*)&data[2], datasize-2);
2642 std::istringstream is(datastring, std::ios_base::binary);
2644 std::string formname = deSerializeString(is);
2645 int num = readU16(is);
2646 std::map<std::string, std::string> fields;
2647 for(int k=0; k<num; k++){
2648 std::string fieldname = deSerializeString(is);
2649 std::string fieldvalue = deSerializeLongString(is);
2650 fields[fieldname] = fieldvalue;
2653 m_script->on_playerReceiveFields(playersao, formname, fields);
2657 infostream<<"Server::ProcessData(): Ignoring "
2658 "unknown command "<<command<<std::endl;
2662 catch(SendFailedException &e)
2664 errorstream<<"Server::ProcessData(): SendFailedException: "
2670 void Server::setTimeOfDay(u32 time)
2672 m_env->setTimeOfDay(time);
2673 m_time_of_day_send_timer = 0;
2676 void Server::onMapEditEvent(MapEditEvent *event)
2678 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2679 if(m_ignore_map_edit_events)
2681 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2683 MapEditEvent *e = event->clone();
2684 m_unsent_map_edit_queue.push_back(e);
2687 Inventory* Server::getInventory(const InventoryLocation &loc)
2690 case InventoryLocation::UNDEFINED:
2693 case InventoryLocation::CURRENT_PLAYER:
2696 case InventoryLocation::PLAYER:
2698 Player *player = m_env->getPlayer(loc.name.c_str());
2701 PlayerSAO *playersao = player->getPlayerSAO();
2704 return playersao->getInventory();
2707 case InventoryLocation::NODEMETA:
2709 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2712 return meta->getInventory();
2715 case InventoryLocation::DETACHED:
2717 if(m_detached_inventories.count(loc.name) == 0)
2719 return m_detached_inventories[loc.name];
2727 void Server::setInventoryModified(const InventoryLocation &loc)
2730 case InventoryLocation::UNDEFINED:
2733 case InventoryLocation::PLAYER:
2735 Player *player = m_env->getPlayer(loc.name.c_str());
2738 PlayerSAO *playersao = player->getPlayerSAO();
2741 playersao->m_inventory_not_sent = true;
2742 playersao->m_wielded_item_not_sent = true;
2745 case InventoryLocation::NODEMETA:
2747 v3s16 blockpos = getNodeBlockPos(loc.p);
2749 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2751 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2753 setBlockNotSent(blockpos);
2756 case InventoryLocation::DETACHED:
2758 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2766 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2768 std::list<u16> clients = m_clients.getClientIDs();
2770 // Set the modified blocks unsent for all the clients
2771 for (std::list<u16>::iterator
2772 i = clients.begin();
2773 i != clients.end(); ++i) {
2774 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2776 client->SetBlocksNotSent(block);
2781 void Server::peerAdded(con::Peer *peer)
2783 DSTACK(__FUNCTION_NAME);
2784 verbosestream<<"Server::peerAdded(): peer->id="
2785 <<peer->id<<std::endl;
2788 c.type = con::PEER_ADDED;
2789 c.peer_id = peer->id;
2791 m_peer_change_queue.push_back(c);
2794 void Server::deletingPeer(con::Peer *peer, bool timeout)
2796 DSTACK(__FUNCTION_NAME);
2797 verbosestream<<"Server::deletingPeer(): peer->id="
2798 <<peer->id<<", timeout="<<timeout<<std::endl;
2800 m_clients.event(peer->id,Disconnect);
2802 c.type = con::PEER_REMOVED;
2803 c.peer_id = peer->id;
2804 c.timeout = timeout;
2805 m_peer_change_queue.push_back(c);
2808 void Server::handlePeerChanges()
2810 while(m_peer_change_queue.size() > 0)
2812 con::PeerChange c = m_peer_change_queue.pop_front();
2814 verbosestream<<"Server: Handling peer change: "
2815 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2820 case con::PEER_ADDED:
2821 m_clients.CreateClient(c.peer_id);
2824 case con::PEER_REMOVED:
2825 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2829 assert("Invalid peer change event received!" == 0);
2835 void Server::SendMovement(u16 peer_id)
2837 DSTACK(__FUNCTION_NAME);
2838 std::ostringstream os(std::ios_base::binary);
2840 writeU16(os, TOCLIENT_MOVEMENT);
2841 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2842 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2843 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2844 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2845 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2846 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2847 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2848 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2849 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2850 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2851 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2852 writeF1000(os, g_settings->getFloat("movement_gravity"));
2855 std::string s = os.str();
2856 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2858 m_clients.send(peer_id, 0, data, true);
2861 void Server::SendHP(u16 peer_id, u8 hp)
2863 DSTACK(__FUNCTION_NAME);
2864 std::ostringstream os(std::ios_base::binary);
2866 writeU16(os, TOCLIENT_HP);
2870 std::string s = os.str();
2871 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2873 m_clients.send(peer_id, 0, data, true);
2876 void Server::SendBreath(u16 peer_id, u16 breath)
2878 DSTACK(__FUNCTION_NAME);
2879 std::ostringstream os(std::ios_base::binary);
2881 writeU16(os, TOCLIENT_BREATH);
2882 writeU16(os, breath);
2885 std::string s = os.str();
2886 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2888 m_clients.send(peer_id, 0, data, true);
2891 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
2893 DSTACK(__FUNCTION_NAME);
2894 std::ostringstream os(std::ios_base::binary);
2896 writeU16(os, TOCLIENT_ACCESS_DENIED);
2897 os<<serializeWideString(reason);
2900 std::string s = os.str();
2901 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2903 m_clients.send(peer_id, 0, data, true);
2906 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
2907 v3f camera_point_target)
2909 DSTACK(__FUNCTION_NAME);
2910 std::ostringstream os(std::ios_base::binary);
2912 writeU16(os, TOCLIENT_DEATHSCREEN);
2913 writeU8(os, set_camera_point_target);
2914 writeV3F1000(os, camera_point_target);
2917 std::string s = os.str();
2918 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2920 m_clients.send(peer_id, 0, data, true);
2923 void Server::SendItemDef(u16 peer_id,
2924 IItemDefManager *itemdef, u16 protocol_version)
2926 DSTACK(__FUNCTION_NAME);
2927 std::ostringstream os(std::ios_base::binary);
2931 u32 length of the next item
2932 zlib-compressed serialized ItemDefManager
2934 writeU16(os, TOCLIENT_ITEMDEF);
2935 std::ostringstream tmp_os(std::ios::binary);
2936 itemdef->serialize(tmp_os, protocol_version);
2937 std::ostringstream tmp_os2(std::ios::binary);
2938 compressZlib(tmp_os.str(), tmp_os2);
2939 os<<serializeLongString(tmp_os2.str());
2942 std::string s = os.str();
2943 verbosestream<<"Server: Sending item definitions to id("<<peer_id
2944 <<"): size="<<s.size()<<std::endl;
2945 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2947 m_clients.send(peer_id, 0, data, true);
2950 void Server::SendNodeDef(u16 peer_id,
2951 INodeDefManager *nodedef, u16 protocol_version)
2953 DSTACK(__FUNCTION_NAME);
2954 std::ostringstream os(std::ios_base::binary);
2958 u32 length of the next item
2959 zlib-compressed serialized NodeDefManager
2961 writeU16(os, TOCLIENT_NODEDEF);
2962 std::ostringstream tmp_os(std::ios::binary);
2963 nodedef->serialize(tmp_os, protocol_version);
2964 std::ostringstream tmp_os2(std::ios::binary);
2965 compressZlib(tmp_os.str(), tmp_os2);
2966 os<<serializeLongString(tmp_os2.str());
2969 std::string s = os.str();
2970 verbosestream<<"Server: Sending node definitions to id("<<peer_id
2971 <<"): size="<<s.size()<<std::endl;
2972 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2974 m_clients.send(peer_id, 0, data, true);
2978 Non-static send methods
2981 void Server::SendInventory(u16 peer_id)
2983 DSTACK(__FUNCTION_NAME);
2985 PlayerSAO *playersao = getPlayerSAO(peer_id);
2988 playersao->m_inventory_not_sent = false;
2994 std::ostringstream os;
2995 playersao->getInventory()->serialize(os);
2997 std::string s = os.str();
2999 SharedBuffer<u8> data(s.size()+2);
3000 writeU16(&data[0], TOCLIENT_INVENTORY);
3001 memcpy(&data[2], s.c_str(), s.size());
3004 m_clients.send(peer_id, 0, data, true);
3007 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3009 DSTACK(__FUNCTION_NAME);
3011 std::ostringstream os(std::ios_base::binary);
3015 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3016 os.write((char*)buf, 2);
3019 writeU16(buf, message.size());
3020 os.write((char*)buf, 2);
3023 for(u32 i=0; i<message.size(); i++)
3027 os.write((char*)buf, 2);
3031 std::string s = os.str();
3032 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3034 if (peer_id != PEER_ID_INEXISTENT)
3037 m_clients.send(peer_id, 0, data, true);
3041 m_clients.sendToAll(0,data,true);
3045 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3046 const std::string formname)
3048 DSTACK(__FUNCTION_NAME);
3050 std::ostringstream os(std::ios_base::binary);
3054 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3055 os.write((char*)buf, 2);
3056 os<<serializeLongString(formspec);
3057 os<<serializeString(formname);
3060 std::string s = os.str();
3061 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3063 m_clients.send(peer_id, 0, data, true);
3066 // Spawns a particle on peer with peer_id
3067 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3068 float expirationtime, float size, bool collisiondetection,
3069 bool vertical, std::string texture)
3071 DSTACK(__FUNCTION_NAME);
3073 std::ostringstream os(std::ios_base::binary);
3074 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3075 writeV3F1000(os, pos);
3076 writeV3F1000(os, velocity);
3077 writeV3F1000(os, acceleration);
3078 writeF1000(os, expirationtime);
3079 writeF1000(os, size);
3080 writeU8(os, collisiondetection);
3081 os<<serializeLongString(texture);
3082 writeU8(os, vertical);
3085 std::string s = os.str();
3086 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3088 if (peer_id != PEER_ID_INEXISTENT)
3091 m_clients.send(peer_id, 0, data, true);
3095 m_clients.sendToAll(0,data,true);
3099 // Adds a ParticleSpawner on peer with peer_id
3100 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3101 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3102 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3104 DSTACK(__FUNCTION_NAME);
3106 std::ostringstream os(std::ios_base::binary);
3107 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3109 writeU16(os, amount);
3110 writeF1000(os, spawntime);
3111 writeV3F1000(os, minpos);
3112 writeV3F1000(os, maxpos);
3113 writeV3F1000(os, minvel);
3114 writeV3F1000(os, maxvel);
3115 writeV3F1000(os, minacc);
3116 writeV3F1000(os, maxacc);
3117 writeF1000(os, minexptime);
3118 writeF1000(os, maxexptime);
3119 writeF1000(os, minsize);
3120 writeF1000(os, maxsize);
3121 writeU8(os, collisiondetection);
3122 os<<serializeLongString(texture);
3124 writeU8(os, vertical);
3127 std::string s = os.str();
3128 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3130 if (peer_id != PEER_ID_INEXISTENT)
3133 m_clients.send(peer_id, 0, data, true);
3136 m_clients.sendToAll(0,data,true);
3140 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3142 DSTACK(__FUNCTION_NAME);
3144 std::ostringstream os(std::ios_base::binary);
3145 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3150 std::string s = os.str();
3151 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3153 if (peer_id != PEER_ID_INEXISTENT) {
3155 m_clients.send(peer_id, 0, data, true);
3158 m_clients.sendToAll(0,data,true);
3163 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3165 std::ostringstream os(std::ios_base::binary);
3168 writeU16(os, TOCLIENT_HUDADD);
3170 writeU8(os, (u8)form->type);
3171 writeV2F1000(os, form->pos);
3172 os << serializeString(form->name);
3173 writeV2F1000(os, form->scale);
3174 os << serializeString(form->text);
3175 writeU32(os, form->number);
3176 writeU32(os, form->item);
3177 writeU32(os, form->dir);
3178 writeV2F1000(os, form->align);
3179 writeV2F1000(os, form->offset);
3180 writeV3F1000(os, form->world_pos);
3183 std::string s = os.str();
3184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3186 m_clients.send(peer_id, 1, data, true);
3189 void Server::SendHUDRemove(u16 peer_id, u32 id)
3191 std::ostringstream os(std::ios_base::binary);
3194 writeU16(os, TOCLIENT_HUDRM);
3198 std::string s = os.str();
3199 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3202 m_clients.send(peer_id, 1, data, true);
3205 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3207 std::ostringstream os(std::ios_base::binary);
3210 writeU16(os, TOCLIENT_HUDCHANGE);
3212 writeU8(os, (u8)stat);
3215 case HUD_STAT_SCALE:
3216 case HUD_STAT_ALIGN:
3217 case HUD_STAT_OFFSET:
3218 writeV2F1000(os, *(v2f *)value);
3222 os << serializeString(*(std::string *)value);
3224 case HUD_STAT_WORLD_POS:
3225 writeV3F1000(os, *(v3f *)value);
3227 case HUD_STAT_NUMBER:
3231 writeU32(os, *(u32 *)value);
3236 std::string s = os.str();
3237 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3239 m_clients.send(peer_id, 0, data, true);
3242 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3244 std::ostringstream os(std::ios_base::binary);
3247 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3248 writeU32(os, flags);
3252 std::string s = os.str();
3253 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3255 m_clients.send(peer_id, 0, data, true);
3258 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3260 std::ostringstream os(std::ios_base::binary);
3263 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3264 writeU16(os, param);
3265 os<<serializeString(value);
3268 std::string s = os.str();
3269 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3271 m_clients.send(peer_id, 0, data, true);
3274 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3275 const std::string &type, const std::vector<std::string> ¶ms)
3277 std::ostringstream os(std::ios_base::binary);
3280 writeU16(os, TOCLIENT_SET_SKY);
3281 writeARGB8(os, bgcolor);
3282 os<<serializeString(type);
3283 writeU16(os, params.size());
3284 for(size_t i=0; i<params.size(); i++)
3285 os<<serializeString(params[i]);
3288 std::string s = os.str();
3289 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3291 m_clients.send(peer_id, 0, data, true);
3294 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3296 DSTACK(__FUNCTION_NAME);
3299 SharedBuffer<u8> data(2+2+4);
3300 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3301 writeU16(&data[2], time);
3302 writeF1000(&data[4], time_speed);
3304 if (peer_id == PEER_ID_INEXISTENT) {
3305 m_clients.sendToAll(0,data,true);
3309 m_clients.send(peer_id, 0, data, true);
3313 void Server::SendPlayerHP(u16 peer_id)
3315 DSTACK(__FUNCTION_NAME);
3316 PlayerSAO *playersao = getPlayerSAO(peer_id);
3318 playersao->m_hp_not_sent = false;
3319 SendHP(peer_id, playersao->getHP());
3321 // Send to other clients
3322 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3323 ActiveObjectMessage aom(playersao->getId(), true, str);
3324 playersao->m_messages_out.push_back(aom);
3327 void Server::SendPlayerBreath(u16 peer_id)
3329 DSTACK(__FUNCTION_NAME);
3330 PlayerSAO *playersao = getPlayerSAO(peer_id);
3332 playersao->m_breath_not_sent = false;
3333 SendBreath(peer_id, playersao->getBreath());
3336 void Server::SendMovePlayer(u16 peer_id)
3338 DSTACK(__FUNCTION_NAME);
3339 Player *player = m_env->getPlayer(peer_id);
3342 std::ostringstream os(std::ios_base::binary);
3343 writeU16(os, TOCLIENT_MOVE_PLAYER);
3344 writeV3F1000(os, player->getPosition());
3345 writeF1000(os, player->getPitch());
3346 writeF1000(os, player->getYaw());
3349 v3f pos = player->getPosition();
3350 f32 pitch = player->getPitch();
3351 f32 yaw = player->getYaw();
3352 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3353 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3360 std::string s = os.str();
3361 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3363 m_clients.send(peer_id, 0, data, true);
3366 void Server::SendPlayerPrivileges(u16 peer_id)
3368 Player *player = m_env->getPlayer(peer_id);
3370 if(player->peer_id == PEER_ID_INEXISTENT)
3373 std::set<std::string> privs;
3374 m_script->getAuth(player->getName(), NULL, &privs);
3376 std::ostringstream os(std::ios_base::binary);
3377 writeU16(os, TOCLIENT_PRIVILEGES);
3378 writeU16(os, privs.size());
3379 for(std::set<std::string>::const_iterator i = privs.begin();
3380 i != privs.end(); i++){
3381 os<<serializeString(*i);
3385 std::string s = os.str();
3386 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3388 m_clients.send(peer_id, 0, data, true);
3391 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3393 Player *player = m_env->getPlayer(peer_id);
3395 if(player->peer_id == PEER_ID_INEXISTENT)
3398 std::ostringstream os(std::ios_base::binary);
3399 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3400 os<<serializeLongString(player->inventory_formspec);
3403 std::string s = os.str();
3404 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3406 m_clients.send(peer_id, 0, data, true);
3409 s32 Server::playSound(const SimpleSoundSpec &spec,
3410 const ServerSoundParams ¶ms)
3412 // Find out initial position of sound
3413 bool pos_exists = false;
3414 v3f pos = params.getPos(m_env, &pos_exists);
3415 // If position is not found while it should be, cancel sound
3416 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3419 // Filter destination clients
3420 std::list<u16> dst_clients;
3421 if(params.to_player != "")
3423 Player *player = m_env->getPlayer(params.to_player.c_str());
3425 infostream<<"Server::playSound: Player \""<<params.to_player
3426 <<"\" not found"<<std::endl;
3429 if(player->peer_id == PEER_ID_INEXISTENT){
3430 infostream<<"Server::playSound: Player \""<<params.to_player
3431 <<"\" not connected"<<std::endl;
3434 dst_clients.push_back(player->peer_id);
3438 std::list<u16> clients = m_clients.getClientIDs();
3440 for(std::list<u16>::iterator
3441 i = clients.begin(); i != clients.end(); ++i)
3443 Player *player = m_env->getPlayer(*i);
3447 if(player->getPosition().getDistanceFrom(pos) >
3448 params.max_hear_distance)
3451 dst_clients.push_back(*i);
3454 if(dst_clients.size() == 0)
3458 s32 id = m_next_sound_id++;
3459 // The sound will exist as a reference in m_playing_sounds
3460 m_playing_sounds[id] = ServerPlayingSound();
3461 ServerPlayingSound &psound = m_playing_sounds[id];
3462 psound.params = params;
3463 for(std::list<u16>::iterator i = dst_clients.begin();
3464 i != dst_clients.end(); i++)
3465 psound.clients.insert(*i);
3467 std::ostringstream os(std::ios_base::binary);
3468 writeU16(os, TOCLIENT_PLAY_SOUND);
3470 os<<serializeString(spec.name);
3471 writeF1000(os, spec.gain * params.gain);
3472 writeU8(os, params.type);
3473 writeV3F1000(os, pos);
3474 writeU16(os, params.object);
3475 writeU8(os, params.loop);
3477 std::string s = os.str();
3478 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3480 for(std::list<u16>::iterator i = dst_clients.begin();
3481 i != dst_clients.end(); i++){
3483 m_clients.send(*i, 0, data, true);
3487 void Server::stopSound(s32 handle)
3489 // Get sound reference
3490 std::map<s32, ServerPlayingSound>::iterator i =
3491 m_playing_sounds.find(handle);
3492 if(i == m_playing_sounds.end())
3494 ServerPlayingSound &psound = i->second;
3496 std::ostringstream os(std::ios_base::binary);
3497 writeU16(os, TOCLIENT_STOP_SOUND);
3498 writeS32(os, handle);
3500 std::string s = os.str();
3501 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3503 for(std::set<u16>::iterator i = psound.clients.begin();
3504 i != psound.clients.end(); i++){
3506 m_clients.send(*i, 0, data, true);
3508 // Remove sound reference
3509 m_playing_sounds.erase(i);
3512 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3513 std::list<u16> *far_players, float far_d_nodes)
3515 float maxd = far_d_nodes*BS;
3516 v3f p_f = intToFloat(p, BS);
3520 SharedBuffer<u8> reply(replysize);
3521 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3522 writeS16(&reply[2], p.X);
3523 writeS16(&reply[4], p.Y);
3524 writeS16(&reply[6], p.Z);
3526 std::list<u16> clients = m_clients.getClientIDs();
3527 for(std::list<u16>::iterator
3528 i = clients.begin();
3529 i != clients.end(); ++i)
3534 Player *player = m_env->getPlayer(*i);
3537 // If player is far away, only set modified blocks not sent
3538 v3f player_pos = player->getPosition();
3539 if(player_pos.getDistanceFrom(p_f) > maxd)
3541 far_players->push_back(*i);
3548 m_clients.send(*i, 0, reply, true);
3552 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3553 std::list<u16> *far_players, float far_d_nodes,
3554 bool remove_metadata)
3556 float maxd = far_d_nodes*BS;
3557 v3f p_f = intToFloat(p, BS);
3559 std::list<u16> clients = m_clients.getClientIDs();
3560 for(std::list<u16>::iterator
3561 i = clients.begin();
3562 i != clients.end(); ++i)
3568 Player *player = m_env->getPlayer(*i);
3571 // If player is far away, only set modified blocks not sent
3572 v3f player_pos = player->getPosition();
3573 if(player_pos.getDistanceFrom(p_f) > maxd)
3575 far_players->push_back(*i);
3580 SharedBuffer<u8> reply(0);
3582 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3586 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3587 reply = SharedBuffer<u8>(replysize);
3588 writeU16(&reply[0], TOCLIENT_ADDNODE);
3589 writeS16(&reply[2], p.X);
3590 writeS16(&reply[4], p.Y);
3591 writeS16(&reply[6], p.Z);
3592 n.serialize(&reply[8], client->serialization_version);
3593 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3594 writeU8(&reply[index], remove_metadata ? 0 : 1);
3596 if (!remove_metadata) {
3597 if (client->net_proto_version <= 21) {
3598 // Old clients always clear metadata; fix it
3599 // by sending the full block again.
3600 client->SetBlockNotSent(p);
3607 if (reply.getSize() > 0)
3608 m_clients.send(*i, 0, reply, true);
3612 void Server::setBlockNotSent(v3s16 p)
3614 std::list<u16> clients = m_clients.getClientIDs();
3616 for(std::list<u16>::iterator
3617 i = clients.begin();
3618 i != clients.end(); ++i)
3620 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3621 client->SetBlockNotSent(p);
3626 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3628 DSTACK(__FUNCTION_NAME);
3630 v3s16 p = block->getPos();
3634 bool completely_air = true;
3635 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3636 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3637 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3639 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3641 completely_air = false;
3642 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3647 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3649 infostream<<"[completely air] ";
3650 infostream<<std::endl;
3654 Create a packet with the block in the right format
3657 std::ostringstream os(std::ios_base::binary);
3658 block->serialize(os, ver, false);
3659 block->serializeNetworkSpecific(os, net_proto_version);
3660 std::string s = os.str();
3661 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3663 u32 replysize = 8 + blockdata.getSize();
3664 SharedBuffer<u8> reply(replysize);
3665 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3666 writeS16(&reply[2], p.X);
3667 writeS16(&reply[4], p.Y);
3668 writeS16(&reply[6], p.Z);
3669 memcpy(&reply[8], *blockdata, blockdata.getSize());
3671 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3672 <<": \tpacket size: "<<replysize<<std::endl;*/
3677 m_clients.send(peer_id, 2, reply, true);
3680 void Server::SendBlocks(float dtime)
3682 DSTACK(__FUNCTION_NAME);
3684 JMutexAutoLock envlock(m_env_mutex);
3685 //TODO check if one big lock could be faster then multiple small ones
3687 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3689 std::vector<PrioritySortedBlockTransfer> queue;
3691 s32 total_sending = 0;
3694 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3696 std::list<u16> clients = m_clients.getClientIDs();
3699 for(std::list<u16>::iterator
3700 i = clients.begin();
3701 i != clients.end(); ++i)
3703 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3708 total_sending += client->SendingCount();
3709 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3715 // Lowest priority number comes first.
3716 // Lowest is most important.
3717 std::sort(queue.begin(), queue.end());
3720 for(u32 i=0; i<queue.size(); i++)
3722 //TODO: Calculate limit dynamically
3723 if(total_sending >= g_settings->getS32
3724 ("max_simultaneous_block_sends_server_total"))
3727 PrioritySortedBlockTransfer q = queue[i];
3729 MapBlock *block = NULL;
3732 block = m_env->getMap().getBlockNoCreate(q.pos);
3734 catch(InvalidPositionException &e)
3739 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3744 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3746 client->SentBlock(q.pos);
3752 void Server::fillMediaCache()
3754 DSTACK(__FUNCTION_NAME);
3756 infostream<<"Server: Calculating media file checksums"<<std::endl;
3758 // Collect all media file paths
3759 std::list<std::string> paths;
3760 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3761 i != m_mods.end(); i++){
3762 const ModSpec &mod = *i;
3763 paths.push_back(mod.path + DIR_DELIM + "textures");
3764 paths.push_back(mod.path + DIR_DELIM + "sounds");
3765 paths.push_back(mod.path + DIR_DELIM + "media");
3766 paths.push_back(mod.path + DIR_DELIM + "models");
3768 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3770 // Collect media file information from paths into cache
3771 for(std::list<std::string>::iterator i = paths.begin();
3772 i != paths.end(); i++)
3774 std::string mediapath = *i;
3775 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3776 for(u32 j=0; j<dirlist.size(); j++){
3777 if(dirlist[j].dir) // Ignode dirs
3779 std::string filename = dirlist[j].name;
3780 // If name contains illegal characters, ignore the file
3781 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3782 infostream<<"Server: ignoring illegal file name: \""
3783 <<filename<<"\""<<std::endl;
3786 // If name is not in a supported format, ignore it
3787 const char *supported_ext[] = {
3788 ".png", ".jpg", ".bmp", ".tga",
3789 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3791 ".x", ".b3d", ".md2", ".obj",
3794 if(removeStringEnd(filename, supported_ext) == ""){
3795 infostream<<"Server: ignoring unsupported file extension: \""
3796 <<filename<<"\""<<std::endl;
3799 // Ok, attempt to load the file and add to cache
3800 std::string filepath = mediapath + DIR_DELIM + filename;
3802 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3803 if(fis.good() == false){
3804 errorstream<<"Server::fillMediaCache(): Could not open \""
3805 <<filename<<"\" for reading"<<std::endl;
3808 std::ostringstream tmp_os(std::ios_base::binary);
3812 fis.read(buf, 1024);
3813 std::streamsize len = fis.gcount();
3814 tmp_os.write(buf, len);
3823 errorstream<<"Server::fillMediaCache(): Failed to read \""
3824 <<filename<<"\""<<std::endl;
3827 if(tmp_os.str().length() == 0){
3828 errorstream<<"Server::fillMediaCache(): Empty file \""
3829 <<filepath<<"\""<<std::endl;
3834 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3836 unsigned char *digest = sha1.getDigest();
3837 std::string sha1_base64 = base64_encode(digest, 20);
3838 std::string sha1_hex = hex_encode((char*)digest, 20);
3842 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3843 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3848 struct SendableMediaAnnouncement
3851 std::string sha1_digest;
3853 SendableMediaAnnouncement(const std::string name_="",
3854 const std::string sha1_digest_=""):
3856 sha1_digest(sha1_digest_)
3860 void Server::sendMediaAnnouncement(u16 peer_id)
3862 DSTACK(__FUNCTION_NAME);
3864 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
3867 std::list<SendableMediaAnnouncement> file_announcements;
3869 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
3870 i != m_media.end(); i++){
3872 file_announcements.push_back(
3873 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
3877 std::ostringstream os(std::ios_base::binary);
3885 u16 length of sha1_digest
3890 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
3891 writeU16(os, file_announcements.size());
3893 for(std::list<SendableMediaAnnouncement>::iterator
3894 j = file_announcements.begin();
3895 j != file_announcements.end(); ++j){
3896 os<<serializeString(j->name);
3897 os<<serializeString(j->sha1_digest);
3899 os<<serializeString(g_settings->get("remote_media"));
3902 std::string s = os.str();
3903 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3906 m_clients.send(peer_id, 0, data, true);
3909 struct SendableMedia
3915 SendableMedia(const std::string &name_="", const std::string path_="",
3916 const std::string &data_=""):
3923 void Server::sendRequestedMedia(u16 peer_id,
3924 const std::list<std::string> &tosend)
3926 DSTACK(__FUNCTION_NAME);
3928 verbosestream<<"Server::sendRequestedMedia(): "
3929 <<"Sending files to client"<<std::endl;
3933 // Put 5kB in one bunch (this is not accurate)
3934 u32 bytes_per_bunch = 5000;
3936 std::vector< std::list<SendableMedia> > file_bunches;
3937 file_bunches.push_back(std::list<SendableMedia>());
3939 u32 file_size_bunch_total = 0;
3941 for(std::list<std::string>::const_iterator i = tosend.begin();
3942 i != tosend.end(); ++i)
3944 const std::string &name = *i;
3946 if(m_media.find(name) == m_media.end()){
3947 errorstream<<"Server::sendRequestedMedia(): Client asked for "
3948 <<"unknown file \""<<(name)<<"\""<<std::endl;
3952 //TODO get path + name
3953 std::string tpath = m_media[name].path;
3956 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
3957 if(fis.good() == false){
3958 errorstream<<"Server::sendRequestedMedia(): Could not open \""
3959 <<tpath<<"\" for reading"<<std::endl;
3962 std::ostringstream tmp_os(std::ios_base::binary);
3966 fis.read(buf, 1024);
3967 std::streamsize len = fis.gcount();
3968 tmp_os.write(buf, len);
3969 file_size_bunch_total += len;
3978 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
3979 <<name<<"\""<<std::endl;
3982 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
3983 <<tname<<"\""<<std::endl;*/
3985 file_bunches[file_bunches.size()-1].push_back(
3986 SendableMedia(name, tpath, tmp_os.str()));
3988 // Start next bunch if got enough data
3989 if(file_size_bunch_total >= bytes_per_bunch){
3990 file_bunches.push_back(std::list<SendableMedia>());
3991 file_size_bunch_total = 0;
3996 /* Create and send packets */
3998 u32 num_bunches = file_bunches.size();
3999 for(u32 i=0; i<num_bunches; i++)
4001 std::ostringstream os(std::ios_base::binary);
4005 u16 total number of texture bunches
4006 u16 index of this bunch
4007 u32 number of files in this bunch
4016 writeU16(os, TOCLIENT_MEDIA);
4017 writeU16(os, num_bunches);
4019 writeU32(os, file_bunches[i].size());
4021 for(std::list<SendableMedia>::iterator
4022 j = file_bunches[i].begin();
4023 j != file_bunches[i].end(); ++j){
4024 os<<serializeString(j->name);
4025 os<<serializeLongString(j->data);
4029 std::string s = os.str();
4030 verbosestream<<"Server::sendRequestedMedia(): bunch "
4031 <<i<<"/"<<num_bunches
4032 <<" files="<<file_bunches[i].size()
4033 <<" size=" <<s.size()<<std::endl;
4034 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4036 m_clients.send(peer_id, 2, data, true);
4040 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4042 if(m_detached_inventories.count(name) == 0){
4043 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4046 Inventory *inv = m_detached_inventories[name];
4048 std::ostringstream os(std::ios_base::binary);
4049 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4050 os<<serializeString(name);
4054 std::string s = os.str();
4055 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4057 if (peer_id != PEER_ID_INEXISTENT)
4060 m_clients.send(peer_id, 0, data, true);
4064 m_clients.sendToAll(0,data,true);
4068 void Server::sendDetachedInventories(u16 peer_id)
4070 DSTACK(__FUNCTION_NAME);
4072 for(std::map<std::string, Inventory*>::iterator
4073 i = m_detached_inventories.begin();
4074 i != m_detached_inventories.end(); i++){
4075 const std::string &name = i->first;
4076 //Inventory *inv = i->second;
4077 sendDetachedInventory(name, peer_id);
4085 void Server::DiePlayer(u16 peer_id)
4087 DSTACK(__FUNCTION_NAME);
4089 PlayerSAO *playersao = getPlayerSAO(peer_id);
4092 infostream<<"Server::DiePlayer(): Player "
4093 <<playersao->getPlayer()->getName()
4094 <<" dies"<<std::endl;
4096 playersao->setHP(0);
4098 // Trigger scripted stuff
4099 m_script->on_dieplayer(playersao);
4101 SendPlayerHP(peer_id);
4102 SendDeathscreen(peer_id, false, v3f(0,0,0));
4105 void Server::RespawnPlayer(u16 peer_id)
4107 DSTACK(__FUNCTION_NAME);
4109 PlayerSAO *playersao = getPlayerSAO(peer_id);
4112 infostream<<"Server::RespawnPlayer(): Player "
4113 <<playersao->getPlayer()->getName()
4114 <<" respawns"<<std::endl;
4116 playersao->setHP(PLAYER_MAX_HP);
4118 bool repositioned = m_script->on_respawnplayer(playersao);
4120 v3f pos = findSpawnPos(m_env->getServerMap());
4121 playersao->setPos(pos);
4125 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4127 DSTACK(__FUNCTION_NAME);
4129 SendAccessDenied(peer_id, reason);
4130 m_clients.event(peer_id,SetDenied);
4131 m_con.DisconnectPeer(peer_id);
4134 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4136 DSTACK(__FUNCTION_NAME);
4137 std::wstring message;
4140 Clear references to playing sounds
4142 for(std::map<s32, ServerPlayingSound>::iterator
4143 i = m_playing_sounds.begin();
4144 i != m_playing_sounds.end();)
4146 ServerPlayingSound &psound = i->second;
4147 psound.clients.erase(peer_id);
4148 if(psound.clients.size() == 0)
4149 m_playing_sounds.erase(i++);
4154 Player *player = m_env->getPlayer(peer_id);
4156 // Collect information about leaving in chat
4158 if(player != NULL && reason != CDR_DENY)
4160 std::wstring name = narrow_to_wide(player->getName());
4163 message += L" left the game.";
4164 if(reason == CDR_TIMEOUT)
4165 message += L" (timed out)";
4169 /* Run scripts and remove from environment */
4173 PlayerSAO *playersao = player->getPlayerSAO();
4176 m_script->on_leaveplayer(playersao);
4178 playersao->disconnected();
4186 if(player != NULL && reason != CDR_DENY)
4188 std::ostringstream os(std::ios_base::binary);
4189 std::list<u16> clients = m_clients.getClientIDs();
4191 for(std::list<u16>::iterator
4192 i = clients.begin();
4193 i != clients.end(); ++i)
4196 Player *player = m_env->getPlayer(*i);
4199 // Get name of player
4200 os<<player->getName()<<" ";
4203 actionstream<<player->getName()<<" "
4204 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4205 <<" List of players: "<<os.str()<<std::endl;
4209 m_clients.DeleteClient(peer_id);
4210 m_env_mutex.Unlock();
4213 // Send leave chat message to all remaining clients
4214 if(message.length() != 0)
4215 SendChatMessage(PEER_ID_INEXISTENT,message);
4218 void Server::UpdateCrafting(u16 peer_id)
4220 DSTACK(__FUNCTION_NAME);
4222 Player* player = m_env->getPlayer(peer_id);
4225 // Get a preview for crafting
4227 InventoryLocation loc;
4228 loc.setPlayer(player->getName());
4229 getCraftingResult(&player->inventory, preview, false, this);
4230 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4232 // Put the new preview in
4233 InventoryList *plist = player->inventory.getList("craftpreview");
4235 assert(plist->getSize() >= 1);
4236 plist->changeItem(0, preview);
4239 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4241 RemoteClient *client = getClientNoEx(peer_id,state_min);
4243 throw ClientNotFoundException("Client not found");
4247 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4249 return m_clients.getClientNoEx(peer_id, state_min);
4252 std::string Server::getPlayerName(u16 peer_id)
4254 Player *player = m_env->getPlayer(peer_id);
4256 return "[id="+itos(peer_id)+"]";
4257 return player->getName();
4260 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4262 Player *player = m_env->getPlayer(peer_id);
4265 return player->getPlayerSAO();
4268 std::wstring Server::getStatusString()
4270 std::wostringstream os(std::ios_base::binary);
4273 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4275 os<<L", uptime="<<m_uptime.get();
4277 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4278 // Information about clients
4281 std::list<u16> clients = m_clients.getClientIDs();
4282 for(std::list<u16>::iterator i = clients.begin();
4283 i != clients.end(); ++i)
4286 Player *player = m_env->getPlayer(*i);
4287 // Get name of player
4288 std::wstring name = L"unknown";
4290 name = narrow_to_wide(player->getName());
4291 // Add name to information string
4299 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4300 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4301 if(g_settings->get("motd") != "")
4302 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4306 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4308 std::set<std::string> privs;
4309 m_script->getAuth(name, NULL, &privs);
4313 bool Server::checkPriv(const std::string &name, const std::string &priv)
4315 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4316 return (privs.count(priv) != 0);
4319 void Server::reportPrivsModified(const std::string &name)
4322 std::list<u16> clients = m_clients.getClientIDs();
4323 for(std::list<u16>::iterator
4324 i = clients.begin();
4325 i != clients.end(); ++i){
4326 Player *player = m_env->getPlayer(*i);
4327 reportPrivsModified(player->getName());
4330 Player *player = m_env->getPlayer(name.c_str());
4333 SendPlayerPrivileges(player->peer_id);
4334 PlayerSAO *sao = player->getPlayerSAO();
4337 sao->updatePrivileges(
4338 getPlayerEffectivePrivs(name),
4343 void Server::reportInventoryFormspecModified(const std::string &name)
4345 Player *player = m_env->getPlayer(name.c_str());
4348 SendPlayerInventoryFormspec(player->peer_id);
4351 void Server::setIpBanned(const std::string &ip, const std::string &name)
4353 m_banmanager->add(ip, name);
4356 void Server::unsetIpBanned(const std::string &ip_or_name)
4358 m_banmanager->remove(ip_or_name);
4361 std::string Server::getBanDescription(const std::string &ip_or_name)
4363 return m_banmanager->getBanDescription(ip_or_name);
4366 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4368 Player *player = m_env->getPlayer(name);
4372 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4374 SendChatMessage(player->peer_id, msg);
4377 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4379 Player *player = m_env->getPlayer(playername);
4383 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4387 SendShowFormspecMessage(player->peer_id, formspec, formname);
4391 u32 Server::hudAdd(Player *player, HudElement *form) {
4395 u32 id = player->getFreeHudID();
4396 if (id < player->hud.size())
4397 player->hud[id] = form;
4399 player->hud.push_back(form);
4401 SendHUDAdd(player->peer_id, id, form);
4405 bool Server::hudRemove(Player *player, u32 id) {
4406 if (!player || id >= player->hud.size() || !player->hud[id])
4409 delete player->hud[id];
4410 player->hud[id] = NULL;
4412 SendHUDRemove(player->peer_id, id);
4416 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4420 SendHUDChange(player->peer_id, id, stat, data);
4424 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4428 SendHUDSetFlags(player->peer_id, flags, mask);
4432 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4435 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4438 std::ostringstream os(std::ios::binary);
4439 writeS32(os, hotbar_itemcount);
4440 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4444 void Server::hudSetHotbarImage(Player *player, std::string name) {
4448 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4451 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4455 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4458 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4459 const std::string &type, const std::vector<std::string> ¶ms)
4464 SendSetSky(player->peer_id, bgcolor, type, params);
4468 void Server::notifyPlayers(const std::wstring msg)
4470 SendChatMessage(PEER_ID_INEXISTENT,msg);
4473 void Server::spawnParticle(const char *playername, v3f pos,
4474 v3f velocity, v3f acceleration,
4475 float expirationtime, float size, bool
4476 collisiondetection, bool vertical, std::string texture)
4478 Player *player = m_env->getPlayer(playername);
4481 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4482 expirationtime, size, collisiondetection, vertical, texture);
4485 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4486 float expirationtime, float size,
4487 bool collisiondetection, bool vertical, std::string texture)
4489 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4490 expirationtime, size, collisiondetection, vertical, texture);
4493 u32 Server::addParticleSpawner(const char *playername,
4494 u16 amount, float spawntime,
4495 v3f minpos, v3f maxpos,
4496 v3f minvel, v3f maxvel,
4497 v3f minacc, v3f maxacc,
4498 float minexptime, float maxexptime,
4499 float minsize, float maxsize,
4500 bool collisiondetection, bool vertical, std::string texture)
4502 Player *player = m_env->getPlayer(playername);
4507 for(;;) // look for unused particlespawner id
4510 if (std::find(m_particlespawner_ids.begin(),
4511 m_particlespawner_ids.end(), id)
4512 == m_particlespawner_ids.end())
4514 m_particlespawner_ids.push_back(id);
4519 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4520 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4521 minexptime, maxexptime, minsize, maxsize,
4522 collisiondetection, vertical, texture, id);
4527 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4528 v3f minpos, v3f maxpos,
4529 v3f minvel, v3f maxvel,
4530 v3f minacc, v3f maxacc,
4531 float minexptime, float maxexptime,
4532 float minsize, float maxsize,
4533 bool collisiondetection, bool vertical, std::string texture)
4536 for(;;) // look for unused particlespawner id
4539 if (std::find(m_particlespawner_ids.begin(),
4540 m_particlespawner_ids.end(), id)
4541 == m_particlespawner_ids.end())
4543 m_particlespawner_ids.push_back(id);
4548 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4549 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4550 minexptime, maxexptime, minsize, maxsize,
4551 collisiondetection, vertical, texture, id);
4556 void Server::deleteParticleSpawner(const char *playername, u32 id)
4558 Player *player = m_env->getPlayer(playername);
4562 m_particlespawner_ids.erase(
4563 std::remove(m_particlespawner_ids.begin(),
4564 m_particlespawner_ids.end(), id),
4565 m_particlespawner_ids.end());
4566 SendDeleteParticleSpawner(player->peer_id, id);
4569 void Server::deleteParticleSpawnerAll(u32 id)
4571 m_particlespawner_ids.erase(
4572 std::remove(m_particlespawner_ids.begin(),
4573 m_particlespawner_ids.end(), id),
4574 m_particlespawner_ids.end());
4575 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4578 Inventory* Server::createDetachedInventory(const std::string &name)
4580 if(m_detached_inventories.count(name) > 0){
4581 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4582 delete m_detached_inventories[name];
4584 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4586 Inventory *inv = new Inventory(m_itemdef);
4588 m_detached_inventories[name] = inv;
4589 //TODO find a better way to do this
4590 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4597 BoolScopeSet(bool *dst, bool val):
4600 m_orig_state = *m_dst;
4605 *m_dst = m_orig_state;
4612 // actions: time-reversed list
4613 // Return value: success/failure
4614 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4615 std::list<std::string> *log)
4617 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4618 ServerMap *map = (ServerMap*)(&m_env->getMap());
4619 // Disable rollback report sink while reverting
4620 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4622 // Fail if no actions to handle
4623 if(actions.empty()){
4624 log->push_back("Nothing to do.");
4631 for(std::list<RollbackAction>::const_iterator
4632 i = actions.begin();
4633 i != actions.end(); i++)
4635 const RollbackAction &action = *i;
4637 bool success = action.applyRevert(map, this, this);
4640 std::ostringstream os;
4641 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4642 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4644 log->push_back(os.str());
4646 std::ostringstream os;
4647 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4648 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4650 log->push_back(os.str());
4654 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4655 <<" failed"<<std::endl;
4657 // Call it done if less than half failed
4658 return num_failed <= num_tried/2;
4661 // IGameDef interface
4663 IItemDefManager* Server::getItemDefManager()
4667 INodeDefManager* Server::getNodeDefManager()
4671 ICraftDefManager* Server::getCraftDefManager()
4675 ITextureSource* Server::getTextureSource()
4679 IShaderSource* Server::getShaderSource()
4683 u16 Server::allocateUnknownNodeId(const std::string &name)
4685 return m_nodedef->allocateDummy(name);
4687 ISoundManager* Server::getSoundManager()
4689 return &dummySoundManager;
4691 MtEventManager* Server::getEventManager()
4695 IRollbackReportSink* Server::getRollbackReportSink()
4697 if(!m_enable_rollback_recording)
4699 if(!m_rollback_sink_enabled)
4704 IWritableItemDefManager* Server::getWritableItemDefManager()
4708 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4712 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4717 const ModSpec* Server::getModSpec(const std::string &modname)
4719 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4720 i != m_mods.end(); i++){
4721 const ModSpec &mod = *i;
4722 if(mod.name == modname)
4727 void Server::getModNames(std::list<std::string> &modlist)
4729 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4731 modlist.push_back(i->name);
4734 std::string Server::getBuiltinLuaPath()
4736 return porting::path_share + DIR_DELIM + "builtin";
4739 v3f findSpawnPos(ServerMap &map)
4741 //return v3f(50,50,50)*BS;
4746 nodepos = v2s16(0,0);
4751 s16 water_level = map.m_mgparams->water_level;
4753 // Try to find a good place a few times
4754 for(s32 i=0; i<1000; i++)
4757 // We're going to try to throw the player to this position
4758 v2s16 nodepos2d = v2s16(
4759 -range + (myrand() % (range * 2)),
4760 -range + (myrand() % (range * 2)));
4762 // Get ground height at point
4763 s16 groundheight = map.findGroundLevel(nodepos2d);
4764 if (groundheight <= water_level) // Don't go underwater
4766 if (groundheight > water_level + 6) // Don't go to high places
4769 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4770 bool is_good = false;
4772 for (s32 i = 0; i < 10; i++) {
4773 v3s16 blockpos = getNodeBlockPos(nodepos);
4774 map.emergeBlock(blockpos, true);
4775 content_t c = map.getNodeNoEx(nodepos).getContent();
4776 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4778 if (air_count >= 2){
4786 // Found a good place
4787 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4793 return intToFloat(nodepos, BS);
4796 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4798 RemotePlayer *player = NULL;
4799 bool newplayer = false;
4802 Try to get an existing player
4804 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4806 // If player is already connected, cancel
4807 if(player != NULL && player->peer_id != 0)
4809 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4814 If player with the wanted peer_id already exists, cancel.
4816 if(m_env->getPlayer(peer_id) != NULL)
4818 infostream<<"emergePlayer(): Player with wrong name but same"
4819 " peer_id already exists"<<std::endl;
4824 Create a new player if it doesn't exist yet
4829 player = new RemotePlayer(this);
4830 player->updateName(name);
4832 /* Set player position */
4833 infostream<<"Server: Finding spawn place for player \""
4834 <<name<<"\""<<std::endl;
4835 v3f pos = findSpawnPos(m_env->getServerMap());
4836 player->setPosition(pos);
4838 /* Add player to environment */
4839 m_env->addPlayer(player);
4843 Create a new player active object
4845 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4846 getPlayerEffectivePrivs(player->getName()),
4849 /* Clean up old HUD elements from previous sessions */
4850 player->hud.clear();
4852 /* Add object to environment */
4853 m_env->addActiveObject(playersao);
4857 m_script->on_newplayer(playersao);
4862 void dedicated_server_loop(Server &server, bool &kill)
4864 DSTACK(__FUNCTION_NAME);
4866 verbosestream<<"dedicated_server_loop()"<<std::endl;
4868 IntervalLimiter m_profiler_interval;
4872 float steplen = g_settings->getFloat("dedicated_server_step");
4873 // This is kind of a hack but can be done like this
4874 // because server.step() is very light
4876 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4877 sleep_ms((int)(steplen*1000.0));
4879 server.step(steplen);
4881 if(server.getShutdownRequested() || kill)
4883 infostream<<"Dedicated server quitting"<<std::endl;
4885 if(g_settings->getBool("server_announce") == true)
4886 ServerList::sendAnnounce("delete");
4894 float profiler_print_interval =
4895 g_settings->getFloat("profiler_print_interval");
4896 if(profiler_print_interval != 0)
4898 if(m_profiler_interval.step(steplen, profiler_print_interval))
4900 infostream<<"Profiler:"<<std::endl;
4901 g_profiler->print(infostream);
4902 g_profiler->clear();