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 // Load the mapgen params from global settings now after any
345 // initial overrides have been set by the mods
346 m_emerge->loadMapgenParams();
348 // Initialize Environment
349 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
350 m_env = new ServerEnvironment(servermap, m_script, this);
352 m_clients.setEnv(m_env);
354 // Run some callbacks after the MG params have been set up but before activation
355 m_script->environment_OnMapgenInit(&m_emerge->params);
357 // Initialize mapgens
358 m_emerge->initMapgens();
360 // Give environment reference to scripting api
361 m_script->initializeEnvironment(m_env);
363 // Register us to receive map edit events
364 servermap->addEventReceiver(this);
366 // If file exists, load environment metadata
367 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
369 infostream<<"Server: Loading environment metadata"<<std::endl;
370 m_env->loadMeta(m_path_world);
374 infostream<<"Server: Loading players"<<std::endl;
375 m_env->deSerializePlayers(m_path_world);
378 Add some test ActiveBlockModifiers to environment
380 add_legacy_abms(m_env, m_nodedef);
382 m_liquid_transform_every = g_settings->getFloat("liquid_update");
387 infostream<<"Server destructing"<<std::endl;
390 Send shutdown message
393 std::wstring line = L"*** Server shutting down";
394 SendChatMessage(PEER_ID_INEXISTENT, line);
398 JMutexAutoLock envlock(m_env_mutex);
401 Execute script shutdown hooks
403 m_script->on_shutdown();
407 JMutexAutoLock envlock(m_env_mutex);
412 infostream<<"Server: Saving players"<<std::endl;
413 m_env->serializePlayers(m_path_world);
416 Save environment metadata
418 infostream<<"Server: Saving environment metadata"<<std::endl;
419 m_env->saveMeta(m_path_world);
428 // stop all emerge threads before deleting players that may have
429 // requested blocks to be emerged
430 m_emerge->stopThreads();
432 // Delete things in the reverse order of creation
435 // N.B. the EmergeManager should be deleted after the Environment since Map
436 // depends on EmergeManager to write its current params to the map meta
445 // Deinitialize scripting
446 infostream<<"Server: Deinitializing scripting"<<std::endl;
449 // Delete detached inventories
451 for(std::map<std::string, Inventory*>::iterator
452 i = m_detached_inventories.begin();
453 i != m_detached_inventories.end(); i++){
459 void Server::start(Address bind_addr)
461 DSTACK(__FUNCTION_NAME);
462 infostream<<"Starting server on "
463 << bind_addr.serializeString() <<"..."<<std::endl;
465 // Stop thread if already running
468 // Initialize connection
469 m_con.SetTimeoutMs(30);
470 m_con.Serve(bind_addr);
475 // ASCII art for the win!
477 <<" .__ __ __ "<<std::endl
478 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
479 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
480 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
481 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
482 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
483 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
484 actionstream<<"Server for gameid=\""<<m_gamespec.id
485 <<"\" listening on "<<bind_addr.serializeString()<<":"
486 <<bind_addr.getPort() << "."<<std::endl;
491 DSTACK(__FUNCTION_NAME);
493 infostream<<"Server: Stopping and waiting threads"<<std::endl;
495 // Stop threads (set run=false first so both start stopping)
497 //m_emergethread.setRun(false);
499 //m_emergethread.stop();
501 infostream<<"Server: Threads stopped"<<std::endl;
504 void Server::step(float dtime)
506 DSTACK(__FUNCTION_NAME);
511 JMutexAutoLock lock(m_step_dtime_mutex);
512 m_step_dtime += dtime;
514 // Throw if fatal error occurred in thread
515 std::string async_err = m_async_fatal_error.get();
517 throw ServerError(async_err);
521 void Server::AsyncRunStep(bool initial_step)
523 DSTACK(__FUNCTION_NAME);
525 g_profiler->add("Server::AsyncRunStep (num)", 1);
529 JMutexAutoLock lock1(m_step_dtime_mutex);
530 dtime = m_step_dtime;
534 // Send blocks to clients
538 if((dtime < 0.001) && (initial_step == false))
541 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
543 //infostream<<"Server steps "<<dtime<<std::endl;
544 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
547 JMutexAutoLock lock1(m_step_dtime_mutex);
548 m_step_dtime -= dtime;
555 m_uptime.set(m_uptime.get() + dtime);
561 Update time of day and overall game time
564 JMutexAutoLock envlock(m_env_mutex);
566 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
569 Send to clients at constant intervals
572 m_time_of_day_send_timer -= dtime;
573 if(m_time_of_day_send_timer < 0.0)
575 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
576 u16 time = m_env->getTimeOfDay();
577 float time_speed = g_settings->getFloat("time_speed");
578 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
583 JMutexAutoLock lock(m_env_mutex);
584 // Figure out and report maximum lag to environment
585 float max_lag = m_env->getMaxLagEstimate();
586 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
588 if(dtime > 0.1 && dtime > max_lag * 2.0)
589 infostream<<"Server: Maximum lag peaked to "<<dtime
593 m_env->reportMaxLagEstimate(max_lag);
595 ScopeProfiler sp(g_profiler, "SEnv step");
596 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
600 const float map_timer_and_unload_dtime = 2.92;
601 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
603 JMutexAutoLock lock(m_env_mutex);
604 // Run Map's timers and unload unused data
605 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
606 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
607 g_settings->getFloat("server_unload_unused_data_timeout"));
618 JMutexAutoLock lock(m_env_mutex);
620 std::list<u16> clientids = m_clients.getClientIDs();
622 ScopeProfiler sp(g_profiler, "Server: handle players");
624 for(std::list<u16>::iterator
625 i = clientids.begin();
626 i != clientids.end(); ++i)
628 PlayerSAO *playersao = getPlayerSAO(*i);
629 if(playersao == NULL)
633 Handle player HPs (die if hp=0)
635 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
637 if(playersao->getHP() == 0)
644 Send player breath if changed
646 if(playersao->m_breath_not_sent){
647 SendPlayerBreath(*i);
651 Send player inventories if necessary
653 if(playersao->m_moved){
655 playersao->m_moved = false;
657 if(playersao->m_inventory_not_sent){
664 /* Transform liquids */
665 m_liquid_transform_timer += dtime;
666 if(m_liquid_transform_timer >= m_liquid_transform_every)
668 m_liquid_transform_timer -= m_liquid_transform_every;
670 JMutexAutoLock lock(m_env_mutex);
672 ScopeProfiler sp(g_profiler, "Server: liquid transform");
674 std::map<v3s16, MapBlock*> modified_blocks;
675 m_env->getMap().transformLiquids(modified_blocks);
680 core::map<v3s16, MapBlock*> lighting_modified_blocks;
681 ServerMap &map = ((ServerMap&)m_env->getMap());
682 map.updateLighting(modified_blocks, lighting_modified_blocks);
684 // Add blocks modified by lighting to modified_blocks
685 for(core::map<v3s16, MapBlock*>::Iterator
686 i = lighting_modified_blocks.getIterator();
687 i.atEnd() == false; i++)
689 MapBlock *block = i.getNode()->getValue();
690 modified_blocks.insert(block->getPos(), block);
694 Set the modified blocks unsent for all the clients
696 if(modified_blocks.size() > 0)
698 SetBlocksNotSent(modified_blocks);
701 m_clients.step(dtime);
703 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
705 // send masterserver announce
707 float &counter = m_masterserver_timer;
708 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
709 g_settings->getBool("server_announce") == true)
711 ServerList::sendAnnounce(!counter ? "start" : "update",
712 m_clients.getPlayerNames(),
714 m_env->getGameTime(),
725 Check added and deleted active objects
728 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
729 JMutexAutoLock envlock(m_env_mutex);
732 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
733 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
735 // Radius inside which objects are active
736 s16 radius = g_settings->getS16("active_object_send_range_blocks");
737 radius *= MAP_BLOCKSIZE;
739 for(std::map<u16, RemoteClient*>::iterator
741 i != clients.end(); ++i)
743 RemoteClient *client = i->second;
745 // If definitions and textures have not been sent, don't
746 // send objects either
747 if (client->getState() < DefinitionsSent)
750 Player *player = m_env->getPlayer(client->peer_id);
753 // This can happen if the client timeouts somehow
754 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
756 <<" has no associated player"<<std::endl;*/
759 v3s16 pos = floatToInt(player->getPosition(), BS);
761 std::set<u16> removed_objects;
762 std::set<u16> added_objects;
763 m_env->getRemovedActiveObjects(pos, radius,
764 client->m_known_objects, removed_objects);
765 m_env->getAddedActiveObjects(pos, radius,
766 client->m_known_objects, added_objects);
768 // Ignore if nothing happened
769 if(removed_objects.size() == 0 && added_objects.size() == 0)
771 //infostream<<"active objects: none changed"<<std::endl;
775 std::string data_buffer;
779 // Handle removed objects
780 writeU16((u8*)buf, removed_objects.size());
781 data_buffer.append(buf, 2);
782 for(std::set<u16>::iterator
783 i = removed_objects.begin();
784 i != removed_objects.end(); ++i)
788 ServerActiveObject* obj = m_env->getActiveObject(id);
790 // Add to data buffer for sending
791 writeU16((u8*)buf, id);
792 data_buffer.append(buf, 2);
794 // Remove from known objects
795 client->m_known_objects.erase(id);
797 if(obj && obj->m_known_by_count > 0)
798 obj->m_known_by_count--;
801 // Handle added objects
802 writeU16((u8*)buf, added_objects.size());
803 data_buffer.append(buf, 2);
804 for(std::set<u16>::iterator
805 i = added_objects.begin();
806 i != added_objects.end(); ++i)
810 ServerActiveObject* obj = m_env->getActiveObject(id);
813 u8 type = ACTIVEOBJECT_TYPE_INVALID;
815 infostream<<"WARNING: "<<__FUNCTION_NAME
816 <<": NULL object"<<std::endl;
818 type = obj->getSendType();
820 // Add to data buffer for sending
821 writeU16((u8*)buf, id);
822 data_buffer.append(buf, 2);
823 writeU8((u8*)buf, type);
824 data_buffer.append(buf, 1);
827 data_buffer.append(serializeLongString(
828 obj->getClientInitializationData(client->net_proto_version)));
830 data_buffer.append(serializeLongString(""));
832 // Add to known objects
833 client->m_known_objects.insert(id);
836 obj->m_known_by_count++;
840 SharedBuffer<u8> reply(2 + data_buffer.size());
841 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
842 memcpy((char*)&reply[2], data_buffer.c_str(),
845 m_clients.send(client->peer_id, 0, reply, true);
847 verbosestream<<"Server: Sent object remove/add: "
848 <<removed_objects.size()<<" removed, "
849 <<added_objects.size()<<" added, "
850 <<"packet size is "<<reply.getSize()<<std::endl;
855 Collect a list of all the objects known by the clients
856 and report it back to the environment.
859 core::map<u16, bool> all_known_objects;
861 for(core::map<u16, RemoteClient*>::Iterator
862 i = m_clients.getIterator();
863 i.atEnd() == false; i++)
865 RemoteClient *client = i.getNode()->getValue();
866 // Go through all known objects of client
867 for(core::map<u16, bool>::Iterator
868 i = client->m_known_objects.getIterator();
869 i.atEnd()==false; i++)
871 u16 id = i.getNode()->getKey();
872 all_known_objects[id] = true;
876 m_env->setKnownActiveObjects(whatever);
885 JMutexAutoLock envlock(m_env_mutex);
886 ScopeProfiler sp(g_profiler, "Server: sending object messages");
889 // Value = data sent by object
890 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
892 // Get active object messages from environment
895 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
899 std::list<ActiveObjectMessage>* message_list = NULL;
900 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
901 n = buffered_messages.find(aom.id);
902 if(n == buffered_messages.end())
904 message_list = new std::list<ActiveObjectMessage>;
905 buffered_messages[aom.id] = message_list;
909 message_list = n->second;
911 message_list->push_back(aom);
915 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
916 // Route data to every client
917 for(std::map<u16, RemoteClient*>::iterator
919 i != clients.end(); ++i)
921 RemoteClient *client = i->second;
922 std::string reliable_data;
923 std::string unreliable_data;
924 // Go through all objects in message buffer
925 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
926 j = buffered_messages.begin();
927 j != buffered_messages.end(); ++j)
929 // If object is not known by client, skip it
931 if(client->m_known_objects.find(id) == client->m_known_objects.end())
933 // Get message list of object
934 std::list<ActiveObjectMessage>* list = j->second;
935 // Go through every message
936 for(std::list<ActiveObjectMessage>::iterator
937 k = list->begin(); k != list->end(); ++k)
939 // Compose the full new data with header
940 ActiveObjectMessage aom = *k;
941 std::string new_data;
944 writeU16((u8*)&buf[0], aom.id);
945 new_data.append(buf, 2);
947 new_data += serializeString(aom.datastring);
948 // Add data to buffer
950 reliable_data += new_data;
952 unreliable_data += new_data;
956 reliable_data and unreliable_data are now ready.
959 if(reliable_data.size() > 0)
961 SharedBuffer<u8> reply(2 + reliable_data.size());
962 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
963 memcpy((char*)&reply[2], reliable_data.c_str(),
964 reliable_data.size());
966 m_clients.send(client->peer_id, 0, reply, true);
968 if(unreliable_data.size() > 0)
970 SharedBuffer<u8> reply(2 + unreliable_data.size());
971 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
972 memcpy((char*)&reply[2], unreliable_data.c_str(),
973 unreliable_data.size());
974 // Send as unreliable
975 m_clients.send(client->peer_id, 1, reply, false);
978 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
980 infostream<<"Server: Size of object message data: "
981 <<"reliable: "<<reliable_data.size()
982 <<", unreliable: "<<unreliable_data.size()
988 // Clear buffered_messages
989 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
990 i = buffered_messages.begin();
991 i != buffered_messages.end(); ++i)
998 Send queued-for-sending map edit events.
1001 // We will be accessing the environment
1002 JMutexAutoLock lock(m_env_mutex);
1004 // Don't send too many at a time
1007 // Single change sending is disabled if queue size is not small
1008 bool disable_single_change_sending = false;
1009 if(m_unsent_map_edit_queue.size() >= 4)
1010 disable_single_change_sending = true;
1012 int event_count = m_unsent_map_edit_queue.size();
1014 // We'll log the amount of each
1017 while(m_unsent_map_edit_queue.size() != 0)
1019 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1021 // Players far away from the change are stored here.
1022 // Instead of sending the changes, MapBlocks are set not sent
1024 std::list<u16> far_players;
1026 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
1028 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1029 prof.add("MEET_ADDNODE", 1);
1030 if(disable_single_change_sending)
1031 sendAddNode(event->p, event->n, event->already_known_by_peer,
1032 &far_players, 5, event->type == MEET_ADDNODE);
1034 sendAddNode(event->p, event->n, event->already_known_by_peer,
1035 &far_players, 30, event->type == MEET_ADDNODE);
1037 else if(event->type == MEET_REMOVENODE)
1039 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1040 prof.add("MEET_REMOVENODE", 1);
1041 if(disable_single_change_sending)
1042 sendRemoveNode(event->p, event->already_known_by_peer,
1045 sendRemoveNode(event->p, event->already_known_by_peer,
1048 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1050 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1051 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1052 setBlockNotSent(event->p);
1054 else if(event->type == MEET_OTHER)
1056 infostream<<"Server: MEET_OTHER"<<std::endl;
1057 prof.add("MEET_OTHER", 1);
1058 for(std::set<v3s16>::iterator
1059 i = event->modified_blocks.begin();
1060 i != event->modified_blocks.end(); ++i)
1062 setBlockNotSent(*i);
1067 prof.add("unknown", 1);
1068 infostream<<"WARNING: Server: Unknown MapEditEvent "
1069 <<((u32)event->type)<<std::endl;
1073 Set blocks not sent to far players
1075 if(far_players.size() > 0)
1077 // Convert list format to that wanted by SetBlocksNotSent
1078 std::map<v3s16, MapBlock*> modified_blocks2;
1079 for(std::set<v3s16>::iterator
1080 i = event->modified_blocks.begin();
1081 i != event->modified_blocks.end(); ++i)
1083 modified_blocks2[*i] =
1084 m_env->getMap().getBlockNoCreateNoEx(*i);
1086 // Set blocks not sent
1087 for(std::list<u16>::iterator
1088 i = far_players.begin();
1089 i != far_players.end(); ++i)
1092 RemoteClient *client = getClient(peer_id);
1095 client->SetBlocksNotSent(modified_blocks2);
1101 /*// Don't send too many at a time
1103 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1107 if(event_count >= 5){
1108 infostream<<"Server: MapEditEvents:"<<std::endl;
1109 prof.print(infostream);
1110 } else if(event_count != 0){
1111 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1112 prof.print(verbosestream);
1118 Trigger emergethread (it somehow gets to a non-triggered but
1119 bysy state sometimes)
1122 float &counter = m_emergethread_trigger_timer;
1128 m_emerge->startThreads();
1130 // Update m_enable_rollback_recording here too
1131 m_enable_rollback_recording =
1132 g_settings->getBool("enable_rollback_recording");
1136 // Save map, players and auth stuff
1138 float &counter = m_savemap_timer;
1140 if(counter >= g_settings->getFloat("server_map_save_interval"))
1143 JMutexAutoLock lock(m_env_mutex);
1145 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1148 if(m_banmanager->isModified())
1149 m_banmanager->save();
1151 // Save changed parts of map
1152 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1155 m_env->serializePlayers(m_path_world);
1157 // Save environment metadata
1158 m_env->saveMeta(m_path_world);
1163 void Server::Receive()
1165 DSTACK(__FUNCTION_NAME);
1166 SharedBuffer<u8> data;
1170 datasize = m_con.Receive(peer_id,data);
1171 ProcessData(*data, datasize, peer_id);
1173 catch(con::InvalidIncomingDataException &e)
1175 infostream<<"Server::Receive(): "
1176 "InvalidIncomingDataException: what()="
1177 <<e.what()<<std::endl;
1179 catch(con::PeerNotFoundException &e)
1181 //NOTE: This is not needed anymore
1183 // The peer has been disconnected.
1184 // Find the associated player and remove it.
1186 /*JMutexAutoLock envlock(m_env_mutex);
1188 infostream<<"ServerThread: peer_id="<<peer_id
1189 <<" has apparently closed connection. "
1190 <<"Removing player."<<std::endl;
1192 m_env->removePlayer(peer_id);*/
1196 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1198 DSTACK(__FUNCTION_NAME);
1199 // Environment is locked first.
1200 JMutexAutoLock envlock(m_env_mutex);
1202 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1206 Address address = getPeerAddress(peer_id);
1207 addr_s = address.serializeString();
1209 // drop player if is ip is banned
1210 if(m_banmanager->isIpBanned(addr_s)){
1211 std::string ban_name = m_banmanager->getBanName(addr_s);
1212 infostream<<"Server: A banned client tried to connect from "
1213 <<addr_s<<"; banned name was "
1214 <<ban_name<<std::endl;
1215 // This actually doesn't seem to transfer to the client
1216 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1217 +narrow_to_wide(ban_name));
1221 catch(con::PeerNotFoundException &e)
1223 errorstream<<"Server::ProcessData(): Cancelling: peer "
1224 <<peer_id<<" not found"<<std::endl;
1234 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1236 if(command == TOSERVER_INIT)
1238 // [0] u16 TOSERVER_INIT
1239 // [2] u8 SER_FMT_VER_HIGHEST_READ
1240 // [3] u8[20] player_name
1241 // [23] u8[28] password <--- can be sent without this, from old versions
1243 if(datasize < 2+1+PLAYERNAME_SIZE)
1246 RemoteClient* client = getClient(peer_id,Created);
1248 // If net_proto_version is set, this client has already been handled
1249 if(client->getState() > Created)
1251 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1252 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1256 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1257 <<peer_id<<")"<<std::endl;
1259 // Do not allow multiple players in simple singleplayer mode.
1260 // This isn't a perfect way to do it, but will suffice for now
1261 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1262 infostream<<"Server: Not allowing another client ("<<addr_s
1263 <<") to connect in simple singleplayer mode"<<std::endl;
1264 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1268 // First byte after command is maximum supported
1269 // serialization version
1270 u8 client_max = data[2];
1271 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1272 // Use the highest version supported by both
1273 u8 deployed = std::min(client_max, our_max);
1274 // If it's lower than the lowest supported, give up.
1275 if(deployed < SER_FMT_VER_LOWEST)
1276 deployed = SER_FMT_VER_INVALID;
1278 if(deployed == SER_FMT_VER_INVALID)
1280 actionstream<<"Server: A mismatched client tried to connect from "
1281 <<addr_s<<std::endl;
1282 infostream<<"Server: Cannot negotiate serialization version with "
1283 <<addr_s<<std::endl;
1284 DenyAccess(peer_id, std::wstring(
1285 L"Your client's version is not supported.\n"
1286 L"Server version is ")
1287 + narrow_to_wide(minetest_version_simple) + L"."
1292 client->setPendingSerializationVersion(deployed);
1295 Read and check network protocol version
1298 u16 min_net_proto_version = 0;
1299 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1300 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1302 // Use same version as minimum and maximum if maximum version field
1303 // doesn't exist (backwards compatibility)
1304 u16 max_net_proto_version = min_net_proto_version;
1305 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1306 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1308 // Start with client's maximum version
1309 u16 net_proto_version = max_net_proto_version;
1311 // Figure out a working version if it is possible at all
1312 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1313 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1315 // If maximum is larger than our maximum, go with our maximum
1316 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1317 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1318 // Else go with client's maximum
1320 net_proto_version = max_net_proto_version;
1323 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1324 <<min_net_proto_version<<", max: "<<max_net_proto_version
1325 <<", chosen: "<<net_proto_version<<std::endl;
1327 client->net_proto_version = net_proto_version;
1329 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1330 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1332 actionstream<<"Server: A mismatched client tried to connect from "
1333 <<addr_s<<std::endl;
1334 DenyAccess(peer_id, std::wstring(
1335 L"Your client's version is not supported.\n"
1336 L"Server version is ")
1337 + narrow_to_wide(minetest_version_simple) + L",\n"
1338 + L"server's PROTOCOL_VERSION is "
1339 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1341 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1342 + L", client's PROTOCOL_VERSION is "
1343 + narrow_to_wide(itos(min_net_proto_version))
1345 + narrow_to_wide(itos(max_net_proto_version))
1350 if(g_settings->getBool("strict_protocol_version_checking"))
1352 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1354 actionstream<<"Server: A mismatched (strict) client tried to "
1355 <<"connect from "<<addr_s<<std::endl;
1356 DenyAccess(peer_id, std::wstring(
1357 L"Your client's version is not supported.\n"
1358 L"Server version is ")
1359 + narrow_to_wide(minetest_version_simple) + L",\n"
1360 + L"server's PROTOCOL_VERSION (strict) is "
1361 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1362 + L", client's PROTOCOL_VERSION is "
1363 + narrow_to_wide(itos(min_net_proto_version))
1365 + narrow_to_wide(itos(max_net_proto_version))
1376 char playername[PLAYERNAME_SIZE];
1377 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1379 playername[i] = data[3+i];
1381 playername[PLAYERNAME_SIZE-1] = 0;
1383 if(playername[0]=='\0')
1385 actionstream<<"Server: Player with an empty name "
1386 <<"tried to connect from "<<addr_s<<std::endl;
1387 DenyAccess(peer_id, L"Empty name");
1391 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1393 actionstream<<"Server: Player with an invalid name "
1394 <<"tried to connect from "<<addr_s<<std::endl;
1395 DenyAccess(peer_id, L"Name contains unallowed characters");
1399 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1401 actionstream<<"Server: Player with the name \"singleplayer\" "
1402 <<"tried to connect from "<<addr_s<<std::endl;
1403 DenyAccess(peer_id, L"Name is not allowed");
1409 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1411 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1412 <<"tried to connect from "<<addr_s<<" "
1413 <<"but it was disallowed for the following reason: "
1414 <<reason<<std::endl;
1415 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1420 infostream<<"Server: New connection: \""<<playername<<"\" from "
1421 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1424 char given_password[PASSWORD_SIZE];
1425 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1427 // old version - assume blank password
1428 given_password[0] = 0;
1432 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1434 given_password[i] = data[23+i];
1436 given_password[PASSWORD_SIZE-1] = 0;
1439 if(!base64_is_valid(given_password)){
1440 actionstream<<"Server: "<<playername
1441 <<" supplied invalid password hash"<<std::endl;
1442 DenyAccess(peer_id, L"Invalid password hash");
1446 // Enforce user limit.
1447 // Don't enforce for users that have some admin right
1448 if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") &&
1449 !checkPriv(playername, "server") &&
1450 !checkPriv(playername, "ban") &&
1451 !checkPriv(playername, "privs") &&
1452 !checkPriv(playername, "password") &&
1453 playername != g_settings->get("name"))
1455 actionstream<<"Server: "<<playername<<" tried to join, but there"
1456 <<" are already max_users="
1457 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1458 DenyAccess(peer_id, L"Too many users.");
1462 std::string checkpwd; // Password hash to check against
1463 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1465 // If no authentication info exists for user, create it
1467 if(!isSingleplayer() &&
1468 g_settings->getBool("disallow_empty_password") &&
1469 std::string(given_password) == ""){
1470 actionstream<<"Server: "<<playername
1471 <<" supplied empty password"<<std::endl;
1472 DenyAccess(peer_id, L"Empty passwords are "
1473 L"disallowed. Set a password and try again.");
1476 std::wstring raw_default_password =
1477 narrow_to_wide(g_settings->get("default_password"));
1478 std::string initial_password =
1479 translatePassword(playername, raw_default_password);
1481 // If default_password is empty, allow any initial password
1482 if (raw_default_password.length() == 0)
1483 initial_password = given_password;
1485 m_script->createAuth(playername, initial_password);
1488 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1491 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1492 <<" (auth handler does not work?)"<<std::endl;
1493 DenyAccess(peer_id, L"Not allowed to login");
1497 if(given_password != checkpwd){
1498 actionstream<<"Server: "<<playername<<" supplied wrong password"
1500 DenyAccess(peer_id, L"Wrong password");
1504 RemotePlayer *player =
1505 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1507 if(player && player->peer_id != 0){
1508 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1509 <<" (player allocated to an another client)"<<std::endl;
1510 DenyAccess(peer_id, L"Another client is connected with this "
1511 L"name. If your client closed unexpectedly, try again in "
1515 m_clients.setPlayerName(peer_id,playername);
1518 Answer with a TOCLIENT_INIT
1521 SharedBuffer<u8> reply(2+1+6+8+4);
1522 writeU16(&reply[0], TOCLIENT_INIT);
1523 writeU8(&reply[2], deployed);
1524 //send dummy pos for legacy reasons only
1525 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1526 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1527 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1530 m_clients.send(peer_id, 0, reply, true);
1531 m_clients.event(peer_id, Init);
1537 if(command == TOSERVER_INIT2)
1540 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1541 <<peer_id<<std::endl;
1543 m_clients.event(peer_id, GotInit2);
1544 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1547 Send some initialization data
1550 infostream<<"Server: Sending content to "
1551 <<getPlayerName(peer_id)<<std::endl;
1553 // Send player movement settings
1554 SendMovement(peer_id);
1556 // Send item definitions
1557 SendItemDef(peer_id, m_itemdef, protocol_version);
1559 // Send node definitions
1560 SendNodeDef(peer_id, m_nodedef, protocol_version);
1562 m_clients.event(peer_id, SetDefinitionsSent);
1564 // Send media announcement
1565 sendMediaAnnouncement(peer_id);
1567 // Send detached inventories
1568 sendDetachedInventories(peer_id);
1571 u16 time = m_env->getTimeOfDay();
1572 float time_speed = g_settings->getFloat("time_speed");
1573 SendTimeOfDay(peer_id, time, time_speed);
1575 // Warnings about protocol version can be issued here
1576 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1578 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1579 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1585 u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version;
1587 if(peer_ser_ver == SER_FMT_VER_INVALID)
1589 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1590 " serialization format invalid or not initialized."
1591 " Skipping incoming command="<<command<<std::endl;
1595 /* Handle commands relate to client startup */
1596 if(command == TOSERVER_REQUEST_MEDIA) {
1597 std::string datastring((char*)&data[2], datasize-2);
1598 std::istringstream is(datastring, std::ios_base::binary);
1600 std::list<std::string> tosend;
1601 u16 numfiles = readU16(is);
1603 infostream<<"Sending "<<numfiles<<" files to "
1604 <<getPlayerName(peer_id)<<std::endl;
1605 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1607 for(int i = 0; i < numfiles; i++) {
1608 std::string name = deSerializeString(is);
1609 tosend.push_back(name);
1610 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1614 sendRequestedMedia(peer_id, tosend);
1617 else if(command == TOSERVER_RECEIVED_MEDIA) {
1618 std::string playername = "";
1619 PlayerSAO *playersao = NULL;
1621 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,DefinitionsSent);
1622 if (client != NULL) {
1623 playername = client->getName();
1624 playersao = emergePlayer(playername.c_str(), peer_id);
1628 RemotePlayer *player =
1629 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1631 // If failed, cancel
1632 if((playersao == NULL) || (player == NULL))
1634 if(player && player->peer_id != 0){
1635 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1636 <<" (player allocated to an another client)"<<std::endl;
1637 DenyAccess(peer_id, L"Another client is connected with this "
1638 L"name. If your client closed unexpectedly, try again in "
1641 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1643 DenyAccess(peer_id, L"Could not allocate player.");
1649 Send complete position information
1651 SendMovePlayer(peer_id);
1654 SendPlayerPrivileges(peer_id);
1656 // Send inventory formspec
1657 SendPlayerInventoryFormspec(peer_id);
1660 UpdateCrafting(peer_id);
1661 SendInventory(peer_id);
1664 if(g_settings->getBool("enable_damage"))
1665 SendPlayerHP(peer_id);
1668 SendPlayerBreath(peer_id);
1670 // Show death screen if necessary
1672 SendDeathscreen(peer_id, false, v3f(0,0,0));
1674 // Note things in chat if not in simple singleplayer mode
1675 if(!m_simple_singleplayer_mode)
1677 // Send information about server to player in chat
1678 SendChatMessage(peer_id, getStatusString());
1680 // Send information about joining in chat
1682 std::wstring name = L"unknown";
1683 Player *player = m_env->getPlayer(peer_id);
1685 name = narrow_to_wide(player->getName());
1687 std::wstring message;
1690 message += L" joined the game.";
1691 SendChatMessage(PEER_ID_INEXISTENT,message);
1695 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. " << std::endl;
1700 std::vector<std::string> names = m_clients.getPlayerNames();
1702 actionstream<<player->getName()<<" ["<<addr_s<<"] "
1703 <<"joins game. List of players: ";
1705 for (std::vector<std::string>::iterator i = names.begin();
1706 i != names.end(); i++)
1708 actionstream << *i << " ";
1711 actionstream<<std::endl;
1714 m_clients.event(peer_id,SetMediaSent);
1715 m_script->on_joinplayer(playersao);
1718 else if(command == TOSERVER_GOTBLOCKS)
1731 u16 count = data[2];
1732 for(u16 i=0; i<count; i++)
1734 if((s16)datasize < 2+1+(i+1)*6)
1735 throw con::InvalidIncomingDataException
1736 ("GOTBLOCKS length is too short");
1737 v3s16 p = readV3S16(&data[2+1+i*6]);
1738 /*infostream<<"Server: GOTBLOCKS ("
1739 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1740 RemoteClient *client = getClient(peer_id);
1741 client->GotBlock(p);
1746 if (m_clients.getClientState(peer_id) < Active)
1748 if (command == TOSERVER_PLAYERPOS) return;
1750 errorstream<<"Got packet command: " << command << " for peer id "
1751 << peer_id << " but client isn't active yet. Dropping packet "
1756 Player *player = m_env->getPlayer(peer_id);
1758 errorstream<<"Server::ProcessData(): Cancelling: "
1759 "No player for peer_id="<<peer_id
1764 PlayerSAO *playersao = player->getPlayerSAO();
1765 if(playersao == NULL){
1766 errorstream<<"Server::ProcessData(): Cancelling: "
1767 "No player object for peer_id="<<peer_id
1772 if(command == TOSERVER_PLAYERPOS)
1774 if(datasize < 2+12+12+4+4)
1778 v3s32 ps = readV3S32(&data[start+2]);
1779 v3s32 ss = readV3S32(&data[start+2+12]);
1780 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1781 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1783 if(datasize >= 2+12+12+4+4+4)
1784 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
1785 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1786 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1787 pitch = wrapDegrees(pitch);
1788 yaw = wrapDegrees(yaw);
1790 player->setPosition(position);
1791 player->setSpeed(speed);
1792 player->setPitch(pitch);
1793 player->setYaw(yaw);
1794 player->keyPressed=keyPressed;
1795 player->control.up = (bool)(keyPressed&1);
1796 player->control.down = (bool)(keyPressed&2);
1797 player->control.left = (bool)(keyPressed&4);
1798 player->control.right = (bool)(keyPressed&8);
1799 player->control.jump = (bool)(keyPressed&16);
1800 player->control.aux1 = (bool)(keyPressed&32);
1801 player->control.sneak = (bool)(keyPressed&64);
1802 player->control.LMB = (bool)(keyPressed&128);
1803 player->control.RMB = (bool)(keyPressed&256);
1805 bool cheated = playersao->checkMovementCheat();
1808 m_script->on_cheat(playersao, "moved_too_fast");
1811 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1812 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1813 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1815 else if(command == TOSERVER_DELETEDBLOCKS)
1828 u16 count = data[2];
1829 for(u16 i=0; i<count; i++)
1831 if((s16)datasize < 2+1+(i+1)*6)
1832 throw con::InvalidIncomingDataException
1833 ("DELETEDBLOCKS length is too short");
1834 v3s16 p = readV3S16(&data[2+1+i*6]);
1835 /*infostream<<"Server: DELETEDBLOCKS ("
1836 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1837 RemoteClient *client = getClient(peer_id);
1838 client->SetBlockNotSent(p);
1841 else if(command == TOSERVER_CLICK_OBJECT)
1843 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
1846 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
1848 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
1851 else if(command == TOSERVER_GROUND_ACTION)
1853 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
1857 else if(command == TOSERVER_RELEASE)
1859 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
1862 else if(command == TOSERVER_SIGNTEXT)
1864 infostream<<"Server: SIGNTEXT not supported anymore"
1868 else if(command == TOSERVER_SIGNNODETEXT)
1870 infostream<<"Server: SIGNNODETEXT not supported anymore"
1874 else if(command == TOSERVER_INVENTORY_ACTION)
1876 // Strip command and create a stream
1877 std::string datastring((char*)&data[2], datasize-2);
1878 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
1879 std::istringstream is(datastring, std::ios_base::binary);
1881 InventoryAction *a = InventoryAction::deSerialize(is);
1884 infostream<<"TOSERVER_INVENTORY_ACTION: "
1885 <<"InventoryAction::deSerialize() returned NULL"
1890 // If something goes wrong, this player is to blame
1891 RollbackScopeActor rollback_scope(m_rollback,
1892 std::string("player:")+player->getName());
1895 Note: Always set inventory not sent, to repair cases
1896 where the client made a bad prediction.
1900 Handle restrictions and special cases of the move action
1902 if(a->getType() == IACTION_MOVE)
1904 IMoveAction *ma = (IMoveAction*)a;
1906 ma->from_inv.applyCurrentPlayer(player->getName());
1907 ma->to_inv.applyCurrentPlayer(player->getName());
1909 setInventoryModified(ma->from_inv);
1910 setInventoryModified(ma->to_inv);
1912 bool from_inv_is_current_player =
1913 (ma->from_inv.type == InventoryLocation::PLAYER) &&
1914 (ma->from_inv.name == player->getName());
1916 bool to_inv_is_current_player =
1917 (ma->to_inv.type == InventoryLocation::PLAYER) &&
1918 (ma->to_inv.name == player->getName());
1921 Disable moving items out of craftpreview
1923 if(ma->from_list == "craftpreview")
1925 infostream<<"Ignoring IMoveAction from "
1926 <<(ma->from_inv.dump())<<":"<<ma->from_list
1927 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1928 <<" because src is "<<ma->from_list<<std::endl;
1934 Disable moving items into craftresult and craftpreview
1936 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
1938 infostream<<"Ignoring IMoveAction from "
1939 <<(ma->from_inv.dump())<<":"<<ma->from_list
1940 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1941 <<" because dst is "<<ma->to_list<<std::endl;
1946 // Disallow moving items in elsewhere than player's inventory
1947 // if not allowed to interact
1948 if(!checkPriv(player->getName(), "interact") &&
1949 (!from_inv_is_current_player ||
1950 !to_inv_is_current_player))
1952 infostream<<"Cannot move outside of player's inventory: "
1953 <<"No interact privilege"<<std::endl;
1959 Handle restrictions and special cases of the drop action
1961 else if(a->getType() == IACTION_DROP)
1963 IDropAction *da = (IDropAction*)a;
1965 da->from_inv.applyCurrentPlayer(player->getName());
1967 setInventoryModified(da->from_inv);
1970 Disable dropping items out of craftpreview
1972 if(da->from_list == "craftpreview")
1974 infostream<<"Ignoring IDropAction from "
1975 <<(da->from_inv.dump())<<":"<<da->from_list
1976 <<" because src is "<<da->from_list<<std::endl;
1981 // Disallow dropping items if not allowed to interact
1982 if(!checkPriv(player->getName(), "interact"))
1989 Handle restrictions and special cases of the craft action
1991 else if(a->getType() == IACTION_CRAFT)
1993 ICraftAction *ca = (ICraftAction*)a;
1995 ca->craft_inv.applyCurrentPlayer(player->getName());
1997 setInventoryModified(ca->craft_inv);
1999 //bool craft_inv_is_current_player =
2000 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2001 // (ca->craft_inv.name == player->getName());
2003 // Disallow crafting if not allowed to interact
2004 if(!checkPriv(player->getName(), "interact"))
2006 infostream<<"Cannot craft: "
2007 <<"No interact privilege"<<std::endl;
2014 a->apply(this, playersao, this);
2018 else if(command == TOSERVER_CHAT_MESSAGE)
2026 std::string datastring((char*)&data[2], datasize-2);
2027 std::istringstream is(datastring, std::ios_base::binary);
2030 is.read((char*)buf, 2);
2031 u16 len = readU16(buf);
2033 std::wstring message;
2034 for(u16 i=0; i<len; i++)
2036 is.read((char*)buf, 2);
2037 message += (wchar_t)readU16(buf);
2040 // If something goes wrong, this player is to blame
2041 RollbackScopeActor rollback_scope(m_rollback,
2042 std::string("player:")+player->getName());
2044 // Get player name of this client
2045 std::wstring name = narrow_to_wide(player->getName());
2048 bool ate = m_script->on_chat_message(player->getName(),
2049 wide_to_narrow(message));
2050 // If script ate the message, don't proceed
2054 // Line to send to players
2056 // Whether to send to the player that sent the line
2057 bool send_to_sender_only = false;
2059 // Commands are implemented in Lua, so only catch invalid
2060 // commands that were not "eaten" and send an error back
2061 if(message[0] == L'/')
2063 message = message.substr(1);
2064 send_to_sender_only = true;
2065 if(message.length() == 0)
2066 line += L"-!- Empty command";
2068 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2072 if(checkPriv(player->getName(), "shout")){
2078 line += L"-!- You don't have permission to shout.";
2079 send_to_sender_only = true;
2086 Send the message to sender
2088 if (send_to_sender_only)
2090 SendChatMessage(peer_id, line);
2093 Send the message to others
2097 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2099 std::list<u16> clients = m_clients.getClientIDs();
2101 for(std::list<u16>::iterator
2102 i = clients.begin();
2103 i != clients.end(); ++i)
2106 SendChatMessage(*i, line);
2111 else if(command == TOSERVER_DAMAGE)
2113 std::string datastring((char*)&data[2], datasize-2);
2114 std::istringstream is(datastring, std::ios_base::binary);
2115 u8 damage = readU8(is);
2117 if(g_settings->getBool("enable_damage"))
2119 actionstream<<player->getName()<<" damaged by "
2120 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2123 playersao->setHP(playersao->getHP() - damage);
2125 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2128 if(playersao->m_hp_not_sent)
2129 SendPlayerHP(peer_id);
2132 else if(command == TOSERVER_BREATH)
2134 std::string datastring((char*)&data[2], datasize-2);
2135 std::istringstream is(datastring, std::ios_base::binary);
2136 u16 breath = readU16(is);
2137 playersao->setBreath(breath);
2139 else if(command == TOSERVER_PASSWORD)
2142 [0] u16 TOSERVER_PASSWORD
2143 [2] u8[28] old password
2144 [30] u8[28] new password
2147 if(datasize != 2+PASSWORD_SIZE*2)
2149 /*char password[PASSWORD_SIZE];
2150 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2151 password[i] = data[2+i];
2152 password[PASSWORD_SIZE-1] = 0;*/
2154 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2162 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2164 char c = data[2+PASSWORD_SIZE+i];
2170 if(!base64_is_valid(newpwd)){
2171 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2172 // Wrong old password supplied!!
2173 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2177 infostream<<"Server: Client requests a password change from "
2178 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2180 std::string playername = player->getName();
2182 std::string checkpwd;
2183 m_script->getAuth(playername, &checkpwd, NULL);
2185 if(oldpwd != checkpwd)
2187 infostream<<"Server: invalid old password"<<std::endl;
2188 // Wrong old password supplied!!
2189 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2193 bool success = m_script->setPassword(playername, newpwd);
2195 actionstream<<player->getName()<<" changes password"<<std::endl;
2196 SendChatMessage(peer_id, L"Password change successful.");
2198 actionstream<<player->getName()<<" tries to change password but "
2199 <<"it fails"<<std::endl;
2200 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2203 else if(command == TOSERVER_PLAYERITEM)
2208 u16 item = readU16(&data[2]);
2209 playersao->setWieldIndex(item);
2211 else if(command == TOSERVER_RESPAWN)
2213 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2216 RespawnPlayer(peer_id);
2218 actionstream<<player->getName()<<" respawns at "
2219 <<PP(player->getPosition()/BS)<<std::endl;
2221 // ActiveObject is added to environment in AsyncRunStep after
2222 // the previous addition has been succesfully removed
2224 else if(command == TOSERVER_INTERACT)
2226 std::string datastring((char*)&data[2], datasize-2);
2227 std::istringstream is(datastring, std::ios_base::binary);
2233 [5] u32 length of the next item
2234 [9] serialized PointedThing
2236 0: start digging (from undersurface) or use
2237 1: stop digging (all parameters ignored)
2238 2: digging completed
2239 3: place block or item (to abovesurface)
2242 u8 action = readU8(is);
2243 u16 item_i = readU16(is);
2244 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2245 PointedThing pointed;
2246 pointed.deSerialize(tmp_is);
2248 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2249 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2253 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2254 <<" tried to interact, but is dead!"<<std::endl;
2258 v3f player_pos = playersao->getLastGoodPosition();
2260 // Update wielded item
2261 playersao->setWieldIndex(item_i);
2263 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2264 v3s16 p_under = pointed.node_undersurface;
2265 v3s16 p_above = pointed.node_abovesurface;
2267 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2268 ServerActiveObject *pointed_object = NULL;
2269 if(pointed.type == POINTEDTHING_OBJECT)
2271 pointed_object = m_env->getActiveObject(pointed.object_id);
2272 if(pointed_object == NULL)
2274 verbosestream<<"TOSERVER_INTERACT: "
2275 "pointed object is NULL"<<std::endl;
2281 v3f pointed_pos_under = player_pos;
2282 v3f pointed_pos_above = player_pos;
2283 if(pointed.type == POINTEDTHING_NODE)
2285 pointed_pos_under = intToFloat(p_under, BS);
2286 pointed_pos_above = intToFloat(p_above, BS);
2288 else if(pointed.type == POINTEDTHING_OBJECT)
2290 pointed_pos_under = pointed_object->getBasePosition();
2291 pointed_pos_above = pointed_pos_under;
2295 Check that target is reasonably close
2296 (only when digging or placing things)
2298 if(action == 0 || action == 2 || action == 3)
2300 float d = player_pos.getDistanceFrom(pointed_pos_under);
2301 float max_d = BS * 14; // Just some large enough value
2303 actionstream<<"Player "<<player->getName()
2304 <<" tried to access "<<pointed.dump()
2306 <<"d="<<d<<", max_d="<<max_d
2307 <<". ignoring."<<std::endl;
2308 // Re-send block to revert change on client-side
2309 RemoteClient *client = getClient(peer_id);
2310 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2311 client->SetBlockNotSent(blockpos);
2313 m_script->on_cheat(playersao, "interacted_too_far");
2320 Make sure the player is allowed to do it
2322 if(!checkPriv(player->getName(), "interact"))
2324 actionstream<<player->getName()<<" attempted to interact with "
2325 <<pointed.dump()<<" without 'interact' privilege"
2327 // Re-send block to revert change on client-side
2328 RemoteClient *client = getClient(peer_id);
2329 // Digging completed -> under
2331 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2332 client->SetBlockNotSent(blockpos);
2334 // Placement -> above
2336 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2337 client->SetBlockNotSent(blockpos);
2343 If something goes wrong, this player is to blame
2345 RollbackScopeActor rollback_scope(m_rollback,
2346 std::string("player:")+player->getName());
2349 0: start digging or punch object
2353 if(pointed.type == POINTEDTHING_NODE)
2356 NOTE: This can be used in the future to check if
2357 somebody is cheating, by checking the timing.
2359 MapNode n(CONTENT_IGNORE);
2362 n = m_env->getMap().getNode(p_under);
2364 catch(InvalidPositionException &e)
2366 infostream<<"Server: Not punching: Node not found."
2367 <<" Adding block to emerge queue."
2369 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2371 if(n.getContent() != CONTENT_IGNORE)
2372 m_script->node_on_punch(p_under, n, playersao, pointed);
2374 playersao->noCheatDigStart(p_under);
2376 else if(pointed.type == POINTEDTHING_OBJECT)
2378 // Skip if object has been removed
2379 if(pointed_object->m_removed)
2382 actionstream<<player->getName()<<" punches object "
2383 <<pointed.object_id<<": "
2384 <<pointed_object->getDescription()<<std::endl;
2386 ItemStack punchitem = playersao->getWieldedItem();
2387 ToolCapabilities toolcap =
2388 punchitem.getToolCapabilities(m_itemdef);
2389 v3f dir = (pointed_object->getBasePosition() -
2390 (player->getPosition() + player->getEyeOffset())
2392 float time_from_last_punch =
2393 playersao->resetTimeFromLastPunch();
2394 pointed_object->punch(dir, &toolcap, playersao,
2395 time_from_last_punch);
2403 else if(action == 1)
2408 2: Digging completed
2410 else if(action == 2)
2412 // Only digging of nodes
2413 if(pointed.type == POINTEDTHING_NODE)
2415 MapNode n(CONTENT_IGNORE);
2418 n = m_env->getMap().getNode(p_under);
2420 catch(InvalidPositionException &e)
2422 infostream<<"Server: Not finishing digging: Node not found."
2423 <<" Adding block to emerge queue."
2425 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2428 /* Cheat prevention */
2429 bool is_valid_dig = true;
2430 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2432 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2433 float nocheat_t = playersao->getNoCheatDigTime();
2434 playersao->noCheatDigEnd();
2435 // If player didn't start digging this, ignore dig
2436 if(nocheat_p != p_under){
2437 infostream<<"Server: NoCheat: "<<player->getName()
2438 <<" started digging "
2439 <<PP(nocheat_p)<<" and completed digging "
2440 <<PP(p_under)<<"; not digging."<<std::endl;
2441 is_valid_dig = false;
2443 m_script->on_cheat(playersao, "finished_unknown_dig");
2445 // Get player's wielded item
2446 ItemStack playeritem;
2447 InventoryList *mlist = playersao->getInventory()->getList("main");
2449 playeritem = mlist->getItem(playersao->getWieldIndex());
2450 ToolCapabilities playeritem_toolcap =
2451 playeritem.getToolCapabilities(m_itemdef);
2452 // Get diggability and expected digging time
2453 DigParams params = getDigParams(m_nodedef->get(n).groups,
2454 &playeritem_toolcap);
2455 // If can't dig, try hand
2456 if(!params.diggable){
2457 const ItemDefinition &hand = m_itemdef->get("");
2458 const ToolCapabilities *tp = hand.tool_capabilities;
2460 params = getDigParams(m_nodedef->get(n).groups, tp);
2462 // If can't dig, ignore dig
2463 if(!params.diggable){
2464 infostream<<"Server: NoCheat: "<<player->getName()
2465 <<" completed digging "<<PP(p_under)
2466 <<", which is not diggable with tool. not digging."
2468 is_valid_dig = false;
2470 m_script->on_cheat(playersao, "dug_unbreakable");
2472 // Check digging time
2473 // If already invalidated, we don't have to
2475 // Well not our problem then
2477 // Clean and long dig
2478 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2479 // All is good, but grab time from pool; don't care if
2480 // it's actually available
2481 playersao->getDigPool().grab(params.time);
2483 // Short or laggy dig
2484 // Try getting the time from pool
2485 else if(playersao->getDigPool().grab(params.time)){
2490 infostream<<"Server: NoCheat: "<<player->getName()
2491 <<" completed digging "<<PP(p_under)
2492 <<"too fast; not digging."<<std::endl;
2493 is_valid_dig = false;
2495 m_script->on_cheat(playersao, "dug_too_fast");
2499 /* Actually dig node */
2501 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2502 m_script->node_on_dig(p_under, n, playersao);
2504 // Send unusual result (that is, node not being removed)
2505 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2507 // Re-send block to revert change on client-side
2508 RemoteClient *client = getClient(peer_id);
2509 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2510 client->SetBlockNotSent(blockpos);
2516 3: place block or right-click object
2518 else if(action == 3)
2520 ItemStack item = playersao->getWieldedItem();
2522 // Reset build time counter
2523 if(pointed.type == POINTEDTHING_NODE &&
2524 item.getDefinition(m_itemdef).type == ITEM_NODE)
2525 getClient(peer_id)->m_time_from_building = 0.0;
2527 if(pointed.type == POINTEDTHING_OBJECT)
2529 // Right click object
2531 // Skip if object has been removed
2532 if(pointed_object->m_removed)
2535 actionstream<<player->getName()<<" right-clicks object "
2536 <<pointed.object_id<<": "
2537 <<pointed_object->getDescription()<<std::endl;
2540 pointed_object->rightClick(playersao);
2542 else if(m_script->item_OnPlace(
2543 item, playersao, pointed))
2545 // Placement was handled in lua
2547 // Apply returned ItemStack
2548 playersao->setWieldedItem(item);
2551 // If item has node placement prediction, always send the
2552 // blocks to make sure the client knows what exactly happened
2553 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2554 RemoteClient *client = getClient(peer_id);
2555 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2556 client->SetBlockNotSent(blockpos);
2557 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2558 if(blockpos2 != blockpos){
2559 client->SetBlockNotSent(blockpos2);
2567 else if(action == 4)
2569 ItemStack item = playersao->getWieldedItem();
2571 actionstream<<player->getName()<<" uses "<<item.name
2572 <<", pointing at "<<pointed.dump()<<std::endl;
2574 if(m_script->item_OnUse(
2575 item, playersao, pointed))
2577 // Apply returned ItemStack
2578 playersao->setWieldedItem(item);
2585 Catch invalid actions
2589 infostream<<"WARNING: Server: Invalid action "
2590 <<action<<std::endl;
2593 else if(command == TOSERVER_REMOVED_SOUNDS)
2595 std::string datastring((char*)&data[2], datasize-2);
2596 std::istringstream is(datastring, std::ios_base::binary);
2598 int num = readU16(is);
2599 for(int k=0; k<num; k++){
2600 s32 id = readS32(is);
2601 std::map<s32, ServerPlayingSound>::iterator i =
2602 m_playing_sounds.find(id);
2603 if(i == m_playing_sounds.end())
2605 ServerPlayingSound &psound = i->second;
2606 psound.clients.erase(peer_id);
2607 if(psound.clients.size() == 0)
2608 m_playing_sounds.erase(i++);
2611 else if(command == TOSERVER_NODEMETA_FIELDS)
2613 std::string datastring((char*)&data[2], datasize-2);
2614 std::istringstream is(datastring, std::ios_base::binary);
2616 v3s16 p = readV3S16(is);
2617 std::string formname = deSerializeString(is);
2618 int num = readU16(is);
2619 std::map<std::string, std::string> fields;
2620 for(int k=0; k<num; k++){
2621 std::string fieldname = deSerializeString(is);
2622 std::string fieldvalue = deSerializeLongString(is);
2623 fields[fieldname] = fieldvalue;
2626 // If something goes wrong, this player is to blame
2627 RollbackScopeActor rollback_scope(m_rollback,
2628 std::string("player:")+player->getName());
2630 // Check the target node for rollback data; leave others unnoticed
2631 RollbackNode rn_old(&m_env->getMap(), p, this);
2633 m_script->node_on_receive_fields(p, formname, fields,playersao);
2635 // Report rollback data
2636 RollbackNode rn_new(&m_env->getMap(), p, this);
2637 if(rollback() && rn_new != rn_old){
2638 RollbackAction action;
2639 action.setSetNode(p, rn_old, rn_new);
2640 rollback()->reportAction(action);
2643 else if(command == TOSERVER_INVENTORY_FIELDS)
2645 std::string datastring((char*)&data[2], datasize-2);
2646 std::istringstream is(datastring, std::ios_base::binary);
2648 std::string formname = deSerializeString(is);
2649 int num = readU16(is);
2650 std::map<std::string, std::string> fields;
2651 for(int k=0; k<num; k++){
2652 std::string fieldname = deSerializeString(is);
2653 std::string fieldvalue = deSerializeLongString(is);
2654 fields[fieldname] = fieldvalue;
2657 m_script->on_playerReceiveFields(playersao, formname, fields);
2661 infostream<<"Server::ProcessData(): Ignoring "
2662 "unknown command "<<command<<std::endl;
2666 catch(SendFailedException &e)
2668 errorstream<<"Server::ProcessData(): SendFailedException: "
2674 void Server::setTimeOfDay(u32 time)
2676 m_env->setTimeOfDay(time);
2677 m_time_of_day_send_timer = 0;
2680 void Server::onMapEditEvent(MapEditEvent *event)
2682 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2683 if(m_ignore_map_edit_events)
2685 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2687 MapEditEvent *e = event->clone();
2688 m_unsent_map_edit_queue.push_back(e);
2691 Inventory* Server::getInventory(const InventoryLocation &loc)
2694 case InventoryLocation::UNDEFINED:
2697 case InventoryLocation::CURRENT_PLAYER:
2700 case InventoryLocation::PLAYER:
2702 Player *player = m_env->getPlayer(loc.name.c_str());
2705 PlayerSAO *playersao = player->getPlayerSAO();
2708 return playersao->getInventory();
2711 case InventoryLocation::NODEMETA:
2713 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2716 return meta->getInventory();
2719 case InventoryLocation::DETACHED:
2721 if(m_detached_inventories.count(loc.name) == 0)
2723 return m_detached_inventories[loc.name];
2731 void Server::setInventoryModified(const InventoryLocation &loc)
2734 case InventoryLocation::UNDEFINED:
2737 case InventoryLocation::PLAYER:
2739 Player *player = m_env->getPlayer(loc.name.c_str());
2742 PlayerSAO *playersao = player->getPlayerSAO();
2745 playersao->m_inventory_not_sent = true;
2746 playersao->m_wielded_item_not_sent = true;
2749 case InventoryLocation::NODEMETA:
2751 v3s16 blockpos = getNodeBlockPos(loc.p);
2753 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2755 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2757 setBlockNotSent(blockpos);
2760 case InventoryLocation::DETACHED:
2762 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2770 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2772 std::list<u16> clients = m_clients.getClientIDs();
2774 // Set the modified blocks unsent for all the clients
2775 for (std::list<u16>::iterator
2776 i = clients.begin();
2777 i != clients.end(); ++i) {
2778 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2780 client->SetBlocksNotSent(block);
2785 void Server::peerAdded(con::Peer *peer)
2787 DSTACK(__FUNCTION_NAME);
2788 verbosestream<<"Server::peerAdded(): peer->id="
2789 <<peer->id<<std::endl;
2792 c.type = con::PEER_ADDED;
2793 c.peer_id = peer->id;
2795 m_peer_change_queue.push_back(c);
2798 void Server::deletingPeer(con::Peer *peer, bool timeout)
2800 DSTACK(__FUNCTION_NAME);
2801 verbosestream<<"Server::deletingPeer(): peer->id="
2802 <<peer->id<<", timeout="<<timeout<<std::endl;
2804 m_clients.event(peer->id,Disconnect);
2806 c.type = con::PEER_REMOVED;
2807 c.peer_id = peer->id;
2808 c.timeout = timeout;
2809 m_peer_change_queue.push_back(c);
2812 void Server::handlePeerChanges()
2814 while(m_peer_change_queue.size() > 0)
2816 con::PeerChange c = m_peer_change_queue.pop_front();
2818 verbosestream<<"Server: Handling peer change: "
2819 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2824 case con::PEER_ADDED:
2825 m_clients.CreateClient(c.peer_id);
2828 case con::PEER_REMOVED:
2829 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2833 assert("Invalid peer change event received!" == 0);
2839 void Server::SendMovement(u16 peer_id)
2841 DSTACK(__FUNCTION_NAME);
2842 std::ostringstream os(std::ios_base::binary);
2844 writeU16(os, TOCLIENT_MOVEMENT);
2845 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2846 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2847 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2848 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2849 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2850 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2851 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2852 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2853 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2854 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2855 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2856 writeF1000(os, g_settings->getFloat("movement_gravity"));
2859 std::string s = os.str();
2860 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2862 m_clients.send(peer_id, 0, data, true);
2865 void Server::SendHP(u16 peer_id, u8 hp)
2867 DSTACK(__FUNCTION_NAME);
2868 std::ostringstream os(std::ios_base::binary);
2870 writeU16(os, TOCLIENT_HP);
2874 std::string s = os.str();
2875 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2877 m_clients.send(peer_id, 0, data, true);
2880 void Server::SendBreath(u16 peer_id, u16 breath)
2882 DSTACK(__FUNCTION_NAME);
2883 std::ostringstream os(std::ios_base::binary);
2885 writeU16(os, TOCLIENT_BREATH);
2886 writeU16(os, breath);
2889 std::string s = os.str();
2890 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2892 m_clients.send(peer_id, 0, data, true);
2895 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
2897 DSTACK(__FUNCTION_NAME);
2898 std::ostringstream os(std::ios_base::binary);
2900 writeU16(os, TOCLIENT_ACCESS_DENIED);
2901 os<<serializeWideString(reason);
2904 std::string s = os.str();
2905 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2907 m_clients.send(peer_id, 0, data, true);
2910 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
2911 v3f camera_point_target)
2913 DSTACK(__FUNCTION_NAME);
2914 std::ostringstream os(std::ios_base::binary);
2916 writeU16(os, TOCLIENT_DEATHSCREEN);
2917 writeU8(os, set_camera_point_target);
2918 writeV3F1000(os, camera_point_target);
2921 std::string s = os.str();
2922 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2924 m_clients.send(peer_id, 0, data, true);
2927 void Server::SendItemDef(u16 peer_id,
2928 IItemDefManager *itemdef, u16 protocol_version)
2930 DSTACK(__FUNCTION_NAME);
2931 std::ostringstream os(std::ios_base::binary);
2935 u32 length of the next item
2936 zlib-compressed serialized ItemDefManager
2938 writeU16(os, TOCLIENT_ITEMDEF);
2939 std::ostringstream tmp_os(std::ios::binary);
2940 itemdef->serialize(tmp_os, protocol_version);
2941 std::ostringstream tmp_os2(std::ios::binary);
2942 compressZlib(tmp_os.str(), tmp_os2);
2943 os<<serializeLongString(tmp_os2.str());
2946 std::string s = os.str();
2947 verbosestream<<"Server: Sending item definitions to id("<<peer_id
2948 <<"): size="<<s.size()<<std::endl;
2949 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2951 m_clients.send(peer_id, 0, data, true);
2954 void Server::SendNodeDef(u16 peer_id,
2955 INodeDefManager *nodedef, u16 protocol_version)
2957 DSTACK(__FUNCTION_NAME);
2958 std::ostringstream os(std::ios_base::binary);
2962 u32 length of the next item
2963 zlib-compressed serialized NodeDefManager
2965 writeU16(os, TOCLIENT_NODEDEF);
2966 std::ostringstream tmp_os(std::ios::binary);
2967 nodedef->serialize(tmp_os, protocol_version);
2968 std::ostringstream tmp_os2(std::ios::binary);
2969 compressZlib(tmp_os.str(), tmp_os2);
2970 os<<serializeLongString(tmp_os2.str());
2973 std::string s = os.str();
2974 verbosestream<<"Server: Sending node definitions to id("<<peer_id
2975 <<"): size="<<s.size()<<std::endl;
2976 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2978 m_clients.send(peer_id, 0, data, true);
2982 Non-static send methods
2985 void Server::SendInventory(u16 peer_id)
2987 DSTACK(__FUNCTION_NAME);
2989 PlayerSAO *playersao = getPlayerSAO(peer_id);
2992 playersao->m_inventory_not_sent = false;
2998 std::ostringstream os;
2999 playersao->getInventory()->serialize(os);
3001 std::string s = os.str();
3003 SharedBuffer<u8> data(s.size()+2);
3004 writeU16(&data[0], TOCLIENT_INVENTORY);
3005 memcpy(&data[2], s.c_str(), s.size());
3008 m_clients.send(peer_id, 0, data, true);
3011 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3013 DSTACK(__FUNCTION_NAME);
3015 std::ostringstream os(std::ios_base::binary);
3019 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3020 os.write((char*)buf, 2);
3023 writeU16(buf, message.size());
3024 os.write((char*)buf, 2);
3027 for(u32 i=0; i<message.size(); i++)
3031 os.write((char*)buf, 2);
3035 std::string s = os.str();
3036 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3038 if (peer_id != PEER_ID_INEXISTENT)
3041 m_clients.send(peer_id, 0, data, true);
3045 m_clients.sendToAll(0,data,true);
3049 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
3050 const std::string &formname)
3052 DSTACK(__FUNCTION_NAME);
3054 std::ostringstream os(std::ios_base::binary);
3058 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3059 os.write((char*)buf, 2);
3060 os<<serializeLongString(formspec);
3061 os<<serializeString(formname);
3064 std::string s = os.str();
3065 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3067 m_clients.send(peer_id, 0, data, true);
3070 // Spawns a particle on peer with peer_id
3071 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3072 float expirationtime, float size, bool collisiondetection,
3073 bool vertical, std::string texture)
3075 DSTACK(__FUNCTION_NAME);
3077 std::ostringstream os(std::ios_base::binary);
3078 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3079 writeV3F1000(os, pos);
3080 writeV3F1000(os, velocity);
3081 writeV3F1000(os, acceleration);
3082 writeF1000(os, expirationtime);
3083 writeF1000(os, size);
3084 writeU8(os, collisiondetection);
3085 os<<serializeLongString(texture);
3086 writeU8(os, vertical);
3089 std::string s = os.str();
3090 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3092 if (peer_id != PEER_ID_INEXISTENT)
3095 m_clients.send(peer_id, 0, data, true);
3099 m_clients.sendToAll(0,data,true);
3103 // Adds a ParticleSpawner on peer with peer_id
3104 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3105 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3106 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3108 DSTACK(__FUNCTION_NAME);
3110 std::ostringstream os(std::ios_base::binary);
3111 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3113 writeU16(os, amount);
3114 writeF1000(os, spawntime);
3115 writeV3F1000(os, minpos);
3116 writeV3F1000(os, maxpos);
3117 writeV3F1000(os, minvel);
3118 writeV3F1000(os, maxvel);
3119 writeV3F1000(os, minacc);
3120 writeV3F1000(os, maxacc);
3121 writeF1000(os, minexptime);
3122 writeF1000(os, maxexptime);
3123 writeF1000(os, minsize);
3124 writeF1000(os, maxsize);
3125 writeU8(os, collisiondetection);
3126 os<<serializeLongString(texture);
3128 writeU8(os, vertical);
3131 std::string s = os.str();
3132 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3134 if (peer_id != PEER_ID_INEXISTENT)
3137 m_clients.send(peer_id, 0, data, true);
3140 m_clients.sendToAll(0,data,true);
3144 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3146 DSTACK(__FUNCTION_NAME);
3148 std::ostringstream os(std::ios_base::binary);
3149 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3154 std::string s = os.str();
3155 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3157 if (peer_id != PEER_ID_INEXISTENT) {
3159 m_clients.send(peer_id, 0, data, true);
3162 m_clients.sendToAll(0,data,true);
3167 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3169 std::ostringstream os(std::ios_base::binary);
3172 writeU16(os, TOCLIENT_HUDADD);
3174 writeU8(os, (u8)form->type);
3175 writeV2F1000(os, form->pos);
3176 os << serializeString(form->name);
3177 writeV2F1000(os, form->scale);
3178 os << serializeString(form->text);
3179 writeU32(os, form->number);
3180 writeU32(os, form->item);
3181 writeU32(os, form->dir);
3182 writeV2F1000(os, form->align);
3183 writeV2F1000(os, form->offset);
3184 writeV3F1000(os, form->world_pos);
3187 std::string s = os.str();
3188 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3190 m_clients.send(peer_id, 1, data, true);
3193 void Server::SendHUDRemove(u16 peer_id, u32 id)
3195 std::ostringstream os(std::ios_base::binary);
3198 writeU16(os, TOCLIENT_HUDRM);
3202 std::string s = os.str();
3203 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3206 m_clients.send(peer_id, 1, data, true);
3209 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3211 std::ostringstream os(std::ios_base::binary);
3214 writeU16(os, TOCLIENT_HUDCHANGE);
3216 writeU8(os, (u8)stat);
3219 case HUD_STAT_SCALE:
3220 case HUD_STAT_ALIGN:
3221 case HUD_STAT_OFFSET:
3222 writeV2F1000(os, *(v2f *)value);
3226 os << serializeString(*(std::string *)value);
3228 case HUD_STAT_WORLD_POS:
3229 writeV3F1000(os, *(v3f *)value);
3231 case HUD_STAT_NUMBER:
3235 writeU32(os, *(u32 *)value);
3240 std::string s = os.str();
3241 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3243 m_clients.send(peer_id, 0, data, true);
3246 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3248 std::ostringstream os(std::ios_base::binary);
3251 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3252 writeU32(os, flags);
3256 std::string s = os.str();
3257 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3259 m_clients.send(peer_id, 0, data, true);
3262 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3264 std::ostringstream os(std::ios_base::binary);
3267 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3268 writeU16(os, param);
3269 os<<serializeString(value);
3272 std::string s = os.str();
3273 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3275 m_clients.send(peer_id, 0, data, true);
3278 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3279 const std::string &type, const std::vector<std::string> ¶ms)
3281 std::ostringstream os(std::ios_base::binary);
3284 writeU16(os, TOCLIENT_SET_SKY);
3285 writeARGB8(os, bgcolor);
3286 os<<serializeString(type);
3287 writeU16(os, params.size());
3288 for(size_t i=0; i<params.size(); i++)
3289 os<<serializeString(params[i]);
3292 std::string s = os.str();
3293 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3295 m_clients.send(peer_id, 0, data, true);
3298 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
3301 std::ostringstream os(std::ios_base::binary);
3304 writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
3305 writeU8(os, do_override);
3306 writeU16(os, ratio*65535);
3309 std::string s = os.str();
3310 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3312 m_clients.send(peer_id, 0, data, true);
3315 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3317 DSTACK(__FUNCTION_NAME);
3320 SharedBuffer<u8> data(2+2+4);
3321 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3322 writeU16(&data[2], time);
3323 writeF1000(&data[4], time_speed);
3325 if (peer_id == PEER_ID_INEXISTENT) {
3326 m_clients.sendToAll(0,data,true);
3330 m_clients.send(peer_id, 0, data, true);
3334 void Server::SendPlayerHP(u16 peer_id)
3336 DSTACK(__FUNCTION_NAME);
3337 PlayerSAO *playersao = getPlayerSAO(peer_id);
3339 playersao->m_hp_not_sent = false;
3340 SendHP(peer_id, playersao->getHP());
3342 // Send to other clients
3343 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3344 ActiveObjectMessage aom(playersao->getId(), true, str);
3345 playersao->m_messages_out.push_back(aom);
3348 void Server::SendPlayerBreath(u16 peer_id)
3350 DSTACK(__FUNCTION_NAME);
3351 PlayerSAO *playersao = getPlayerSAO(peer_id);
3353 playersao->m_breath_not_sent = false;
3354 SendBreath(peer_id, playersao->getBreath());
3357 void Server::SendMovePlayer(u16 peer_id)
3359 DSTACK(__FUNCTION_NAME);
3360 Player *player = m_env->getPlayer(peer_id);
3363 std::ostringstream os(std::ios_base::binary);
3364 writeU16(os, TOCLIENT_MOVE_PLAYER);
3365 writeV3F1000(os, player->getPosition());
3366 writeF1000(os, player->getPitch());
3367 writeF1000(os, player->getYaw());
3370 v3f pos = player->getPosition();
3371 f32 pitch = player->getPitch();
3372 f32 yaw = player->getYaw();
3373 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3374 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3381 std::string s = os.str();
3382 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3384 m_clients.send(peer_id, 0, data, true);
3387 void Server::SendPlayerPrivileges(u16 peer_id)
3389 Player *player = m_env->getPlayer(peer_id);
3391 if(player->peer_id == PEER_ID_INEXISTENT)
3394 std::set<std::string> privs;
3395 m_script->getAuth(player->getName(), NULL, &privs);
3397 std::ostringstream os(std::ios_base::binary);
3398 writeU16(os, TOCLIENT_PRIVILEGES);
3399 writeU16(os, privs.size());
3400 for(std::set<std::string>::const_iterator i = privs.begin();
3401 i != privs.end(); i++){
3402 os<<serializeString(*i);
3406 std::string s = os.str();
3407 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3409 m_clients.send(peer_id, 0, data, true);
3412 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3414 Player *player = m_env->getPlayer(peer_id);
3416 if(player->peer_id == PEER_ID_INEXISTENT)
3419 std::ostringstream os(std::ios_base::binary);
3420 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3421 os<<serializeLongString(player->inventory_formspec);
3424 std::string s = os.str();
3425 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3427 m_clients.send(peer_id, 0, data, true);
3430 s32 Server::playSound(const SimpleSoundSpec &spec,
3431 const ServerSoundParams ¶ms)
3433 // Find out initial position of sound
3434 bool pos_exists = false;
3435 v3f pos = params.getPos(m_env, &pos_exists);
3436 // If position is not found while it should be, cancel sound
3437 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3440 // Filter destination clients
3441 std::list<u16> dst_clients;
3442 if(params.to_player != "")
3444 Player *player = m_env->getPlayer(params.to_player.c_str());
3446 infostream<<"Server::playSound: Player \""<<params.to_player
3447 <<"\" not found"<<std::endl;
3450 if(player->peer_id == PEER_ID_INEXISTENT){
3451 infostream<<"Server::playSound: Player \""<<params.to_player
3452 <<"\" not connected"<<std::endl;
3455 dst_clients.push_back(player->peer_id);
3459 std::list<u16> clients = m_clients.getClientIDs();
3461 for(std::list<u16>::iterator
3462 i = clients.begin(); i != clients.end(); ++i)
3464 Player *player = m_env->getPlayer(*i);
3468 if(player->getPosition().getDistanceFrom(pos) >
3469 params.max_hear_distance)
3472 dst_clients.push_back(*i);
3475 if(dst_clients.size() == 0)
3479 s32 id = m_next_sound_id++;
3480 // The sound will exist as a reference in m_playing_sounds
3481 m_playing_sounds[id] = ServerPlayingSound();
3482 ServerPlayingSound &psound = m_playing_sounds[id];
3483 psound.params = params;
3484 for(std::list<u16>::iterator i = dst_clients.begin();
3485 i != dst_clients.end(); i++)
3486 psound.clients.insert(*i);
3488 std::ostringstream os(std::ios_base::binary);
3489 writeU16(os, TOCLIENT_PLAY_SOUND);
3491 os<<serializeString(spec.name);
3492 writeF1000(os, spec.gain * params.gain);
3493 writeU8(os, params.type);
3494 writeV3F1000(os, pos);
3495 writeU16(os, params.object);
3496 writeU8(os, params.loop);
3498 std::string s = os.str();
3499 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3501 for(std::list<u16>::iterator i = dst_clients.begin();
3502 i != dst_clients.end(); i++){
3504 m_clients.send(*i, 0, data, true);
3508 void Server::stopSound(s32 handle)
3510 // Get sound reference
3511 std::map<s32, ServerPlayingSound>::iterator i =
3512 m_playing_sounds.find(handle);
3513 if(i == m_playing_sounds.end())
3515 ServerPlayingSound &psound = i->second;
3517 std::ostringstream os(std::ios_base::binary);
3518 writeU16(os, TOCLIENT_STOP_SOUND);
3519 writeS32(os, handle);
3521 std::string s = os.str();
3522 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3524 for(std::set<u16>::iterator i = psound.clients.begin();
3525 i != psound.clients.end(); i++){
3527 m_clients.send(*i, 0, data, true);
3529 // Remove sound reference
3530 m_playing_sounds.erase(i);
3533 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3534 std::list<u16> *far_players, float far_d_nodes)
3536 float maxd = far_d_nodes*BS;
3537 v3f p_f = intToFloat(p, BS);
3541 SharedBuffer<u8> reply(replysize);
3542 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3543 writeS16(&reply[2], p.X);
3544 writeS16(&reply[4], p.Y);
3545 writeS16(&reply[6], p.Z);
3547 std::list<u16> clients = m_clients.getClientIDs();
3548 for(std::list<u16>::iterator
3549 i = clients.begin();
3550 i != clients.end(); ++i)
3555 Player *player = m_env->getPlayer(*i);
3558 // If player is far away, only set modified blocks not sent
3559 v3f player_pos = player->getPosition();
3560 if(player_pos.getDistanceFrom(p_f) > maxd)
3562 far_players->push_back(*i);
3569 m_clients.send(*i, 0, reply, true);
3573 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3574 std::list<u16> *far_players, float far_d_nodes,
3575 bool remove_metadata)
3577 float maxd = far_d_nodes*BS;
3578 v3f p_f = intToFloat(p, BS);
3580 std::list<u16> clients = m_clients.getClientIDs();
3581 for(std::list<u16>::iterator
3582 i = clients.begin();
3583 i != clients.end(); ++i)
3589 Player *player = m_env->getPlayer(*i);
3592 // If player is far away, only set modified blocks not sent
3593 v3f player_pos = player->getPosition();
3594 if(player_pos.getDistanceFrom(p_f) > maxd)
3596 far_players->push_back(*i);
3601 SharedBuffer<u8> reply(0);
3603 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3607 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3608 reply = SharedBuffer<u8>(replysize);
3609 writeU16(&reply[0], TOCLIENT_ADDNODE);
3610 writeS16(&reply[2], p.X);
3611 writeS16(&reply[4], p.Y);
3612 writeS16(&reply[6], p.Z);
3613 n.serialize(&reply[8], client->serialization_version);
3614 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3615 writeU8(&reply[index], remove_metadata ? 0 : 1);
3617 if (!remove_metadata) {
3618 if (client->net_proto_version <= 21) {
3619 // Old clients always clear metadata; fix it
3620 // by sending the full block again.
3621 client->SetBlockNotSent(p);
3628 if (reply.getSize() > 0)
3629 m_clients.send(*i, 0, reply, true);
3633 void Server::setBlockNotSent(v3s16 p)
3635 std::list<u16> clients = m_clients.getClientIDs();
3637 for(std::list<u16>::iterator
3638 i = clients.begin();
3639 i != clients.end(); ++i)
3641 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3642 client->SetBlockNotSent(p);
3647 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3649 DSTACK(__FUNCTION_NAME);
3651 v3s16 p = block->getPos();
3655 bool completely_air = true;
3656 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3657 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3658 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3660 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3662 completely_air = false;
3663 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3668 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3670 infostream<<"[completely air] ";
3671 infostream<<std::endl;
3675 Create a packet with the block in the right format
3678 std::ostringstream os(std::ios_base::binary);
3679 block->serialize(os, ver, false);
3680 block->serializeNetworkSpecific(os, net_proto_version);
3681 std::string s = os.str();
3682 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3684 u32 replysize = 8 + blockdata.getSize();
3685 SharedBuffer<u8> reply(replysize);
3686 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3687 writeS16(&reply[2], p.X);
3688 writeS16(&reply[4], p.Y);
3689 writeS16(&reply[6], p.Z);
3690 memcpy(&reply[8], *blockdata, blockdata.getSize());
3692 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3693 <<": \tpacket size: "<<replysize<<std::endl;*/
3698 m_clients.send(peer_id, 2, reply, true);
3701 void Server::SendBlocks(float dtime)
3703 DSTACK(__FUNCTION_NAME);
3705 JMutexAutoLock envlock(m_env_mutex);
3706 //TODO check if one big lock could be faster then multiple small ones
3708 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3710 std::vector<PrioritySortedBlockTransfer> queue;
3712 s32 total_sending = 0;
3715 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3717 std::list<u16> clients = m_clients.getClientIDs();
3720 for(std::list<u16>::iterator
3721 i = clients.begin();
3722 i != clients.end(); ++i)
3724 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3729 total_sending += client->SendingCount();
3730 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3736 // Lowest priority number comes first.
3737 // Lowest is most important.
3738 std::sort(queue.begin(), queue.end());
3741 for(u32 i=0; i<queue.size(); i++)
3743 //TODO: Calculate limit dynamically
3744 if(total_sending >= g_settings->getS32
3745 ("max_simultaneous_block_sends_server_total"))
3748 PrioritySortedBlockTransfer q = queue[i];
3750 MapBlock *block = NULL;
3753 block = m_env->getMap().getBlockNoCreate(q.pos);
3755 catch(InvalidPositionException &e)
3760 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3765 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3767 client->SentBlock(q.pos);
3773 void Server::fillMediaCache()
3775 DSTACK(__FUNCTION_NAME);
3777 infostream<<"Server: Calculating media file checksums"<<std::endl;
3779 // Collect all media file paths
3780 std::list<std::string> paths;
3781 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3782 i != m_mods.end(); i++){
3783 const ModSpec &mod = *i;
3784 paths.push_back(mod.path + DIR_DELIM + "textures");
3785 paths.push_back(mod.path + DIR_DELIM + "sounds");
3786 paths.push_back(mod.path + DIR_DELIM + "media");
3787 paths.push_back(mod.path + DIR_DELIM + "models");
3789 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3791 // Collect media file information from paths into cache
3792 for(std::list<std::string>::iterator i = paths.begin();
3793 i != paths.end(); i++)
3795 std::string mediapath = *i;
3796 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3797 for(u32 j=0; j<dirlist.size(); j++){
3798 if(dirlist[j].dir) // Ignode dirs
3800 std::string filename = dirlist[j].name;
3801 // If name contains illegal characters, ignore the file
3802 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3803 infostream<<"Server: ignoring illegal file name: \""
3804 <<filename<<"\""<<std::endl;
3807 // If name is not in a supported format, ignore it
3808 const char *supported_ext[] = {
3809 ".png", ".jpg", ".bmp", ".tga",
3810 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3812 ".x", ".b3d", ".md2", ".obj",
3815 if(removeStringEnd(filename, supported_ext) == ""){
3816 infostream<<"Server: ignoring unsupported file extension: \""
3817 <<filename<<"\""<<std::endl;
3820 // Ok, attempt to load the file and add to cache
3821 std::string filepath = mediapath + DIR_DELIM + filename;
3823 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3824 if(fis.good() == false){
3825 errorstream<<"Server::fillMediaCache(): Could not open \""
3826 <<filename<<"\" for reading"<<std::endl;
3829 std::ostringstream tmp_os(std::ios_base::binary);
3833 fis.read(buf, 1024);
3834 std::streamsize len = fis.gcount();
3835 tmp_os.write(buf, len);
3844 errorstream<<"Server::fillMediaCache(): Failed to read \""
3845 <<filename<<"\""<<std::endl;
3848 if(tmp_os.str().length() == 0){
3849 errorstream<<"Server::fillMediaCache(): Empty file \""
3850 <<filepath<<"\""<<std::endl;
3855 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3857 unsigned char *digest = sha1.getDigest();
3858 std::string sha1_base64 = base64_encode(digest, 20);
3859 std::string sha1_hex = hex_encode((char*)digest, 20);
3863 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3864 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3869 struct SendableMediaAnnouncement
3872 std::string sha1_digest;
3874 SendableMediaAnnouncement(const std::string &name_="",
3875 const std::string &sha1_digest_=""):
3877 sha1_digest(sha1_digest_)
3881 void Server::sendMediaAnnouncement(u16 peer_id)
3883 DSTACK(__FUNCTION_NAME);
3885 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
3888 std::list<SendableMediaAnnouncement> file_announcements;
3890 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
3891 i != m_media.end(); i++){
3893 file_announcements.push_back(
3894 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
3898 std::ostringstream os(std::ios_base::binary);
3906 u16 length of sha1_digest
3911 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
3912 writeU16(os, file_announcements.size());
3914 for(std::list<SendableMediaAnnouncement>::iterator
3915 j = file_announcements.begin();
3916 j != file_announcements.end(); ++j){
3917 os<<serializeString(j->name);
3918 os<<serializeString(j->sha1_digest);
3920 os<<serializeString(g_settings->get("remote_media"));
3923 std::string s = os.str();
3924 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3927 m_clients.send(peer_id, 0, data, true);
3930 struct SendableMedia
3936 SendableMedia(const std::string &name_="", const std::string &path_="",
3937 const std::string &data_=""):
3944 void Server::sendRequestedMedia(u16 peer_id,
3945 const std::list<std::string> &tosend)
3947 DSTACK(__FUNCTION_NAME);
3949 verbosestream<<"Server::sendRequestedMedia(): "
3950 <<"Sending files to client"<<std::endl;
3954 // Put 5kB in one bunch (this is not accurate)
3955 u32 bytes_per_bunch = 5000;
3957 std::vector< std::list<SendableMedia> > file_bunches;
3958 file_bunches.push_back(std::list<SendableMedia>());
3960 u32 file_size_bunch_total = 0;
3962 for(std::list<std::string>::const_iterator i = tosend.begin();
3963 i != tosend.end(); ++i)
3965 const std::string &name = *i;
3967 if(m_media.find(name) == m_media.end()){
3968 errorstream<<"Server::sendRequestedMedia(): Client asked for "
3969 <<"unknown file \""<<(name)<<"\""<<std::endl;
3973 //TODO get path + name
3974 std::string tpath = m_media[name].path;
3977 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
3978 if(fis.good() == false){
3979 errorstream<<"Server::sendRequestedMedia(): Could not open \""
3980 <<tpath<<"\" for reading"<<std::endl;
3983 std::ostringstream tmp_os(std::ios_base::binary);
3987 fis.read(buf, 1024);
3988 std::streamsize len = fis.gcount();
3989 tmp_os.write(buf, len);
3990 file_size_bunch_total += len;
3999 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4000 <<name<<"\""<<std::endl;
4003 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4004 <<tname<<"\""<<std::endl;*/
4006 file_bunches[file_bunches.size()-1].push_back(
4007 SendableMedia(name, tpath, tmp_os.str()));
4009 // Start next bunch if got enough data
4010 if(file_size_bunch_total >= bytes_per_bunch){
4011 file_bunches.push_back(std::list<SendableMedia>());
4012 file_size_bunch_total = 0;
4017 /* Create and send packets */
4019 u32 num_bunches = file_bunches.size();
4020 for(u32 i=0; i<num_bunches; i++)
4022 std::ostringstream os(std::ios_base::binary);
4026 u16 total number of texture bunches
4027 u16 index of this bunch
4028 u32 number of files in this bunch
4037 writeU16(os, TOCLIENT_MEDIA);
4038 writeU16(os, num_bunches);
4040 writeU32(os, file_bunches[i].size());
4042 for(std::list<SendableMedia>::iterator
4043 j = file_bunches[i].begin();
4044 j != file_bunches[i].end(); ++j){
4045 os<<serializeString(j->name);
4046 os<<serializeLongString(j->data);
4050 std::string s = os.str();
4051 verbosestream<<"Server::sendRequestedMedia(): bunch "
4052 <<i<<"/"<<num_bunches
4053 <<" files="<<file_bunches[i].size()
4054 <<" size=" <<s.size()<<std::endl;
4055 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4057 m_clients.send(peer_id, 2, data, true);
4061 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4063 if(m_detached_inventories.count(name) == 0){
4064 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4067 Inventory *inv = m_detached_inventories[name];
4069 std::ostringstream os(std::ios_base::binary);
4070 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4071 os<<serializeString(name);
4075 std::string s = os.str();
4076 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4078 if (peer_id != PEER_ID_INEXISTENT)
4081 m_clients.send(peer_id, 0, data, true);
4085 m_clients.sendToAll(0,data,true);
4089 void Server::sendDetachedInventories(u16 peer_id)
4091 DSTACK(__FUNCTION_NAME);
4093 for(std::map<std::string, Inventory*>::iterator
4094 i = m_detached_inventories.begin();
4095 i != m_detached_inventories.end(); i++){
4096 const std::string &name = i->first;
4097 //Inventory *inv = i->second;
4098 sendDetachedInventory(name, peer_id);
4106 void Server::DiePlayer(u16 peer_id)
4108 DSTACK(__FUNCTION_NAME);
4110 PlayerSAO *playersao = getPlayerSAO(peer_id);
4113 infostream<<"Server::DiePlayer(): Player "
4114 <<playersao->getPlayer()->getName()
4115 <<" dies"<<std::endl;
4117 playersao->setHP(0);
4119 // Trigger scripted stuff
4120 m_script->on_dieplayer(playersao);
4122 SendPlayerHP(peer_id);
4123 SendDeathscreen(peer_id, false, v3f(0,0,0));
4126 void Server::RespawnPlayer(u16 peer_id)
4128 DSTACK(__FUNCTION_NAME);
4130 PlayerSAO *playersao = getPlayerSAO(peer_id);
4133 infostream<<"Server::RespawnPlayer(): Player "
4134 <<playersao->getPlayer()->getName()
4135 <<" respawns"<<std::endl;
4137 playersao->setHP(PLAYER_MAX_HP);
4139 bool repositioned = m_script->on_respawnplayer(playersao);
4141 v3f pos = findSpawnPos(m_env->getServerMap());
4142 playersao->setPos(pos);
4146 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4148 DSTACK(__FUNCTION_NAME);
4150 SendAccessDenied(peer_id, reason);
4151 m_clients.event(peer_id,SetDenied);
4152 m_con.DisconnectPeer(peer_id);
4155 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4157 DSTACK(__FUNCTION_NAME);
4158 std::wstring message;
4161 Clear references to playing sounds
4163 for(std::map<s32, ServerPlayingSound>::iterator
4164 i = m_playing_sounds.begin();
4165 i != m_playing_sounds.end();)
4167 ServerPlayingSound &psound = i->second;
4168 psound.clients.erase(peer_id);
4169 if(psound.clients.size() == 0)
4170 m_playing_sounds.erase(i++);
4175 Player *player = m_env->getPlayer(peer_id);
4177 // Collect information about leaving in chat
4179 if(player != NULL && reason != CDR_DENY)
4181 std::wstring name = narrow_to_wide(player->getName());
4184 message += L" left the game.";
4185 if(reason == CDR_TIMEOUT)
4186 message += L" (timed out)";
4190 /* Run scripts and remove from environment */
4194 PlayerSAO *playersao = player->getPlayerSAO();
4197 m_script->on_leaveplayer(playersao);
4199 playersao->disconnected();
4207 if(player != NULL && reason != CDR_DENY)
4209 std::ostringstream os(std::ios_base::binary);
4210 std::list<u16> clients = m_clients.getClientIDs();
4212 for(std::list<u16>::iterator
4213 i = clients.begin();
4214 i != clients.end(); ++i)
4217 Player *player = m_env->getPlayer(*i);
4220 // Get name of player
4221 os<<player->getName()<<" ";
4224 actionstream<<player->getName()<<" "
4225 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4226 <<" List of players: "<<os.str()<<std::endl;
4230 JMutexAutoLock env_lock(m_env_mutex);
4231 m_clients.DeleteClient(peer_id);
4235 // Send leave chat message to all remaining clients
4236 if(message.length() != 0)
4237 SendChatMessage(PEER_ID_INEXISTENT,message);
4240 void Server::UpdateCrafting(u16 peer_id)
4242 DSTACK(__FUNCTION_NAME);
4244 Player* player = m_env->getPlayer(peer_id);
4247 // Get a preview for crafting
4249 InventoryLocation loc;
4250 loc.setPlayer(player->getName());
4251 getCraftingResult(&player->inventory, preview, false, this);
4252 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4254 // Put the new preview in
4255 InventoryList *plist = player->inventory.getList("craftpreview");
4257 assert(plist->getSize() >= 1);
4258 plist->changeItem(0, preview);
4261 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4263 RemoteClient *client = getClientNoEx(peer_id,state_min);
4265 throw ClientNotFoundException("Client not found");
4269 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4271 return m_clients.getClientNoEx(peer_id, state_min);
4274 std::string Server::getPlayerName(u16 peer_id)
4276 Player *player = m_env->getPlayer(peer_id);
4278 return "[id="+itos(peer_id)+"]";
4279 return player->getName();
4282 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4284 Player *player = m_env->getPlayer(peer_id);
4287 return player->getPlayerSAO();
4290 std::wstring Server::getStatusString()
4292 std::wostringstream os(std::ios_base::binary);
4295 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4297 os<<L", uptime="<<m_uptime.get();
4299 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4300 // Information about clients
4303 std::list<u16> clients = m_clients.getClientIDs();
4304 for(std::list<u16>::iterator i = clients.begin();
4305 i != clients.end(); ++i)
4308 Player *player = m_env->getPlayer(*i);
4309 // Get name of player
4310 std::wstring name = L"unknown";
4312 name = narrow_to_wide(player->getName());
4313 // Add name to information string
4321 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4322 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4323 if(g_settings->get("motd") != "")
4324 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4328 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4330 std::set<std::string> privs;
4331 m_script->getAuth(name, NULL, &privs);
4335 bool Server::checkPriv(const std::string &name, const std::string &priv)
4337 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4338 return (privs.count(priv) != 0);
4341 void Server::reportPrivsModified(const std::string &name)
4344 std::list<u16> clients = m_clients.getClientIDs();
4345 for(std::list<u16>::iterator
4346 i = clients.begin();
4347 i != clients.end(); ++i){
4348 Player *player = m_env->getPlayer(*i);
4349 reportPrivsModified(player->getName());
4352 Player *player = m_env->getPlayer(name.c_str());
4355 SendPlayerPrivileges(player->peer_id);
4356 PlayerSAO *sao = player->getPlayerSAO();
4359 sao->updatePrivileges(
4360 getPlayerEffectivePrivs(name),
4365 void Server::reportInventoryFormspecModified(const std::string &name)
4367 Player *player = m_env->getPlayer(name.c_str());
4370 SendPlayerInventoryFormspec(player->peer_id);
4373 void Server::setIpBanned(const std::string &ip, const std::string &name)
4375 m_banmanager->add(ip, name);
4378 void Server::unsetIpBanned(const std::string &ip_or_name)
4380 m_banmanager->remove(ip_or_name);
4383 std::string Server::getBanDescription(const std::string &ip_or_name)
4385 return m_banmanager->getBanDescription(ip_or_name);
4388 void Server::notifyPlayer(const char *name, const std::wstring &msg)
4390 Player *player = m_env->getPlayer(name);
4394 if (player->peer_id == PEER_ID_INEXISTENT)
4397 SendChatMessage(player->peer_id, msg);
4400 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4402 Player *player = m_env->getPlayer(playername);
4406 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4410 SendShowFormspecMessage(player->peer_id, formspec, formname);
4414 u32 Server::hudAdd(Player *player, HudElement *form) {
4418 u32 id = player->getFreeHudID();
4419 if (id < player->hud.size())
4420 player->hud[id] = form;
4422 player->hud.push_back(form);
4424 SendHUDAdd(player->peer_id, id, form);
4428 bool Server::hudRemove(Player *player, u32 id) {
4429 if (!player || id >= player->hud.size() || !player->hud[id])
4432 delete player->hud[id];
4433 player->hud[id] = NULL;
4435 SendHUDRemove(player->peer_id, id);
4439 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4443 SendHUDChange(player->peer_id, id, stat, data);
4447 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4451 SendHUDSetFlags(player->peer_id, flags, mask);
4455 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4458 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4461 std::ostringstream os(std::ios::binary);
4462 writeS32(os, hotbar_itemcount);
4463 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4467 void Server::hudSetHotbarImage(Player *player, std::string name) {
4471 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4474 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4478 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4481 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4482 const std::string &type, const std::vector<std::string> ¶ms)
4487 SendSetSky(player->peer_id, bgcolor, type, params);
4491 bool Server::overrideDayNightRatio(Player *player, bool do_override,
4497 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
4501 void Server::notifyPlayers(const std::wstring &msg)
4503 SendChatMessage(PEER_ID_INEXISTENT,msg);
4506 void Server::spawnParticle(const char *playername, v3f pos,
4507 v3f velocity, v3f acceleration,
4508 float expirationtime, float size, bool
4509 collisiondetection, bool vertical, std::string texture)
4511 Player *player = m_env->getPlayer(playername);
4514 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4515 expirationtime, size, collisiondetection, vertical, texture);
4518 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4519 float expirationtime, float size,
4520 bool collisiondetection, bool vertical, std::string texture)
4522 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4523 expirationtime, size, collisiondetection, vertical, texture);
4526 u32 Server::addParticleSpawner(const char *playername,
4527 u16 amount, float spawntime,
4528 v3f minpos, v3f maxpos,
4529 v3f minvel, v3f maxvel,
4530 v3f minacc, v3f maxacc,
4531 float minexptime, float maxexptime,
4532 float minsize, float maxsize,
4533 bool collisiondetection, bool vertical, std::string texture)
4535 Player *player = m_env->getPlayer(playername);
4540 for(;;) // look for unused particlespawner id
4543 if (std::find(m_particlespawner_ids.begin(),
4544 m_particlespawner_ids.end(), id)
4545 == m_particlespawner_ids.end())
4547 m_particlespawner_ids.push_back(id);
4552 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4553 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4554 minexptime, maxexptime, minsize, maxsize,
4555 collisiondetection, vertical, texture, id);
4560 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4561 v3f minpos, v3f maxpos,
4562 v3f minvel, v3f maxvel,
4563 v3f minacc, v3f maxacc,
4564 float minexptime, float maxexptime,
4565 float minsize, float maxsize,
4566 bool collisiondetection, bool vertical, std::string texture)
4569 for(;;) // look for unused particlespawner id
4572 if (std::find(m_particlespawner_ids.begin(),
4573 m_particlespawner_ids.end(), id)
4574 == m_particlespawner_ids.end())
4576 m_particlespawner_ids.push_back(id);
4581 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4582 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4583 minexptime, maxexptime, minsize, maxsize,
4584 collisiondetection, vertical, texture, id);
4589 void Server::deleteParticleSpawner(const char *playername, u32 id)
4591 Player *player = m_env->getPlayer(playername);
4595 m_particlespawner_ids.erase(
4596 std::remove(m_particlespawner_ids.begin(),
4597 m_particlespawner_ids.end(), id),
4598 m_particlespawner_ids.end());
4599 SendDeleteParticleSpawner(player->peer_id, id);
4602 void Server::deleteParticleSpawnerAll(u32 id)
4604 m_particlespawner_ids.erase(
4605 std::remove(m_particlespawner_ids.begin(),
4606 m_particlespawner_ids.end(), id),
4607 m_particlespawner_ids.end());
4608 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4611 Inventory* Server::createDetachedInventory(const std::string &name)
4613 if(m_detached_inventories.count(name) > 0){
4614 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4615 delete m_detached_inventories[name];
4617 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4619 Inventory *inv = new Inventory(m_itemdef);
4621 m_detached_inventories[name] = inv;
4622 //TODO find a better way to do this
4623 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4630 BoolScopeSet(bool *dst, bool val):
4633 m_orig_state = *m_dst;
4638 *m_dst = m_orig_state;
4645 // actions: time-reversed list
4646 // Return value: success/failure
4647 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4648 std::list<std::string> *log)
4650 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4651 ServerMap *map = (ServerMap*)(&m_env->getMap());
4652 // Disable rollback report sink while reverting
4653 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4655 // Fail if no actions to handle
4656 if(actions.empty()){
4657 log->push_back("Nothing to do.");
4664 for(std::list<RollbackAction>::const_iterator
4665 i = actions.begin();
4666 i != actions.end(); i++)
4668 const RollbackAction &action = *i;
4670 bool success = action.applyRevert(map, this, this);
4673 std::ostringstream os;
4674 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4675 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4677 log->push_back(os.str());
4679 std::ostringstream os;
4680 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4681 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4683 log->push_back(os.str());
4687 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4688 <<" failed"<<std::endl;
4690 // Call it done if less than half failed
4691 return num_failed <= num_tried/2;
4694 // IGameDef interface
4696 IItemDefManager* Server::getItemDefManager()
4700 INodeDefManager* Server::getNodeDefManager()
4704 ICraftDefManager* Server::getCraftDefManager()
4708 ITextureSource* Server::getTextureSource()
4712 IShaderSource* Server::getShaderSource()
4716 u16 Server::allocateUnknownNodeId(const std::string &name)
4718 return m_nodedef->allocateDummy(name);
4720 ISoundManager* Server::getSoundManager()
4722 return &dummySoundManager;
4724 MtEventManager* Server::getEventManager()
4728 IRollbackReportSink* Server::getRollbackReportSink()
4730 if(!m_enable_rollback_recording)
4732 if(!m_rollback_sink_enabled)
4737 IWritableItemDefManager* Server::getWritableItemDefManager()
4741 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4745 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4750 const ModSpec* Server::getModSpec(const std::string &modname)
4752 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4753 i != m_mods.end(); i++){
4754 const ModSpec &mod = *i;
4755 if(mod.name == modname)
4760 void Server::getModNames(std::list<std::string> &modlist)
4762 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4764 modlist.push_back(i->name);
4767 std::string Server::getBuiltinLuaPath()
4769 return porting::path_share + DIR_DELIM + "builtin";
4772 v3f findSpawnPos(ServerMap &map)
4774 //return v3f(50,50,50)*BS;
4779 nodepos = v2s16(0,0);
4784 s16 water_level = map.getWaterLevel();
4786 // Try to find a good place a few times
4787 for(s32 i=0; i<1000; i++)
4790 // We're going to try to throw the player to this position
4791 v2s16 nodepos2d = v2s16(
4792 -range + (myrand() % (range * 2)),
4793 -range + (myrand() % (range * 2)));
4795 // Get ground height at point
4796 s16 groundheight = map.findGroundLevel(nodepos2d);
4797 if (groundheight <= water_level) // Don't go underwater
4799 if (groundheight > water_level + 6) // Don't go to high places
4802 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4803 bool is_good = false;
4805 for (s32 i = 0; i < 10; i++) {
4806 v3s16 blockpos = getNodeBlockPos(nodepos);
4807 map.emergeBlock(blockpos, true);
4808 content_t c = map.getNodeNoEx(nodepos).getContent();
4809 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4811 if (air_count >= 2){
4819 // Found a good place
4820 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4826 return intToFloat(nodepos, BS);
4829 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4831 RemotePlayer *player = NULL;
4832 bool newplayer = false;
4835 Try to get an existing player
4837 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4839 // If player is already connected, cancel
4840 if(player != NULL && player->peer_id != 0)
4842 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4847 If player with the wanted peer_id already exists, cancel.
4849 if(m_env->getPlayer(peer_id) != NULL)
4851 infostream<<"emergePlayer(): Player with wrong name but same"
4852 " peer_id already exists"<<std::endl;
4857 Create a new player if it doesn't exist yet
4862 player = new RemotePlayer(this);
4863 player->updateName(name);
4865 /* Set player position */
4866 infostream<<"Server: Finding spawn place for player \""
4867 <<name<<"\""<<std::endl;
4868 v3f pos = findSpawnPos(m_env->getServerMap());
4869 player->setPosition(pos);
4871 /* Add player to environment */
4872 m_env->addPlayer(player);
4876 Create a new player active object
4878 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4879 getPlayerEffectivePrivs(player->getName()),
4882 /* Clean up old HUD elements from previous sessions */
4883 player->hud.clear();
4885 /* Add object to environment */
4886 m_env->addActiveObject(playersao);
4890 m_script->on_newplayer(playersao);
4895 void dedicated_server_loop(Server &server, bool &kill)
4897 DSTACK(__FUNCTION_NAME);
4899 verbosestream<<"dedicated_server_loop()"<<std::endl;
4901 IntervalLimiter m_profiler_interval;
4905 float steplen = g_settings->getFloat("dedicated_server_step");
4906 // This is kind of a hack but can be done like this
4907 // because server.step() is very light
4909 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4910 sleep_ms((int)(steplen*1000.0));
4912 server.step(steplen);
4914 if(server.getShutdownRequested() || kill)
4916 infostream<<"Dedicated server quitting"<<std::endl;
4918 if(g_settings->getBool("server_announce") == true)
4919 ServerList::sendAnnounce("delete");
4927 float profiler_print_interval =
4928 g_settings->getFloat("profiler_print_interval");
4929 if(profiler_print_interval != 0)
4931 if(m_profiler_interval.step(steplen, profiler_print_interval))
4933 infostream<<"Profiler:"<<std::endl;
4934 g_profiler->print(infostream);
4935 g_profiler->clear();