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::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3276 DSTACK(__FUNCTION_NAME);
3279 SharedBuffer<u8> data(2+2+4);
3280 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3281 writeU16(&data[2], time);
3282 writeF1000(&data[4], time_speed);
3284 if (peer_id == PEER_ID_INEXISTENT) {
3285 m_clients.sendToAll(0,data,true);
3289 m_clients.send(peer_id, 0, data, true);
3293 void Server::SendPlayerHP(u16 peer_id)
3295 DSTACK(__FUNCTION_NAME);
3296 PlayerSAO *playersao = getPlayerSAO(peer_id);
3298 playersao->m_hp_not_sent = false;
3299 SendHP(peer_id, playersao->getHP());
3301 // Send to other clients
3302 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3303 ActiveObjectMessage aom(playersao->getId(), true, str);
3304 playersao->m_messages_out.push_back(aom);
3307 void Server::SendPlayerBreath(u16 peer_id)
3309 DSTACK(__FUNCTION_NAME);
3310 PlayerSAO *playersao = getPlayerSAO(peer_id);
3312 playersao->m_breath_not_sent = false;
3313 SendBreath(peer_id, playersao->getBreath());
3316 void Server::SendMovePlayer(u16 peer_id)
3318 DSTACK(__FUNCTION_NAME);
3319 Player *player = m_env->getPlayer(peer_id);
3322 std::ostringstream os(std::ios_base::binary);
3323 writeU16(os, TOCLIENT_MOVE_PLAYER);
3324 writeV3F1000(os, player->getPosition());
3325 writeF1000(os, player->getPitch());
3326 writeF1000(os, player->getYaw());
3329 v3f pos = player->getPosition();
3330 f32 pitch = player->getPitch();
3331 f32 yaw = player->getYaw();
3332 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3333 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3340 std::string s = os.str();
3341 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3343 m_clients.send(peer_id, 0, data, true);
3346 void Server::SendPlayerPrivileges(u16 peer_id)
3348 Player *player = m_env->getPlayer(peer_id);
3350 if(player->peer_id == PEER_ID_INEXISTENT)
3353 std::set<std::string> privs;
3354 m_script->getAuth(player->getName(), NULL, &privs);
3356 std::ostringstream os(std::ios_base::binary);
3357 writeU16(os, TOCLIENT_PRIVILEGES);
3358 writeU16(os, privs.size());
3359 for(std::set<std::string>::const_iterator i = privs.begin();
3360 i != privs.end(); i++){
3361 os<<serializeString(*i);
3365 std::string s = os.str();
3366 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3368 m_clients.send(peer_id, 0, data, true);
3371 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3373 Player *player = m_env->getPlayer(peer_id);
3375 if(player->peer_id == PEER_ID_INEXISTENT)
3378 std::ostringstream os(std::ios_base::binary);
3379 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3380 os<<serializeLongString(player->inventory_formspec);
3383 std::string s = os.str();
3384 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3386 m_clients.send(peer_id, 0, data, true);
3389 s32 Server::playSound(const SimpleSoundSpec &spec,
3390 const ServerSoundParams ¶ms)
3392 // Find out initial position of sound
3393 bool pos_exists = false;
3394 v3f pos = params.getPos(m_env, &pos_exists);
3395 // If position is not found while it should be, cancel sound
3396 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3399 // Filter destination clients
3400 std::list<u16> dst_clients;
3401 if(params.to_player != "")
3403 Player *player = m_env->getPlayer(params.to_player.c_str());
3405 infostream<<"Server::playSound: Player \""<<params.to_player
3406 <<"\" not found"<<std::endl;
3409 if(player->peer_id == PEER_ID_INEXISTENT){
3410 infostream<<"Server::playSound: Player \""<<params.to_player
3411 <<"\" not connected"<<std::endl;
3414 dst_clients.push_back(player->peer_id);
3418 std::list<u16> clients = m_clients.getClientIDs();
3420 for(std::list<u16>::iterator
3421 i = clients.begin(); i != clients.end(); ++i)
3423 Player *player = m_env->getPlayer(*i);
3427 if(player->getPosition().getDistanceFrom(pos) >
3428 params.max_hear_distance)
3431 dst_clients.push_back(*i);
3434 if(dst_clients.size() == 0)
3438 s32 id = m_next_sound_id++;
3439 // The sound will exist as a reference in m_playing_sounds
3440 m_playing_sounds[id] = ServerPlayingSound();
3441 ServerPlayingSound &psound = m_playing_sounds[id];
3442 psound.params = params;
3443 for(std::list<u16>::iterator i = dst_clients.begin();
3444 i != dst_clients.end(); i++)
3445 psound.clients.insert(*i);
3447 std::ostringstream os(std::ios_base::binary);
3448 writeU16(os, TOCLIENT_PLAY_SOUND);
3450 os<<serializeString(spec.name);
3451 writeF1000(os, spec.gain * params.gain);
3452 writeU8(os, params.type);
3453 writeV3F1000(os, pos);
3454 writeU16(os, params.object);
3455 writeU8(os, params.loop);
3457 std::string s = os.str();
3458 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3460 for(std::list<u16>::iterator i = dst_clients.begin();
3461 i != dst_clients.end(); i++){
3463 m_clients.send(*i, 0, data, true);
3467 void Server::stopSound(s32 handle)
3469 // Get sound reference
3470 std::map<s32, ServerPlayingSound>::iterator i =
3471 m_playing_sounds.find(handle);
3472 if(i == m_playing_sounds.end())
3474 ServerPlayingSound &psound = i->second;
3476 std::ostringstream os(std::ios_base::binary);
3477 writeU16(os, TOCLIENT_STOP_SOUND);
3478 writeS32(os, handle);
3480 std::string s = os.str();
3481 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3483 for(std::set<u16>::iterator i = psound.clients.begin();
3484 i != psound.clients.end(); i++){
3486 m_clients.send(*i, 0, data, true);
3488 // Remove sound reference
3489 m_playing_sounds.erase(i);
3492 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3493 std::list<u16> *far_players, float far_d_nodes)
3495 float maxd = far_d_nodes*BS;
3496 v3f p_f = intToFloat(p, BS);
3500 SharedBuffer<u8> reply(replysize);
3501 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3502 writeS16(&reply[2], p.X);
3503 writeS16(&reply[4], p.Y);
3504 writeS16(&reply[6], p.Z);
3506 std::list<u16> clients = m_clients.getClientIDs();
3507 for(std::list<u16>::iterator
3508 i = clients.begin();
3509 i != clients.end(); ++i)
3514 Player *player = m_env->getPlayer(*i);
3517 // If player is far away, only set modified blocks not sent
3518 v3f player_pos = player->getPosition();
3519 if(player_pos.getDistanceFrom(p_f) > maxd)
3521 far_players->push_back(*i);
3528 m_clients.send(*i, 0, reply, true);
3532 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3533 std::list<u16> *far_players, float far_d_nodes,
3534 bool remove_metadata)
3536 float maxd = far_d_nodes*BS;
3537 v3f p_f = intToFloat(p, BS);
3539 std::list<u16> clients = m_clients.getClientIDs();
3540 for(std::list<u16>::iterator
3541 i = clients.begin();
3542 i != clients.end(); ++i)
3548 Player *player = m_env->getPlayer(*i);
3551 // If player is far away, only set modified blocks not sent
3552 v3f player_pos = player->getPosition();
3553 if(player_pos.getDistanceFrom(p_f) > maxd)
3555 far_players->push_back(*i);
3560 SharedBuffer<u8> reply(0);
3562 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3566 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3567 reply = SharedBuffer<u8>(replysize);
3568 writeU16(&reply[0], TOCLIENT_ADDNODE);
3569 writeS16(&reply[2], p.X);
3570 writeS16(&reply[4], p.Y);
3571 writeS16(&reply[6], p.Z);
3572 n.serialize(&reply[8], client->serialization_version);
3573 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3574 writeU8(&reply[index], remove_metadata ? 0 : 1);
3576 if (!remove_metadata) {
3577 if (client->net_proto_version <= 21) {
3578 // Old clients always clear metadata; fix it
3579 // by sending the full block again.
3580 client->SetBlockNotSent(p);
3587 if (reply.getSize() > 0)
3588 m_clients.send(*i, 0, reply, true);
3592 void Server::setBlockNotSent(v3s16 p)
3594 std::list<u16> clients = m_clients.getClientIDs();
3596 for(std::list<u16>::iterator
3597 i = clients.begin();
3598 i != clients.end(); ++i)
3600 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3601 client->SetBlockNotSent(p);
3606 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3608 DSTACK(__FUNCTION_NAME);
3610 v3s16 p = block->getPos();
3614 bool completely_air = true;
3615 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3616 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3617 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3619 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3621 completely_air = false;
3622 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3627 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3629 infostream<<"[completely air] ";
3630 infostream<<std::endl;
3634 Create a packet with the block in the right format
3637 std::ostringstream os(std::ios_base::binary);
3638 block->serialize(os, ver, false);
3639 block->serializeNetworkSpecific(os, net_proto_version);
3640 std::string s = os.str();
3641 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3643 u32 replysize = 8 + blockdata.getSize();
3644 SharedBuffer<u8> reply(replysize);
3645 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3646 writeS16(&reply[2], p.X);
3647 writeS16(&reply[4], p.Y);
3648 writeS16(&reply[6], p.Z);
3649 memcpy(&reply[8], *blockdata, blockdata.getSize());
3651 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3652 <<": \tpacket size: "<<replysize<<std::endl;*/
3657 m_clients.send(peer_id, 2, reply, true);
3660 void Server::SendBlocks(float dtime)
3662 DSTACK(__FUNCTION_NAME);
3664 JMutexAutoLock envlock(m_env_mutex);
3665 //TODO check if one big lock could be faster then multiple small ones
3667 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3669 std::vector<PrioritySortedBlockTransfer> queue;
3671 s32 total_sending = 0;
3674 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3676 std::list<u16> clients = m_clients.getClientIDs();
3679 for(std::list<u16>::iterator
3680 i = clients.begin();
3681 i != clients.end(); ++i)
3683 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3688 total_sending += client->SendingCount();
3689 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3695 // Lowest priority number comes first.
3696 // Lowest is most important.
3697 std::sort(queue.begin(), queue.end());
3700 for(u32 i=0; i<queue.size(); i++)
3702 //TODO: Calculate limit dynamically
3703 if(total_sending >= g_settings->getS32
3704 ("max_simultaneous_block_sends_server_total"))
3707 PrioritySortedBlockTransfer q = queue[i];
3709 MapBlock *block = NULL;
3712 block = m_env->getMap().getBlockNoCreate(q.pos);
3714 catch(InvalidPositionException &e)
3719 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3724 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3726 client->SentBlock(q.pos);
3732 void Server::fillMediaCache()
3734 DSTACK(__FUNCTION_NAME);
3736 infostream<<"Server: Calculating media file checksums"<<std::endl;
3738 // Collect all media file paths
3739 std::list<std::string> paths;
3740 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3741 i != m_mods.end(); i++){
3742 const ModSpec &mod = *i;
3743 paths.push_back(mod.path + DIR_DELIM + "textures");
3744 paths.push_back(mod.path + DIR_DELIM + "sounds");
3745 paths.push_back(mod.path + DIR_DELIM + "media");
3746 paths.push_back(mod.path + DIR_DELIM + "models");
3748 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3750 // Collect media file information from paths into cache
3751 for(std::list<std::string>::iterator i = paths.begin();
3752 i != paths.end(); i++)
3754 std::string mediapath = *i;
3755 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3756 for(u32 j=0; j<dirlist.size(); j++){
3757 if(dirlist[j].dir) // Ignode dirs
3759 std::string filename = dirlist[j].name;
3760 // If name contains illegal characters, ignore the file
3761 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3762 infostream<<"Server: ignoring illegal file name: \""
3763 <<filename<<"\""<<std::endl;
3766 // If name is not in a supported format, ignore it
3767 const char *supported_ext[] = {
3768 ".png", ".jpg", ".bmp", ".tga",
3769 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3771 ".x", ".b3d", ".md2", ".obj",
3774 if(removeStringEnd(filename, supported_ext) == ""){
3775 infostream<<"Server: ignoring unsupported file extension: \""
3776 <<filename<<"\""<<std::endl;
3779 // Ok, attempt to load the file and add to cache
3780 std::string filepath = mediapath + DIR_DELIM + filename;
3782 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3783 if(fis.good() == false){
3784 errorstream<<"Server::fillMediaCache(): Could not open \""
3785 <<filename<<"\" for reading"<<std::endl;
3788 std::ostringstream tmp_os(std::ios_base::binary);
3792 fis.read(buf, 1024);
3793 std::streamsize len = fis.gcount();
3794 tmp_os.write(buf, len);
3803 errorstream<<"Server::fillMediaCache(): Failed to read \""
3804 <<filename<<"\""<<std::endl;
3807 if(tmp_os.str().length() == 0){
3808 errorstream<<"Server::fillMediaCache(): Empty file \""
3809 <<filepath<<"\""<<std::endl;
3814 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3816 unsigned char *digest = sha1.getDigest();
3817 std::string sha1_base64 = base64_encode(digest, 20);
3818 std::string sha1_hex = hex_encode((char*)digest, 20);
3822 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3823 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3828 struct SendableMediaAnnouncement
3831 std::string sha1_digest;
3833 SendableMediaAnnouncement(const std::string name_="",
3834 const std::string sha1_digest_=""):
3836 sha1_digest(sha1_digest_)
3840 void Server::sendMediaAnnouncement(u16 peer_id)
3842 DSTACK(__FUNCTION_NAME);
3844 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
3847 std::list<SendableMediaAnnouncement> file_announcements;
3849 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
3850 i != m_media.end(); i++){
3852 file_announcements.push_back(
3853 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
3857 std::ostringstream os(std::ios_base::binary);
3865 u16 length of sha1_digest
3870 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
3871 writeU16(os, file_announcements.size());
3873 for(std::list<SendableMediaAnnouncement>::iterator
3874 j = file_announcements.begin();
3875 j != file_announcements.end(); ++j){
3876 os<<serializeString(j->name);
3877 os<<serializeString(j->sha1_digest);
3879 os<<serializeString(g_settings->get("remote_media"));
3882 std::string s = os.str();
3883 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3886 m_clients.send(peer_id, 0, data, true);
3889 struct SendableMedia
3895 SendableMedia(const std::string &name_="", const std::string path_="",
3896 const std::string &data_=""):
3903 void Server::sendRequestedMedia(u16 peer_id,
3904 const std::list<std::string> &tosend)
3906 DSTACK(__FUNCTION_NAME);
3908 verbosestream<<"Server::sendRequestedMedia(): "
3909 <<"Sending files to client"<<std::endl;
3913 // Put 5kB in one bunch (this is not accurate)
3914 u32 bytes_per_bunch = 5000;
3916 std::vector< std::list<SendableMedia> > file_bunches;
3917 file_bunches.push_back(std::list<SendableMedia>());
3919 u32 file_size_bunch_total = 0;
3921 for(std::list<std::string>::const_iterator i = tosend.begin();
3922 i != tosend.end(); ++i)
3924 const std::string &name = *i;
3926 if(m_media.find(name) == m_media.end()){
3927 errorstream<<"Server::sendRequestedMedia(): Client asked for "
3928 <<"unknown file \""<<(name)<<"\""<<std::endl;
3932 //TODO get path + name
3933 std::string tpath = m_media[name].path;
3936 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
3937 if(fis.good() == false){
3938 errorstream<<"Server::sendRequestedMedia(): Could not open \""
3939 <<tpath<<"\" for reading"<<std::endl;
3942 std::ostringstream tmp_os(std::ios_base::binary);
3946 fis.read(buf, 1024);
3947 std::streamsize len = fis.gcount();
3948 tmp_os.write(buf, len);
3949 file_size_bunch_total += len;
3958 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
3959 <<name<<"\""<<std::endl;
3962 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
3963 <<tname<<"\""<<std::endl;*/
3965 file_bunches[file_bunches.size()-1].push_back(
3966 SendableMedia(name, tpath, tmp_os.str()));
3968 // Start next bunch if got enough data
3969 if(file_size_bunch_total >= bytes_per_bunch){
3970 file_bunches.push_back(std::list<SendableMedia>());
3971 file_size_bunch_total = 0;
3976 /* Create and send packets */
3978 u32 num_bunches = file_bunches.size();
3979 for(u32 i=0; i<num_bunches; i++)
3981 std::ostringstream os(std::ios_base::binary);
3985 u16 total number of texture bunches
3986 u16 index of this bunch
3987 u32 number of files in this bunch
3996 writeU16(os, TOCLIENT_MEDIA);
3997 writeU16(os, num_bunches);
3999 writeU32(os, file_bunches[i].size());
4001 for(std::list<SendableMedia>::iterator
4002 j = file_bunches[i].begin();
4003 j != file_bunches[i].end(); ++j){
4004 os<<serializeString(j->name);
4005 os<<serializeLongString(j->data);
4009 std::string s = os.str();
4010 verbosestream<<"Server::sendRequestedMedia(): bunch "
4011 <<i<<"/"<<num_bunches
4012 <<" files="<<file_bunches[i].size()
4013 <<" size=" <<s.size()<<std::endl;
4014 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4016 m_clients.send(peer_id, 2, data, true);
4020 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4022 if(m_detached_inventories.count(name) == 0){
4023 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4026 Inventory *inv = m_detached_inventories[name];
4028 std::ostringstream os(std::ios_base::binary);
4029 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4030 os<<serializeString(name);
4034 std::string s = os.str();
4035 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4037 if (peer_id != PEER_ID_INEXISTENT)
4040 m_clients.send(peer_id, 0, data, true);
4044 m_clients.sendToAll(0,data,true);
4048 void Server::sendDetachedInventories(u16 peer_id)
4050 DSTACK(__FUNCTION_NAME);
4052 for(std::map<std::string, Inventory*>::iterator
4053 i = m_detached_inventories.begin();
4054 i != m_detached_inventories.end(); i++){
4055 const std::string &name = i->first;
4056 //Inventory *inv = i->second;
4057 sendDetachedInventory(name, peer_id);
4065 void Server::DiePlayer(u16 peer_id)
4067 DSTACK(__FUNCTION_NAME);
4069 PlayerSAO *playersao = getPlayerSAO(peer_id);
4072 infostream<<"Server::DiePlayer(): Player "
4073 <<playersao->getPlayer()->getName()
4074 <<" dies"<<std::endl;
4076 playersao->setHP(0);
4078 // Trigger scripted stuff
4079 m_script->on_dieplayer(playersao);
4081 SendPlayerHP(peer_id);
4082 SendDeathscreen(peer_id, false, v3f(0,0,0));
4085 void Server::RespawnPlayer(u16 peer_id)
4087 DSTACK(__FUNCTION_NAME);
4089 PlayerSAO *playersao = getPlayerSAO(peer_id);
4092 infostream<<"Server::RespawnPlayer(): Player "
4093 <<playersao->getPlayer()->getName()
4094 <<" respawns"<<std::endl;
4096 playersao->setHP(PLAYER_MAX_HP);
4098 bool repositioned = m_script->on_respawnplayer(playersao);
4100 v3f pos = findSpawnPos(m_env->getServerMap());
4101 playersao->setPos(pos);
4105 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4107 DSTACK(__FUNCTION_NAME);
4109 SendAccessDenied(peer_id, reason);
4110 m_clients.event(peer_id,SetDenied);
4111 m_con.DisconnectPeer(peer_id);
4114 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4116 DSTACK(__FUNCTION_NAME);
4117 std::wstring message;
4120 Clear references to playing sounds
4122 for(std::map<s32, ServerPlayingSound>::iterator
4123 i = m_playing_sounds.begin();
4124 i != m_playing_sounds.end();)
4126 ServerPlayingSound &psound = i->second;
4127 psound.clients.erase(peer_id);
4128 if(psound.clients.size() == 0)
4129 m_playing_sounds.erase(i++);
4134 Player *player = m_env->getPlayer(peer_id);
4136 // Collect information about leaving in chat
4138 if(player != NULL && reason != CDR_DENY)
4140 std::wstring name = narrow_to_wide(player->getName());
4143 message += L" left the game.";
4144 if(reason == CDR_TIMEOUT)
4145 message += L" (timed out)";
4149 /* Run scripts and remove from environment */
4153 PlayerSAO *playersao = player->getPlayerSAO();
4156 m_script->on_leaveplayer(playersao);
4158 playersao->disconnected();
4166 if(player != NULL && reason != CDR_DENY)
4168 std::ostringstream os(std::ios_base::binary);
4169 std::list<u16> clients = m_clients.getClientIDs();
4171 for(std::list<u16>::iterator
4172 i = clients.begin();
4173 i != clients.end(); ++i)
4176 Player *player = m_env->getPlayer(*i);
4179 // Get name of player
4180 os<<player->getName()<<" ";
4183 actionstream<<player->getName()<<" "
4184 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4185 <<" List of players: "<<os.str()<<std::endl;
4189 m_clients.DeleteClient(peer_id);
4190 m_env_mutex.Unlock();
4193 // Send leave chat message to all remaining clients
4194 if(message.length() != 0)
4195 SendChatMessage(PEER_ID_INEXISTENT,message);
4198 void Server::UpdateCrafting(u16 peer_id)
4200 DSTACK(__FUNCTION_NAME);
4202 Player* player = m_env->getPlayer(peer_id);
4205 // Get a preview for crafting
4207 InventoryLocation loc;
4208 loc.setPlayer(player->getName());
4209 getCraftingResult(&player->inventory, preview, false, this);
4210 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4212 // Put the new preview in
4213 InventoryList *plist = player->inventory.getList("craftpreview");
4215 assert(plist->getSize() >= 1);
4216 plist->changeItem(0, preview);
4219 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4221 RemoteClient *client = getClientNoEx(peer_id,state_min);
4223 throw ClientNotFoundException("Client not found");
4227 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4229 return m_clients.getClientNoEx(peer_id, state_min);
4232 std::string Server::getPlayerName(u16 peer_id)
4234 Player *player = m_env->getPlayer(peer_id);
4236 return "[id="+itos(peer_id)+"]";
4237 return player->getName();
4240 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4242 Player *player = m_env->getPlayer(peer_id);
4245 return player->getPlayerSAO();
4248 std::wstring Server::getStatusString()
4250 std::wostringstream os(std::ios_base::binary);
4253 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4255 os<<L", uptime="<<m_uptime.get();
4257 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4258 // Information about clients
4261 std::list<u16> clients = m_clients.getClientIDs();
4262 for(std::list<u16>::iterator i = clients.begin();
4263 i != clients.end(); ++i)
4266 Player *player = m_env->getPlayer(*i);
4267 // Get name of player
4268 std::wstring name = L"unknown";
4270 name = narrow_to_wide(player->getName());
4271 // Add name to information string
4279 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4280 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4281 if(g_settings->get("motd") != "")
4282 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4286 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4288 std::set<std::string> privs;
4289 m_script->getAuth(name, NULL, &privs);
4293 bool Server::checkPriv(const std::string &name, const std::string &priv)
4295 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4296 return (privs.count(priv) != 0);
4299 void Server::reportPrivsModified(const std::string &name)
4302 std::list<u16> clients = m_clients.getClientIDs();
4303 for(std::list<u16>::iterator
4304 i = clients.begin();
4305 i != clients.end(); ++i){
4306 Player *player = m_env->getPlayer(*i);
4307 reportPrivsModified(player->getName());
4310 Player *player = m_env->getPlayer(name.c_str());
4313 SendPlayerPrivileges(player->peer_id);
4314 PlayerSAO *sao = player->getPlayerSAO();
4317 sao->updatePrivileges(
4318 getPlayerEffectivePrivs(name),
4323 void Server::reportInventoryFormspecModified(const std::string &name)
4325 Player *player = m_env->getPlayer(name.c_str());
4328 SendPlayerInventoryFormspec(player->peer_id);
4331 void Server::setIpBanned(const std::string &ip, const std::string &name)
4333 m_banmanager->add(ip, name);
4336 void Server::unsetIpBanned(const std::string &ip_or_name)
4338 m_banmanager->remove(ip_or_name);
4341 std::string Server::getBanDescription(const std::string &ip_or_name)
4343 return m_banmanager->getBanDescription(ip_or_name);
4346 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4348 Player *player = m_env->getPlayer(name);
4352 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4354 SendChatMessage(player->peer_id, msg);
4357 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4359 Player *player = m_env->getPlayer(playername);
4363 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4367 SendShowFormspecMessage(player->peer_id, formspec, formname);
4371 u32 Server::hudAdd(Player *player, HudElement *form) {
4375 u32 id = player->getFreeHudID();
4376 if (id < player->hud.size())
4377 player->hud[id] = form;
4379 player->hud.push_back(form);
4381 SendHUDAdd(player->peer_id, id, form);
4385 bool Server::hudRemove(Player *player, u32 id) {
4386 if (!player || id >= player->hud.size() || !player->hud[id])
4389 delete player->hud[id];
4390 player->hud[id] = NULL;
4392 SendHUDRemove(player->peer_id, id);
4396 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4400 SendHUDChange(player->peer_id, id, stat, data);
4404 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4408 SendHUDSetFlags(player->peer_id, flags, mask);
4412 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4415 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4418 std::ostringstream os(std::ios::binary);
4419 writeS32(os, hotbar_itemcount);
4420 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4424 void Server::hudSetHotbarImage(Player *player, std::string name) {
4428 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4431 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4435 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4438 void Server::notifyPlayers(const std::wstring msg)
4440 SendChatMessage(PEER_ID_INEXISTENT,msg);
4443 void Server::spawnParticle(const char *playername, v3f pos,
4444 v3f velocity, v3f acceleration,
4445 float expirationtime, float size, bool
4446 collisiondetection, bool vertical, std::string texture)
4448 Player *player = m_env->getPlayer(playername);
4451 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4452 expirationtime, size, collisiondetection, vertical, texture);
4455 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4456 float expirationtime, float size,
4457 bool collisiondetection, bool vertical, std::string texture)
4459 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4460 expirationtime, size, collisiondetection, vertical, texture);
4463 u32 Server::addParticleSpawner(const char *playername,
4464 u16 amount, float spawntime,
4465 v3f minpos, v3f maxpos,
4466 v3f minvel, v3f maxvel,
4467 v3f minacc, v3f maxacc,
4468 float minexptime, float maxexptime,
4469 float minsize, float maxsize,
4470 bool collisiondetection, bool vertical, std::string texture)
4472 Player *player = m_env->getPlayer(playername);
4477 for(;;) // look for unused particlespawner id
4480 if (std::find(m_particlespawner_ids.begin(),
4481 m_particlespawner_ids.end(), id)
4482 == m_particlespawner_ids.end())
4484 m_particlespawner_ids.push_back(id);
4489 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4490 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4491 minexptime, maxexptime, minsize, maxsize,
4492 collisiondetection, vertical, texture, id);
4497 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4498 v3f minpos, v3f maxpos,
4499 v3f minvel, v3f maxvel,
4500 v3f minacc, v3f maxacc,
4501 float minexptime, float maxexptime,
4502 float minsize, float maxsize,
4503 bool collisiondetection, bool vertical, std::string texture)
4506 for(;;) // look for unused particlespawner id
4509 if (std::find(m_particlespawner_ids.begin(),
4510 m_particlespawner_ids.end(), id)
4511 == m_particlespawner_ids.end())
4513 m_particlespawner_ids.push_back(id);
4518 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4519 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4520 minexptime, maxexptime, minsize, maxsize,
4521 collisiondetection, vertical, texture, id);
4526 void Server::deleteParticleSpawner(const char *playername, u32 id)
4528 Player *player = m_env->getPlayer(playername);
4532 m_particlespawner_ids.erase(
4533 std::remove(m_particlespawner_ids.begin(),
4534 m_particlespawner_ids.end(), id),
4535 m_particlespawner_ids.end());
4536 SendDeleteParticleSpawner(player->peer_id, id);
4539 void Server::deleteParticleSpawnerAll(u32 id)
4541 m_particlespawner_ids.erase(
4542 std::remove(m_particlespawner_ids.begin(),
4543 m_particlespawner_ids.end(), id),
4544 m_particlespawner_ids.end());
4545 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4548 Inventory* Server::createDetachedInventory(const std::string &name)
4550 if(m_detached_inventories.count(name) > 0){
4551 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4552 delete m_detached_inventories[name];
4554 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4556 Inventory *inv = new Inventory(m_itemdef);
4558 m_detached_inventories[name] = inv;
4559 //TODO find a better way to do this
4560 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4567 BoolScopeSet(bool *dst, bool val):
4570 m_orig_state = *m_dst;
4575 *m_dst = m_orig_state;
4582 // actions: time-reversed list
4583 // Return value: success/failure
4584 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4585 std::list<std::string> *log)
4587 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4588 ServerMap *map = (ServerMap*)(&m_env->getMap());
4589 // Disable rollback report sink while reverting
4590 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4592 // Fail if no actions to handle
4593 if(actions.empty()){
4594 log->push_back("Nothing to do.");
4601 for(std::list<RollbackAction>::const_iterator
4602 i = actions.begin();
4603 i != actions.end(); i++)
4605 const RollbackAction &action = *i;
4607 bool success = action.applyRevert(map, this, this);
4610 std::ostringstream os;
4611 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4612 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4614 log->push_back(os.str());
4616 std::ostringstream os;
4617 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4618 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4620 log->push_back(os.str());
4624 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4625 <<" failed"<<std::endl;
4627 // Call it done if less than half failed
4628 return num_failed <= num_tried/2;
4631 // IGameDef interface
4633 IItemDefManager* Server::getItemDefManager()
4637 INodeDefManager* Server::getNodeDefManager()
4641 ICraftDefManager* Server::getCraftDefManager()
4645 ITextureSource* Server::getTextureSource()
4649 IShaderSource* Server::getShaderSource()
4653 u16 Server::allocateUnknownNodeId(const std::string &name)
4655 return m_nodedef->allocateDummy(name);
4657 ISoundManager* Server::getSoundManager()
4659 return &dummySoundManager;
4661 MtEventManager* Server::getEventManager()
4665 IRollbackReportSink* Server::getRollbackReportSink()
4667 if(!m_enable_rollback_recording)
4669 if(!m_rollback_sink_enabled)
4674 IWritableItemDefManager* Server::getWritableItemDefManager()
4678 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4682 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4687 const ModSpec* Server::getModSpec(const std::string &modname)
4689 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4690 i != m_mods.end(); i++){
4691 const ModSpec &mod = *i;
4692 if(mod.name == modname)
4697 void Server::getModNames(std::list<std::string> &modlist)
4699 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4701 modlist.push_back(i->name);
4704 std::string Server::getBuiltinLuaPath()
4706 return porting::path_share + DIR_DELIM + "builtin";
4709 v3f findSpawnPos(ServerMap &map)
4711 //return v3f(50,50,50)*BS;
4716 nodepos = v2s16(0,0);
4721 s16 water_level = map.m_mgparams->water_level;
4723 // Try to find a good place a few times
4724 for(s32 i=0; i<1000; i++)
4727 // We're going to try to throw the player to this position
4728 v2s16 nodepos2d = v2s16(
4729 -range + (myrand() % (range * 2)),
4730 -range + (myrand() % (range * 2)));
4732 // Get ground height at point
4733 s16 groundheight = map.findGroundLevel(nodepos2d);
4734 if (groundheight <= water_level) // Don't go underwater
4736 if (groundheight > water_level + 6) // Don't go to high places
4739 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4740 bool is_good = false;
4742 for (s32 i = 0; i < 10; i++) {
4743 v3s16 blockpos = getNodeBlockPos(nodepos);
4744 map.emergeBlock(blockpos, true);
4745 content_t c = map.getNodeNoEx(nodepos).getContent();
4746 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4748 if (air_count >= 2){
4756 // Found a good place
4757 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4763 return intToFloat(nodepos, BS);
4766 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4768 RemotePlayer *player = NULL;
4769 bool newplayer = false;
4772 Try to get an existing player
4774 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4776 // If player is already connected, cancel
4777 if(player != NULL && player->peer_id != 0)
4779 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4784 If player with the wanted peer_id already exists, cancel.
4786 if(m_env->getPlayer(peer_id) != NULL)
4788 infostream<<"emergePlayer(): Player with wrong name but same"
4789 " peer_id already exists"<<std::endl;
4794 Create a new player if it doesn't exist yet
4799 player = new RemotePlayer(this);
4800 player->updateName(name);
4802 /* Set player position */
4803 infostream<<"Server: Finding spawn place for player \""
4804 <<name<<"\""<<std::endl;
4805 v3f pos = findSpawnPos(m_env->getServerMap());
4806 player->setPosition(pos);
4808 /* Add player to environment */
4809 m_env->addPlayer(player);
4813 Create a new player active object
4815 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4816 getPlayerEffectivePrivs(player->getName()),
4819 /* Clean up old HUD elements from previous sessions */
4820 player->hud.clear();
4822 /* Add object to environment */
4823 m_env->addActiveObject(playersao);
4827 m_script->on_newplayer(playersao);
4832 void dedicated_server_loop(Server &server, bool &kill)
4834 DSTACK(__FUNCTION_NAME);
4836 verbosestream<<"dedicated_server_loop()"<<std::endl;
4838 IntervalLimiter m_profiler_interval;
4842 float steplen = g_settings->getFloat("dedicated_server_step");
4843 // This is kind of a hack but can be done like this
4844 // because server.step() is very light
4846 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4847 sleep_ms((int)(steplen*1000.0));
4849 server.step(steplen);
4851 if(server.getShutdownRequested() || kill)
4853 infostream<<"Dedicated server quitting"<<std::endl;
4855 if(g_settings->getBool("server_announce") == true)
4856 ServerList::sendAnnounce("delete");
4864 float profiler_print_interval =
4865 g_settings->getFloat("profiler_print_interval");
4866 if(profiler_print_interval != 0)
4868 if(m_profiler_interval.step(steplen, profiler_print_interval))
4870 infostream<<"Profiler:"<<std::endl;
4871 g_profiler->print(infostream);
4872 g_profiler->clear();