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 porting::setThreadName("ServerThread");
104 while(!StopRequested())
107 //TimeTaker timer("AsyncRunStep() + Receive()");
109 m_server->AsyncRunStep();
114 catch(con::NoIncomingDataException &e)
117 catch(con::PeerNotFoundException &e)
119 infostream<<"Server: PeerNotFoundException"<<std::endl;
121 catch(ClientNotFoundException &e)
124 catch(con::ConnectionBindFailed &e)
126 m_server->setAsyncFatalError(e.what());
130 m_server->setAsyncFatalError(e.what());
134 END_DEBUG_EXCEPTION_HANDLER(errorstream)
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
167 const std::string &path_world,
168 const SubgameSpec &gamespec,
169 bool simple_singleplayer_mode,
172 m_path_world(path_world),
173 m_gamespec(gamespec),
174 m_simple_singleplayer_mode(simple_singleplayer_mode),
175 m_async_fatal_error(""),
184 m_rollback_sink_enabled(true),
185 m_enable_rollback_recording(false),
188 m_itemdef(createItemDefManager()),
189 m_nodedef(createNodeDefManager()),
190 m_craftdef(createCraftDefManager()),
191 m_event(new EventManager()),
193 m_time_of_day_send_timer(0),
196 m_shutdown_requested(false),
197 m_ignore_map_edit_events(false),
198 m_ignore_map_edit_events_peer_id(0)
201 m_liquid_transform_timer = 0.0;
202 m_liquid_transform_every = 1.0;
203 m_print_info_timer = 0.0;
204 m_masterserver_timer = 0.0;
205 m_objectdata_timer = 0.0;
206 m_emergethread_trigger_timer = 0.0;
207 m_savemap_timer = 0.0;
210 m_lag = g_settings->getFloat("dedicated_server_step");
213 throw ServerError("Supplied empty world path");
215 if(!gamespec.isValid())
216 throw ServerError("Supplied invalid gamespec");
218 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
219 if(m_simple_singleplayer_mode)
220 infostream<<" in simple singleplayer mode"<<std::endl;
222 infostream<<std::endl;
223 infostream<<"- world: "<<m_path_world<<std::endl;
224 infostream<<"- game: "<<m_gamespec.path<<std::endl;
226 // Initialize default settings and override defaults with those provided
228 set_default_settings(g_settings);
229 Settings gamedefaults;
230 getGameMinetestConfig(gamespec.path, gamedefaults);
231 override_default_settings(g_settings, &gamedefaults);
233 // Create server thread
234 m_thread = new ServerThread(this);
236 // Create emerge manager
237 m_emerge = new EmergeManager(this);
239 // Create world if it doesn't exist
240 if(!initializeWorld(m_path_world, m_gamespec.id))
241 throw ServerError("Failed to initialize world");
243 // Create ban manager
244 std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
245 m_banmanager = new BanManager(ban_path);
247 // Create rollback manager
248 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
249 m_rollback = createRollbackManager(rollback_path, this);
251 ModConfiguration modconf(m_path_world);
252 m_mods = modconf.getMods();
253 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
254 // complain about mods with unsatisfied dependencies
255 if(!modconf.isConsistent())
257 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
258 it != unsatisfied_mods.end(); ++it)
261 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
262 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
263 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
264 errorstream << " \"" << *dep_it << "\"";
265 errorstream << std::endl;
269 Settings worldmt_settings;
270 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
271 worldmt_settings.readConfigFile(worldmt.c_str());
272 std::vector<std::string> names = worldmt_settings.getNames();
273 std::set<std::string> load_mod_names;
274 for(std::vector<std::string>::iterator it = names.begin();
275 it != names.end(); ++it)
277 std::string name = *it;
278 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
279 load_mod_names.insert(name.substr(9));
281 // complain about mods declared to be loaded, but not found
282 for(std::vector<ModSpec>::iterator it = m_mods.begin();
283 it != m_mods.end(); ++it)
284 load_mod_names.erase((*it).name);
285 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
286 it != unsatisfied_mods.end(); ++it)
287 load_mod_names.erase((*it).name);
288 if(!load_mod_names.empty())
290 errorstream << "The following mods could not be found:";
291 for(std::set<std::string>::iterator it = load_mod_names.begin();
292 it != load_mod_names.end(); ++it)
293 errorstream << " \"" << (*it) << "\"";
294 errorstream << std::endl;
297 // Path to builtin.lua
298 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
301 JMutexAutoLock envlock(m_env_mutex);
303 // Initialize scripting
304 infostream<<"Server: Initializing Lua"<<std::endl;
306 m_script = new GameScripting(this);
309 // Load and run builtin.lua
310 infostream<<"Server: Loading builtin.lua [\""
311 <<builtinpath<<"\"]"<<std::endl;
312 bool success = m_script->loadMod(builtinpath, "__builtin");
314 errorstream<<"Server: Failed to load and run "
315 <<builtinpath<<std::endl;
316 throw ModError("Failed to load and run "+builtinpath);
319 infostream<<"Server: Loading mods: ";
320 for(std::vector<ModSpec>::iterator i = m_mods.begin();
321 i != m_mods.end(); i++){
322 const ModSpec &mod = *i;
323 infostream<<mod.name<<" ";
325 infostream<<std::endl;
326 // Load and run "mod" scripts
327 for(std::vector<ModSpec>::iterator i = m_mods.begin();
328 i != m_mods.end(); i++){
329 const ModSpec &mod = *i;
330 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
331 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
332 <<scriptpath<<"\"]"<<std::endl;
333 bool success = m_script->loadMod(scriptpath, mod.name);
335 errorstream<<"Server: Failed to load and run "
336 <<scriptpath<<std::endl;
337 throw ModError("Failed to load and run "+scriptpath);
341 // Read Textures and calculate sha1 sums
344 // Apply item aliases in the node definition manager
345 m_nodedef->updateAliases(m_itemdef);
347 // Load the mapgen params from global settings now after any
348 // initial overrides have been set by the mods
349 m_emerge->loadMapgenParams();
351 // Initialize Environment
352 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
353 m_env = new ServerEnvironment(servermap, m_script, this);
355 m_clients.setEnv(m_env);
357 // Run some callbacks after the MG params have been set up but before activation
358 m_script->environment_OnMapgenInit(&m_emerge->params);
360 // Initialize mapgens
361 m_emerge->initMapgens();
363 // Give environment reference to scripting api
364 m_script->initializeEnvironment(m_env);
366 // Register us to receive map edit events
367 servermap->addEventReceiver(this);
369 // If file exists, load environment metadata
370 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
372 infostream<<"Server: Loading environment metadata"<<std::endl;
373 m_env->loadMeta(m_path_world);
377 infostream<<"Server: Loading players"<<std::endl;
378 m_env->deSerializePlayers(m_path_world);
381 Add some test ActiveBlockModifiers to environment
383 add_legacy_abms(m_env, m_nodedef);
385 m_liquid_transform_every = g_settings->getFloat("liquid_update");
390 infostream<<"Server destructing"<<std::endl;
393 Send shutdown message
396 std::wstring line = L"*** Server shutting down";
397 SendChatMessage(PEER_ID_INEXISTENT, line);
401 JMutexAutoLock envlock(m_env_mutex);
404 Execute script shutdown hooks
406 m_script->on_shutdown();
410 JMutexAutoLock envlock(m_env_mutex);
415 infostream<<"Server: Saving players"<<std::endl;
416 m_env->serializePlayers(m_path_world);
419 Save environment metadata
421 infostream<<"Server: Saving environment metadata"<<std::endl;
422 m_env->saveMeta(m_path_world);
431 // stop all emerge threads before deleting players that may have
432 // requested blocks to be emerged
433 m_emerge->stopThreads();
435 // Delete things in the reverse order of creation
438 // N.B. the EmergeManager should be deleted after the Environment since Map
439 // depends on EmergeManager to write its current params to the map meta
448 // Deinitialize scripting
449 infostream<<"Server: Deinitializing scripting"<<std::endl;
452 // Delete detached inventories
454 for(std::map<std::string, Inventory*>::iterator
455 i = m_detached_inventories.begin();
456 i != m_detached_inventories.end(); i++){
462 void Server::start(Address bind_addr)
464 DSTACK(__FUNCTION_NAME);
465 infostream<<"Starting server on "
466 << bind_addr.serializeString() <<"..."<<std::endl;
468 // Stop thread if already running
471 // Initialize connection
472 m_con.SetTimeoutMs(30);
473 m_con.Serve(bind_addr);
478 // ASCII art for the win!
480 <<" .__ __ __ "<<std::endl
481 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
482 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
483 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
484 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
485 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
486 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
487 actionstream<<"Server for gameid=\""<<m_gamespec.id
488 <<"\" listening on "<<bind_addr.serializeString()<<":"
489 <<bind_addr.getPort() << "."<<std::endl;
494 DSTACK(__FUNCTION_NAME);
496 infostream<<"Server: Stopping and waiting threads"<<std::endl;
498 // Stop threads (set run=false first so both start stopping)
500 //m_emergethread.setRun(false);
502 //m_emergethread.stop();
504 infostream<<"Server: Threads stopped"<<std::endl;
507 void Server::step(float dtime)
509 DSTACK(__FUNCTION_NAME);
514 JMutexAutoLock lock(m_step_dtime_mutex);
515 m_step_dtime += dtime;
517 // Throw if fatal error occurred in thread
518 std::string async_err = m_async_fatal_error.get();
520 throw ServerError(async_err);
524 void Server::AsyncRunStep(bool initial_step)
526 DSTACK(__FUNCTION_NAME);
528 g_profiler->add("Server::AsyncRunStep (num)", 1);
532 JMutexAutoLock lock1(m_step_dtime_mutex);
533 dtime = m_step_dtime;
537 // Send blocks to clients
541 if((dtime < 0.001) && (initial_step == false))
544 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
546 //infostream<<"Server steps "<<dtime<<std::endl;
547 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
550 JMutexAutoLock lock1(m_step_dtime_mutex);
551 m_step_dtime -= dtime;
558 m_uptime.set(m_uptime.get() + dtime);
564 Update time of day and overall game time
567 JMutexAutoLock envlock(m_env_mutex);
569 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
572 Send to clients at constant intervals
575 m_time_of_day_send_timer -= dtime;
576 if(m_time_of_day_send_timer < 0.0)
578 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
579 u16 time = m_env->getTimeOfDay();
580 float time_speed = g_settings->getFloat("time_speed");
581 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
586 JMutexAutoLock lock(m_env_mutex);
587 // Figure out and report maximum lag to environment
588 float max_lag = m_env->getMaxLagEstimate();
589 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
591 if(dtime > 0.1 && dtime > max_lag * 2.0)
592 infostream<<"Server: Maximum lag peaked to "<<dtime
596 m_env->reportMaxLagEstimate(max_lag);
598 ScopeProfiler sp(g_profiler, "SEnv step");
599 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
603 const float map_timer_and_unload_dtime = 2.92;
604 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
606 JMutexAutoLock lock(m_env_mutex);
607 // Run Map's timers and unload unused data
608 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
609 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
610 g_settings->getFloat("server_unload_unused_data_timeout"));
621 JMutexAutoLock lock(m_env_mutex);
623 std::list<u16> clientids = m_clients.getClientIDs();
625 ScopeProfiler sp(g_profiler, "Server: handle players");
627 for(std::list<u16>::iterator
628 i = clientids.begin();
629 i != clientids.end(); ++i)
631 PlayerSAO *playersao = getPlayerSAO(*i);
632 if(playersao == NULL)
636 Handle player HPs (die if hp=0)
638 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
640 if(playersao->getHP() == 0)
647 Send player breath if changed
649 if(playersao->m_breath_not_sent){
650 SendPlayerBreath(*i);
654 Send player inventories if necessary
656 if(playersao->m_moved){
658 playersao->m_moved = false;
660 if(playersao->m_inventory_not_sent){
667 /* Transform liquids */
668 m_liquid_transform_timer += dtime;
669 if(m_liquid_transform_timer >= m_liquid_transform_every)
671 m_liquid_transform_timer -= m_liquid_transform_every;
673 JMutexAutoLock lock(m_env_mutex);
675 ScopeProfiler sp(g_profiler, "Server: liquid transform");
677 std::map<v3s16, MapBlock*> modified_blocks;
678 m_env->getMap().transformLiquids(modified_blocks);
683 core::map<v3s16, MapBlock*> lighting_modified_blocks;
684 ServerMap &map = ((ServerMap&)m_env->getMap());
685 map.updateLighting(modified_blocks, lighting_modified_blocks);
687 // Add blocks modified by lighting to modified_blocks
688 for(core::map<v3s16, MapBlock*>::Iterator
689 i = lighting_modified_blocks.getIterator();
690 i.atEnd() == false; i++)
692 MapBlock *block = i.getNode()->getValue();
693 modified_blocks.insert(block->getPos(), block);
697 Set the modified blocks unsent for all the clients
699 if(modified_blocks.size() > 0)
701 SetBlocksNotSent(modified_blocks);
704 m_clients.step(dtime);
706 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
708 // send masterserver announce
710 float &counter = m_masterserver_timer;
711 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
712 g_settings->getBool("server_announce") == true)
714 ServerList::sendAnnounce(!counter ? "start" : "update",
715 m_clients.getPlayerNames(),
717 m_env->getGameTime(),
728 Check added and deleted active objects
731 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
732 JMutexAutoLock envlock(m_env_mutex);
735 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
736 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
738 // Radius inside which objects are active
739 s16 radius = g_settings->getS16("active_object_send_range_blocks");
740 radius *= MAP_BLOCKSIZE;
742 for(std::map<u16, RemoteClient*>::iterator
744 i != clients.end(); ++i)
746 RemoteClient *client = i->second;
748 // If definitions and textures have not been sent, don't
749 // send objects either
750 if (client->getState() < DefinitionsSent)
753 Player *player = m_env->getPlayer(client->peer_id);
756 // This can happen if the client timeouts somehow
757 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
759 <<" has no associated player"<<std::endl;*/
762 v3s16 pos = floatToInt(player->getPosition(), BS);
764 std::set<u16> removed_objects;
765 std::set<u16> added_objects;
766 m_env->getRemovedActiveObjects(pos, radius,
767 client->m_known_objects, removed_objects);
768 m_env->getAddedActiveObjects(pos, radius,
769 client->m_known_objects, added_objects);
771 // Ignore if nothing happened
772 if(removed_objects.size() == 0 && added_objects.size() == 0)
774 //infostream<<"active objects: none changed"<<std::endl;
778 std::string data_buffer;
782 // Handle removed objects
783 writeU16((u8*)buf, removed_objects.size());
784 data_buffer.append(buf, 2);
785 for(std::set<u16>::iterator
786 i = removed_objects.begin();
787 i != removed_objects.end(); ++i)
791 ServerActiveObject* obj = m_env->getActiveObject(id);
793 // Add to data buffer for sending
794 writeU16((u8*)buf, id);
795 data_buffer.append(buf, 2);
797 // Remove from known objects
798 client->m_known_objects.erase(id);
800 if(obj && obj->m_known_by_count > 0)
801 obj->m_known_by_count--;
804 // Handle added objects
805 writeU16((u8*)buf, added_objects.size());
806 data_buffer.append(buf, 2);
807 for(std::set<u16>::iterator
808 i = added_objects.begin();
809 i != added_objects.end(); ++i)
813 ServerActiveObject* obj = m_env->getActiveObject(id);
816 u8 type = ACTIVEOBJECT_TYPE_INVALID;
818 infostream<<"WARNING: "<<__FUNCTION_NAME
819 <<": NULL object"<<std::endl;
821 type = obj->getSendType();
823 // Add to data buffer for sending
824 writeU16((u8*)buf, id);
825 data_buffer.append(buf, 2);
826 writeU8((u8*)buf, type);
827 data_buffer.append(buf, 1);
830 data_buffer.append(serializeLongString(
831 obj->getClientInitializationData(client->net_proto_version)));
833 data_buffer.append(serializeLongString(""));
835 // Add to known objects
836 client->m_known_objects.insert(id);
839 obj->m_known_by_count++;
843 SharedBuffer<u8> reply(2 + data_buffer.size());
844 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
845 memcpy((char*)&reply[2], data_buffer.c_str(),
848 m_clients.send(client->peer_id, 0, reply, true);
850 verbosestream<<"Server: Sent object remove/add: "
851 <<removed_objects.size()<<" removed, "
852 <<added_objects.size()<<" added, "
853 <<"packet size is "<<reply.getSize()<<std::endl;
858 Collect a list of all the objects known by the clients
859 and report it back to the environment.
862 core::map<u16, bool> all_known_objects;
864 for(core::map<u16, RemoteClient*>::Iterator
865 i = m_clients.getIterator();
866 i.atEnd() == false; i++)
868 RemoteClient *client = i.getNode()->getValue();
869 // Go through all known objects of client
870 for(core::map<u16, bool>::Iterator
871 i = client->m_known_objects.getIterator();
872 i.atEnd()==false; i++)
874 u16 id = i.getNode()->getKey();
875 all_known_objects[id] = true;
879 m_env->setKnownActiveObjects(whatever);
888 JMutexAutoLock envlock(m_env_mutex);
889 ScopeProfiler sp(g_profiler, "Server: sending object messages");
892 // Value = data sent by object
893 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
895 // Get active object messages from environment
898 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
902 std::list<ActiveObjectMessage>* message_list = NULL;
903 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
904 n = buffered_messages.find(aom.id);
905 if(n == buffered_messages.end())
907 message_list = new std::list<ActiveObjectMessage>;
908 buffered_messages[aom.id] = message_list;
912 message_list = n->second;
914 message_list->push_back(aom);
918 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
919 // Route data to every client
920 for(std::map<u16, RemoteClient*>::iterator
922 i != clients.end(); ++i)
924 RemoteClient *client = i->second;
925 std::string reliable_data;
926 std::string unreliable_data;
927 // Go through all objects in message buffer
928 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
929 j = buffered_messages.begin();
930 j != buffered_messages.end(); ++j)
932 // If object is not known by client, skip it
934 if(client->m_known_objects.find(id) == client->m_known_objects.end())
936 // Get message list of object
937 std::list<ActiveObjectMessage>* list = j->second;
938 // Go through every message
939 for(std::list<ActiveObjectMessage>::iterator
940 k = list->begin(); k != list->end(); ++k)
942 // Compose the full new data with header
943 ActiveObjectMessage aom = *k;
944 std::string new_data;
947 writeU16((u8*)&buf[0], aom.id);
948 new_data.append(buf, 2);
950 new_data += serializeString(aom.datastring);
951 // Add data to buffer
953 reliable_data += new_data;
955 unreliable_data += new_data;
959 reliable_data and unreliable_data are now ready.
962 if(reliable_data.size() > 0)
964 SharedBuffer<u8> reply(2 + reliable_data.size());
965 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
966 memcpy((char*)&reply[2], reliable_data.c_str(),
967 reliable_data.size());
969 m_clients.send(client->peer_id, 0, reply, true);
971 if(unreliable_data.size() > 0)
973 SharedBuffer<u8> reply(2 + unreliable_data.size());
974 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
975 memcpy((char*)&reply[2], unreliable_data.c_str(),
976 unreliable_data.size());
977 // Send as unreliable
978 m_clients.send(client->peer_id, 1, reply, false);
981 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
983 infostream<<"Server: Size of object message data: "
984 <<"reliable: "<<reliable_data.size()
985 <<", unreliable: "<<unreliable_data.size()
991 // Clear buffered_messages
992 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
993 i = buffered_messages.begin();
994 i != buffered_messages.end(); ++i)
1001 Send queued-for-sending map edit events.
1004 // We will be accessing the environment
1005 JMutexAutoLock lock(m_env_mutex);
1007 // Don't send too many at a time
1010 // Single change sending is disabled if queue size is not small
1011 bool disable_single_change_sending = false;
1012 if(m_unsent_map_edit_queue.size() >= 4)
1013 disable_single_change_sending = true;
1015 int event_count = m_unsent_map_edit_queue.size();
1017 // We'll log the amount of each
1020 while(m_unsent_map_edit_queue.size() != 0)
1022 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1024 // Players far away from the change are stored here.
1025 // Instead of sending the changes, MapBlocks are set not sent
1027 std::list<u16> far_players;
1029 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
1031 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1032 prof.add("MEET_ADDNODE", 1);
1033 if(disable_single_change_sending)
1034 sendAddNode(event->p, event->n, event->already_known_by_peer,
1035 &far_players, 5, event->type == MEET_ADDNODE);
1037 sendAddNode(event->p, event->n, event->already_known_by_peer,
1038 &far_players, 30, event->type == MEET_ADDNODE);
1040 else if(event->type == MEET_REMOVENODE)
1042 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1043 prof.add("MEET_REMOVENODE", 1);
1044 if(disable_single_change_sending)
1045 sendRemoveNode(event->p, event->already_known_by_peer,
1048 sendRemoveNode(event->p, event->already_known_by_peer,
1051 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1053 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1054 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1055 setBlockNotSent(event->p);
1057 else if(event->type == MEET_OTHER)
1059 infostream<<"Server: MEET_OTHER"<<std::endl;
1060 prof.add("MEET_OTHER", 1);
1061 for(std::set<v3s16>::iterator
1062 i = event->modified_blocks.begin();
1063 i != event->modified_blocks.end(); ++i)
1065 setBlockNotSent(*i);
1070 prof.add("unknown", 1);
1071 infostream<<"WARNING: Server: Unknown MapEditEvent "
1072 <<((u32)event->type)<<std::endl;
1076 Set blocks not sent to far players
1078 if(far_players.size() > 0)
1080 // Convert list format to that wanted by SetBlocksNotSent
1081 std::map<v3s16, MapBlock*> modified_blocks2;
1082 for(std::set<v3s16>::iterator
1083 i = event->modified_blocks.begin();
1084 i != event->modified_blocks.end(); ++i)
1086 modified_blocks2[*i] =
1087 m_env->getMap().getBlockNoCreateNoEx(*i);
1089 // Set blocks not sent
1090 for(std::list<u16>::iterator
1091 i = far_players.begin();
1092 i != far_players.end(); ++i)
1095 RemoteClient *client = getClient(peer_id);
1098 client->SetBlocksNotSent(modified_blocks2);
1104 /*// Don't send too many at a time
1106 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1110 if(event_count >= 5){
1111 infostream<<"Server: MapEditEvents:"<<std::endl;
1112 prof.print(infostream);
1113 } else if(event_count != 0){
1114 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1115 prof.print(verbosestream);
1121 Trigger emergethread (it somehow gets to a non-triggered but
1122 bysy state sometimes)
1125 float &counter = m_emergethread_trigger_timer;
1131 m_emerge->startThreads();
1133 // Update m_enable_rollback_recording here too
1134 m_enable_rollback_recording =
1135 g_settings->getBool("enable_rollback_recording");
1139 // Save map, players and auth stuff
1141 float &counter = m_savemap_timer;
1143 if(counter >= g_settings->getFloat("server_map_save_interval"))
1146 JMutexAutoLock lock(m_env_mutex);
1148 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1151 if(m_banmanager->isModified())
1152 m_banmanager->save();
1154 // Save changed parts of map
1155 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1158 m_env->serializePlayers(m_path_world);
1160 // Save environment metadata
1161 m_env->saveMeta(m_path_world);
1166 void Server::Receive()
1168 DSTACK(__FUNCTION_NAME);
1169 SharedBuffer<u8> data;
1173 datasize = m_con.Receive(peer_id,data);
1174 ProcessData(*data, datasize, peer_id);
1176 catch(con::InvalidIncomingDataException &e)
1178 infostream<<"Server::Receive(): "
1179 "InvalidIncomingDataException: what()="
1180 <<e.what()<<std::endl;
1182 catch(con::PeerNotFoundException &e)
1184 //NOTE: This is not needed anymore
1186 // The peer has been disconnected.
1187 // Find the associated player and remove it.
1189 /*JMutexAutoLock envlock(m_env_mutex);
1191 infostream<<"ServerThread: peer_id="<<peer_id
1192 <<" has apparently closed connection. "
1193 <<"Removing player."<<std::endl;
1195 m_env->removePlayer(peer_id);*/
1197 catch(ClientStateError &e)
1199 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1200 DenyAccess(peer_id, L"Your client sent something server didn't expect."
1201 L"Try reconnecting or updating your client");
1205 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1207 std::string playername = "";
1208 PlayerSAO *playersao = NULL;
1210 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,InitDone);
1211 if (client != NULL) {
1212 playername = client->getName();
1213 playersao = emergePlayer(playername.c_str(), peer_id);
1217 RemotePlayer *player =
1218 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1220 // If failed, cancel
1221 if((playersao == NULL) || (player == NULL))
1223 if(player && player->peer_id != 0){
1224 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1225 <<" (player allocated to an another client)"<<std::endl;
1226 DenyAccess(peer_id, L"Another client is connected with this "
1227 L"name. If your client closed unexpectedly, try again in "
1230 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1232 DenyAccess(peer_id, L"Could not allocate player.");
1238 Send complete position information
1240 SendMovePlayer(peer_id);
1243 SendPlayerPrivileges(peer_id);
1245 // Send inventory formspec
1246 SendPlayerInventoryFormspec(peer_id);
1249 UpdateCrafting(peer_id);
1250 SendInventory(peer_id);
1253 if(g_settings->getBool("enable_damage"))
1254 SendPlayerHP(peer_id);
1257 SendPlayerBreath(peer_id);
1259 // Show death screen if necessary
1261 SendDeathscreen(peer_id, false, v3f(0,0,0));
1263 // Note things in chat if not in simple singleplayer mode
1264 if(!m_simple_singleplayer_mode)
1266 // Send information about server to player in chat
1267 SendChatMessage(peer_id, getStatusString());
1269 // Send information about joining in chat
1271 std::wstring name = L"unknown";
1272 Player *player = m_env->getPlayer(peer_id);
1274 name = narrow_to_wide(player->getName());
1276 std::wstring message;
1279 message += L" joined the game.";
1280 SendChatMessage(PEER_ID_INEXISTENT,message);
1283 Address addr = getPeerAddress(player->peer_id);
1284 std::string ip_str = addr.serializeString();
1285 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1290 std::vector<std::string> names = m_clients.getPlayerNames();
1292 actionstream<<player->getName() <<" joins game. List of players: ";
1294 for (std::vector<std::string>::iterator i = names.begin();
1295 i != names.end(); i++)
1297 actionstream << *i << " ";
1300 actionstream<<std::endl;
1305 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1307 DSTACK(__FUNCTION_NAME);
1308 // Environment is locked first.
1309 JMutexAutoLock envlock(m_env_mutex);
1311 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1315 Address address = getPeerAddress(peer_id);
1316 addr_s = address.serializeString();
1318 // drop player if is ip is banned
1319 if(m_banmanager->isIpBanned(addr_s)){
1320 std::string ban_name = m_banmanager->getBanName(addr_s);
1321 infostream<<"Server: A banned client tried to connect from "
1322 <<addr_s<<"; banned name was "
1323 <<ban_name<<std::endl;
1324 // This actually doesn't seem to transfer to the client
1325 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1326 +narrow_to_wide(ban_name));
1330 catch(con::PeerNotFoundException &e)
1333 * no peer for this packet found
1334 * most common reason is peer timeout, e.g. peer didn't
1335 * respond for some time, your server was overloaded or
1338 infostream<<"Server::ProcessData(): Cancelling: peer "
1339 <<peer_id<<" not found"<<std::endl;
1349 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1351 if(command == TOSERVER_INIT)
1353 // [0] u16 TOSERVER_INIT
1354 // [2] u8 SER_FMT_VER_HIGHEST_READ
1355 // [3] u8[20] player_name
1356 // [23] u8[28] password <--- can be sent without this, from old versions
1358 if(datasize < 2+1+PLAYERNAME_SIZE)
1361 RemoteClient* client = getClient(peer_id,Created);
1363 // If net_proto_version is set, this client has already been handled
1364 if(client->getState() > Created)
1366 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1367 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1371 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1372 <<peer_id<<")"<<std::endl;
1374 // Do not allow multiple players in simple singleplayer mode.
1375 // This isn't a perfect way to do it, but will suffice for now
1376 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1377 infostream<<"Server: Not allowing another client ("<<addr_s
1378 <<") to connect in simple singleplayer mode"<<std::endl;
1379 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1383 // First byte after command is maximum supported
1384 // serialization version
1385 u8 client_max = data[2];
1386 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1387 // Use the highest version supported by both
1388 u8 deployed = std::min(client_max, our_max);
1389 // If it's lower than the lowest supported, give up.
1390 if(deployed < SER_FMT_VER_LOWEST)
1391 deployed = SER_FMT_VER_INVALID;
1393 if(deployed == SER_FMT_VER_INVALID)
1395 actionstream<<"Server: A mismatched client tried to connect from "
1396 <<addr_s<<std::endl;
1397 infostream<<"Server: Cannot negotiate serialization version with "
1398 <<addr_s<<std::endl;
1399 DenyAccess(peer_id, std::wstring(
1400 L"Your client's version is not supported.\n"
1401 L"Server version is ")
1402 + narrow_to_wide(minetest_version_simple) + L"."
1407 client->setPendingSerializationVersion(deployed);
1410 Read and check network protocol version
1413 u16 min_net_proto_version = 0;
1414 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1415 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1417 // Use same version as minimum and maximum if maximum version field
1418 // doesn't exist (backwards compatibility)
1419 u16 max_net_proto_version = min_net_proto_version;
1420 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1421 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1423 // Start with client's maximum version
1424 u16 net_proto_version = max_net_proto_version;
1426 // Figure out a working version if it is possible at all
1427 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1428 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1430 // If maximum is larger than our maximum, go with our maximum
1431 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1432 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1433 // Else go with client's maximum
1435 net_proto_version = max_net_proto_version;
1438 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1439 <<min_net_proto_version<<", max: "<<max_net_proto_version
1440 <<", chosen: "<<net_proto_version<<std::endl;
1442 client->net_proto_version = net_proto_version;
1444 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1445 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1447 actionstream<<"Server: A mismatched client tried to connect from "
1448 <<addr_s<<std::endl;
1449 DenyAccess(peer_id, std::wstring(
1450 L"Your client's version is not supported.\n"
1451 L"Server version is ")
1452 + narrow_to_wide(minetest_version_simple) + L",\n"
1453 + L"server's PROTOCOL_VERSION is "
1454 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1456 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1457 + L", client's PROTOCOL_VERSION is "
1458 + narrow_to_wide(itos(min_net_proto_version))
1460 + narrow_to_wide(itos(max_net_proto_version))
1465 if(g_settings->getBool("strict_protocol_version_checking"))
1467 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1469 actionstream<<"Server: A mismatched (strict) client tried to "
1470 <<"connect from "<<addr_s<<std::endl;
1471 DenyAccess(peer_id, std::wstring(
1472 L"Your client's version is not supported.\n"
1473 L"Server version is ")
1474 + narrow_to_wide(minetest_version_simple) + L",\n"
1475 + L"server's PROTOCOL_VERSION (strict) is "
1476 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1477 + L", client's PROTOCOL_VERSION is "
1478 + narrow_to_wide(itos(min_net_proto_version))
1480 + narrow_to_wide(itos(max_net_proto_version))
1491 char playername[PLAYERNAME_SIZE];
1492 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1494 playername[i] = data[3+i];
1496 playername[PLAYERNAME_SIZE-1] = 0;
1498 if(playername[0]=='\0')
1500 actionstream<<"Server: Player with an empty name "
1501 <<"tried to connect from "<<addr_s<<std::endl;
1502 DenyAccess(peer_id, L"Empty name");
1506 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1508 actionstream<<"Server: Player with an invalid name "
1509 <<"tried to connect from "<<addr_s<<std::endl;
1510 DenyAccess(peer_id, L"Name contains unallowed characters");
1514 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1516 actionstream<<"Server: Player with the name \"singleplayer\" "
1517 <<"tried to connect from "<<addr_s<<std::endl;
1518 DenyAccess(peer_id, L"Name is not allowed");
1524 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1526 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1527 <<"tried to connect from "<<addr_s<<" "
1528 <<"but it was disallowed for the following reason: "
1529 <<reason<<std::endl;
1530 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1535 infostream<<"Server: New connection: \""<<playername<<"\" from "
1536 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1539 char given_password[PASSWORD_SIZE];
1540 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1542 // old version - assume blank password
1543 given_password[0] = 0;
1547 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1549 given_password[i] = data[23+i];
1551 given_password[PASSWORD_SIZE-1] = 0;
1554 if(!base64_is_valid(given_password)){
1555 actionstream<<"Server: "<<playername
1556 <<" supplied invalid password hash"<<std::endl;
1557 DenyAccess(peer_id, L"Invalid password hash");
1561 // Enforce user limit.
1562 // Don't enforce for users that have some admin right
1563 if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") &&
1564 !checkPriv(playername, "server") &&
1565 !checkPriv(playername, "ban") &&
1566 !checkPriv(playername, "privs") &&
1567 !checkPriv(playername, "password") &&
1568 playername != g_settings->get("name"))
1570 actionstream<<"Server: "<<playername<<" tried to join, but there"
1571 <<" are already max_users="
1572 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1573 DenyAccess(peer_id, L"Too many users.");
1577 std::string checkpwd; // Password hash to check against
1578 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1580 // If no authentication info exists for user, create it
1582 if(!isSingleplayer() &&
1583 g_settings->getBool("disallow_empty_password") &&
1584 std::string(given_password) == ""){
1585 actionstream<<"Server: "<<playername
1586 <<" supplied empty password"<<std::endl;
1587 DenyAccess(peer_id, L"Empty passwords are "
1588 L"disallowed. Set a password and try again.");
1591 std::wstring raw_default_password =
1592 narrow_to_wide(g_settings->get("default_password"));
1593 std::string initial_password =
1594 translatePassword(playername, raw_default_password);
1596 // If default_password is empty, allow any initial password
1597 if (raw_default_password.length() == 0)
1598 initial_password = given_password;
1600 m_script->createAuth(playername, initial_password);
1603 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1606 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1607 <<" (auth handler does not work?)"<<std::endl;
1608 DenyAccess(peer_id, L"Not allowed to login");
1612 if(given_password != checkpwd){
1613 actionstream<<"Server: "<<playername<<" supplied wrong password"
1615 DenyAccess(peer_id, L"Wrong password");
1619 RemotePlayer *player =
1620 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1622 if(player && player->peer_id != 0){
1623 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1624 <<" (player allocated to an another client)"<<std::endl;
1625 DenyAccess(peer_id, L"Another client is connected with this "
1626 L"name. If your client closed unexpectedly, try again in "
1630 m_clients.setPlayerName(peer_id,playername);
1633 Answer with a TOCLIENT_INIT
1636 SharedBuffer<u8> reply(2+1+6+8+4);
1637 writeU16(&reply[0], TOCLIENT_INIT);
1638 writeU8(&reply[2], deployed);
1639 //send dummy pos for legacy reasons only
1640 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1641 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1642 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1645 m_clients.send(peer_id, 0, reply, true);
1646 m_clients.event(peer_id, Init);
1652 if(command == TOSERVER_INIT2)
1655 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1656 <<peer_id<<std::endl;
1658 m_clients.event(peer_id, GotInit2);
1659 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1662 ///// begin compatibility code
1663 PlayerSAO* playersao = NULL;
1664 if (protocol_version <= 22) {
1665 playersao = StageTwoClientInit(peer_id);
1667 if (playersao == NULL) {
1669 << "TOSERVER_INIT2 stage 2 client init failed for peer "
1670 << peer_id << std::endl;
1674 ///// end compatibility code
1677 Send some initialization data
1680 infostream<<"Server: Sending content to "
1681 <<getPlayerName(peer_id)<<std::endl;
1683 // Send player movement settings
1684 SendMovement(peer_id);
1686 // Send item definitions
1687 SendItemDef(peer_id, m_itemdef, protocol_version);
1689 // Send node definitions
1690 SendNodeDef(peer_id, m_nodedef, protocol_version);
1692 m_clients.event(peer_id, SetDefinitionsSent);
1694 // Send media announcement
1695 sendMediaAnnouncement(peer_id);
1697 // Send detached inventories
1698 sendDetachedInventories(peer_id);
1701 u16 time = m_env->getTimeOfDay();
1702 float time_speed = g_settings->getFloat("time_speed");
1703 SendTimeOfDay(peer_id, time, time_speed);
1705 ///// begin compatibility code
1706 if (protocol_version <= 22) {
1707 m_clients.event(peer_id, SetClientReady);
1708 m_script->on_joinplayer(playersao);
1710 ///// end compatibility code
1712 // Warnings about protocol version can be issued here
1713 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1715 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1716 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1722 u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version;
1723 u16 peer_proto_ver = getClient(peer_id,InitDone)->net_proto_version;
1725 if(peer_ser_ver == SER_FMT_VER_INVALID)
1727 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1728 " serialization format invalid or not initialized."
1729 " Skipping incoming command="<<command<<std::endl;
1733 /* Handle commands relate to client startup */
1734 if(command == TOSERVER_REQUEST_MEDIA) {
1735 std::string datastring((char*)&data[2], datasize-2);
1736 std::istringstream is(datastring, std::ios_base::binary);
1738 std::list<std::string> tosend;
1739 u16 numfiles = readU16(is);
1741 infostream<<"Sending "<<numfiles<<" files to "
1742 <<getPlayerName(peer_id)<<std::endl;
1743 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1745 for(int i = 0; i < numfiles; i++) {
1746 std::string name = deSerializeString(is);
1747 tosend.push_back(name);
1748 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1752 sendRequestedMedia(peer_id, tosend);
1755 else if(command == TOSERVER_RECEIVED_MEDIA) {
1758 else if(command == TOSERVER_CLIENT_READY) {
1759 // clients <= protocol version 22 did not send ready message,
1760 // they're already initialized
1761 assert(peer_proto_ver > 22);
1763 PlayerSAO* playersao = StageTwoClientInit(peer_id);
1765 if (playersao == NULL) {
1767 << "TOSERVER_CLIENT_READY stage 2 client init failed for peer "
1768 << peer_id << std::endl;
1776 m_clients.setClientVersion(
1778 data[2], data[3], data[4],
1779 std::string((char*) &data[8],(u16) data[6]));
1781 m_clients.event(peer_id, SetClientReady);
1782 m_script->on_joinplayer(playersao);
1785 else if(command == TOSERVER_GOTBLOCKS)
1798 u16 count = data[2];
1799 for(u16 i=0; i<count; i++)
1801 if((s16)datasize < 2+1+(i+1)*6)
1802 throw con::InvalidIncomingDataException
1803 ("GOTBLOCKS length is too short");
1804 v3s16 p = readV3S16(&data[2+1+i*6]);
1805 /*infostream<<"Server: GOTBLOCKS ("
1806 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1807 RemoteClient *client = getClient(peer_id);
1808 client->GotBlock(p);
1813 if (m_clients.getClientState(peer_id) < Active)
1815 if (command == TOSERVER_PLAYERPOS) return;
1817 errorstream<<"Got packet command: " << command << " for peer id "
1818 << peer_id << " but client isn't active yet. Dropping packet "
1823 Player *player = m_env->getPlayer(peer_id);
1825 errorstream<<"Server::ProcessData(): Cancelling: "
1826 "No player for peer_id="<<peer_id
1831 PlayerSAO *playersao = player->getPlayerSAO();
1832 if(playersao == NULL){
1833 errorstream<<"Server::ProcessData(): Cancelling: "
1834 "No player object for peer_id="<<peer_id
1839 if(command == TOSERVER_PLAYERPOS)
1841 if(datasize < 2+12+12+4+4)
1845 v3s32 ps = readV3S32(&data[start+2]);
1846 v3s32 ss = readV3S32(&data[start+2+12]);
1847 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1848 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1850 if(datasize >= 2+12+12+4+4+4)
1851 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
1852 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1853 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1854 pitch = wrapDegrees(pitch);
1855 yaw = wrapDegrees(yaw);
1857 player->setPosition(position);
1858 player->setSpeed(speed);
1859 player->setPitch(pitch);
1860 player->setYaw(yaw);
1861 player->keyPressed=keyPressed;
1862 player->control.up = (bool)(keyPressed&1);
1863 player->control.down = (bool)(keyPressed&2);
1864 player->control.left = (bool)(keyPressed&4);
1865 player->control.right = (bool)(keyPressed&8);
1866 player->control.jump = (bool)(keyPressed&16);
1867 player->control.aux1 = (bool)(keyPressed&32);
1868 player->control.sneak = (bool)(keyPressed&64);
1869 player->control.LMB = (bool)(keyPressed&128);
1870 player->control.RMB = (bool)(keyPressed&256);
1872 bool cheated = playersao->checkMovementCheat();
1875 m_script->on_cheat(playersao, "moved_too_fast");
1878 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1879 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1880 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1882 else if(command == TOSERVER_DELETEDBLOCKS)
1895 u16 count = data[2];
1896 for(u16 i=0; i<count; i++)
1898 if((s16)datasize < 2+1+(i+1)*6)
1899 throw con::InvalidIncomingDataException
1900 ("DELETEDBLOCKS length is too short");
1901 v3s16 p = readV3S16(&data[2+1+i*6]);
1902 /*infostream<<"Server: DELETEDBLOCKS ("
1903 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1904 RemoteClient *client = getClient(peer_id);
1905 client->SetBlockNotSent(p);
1908 else if(command == TOSERVER_CLICK_OBJECT)
1910 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
1913 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
1915 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
1918 else if(command == TOSERVER_GROUND_ACTION)
1920 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
1924 else if(command == TOSERVER_RELEASE)
1926 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
1929 else if(command == TOSERVER_SIGNTEXT)
1931 infostream<<"Server: SIGNTEXT not supported anymore"
1935 else if(command == TOSERVER_SIGNNODETEXT)
1937 infostream<<"Server: SIGNNODETEXT not supported anymore"
1941 else if(command == TOSERVER_INVENTORY_ACTION)
1943 // Strip command and create a stream
1944 std::string datastring((char*)&data[2], datasize-2);
1945 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
1946 std::istringstream is(datastring, std::ios_base::binary);
1948 InventoryAction *a = InventoryAction::deSerialize(is);
1951 infostream<<"TOSERVER_INVENTORY_ACTION: "
1952 <<"InventoryAction::deSerialize() returned NULL"
1957 // If something goes wrong, this player is to blame
1958 RollbackScopeActor rollback_scope(m_rollback,
1959 std::string("player:")+player->getName());
1962 Note: Always set inventory not sent, to repair cases
1963 where the client made a bad prediction.
1967 Handle restrictions and special cases of the move action
1969 if(a->getType() == IACTION_MOVE)
1971 IMoveAction *ma = (IMoveAction*)a;
1973 ma->from_inv.applyCurrentPlayer(player->getName());
1974 ma->to_inv.applyCurrentPlayer(player->getName());
1976 setInventoryModified(ma->from_inv);
1977 setInventoryModified(ma->to_inv);
1979 bool from_inv_is_current_player =
1980 (ma->from_inv.type == InventoryLocation::PLAYER) &&
1981 (ma->from_inv.name == player->getName());
1983 bool to_inv_is_current_player =
1984 (ma->to_inv.type == InventoryLocation::PLAYER) &&
1985 (ma->to_inv.name == player->getName());
1988 Disable moving items out of craftpreview
1990 if(ma->from_list == "craftpreview")
1992 infostream<<"Ignoring IMoveAction from "
1993 <<(ma->from_inv.dump())<<":"<<ma->from_list
1994 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1995 <<" because src is "<<ma->from_list<<std::endl;
2001 Disable moving items into craftresult and craftpreview
2003 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2005 infostream<<"Ignoring IMoveAction from "
2006 <<(ma->from_inv.dump())<<":"<<ma->from_list
2007 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2008 <<" because dst is "<<ma->to_list<<std::endl;
2013 // Disallow moving items in elsewhere than player's inventory
2014 // if not allowed to interact
2015 if(!checkPriv(player->getName(), "interact") &&
2016 (!from_inv_is_current_player ||
2017 !to_inv_is_current_player))
2019 infostream<<"Cannot move outside of player's inventory: "
2020 <<"No interact privilege"<<std::endl;
2026 Handle restrictions and special cases of the drop action
2028 else if(a->getType() == IACTION_DROP)
2030 IDropAction *da = (IDropAction*)a;
2032 da->from_inv.applyCurrentPlayer(player->getName());
2034 setInventoryModified(da->from_inv);
2037 Disable dropping items out of craftpreview
2039 if(da->from_list == "craftpreview")
2041 infostream<<"Ignoring IDropAction from "
2042 <<(da->from_inv.dump())<<":"<<da->from_list
2043 <<" because src is "<<da->from_list<<std::endl;
2048 // Disallow dropping items if not allowed to interact
2049 if(!checkPriv(player->getName(), "interact"))
2056 Handle restrictions and special cases of the craft action
2058 else if(a->getType() == IACTION_CRAFT)
2060 ICraftAction *ca = (ICraftAction*)a;
2062 ca->craft_inv.applyCurrentPlayer(player->getName());
2064 setInventoryModified(ca->craft_inv);
2066 //bool craft_inv_is_current_player =
2067 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2068 // (ca->craft_inv.name == player->getName());
2070 // Disallow crafting if not allowed to interact
2071 if(!checkPriv(player->getName(), "interact"))
2073 infostream<<"Cannot craft: "
2074 <<"No interact privilege"<<std::endl;
2081 a->apply(this, playersao, this);
2085 else if(command == TOSERVER_CHAT_MESSAGE)
2093 std::string datastring((char*)&data[2], datasize-2);
2094 std::istringstream is(datastring, std::ios_base::binary);
2097 is.read((char*)buf, 2);
2098 u16 len = readU16(buf);
2100 std::wstring message;
2101 for(u16 i=0; i<len; i++)
2103 is.read((char*)buf, 2);
2104 message += (wchar_t)readU16(buf);
2107 // If something goes wrong, this player is to blame
2108 RollbackScopeActor rollback_scope(m_rollback,
2109 std::string("player:")+player->getName());
2111 // Get player name of this client
2112 std::wstring name = narrow_to_wide(player->getName());
2115 bool ate = m_script->on_chat_message(player->getName(),
2116 wide_to_narrow(message));
2117 // If script ate the message, don't proceed
2121 // Line to send to players
2123 // Whether to send to the player that sent the line
2124 bool send_to_sender_only = false;
2126 // Commands are implemented in Lua, so only catch invalid
2127 // commands that were not "eaten" and send an error back
2128 if(message[0] == L'/')
2130 message = message.substr(1);
2131 send_to_sender_only = true;
2132 if(message.length() == 0)
2133 line += L"-!- Empty command";
2135 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2139 if(checkPriv(player->getName(), "shout")){
2145 line += L"-!- You don't have permission to shout.";
2146 send_to_sender_only = true;
2153 Send the message to sender
2155 if (send_to_sender_only)
2157 SendChatMessage(peer_id, line);
2160 Send the message to others
2164 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2166 std::list<u16> clients = m_clients.getClientIDs();
2168 for(std::list<u16>::iterator
2169 i = clients.begin();
2170 i != clients.end(); ++i)
2173 SendChatMessage(*i, line);
2178 else if(command == TOSERVER_DAMAGE)
2180 std::string datastring((char*)&data[2], datasize-2);
2181 std::istringstream is(datastring, std::ios_base::binary);
2182 u8 damage = readU8(is);
2184 if(g_settings->getBool("enable_damage"))
2186 actionstream<<player->getName()<<" damaged by "
2187 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2190 playersao->setHP(playersao->getHP() - damage);
2192 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2195 if(playersao->m_hp_not_sent)
2196 SendPlayerHP(peer_id);
2199 else if(command == TOSERVER_BREATH)
2201 std::string datastring((char*)&data[2], datasize-2);
2202 std::istringstream is(datastring, std::ios_base::binary);
2203 u16 breath = readU16(is);
2204 playersao->setBreath(breath);
2206 else if(command == TOSERVER_PASSWORD)
2209 [0] u16 TOSERVER_PASSWORD
2210 [2] u8[28] old password
2211 [30] u8[28] new password
2214 if(datasize != 2+PASSWORD_SIZE*2)
2216 /*char password[PASSWORD_SIZE];
2217 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2218 password[i] = data[2+i];
2219 password[PASSWORD_SIZE-1] = 0;*/
2221 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2229 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2231 char c = data[2+PASSWORD_SIZE+i];
2237 if(!base64_is_valid(newpwd)){
2238 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2239 // Wrong old password supplied!!
2240 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2244 infostream<<"Server: Client requests a password change from "
2245 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2247 std::string playername = player->getName();
2249 std::string checkpwd;
2250 m_script->getAuth(playername, &checkpwd, NULL);
2252 if(oldpwd != checkpwd)
2254 infostream<<"Server: invalid old password"<<std::endl;
2255 // Wrong old password supplied!!
2256 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2260 bool success = m_script->setPassword(playername, newpwd);
2262 actionstream<<player->getName()<<" changes password"<<std::endl;
2263 SendChatMessage(peer_id, L"Password change successful.");
2265 actionstream<<player->getName()<<" tries to change password but "
2266 <<"it fails"<<std::endl;
2267 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2270 else if(command == TOSERVER_PLAYERITEM)
2275 u16 item = readU16(&data[2]);
2276 playersao->setWieldIndex(item);
2278 else if(command == TOSERVER_RESPAWN)
2280 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2283 RespawnPlayer(peer_id);
2285 actionstream<<player->getName()<<" respawns at "
2286 <<PP(player->getPosition()/BS)<<std::endl;
2288 // ActiveObject is added to environment in AsyncRunStep after
2289 // the previous addition has been succesfully removed
2291 else if(command == TOSERVER_INTERACT)
2293 std::string datastring((char*)&data[2], datasize-2);
2294 std::istringstream is(datastring, std::ios_base::binary);
2300 [5] u32 length of the next item
2301 [9] serialized PointedThing
2303 0: start digging (from undersurface) or use
2304 1: stop digging (all parameters ignored)
2305 2: digging completed
2306 3: place block or item (to abovesurface)
2309 u8 action = readU8(is);
2310 u16 item_i = readU16(is);
2311 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2312 PointedThing pointed;
2313 pointed.deSerialize(tmp_is);
2315 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2316 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2320 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2321 <<" tried to interact, but is dead!"<<std::endl;
2325 v3f player_pos = playersao->getLastGoodPosition();
2327 // Update wielded item
2328 playersao->setWieldIndex(item_i);
2330 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2331 v3s16 p_under = pointed.node_undersurface;
2332 v3s16 p_above = pointed.node_abovesurface;
2334 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2335 ServerActiveObject *pointed_object = NULL;
2336 if(pointed.type == POINTEDTHING_OBJECT)
2338 pointed_object = m_env->getActiveObject(pointed.object_id);
2339 if(pointed_object == NULL)
2341 verbosestream<<"TOSERVER_INTERACT: "
2342 "pointed object is NULL"<<std::endl;
2348 v3f pointed_pos_under = player_pos;
2349 v3f pointed_pos_above = player_pos;
2350 if(pointed.type == POINTEDTHING_NODE)
2352 pointed_pos_under = intToFloat(p_under, BS);
2353 pointed_pos_above = intToFloat(p_above, BS);
2355 else if(pointed.type == POINTEDTHING_OBJECT)
2357 pointed_pos_under = pointed_object->getBasePosition();
2358 pointed_pos_above = pointed_pos_under;
2362 Check that target is reasonably close
2363 (only when digging or placing things)
2365 if(action == 0 || action == 2 || action == 3)
2367 float d = player_pos.getDistanceFrom(pointed_pos_under);
2368 float max_d = BS * 14; // Just some large enough value
2370 actionstream<<"Player "<<player->getName()
2371 <<" tried to access "<<pointed.dump()
2373 <<"d="<<d<<", max_d="<<max_d
2374 <<". ignoring."<<std::endl;
2375 // Re-send block to revert change on client-side
2376 RemoteClient *client = getClient(peer_id);
2377 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2378 client->SetBlockNotSent(blockpos);
2380 m_script->on_cheat(playersao, "interacted_too_far");
2387 Make sure the player is allowed to do it
2389 if(!checkPriv(player->getName(), "interact"))
2391 actionstream<<player->getName()<<" attempted to interact with "
2392 <<pointed.dump()<<" without 'interact' privilege"
2394 // Re-send block to revert change on client-side
2395 RemoteClient *client = getClient(peer_id);
2396 // Digging completed -> under
2398 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2399 client->SetBlockNotSent(blockpos);
2401 // Placement -> above
2403 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2404 client->SetBlockNotSent(blockpos);
2410 If something goes wrong, this player is to blame
2412 RollbackScopeActor rollback_scope(m_rollback,
2413 std::string("player:")+player->getName());
2416 0: start digging or punch object
2420 if(pointed.type == POINTEDTHING_NODE)
2423 NOTE: This can be used in the future to check if
2424 somebody is cheating, by checking the timing.
2426 MapNode n(CONTENT_IGNORE);
2429 n = m_env->getMap().getNode(p_under);
2431 catch(InvalidPositionException &e)
2433 infostream<<"Server: Not punching: Node not found."
2434 <<" Adding block to emerge queue."
2436 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2438 if(n.getContent() != CONTENT_IGNORE)
2439 m_script->node_on_punch(p_under, n, playersao, pointed);
2441 playersao->noCheatDigStart(p_under);
2443 else if(pointed.type == POINTEDTHING_OBJECT)
2445 // Skip if object has been removed
2446 if(pointed_object->m_removed)
2449 actionstream<<player->getName()<<" punches object "
2450 <<pointed.object_id<<": "
2451 <<pointed_object->getDescription()<<std::endl;
2453 ItemStack punchitem = playersao->getWieldedItem();
2454 ToolCapabilities toolcap =
2455 punchitem.getToolCapabilities(m_itemdef);
2456 v3f dir = (pointed_object->getBasePosition() -
2457 (player->getPosition() + player->getEyeOffset())
2459 float time_from_last_punch =
2460 playersao->resetTimeFromLastPunch();
2461 pointed_object->punch(dir, &toolcap, playersao,
2462 time_from_last_punch);
2470 else if(action == 1)
2475 2: Digging completed
2477 else if(action == 2)
2479 // Only digging of nodes
2480 if(pointed.type == POINTEDTHING_NODE)
2482 MapNode n(CONTENT_IGNORE);
2485 n = m_env->getMap().getNode(p_under);
2487 catch(InvalidPositionException &e)
2489 infostream<<"Server: Not finishing digging: Node not found."
2490 <<" Adding block to emerge queue."
2492 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2495 /* Cheat prevention */
2496 bool is_valid_dig = true;
2497 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2499 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2500 float nocheat_t = playersao->getNoCheatDigTime();
2501 playersao->noCheatDigEnd();
2502 // If player didn't start digging this, ignore dig
2503 if(nocheat_p != p_under){
2504 infostream<<"Server: NoCheat: "<<player->getName()
2505 <<" started digging "
2506 <<PP(nocheat_p)<<" and completed digging "
2507 <<PP(p_under)<<"; not digging."<<std::endl;
2508 is_valid_dig = false;
2510 m_script->on_cheat(playersao, "finished_unknown_dig");
2512 // Get player's wielded item
2513 ItemStack playeritem;
2514 InventoryList *mlist = playersao->getInventory()->getList("main");
2516 playeritem = mlist->getItem(playersao->getWieldIndex());
2517 ToolCapabilities playeritem_toolcap =
2518 playeritem.getToolCapabilities(m_itemdef);
2519 // Get diggability and expected digging time
2520 DigParams params = getDigParams(m_nodedef->get(n).groups,
2521 &playeritem_toolcap);
2522 // If can't dig, try hand
2523 if(!params.diggable){
2524 const ItemDefinition &hand = m_itemdef->get("");
2525 const ToolCapabilities *tp = hand.tool_capabilities;
2527 params = getDigParams(m_nodedef->get(n).groups, tp);
2529 // If can't dig, ignore dig
2530 if(!params.diggable){
2531 infostream<<"Server: NoCheat: "<<player->getName()
2532 <<" completed digging "<<PP(p_under)
2533 <<", which is not diggable with tool. not digging."
2535 is_valid_dig = false;
2537 m_script->on_cheat(playersao, "dug_unbreakable");
2539 // Check digging time
2540 // If already invalidated, we don't have to
2542 // Well not our problem then
2544 // Clean and long dig
2545 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2546 // All is good, but grab time from pool; don't care if
2547 // it's actually available
2548 playersao->getDigPool().grab(params.time);
2550 // Short or laggy dig
2551 // Try getting the time from pool
2552 else if(playersao->getDigPool().grab(params.time)){
2557 infostream<<"Server: NoCheat: "<<player->getName()
2558 <<" completed digging "<<PP(p_under)
2559 <<"too fast; not digging."<<std::endl;
2560 is_valid_dig = false;
2562 m_script->on_cheat(playersao, "dug_too_fast");
2566 /* Actually dig node */
2568 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2569 m_script->node_on_dig(p_under, n, playersao);
2571 // Send unusual result (that is, node not being removed)
2572 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2574 // Re-send block to revert change on client-side
2575 RemoteClient *client = getClient(peer_id);
2576 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2577 client->SetBlockNotSent(blockpos);
2583 3: place block or right-click object
2585 else if(action == 3)
2587 ItemStack item = playersao->getWieldedItem();
2589 // Reset build time counter
2590 if(pointed.type == POINTEDTHING_NODE &&
2591 item.getDefinition(m_itemdef).type == ITEM_NODE)
2592 getClient(peer_id)->m_time_from_building = 0.0;
2594 if(pointed.type == POINTEDTHING_OBJECT)
2596 // Right click object
2598 // Skip if object has been removed
2599 if(pointed_object->m_removed)
2602 actionstream<<player->getName()<<" right-clicks object "
2603 <<pointed.object_id<<": "
2604 <<pointed_object->getDescription()<<std::endl;
2607 pointed_object->rightClick(playersao);
2609 else if(m_script->item_OnPlace(
2610 item, playersao, pointed))
2612 // Placement was handled in lua
2614 // Apply returned ItemStack
2615 playersao->setWieldedItem(item);
2618 // If item has node placement prediction, always send the
2619 // blocks to make sure the client knows what exactly happened
2620 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2621 RemoteClient *client = getClient(peer_id);
2622 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2623 client->SetBlockNotSent(blockpos);
2624 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2625 if(blockpos2 != blockpos){
2626 client->SetBlockNotSent(blockpos2);
2634 else if(action == 4)
2636 ItemStack item = playersao->getWieldedItem();
2638 actionstream<<player->getName()<<" uses "<<item.name
2639 <<", pointing at "<<pointed.dump()<<std::endl;
2641 if(m_script->item_OnUse(
2642 item, playersao, pointed))
2644 // Apply returned ItemStack
2645 playersao->setWieldedItem(item);
2652 Catch invalid actions
2656 infostream<<"WARNING: Server: Invalid action "
2657 <<action<<std::endl;
2660 else if(command == TOSERVER_REMOVED_SOUNDS)
2662 std::string datastring((char*)&data[2], datasize-2);
2663 std::istringstream is(datastring, std::ios_base::binary);
2665 int num = readU16(is);
2666 for(int k=0; k<num; k++){
2667 s32 id = readS32(is);
2668 std::map<s32, ServerPlayingSound>::iterator i =
2669 m_playing_sounds.find(id);
2670 if(i == m_playing_sounds.end())
2672 ServerPlayingSound &psound = i->second;
2673 psound.clients.erase(peer_id);
2674 if(psound.clients.size() == 0)
2675 m_playing_sounds.erase(i++);
2678 else if(command == TOSERVER_NODEMETA_FIELDS)
2680 std::string datastring((char*)&data[2], datasize-2);
2681 std::istringstream is(datastring, std::ios_base::binary);
2683 v3s16 p = readV3S16(is);
2684 std::string formname = deSerializeString(is);
2685 int num = readU16(is);
2686 std::map<std::string, std::string> fields;
2687 for(int k=0; k<num; k++){
2688 std::string fieldname = deSerializeString(is);
2689 std::string fieldvalue = deSerializeLongString(is);
2690 fields[fieldname] = fieldvalue;
2693 // If something goes wrong, this player is to blame
2694 RollbackScopeActor rollback_scope(m_rollback,
2695 std::string("player:")+player->getName());
2697 // Check the target node for rollback data; leave others unnoticed
2698 RollbackNode rn_old(&m_env->getMap(), p, this);
2700 m_script->node_on_receive_fields(p, formname, fields,playersao);
2702 // Report rollback data
2703 RollbackNode rn_new(&m_env->getMap(), p, this);
2704 if(rollback() && rn_new != rn_old){
2705 RollbackAction action;
2706 action.setSetNode(p, rn_old, rn_new);
2707 rollback()->reportAction(action);
2710 else if(command == TOSERVER_INVENTORY_FIELDS)
2712 std::string datastring((char*)&data[2], datasize-2);
2713 std::istringstream is(datastring, std::ios_base::binary);
2715 std::string formname = deSerializeString(is);
2716 int num = readU16(is);
2717 std::map<std::string, std::string> fields;
2718 for(int k=0; k<num; k++){
2719 std::string fieldname = deSerializeString(is);
2720 std::string fieldvalue = deSerializeLongString(is);
2721 fields[fieldname] = fieldvalue;
2724 m_script->on_playerReceiveFields(playersao, formname, fields);
2728 infostream<<"Server::ProcessData(): Ignoring "
2729 "unknown command "<<command<<std::endl;
2733 catch(SendFailedException &e)
2735 errorstream<<"Server::ProcessData(): SendFailedException: "
2741 void Server::setTimeOfDay(u32 time)
2743 m_env->setTimeOfDay(time);
2744 m_time_of_day_send_timer = 0;
2747 void Server::onMapEditEvent(MapEditEvent *event)
2749 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2750 if(m_ignore_map_edit_events)
2752 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2754 MapEditEvent *e = event->clone();
2755 m_unsent_map_edit_queue.push_back(e);
2758 Inventory* Server::getInventory(const InventoryLocation &loc)
2761 case InventoryLocation::UNDEFINED:
2764 case InventoryLocation::CURRENT_PLAYER:
2767 case InventoryLocation::PLAYER:
2769 Player *player = m_env->getPlayer(loc.name.c_str());
2772 PlayerSAO *playersao = player->getPlayerSAO();
2775 return playersao->getInventory();
2778 case InventoryLocation::NODEMETA:
2780 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2783 return meta->getInventory();
2786 case InventoryLocation::DETACHED:
2788 if(m_detached_inventories.count(loc.name) == 0)
2790 return m_detached_inventories[loc.name];
2798 void Server::setInventoryModified(const InventoryLocation &loc)
2801 case InventoryLocation::UNDEFINED:
2804 case InventoryLocation::PLAYER:
2806 Player *player = m_env->getPlayer(loc.name.c_str());
2809 PlayerSAO *playersao = player->getPlayerSAO();
2812 playersao->m_inventory_not_sent = true;
2813 playersao->m_wielded_item_not_sent = true;
2816 case InventoryLocation::NODEMETA:
2818 v3s16 blockpos = getNodeBlockPos(loc.p);
2820 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2822 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2824 setBlockNotSent(blockpos);
2827 case InventoryLocation::DETACHED:
2829 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2837 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2839 std::list<u16> clients = m_clients.getClientIDs();
2841 // Set the modified blocks unsent for all the clients
2842 for (std::list<u16>::iterator
2843 i = clients.begin();
2844 i != clients.end(); ++i) {
2845 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2847 client->SetBlocksNotSent(block);
2852 void Server::peerAdded(con::Peer *peer)
2854 DSTACK(__FUNCTION_NAME);
2855 verbosestream<<"Server::peerAdded(): peer->id="
2856 <<peer->id<<std::endl;
2859 c.type = con::PEER_ADDED;
2860 c.peer_id = peer->id;
2862 m_peer_change_queue.push_back(c);
2865 void Server::deletingPeer(con::Peer *peer, bool timeout)
2867 DSTACK(__FUNCTION_NAME);
2868 verbosestream<<"Server::deletingPeer(): peer->id="
2869 <<peer->id<<", timeout="<<timeout<<std::endl;
2871 m_clients.event(peer->id,Disconnect);
2873 c.type = con::PEER_REMOVED;
2874 c.peer_id = peer->id;
2875 c.timeout = timeout;
2876 m_peer_change_queue.push_back(c);
2879 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
2881 *retval = m_con.getPeerStat(peer_id,type);
2882 if (*retval == -1) return false;
2886 bool Server::getClientInfo(
2895 std::string* vers_string
2898 *state = m_clients.getClientState(peer_id);
2900 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,Invalid);
2905 *uptime = client->uptime();
2906 *ser_vers = client->serialization_version;
2907 *prot_vers = client->net_proto_version;
2909 *major = client->getMajor();
2910 *minor = client->getMinor();
2911 *patch = client->getPatch();
2912 *vers_string = client->getPatch();
2919 void Server::handlePeerChanges()
2921 while(m_peer_change_queue.size() > 0)
2923 con::PeerChange c = m_peer_change_queue.pop_front();
2925 verbosestream<<"Server: Handling peer change: "
2926 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2931 case con::PEER_ADDED:
2932 m_clients.CreateClient(c.peer_id);
2935 case con::PEER_REMOVED:
2936 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2940 assert("Invalid peer change event received!" == 0);
2946 void Server::SendMovement(u16 peer_id)
2948 DSTACK(__FUNCTION_NAME);
2949 std::ostringstream os(std::ios_base::binary);
2951 writeU16(os, TOCLIENT_MOVEMENT);
2952 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2953 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2954 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2955 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2956 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2957 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2958 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2959 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2960 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2961 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2962 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2963 writeF1000(os, g_settings->getFloat("movement_gravity"));
2966 std::string s = os.str();
2967 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2969 m_clients.send(peer_id, 0, data, true);
2972 void Server::SendHP(u16 peer_id, u8 hp)
2974 DSTACK(__FUNCTION_NAME);
2975 std::ostringstream os(std::ios_base::binary);
2977 writeU16(os, TOCLIENT_HP);
2981 std::string s = os.str();
2982 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2984 m_clients.send(peer_id, 0, data, true);
2987 void Server::SendBreath(u16 peer_id, u16 breath)
2989 DSTACK(__FUNCTION_NAME);
2990 std::ostringstream os(std::ios_base::binary);
2992 writeU16(os, TOCLIENT_BREATH);
2993 writeU16(os, breath);
2996 std::string s = os.str();
2997 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2999 m_clients.send(peer_id, 0, data, true);
3002 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
3004 DSTACK(__FUNCTION_NAME);
3005 std::ostringstream os(std::ios_base::binary);
3007 writeU16(os, TOCLIENT_ACCESS_DENIED);
3008 os<<serializeWideString(reason);
3011 std::string s = os.str();
3012 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3014 m_clients.send(peer_id, 0, data, true);
3017 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
3018 v3f camera_point_target)
3020 DSTACK(__FUNCTION_NAME);
3021 std::ostringstream os(std::ios_base::binary);
3023 writeU16(os, TOCLIENT_DEATHSCREEN);
3024 writeU8(os, set_camera_point_target);
3025 writeV3F1000(os, camera_point_target);
3028 std::string s = os.str();
3029 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3031 m_clients.send(peer_id, 0, data, true);
3034 void Server::SendItemDef(u16 peer_id,
3035 IItemDefManager *itemdef, u16 protocol_version)
3037 DSTACK(__FUNCTION_NAME);
3038 std::ostringstream os(std::ios_base::binary);
3042 u32 length of the next item
3043 zlib-compressed serialized ItemDefManager
3045 writeU16(os, TOCLIENT_ITEMDEF);
3046 std::ostringstream tmp_os(std::ios::binary);
3047 itemdef->serialize(tmp_os, protocol_version);
3048 std::ostringstream tmp_os2(std::ios::binary);
3049 compressZlib(tmp_os.str(), tmp_os2);
3050 os<<serializeLongString(tmp_os2.str());
3053 std::string s = os.str();
3054 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3055 <<"): size="<<s.size()<<std::endl;
3056 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3058 m_clients.send(peer_id, 0, data, true);
3061 void Server::SendNodeDef(u16 peer_id,
3062 INodeDefManager *nodedef, u16 protocol_version)
3064 DSTACK(__FUNCTION_NAME);
3065 std::ostringstream os(std::ios_base::binary);
3069 u32 length of the next item
3070 zlib-compressed serialized NodeDefManager
3072 writeU16(os, TOCLIENT_NODEDEF);
3073 std::ostringstream tmp_os(std::ios::binary);
3074 nodedef->serialize(tmp_os, protocol_version);
3075 std::ostringstream tmp_os2(std::ios::binary);
3076 compressZlib(tmp_os.str(), tmp_os2);
3077 os<<serializeLongString(tmp_os2.str());
3080 std::string s = os.str();
3081 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3082 <<"): size="<<s.size()<<std::endl;
3083 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3085 m_clients.send(peer_id, 0, data, true);
3089 Non-static send methods
3092 void Server::SendInventory(u16 peer_id)
3094 DSTACK(__FUNCTION_NAME);
3096 PlayerSAO *playersao = getPlayerSAO(peer_id);
3099 playersao->m_inventory_not_sent = false;
3105 std::ostringstream os;
3106 playersao->getInventory()->serialize(os);
3108 std::string s = os.str();
3110 SharedBuffer<u8> data(s.size()+2);
3111 writeU16(&data[0], TOCLIENT_INVENTORY);
3112 memcpy(&data[2], s.c_str(), s.size());
3115 m_clients.send(peer_id, 0, data, true);
3118 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3120 DSTACK(__FUNCTION_NAME);
3122 std::ostringstream os(std::ios_base::binary);
3126 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3127 os.write((char*)buf, 2);
3130 writeU16(buf, message.size());
3131 os.write((char*)buf, 2);
3134 for(u32 i=0; i<message.size(); i++)
3138 os.write((char*)buf, 2);
3142 std::string s = os.str();
3143 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3145 if (peer_id != PEER_ID_INEXISTENT)
3148 m_clients.send(peer_id, 0, data, true);
3152 m_clients.sendToAll(0,data,true);
3156 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
3157 const std::string &formname)
3159 DSTACK(__FUNCTION_NAME);
3161 std::ostringstream os(std::ios_base::binary);
3165 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3166 os.write((char*)buf, 2);
3167 os<<serializeLongString(formspec);
3168 os<<serializeString(formname);
3171 std::string s = os.str();
3172 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3174 m_clients.send(peer_id, 0, data, true);
3177 // Spawns a particle on peer with peer_id
3178 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3179 float expirationtime, float size, bool collisiondetection,
3180 bool vertical, std::string texture)
3182 DSTACK(__FUNCTION_NAME);
3184 std::ostringstream os(std::ios_base::binary);
3185 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3186 writeV3F1000(os, pos);
3187 writeV3F1000(os, velocity);
3188 writeV3F1000(os, acceleration);
3189 writeF1000(os, expirationtime);
3190 writeF1000(os, size);
3191 writeU8(os, collisiondetection);
3192 os<<serializeLongString(texture);
3193 writeU8(os, vertical);
3196 std::string s = os.str();
3197 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3199 if (peer_id != PEER_ID_INEXISTENT)
3202 m_clients.send(peer_id, 0, data, true);
3206 m_clients.sendToAll(0,data,true);
3210 // Adds a ParticleSpawner on peer with peer_id
3211 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3212 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3213 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3215 DSTACK(__FUNCTION_NAME);
3217 std::ostringstream os(std::ios_base::binary);
3218 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3220 writeU16(os, amount);
3221 writeF1000(os, spawntime);
3222 writeV3F1000(os, minpos);
3223 writeV3F1000(os, maxpos);
3224 writeV3F1000(os, minvel);
3225 writeV3F1000(os, maxvel);
3226 writeV3F1000(os, minacc);
3227 writeV3F1000(os, maxacc);
3228 writeF1000(os, minexptime);
3229 writeF1000(os, maxexptime);
3230 writeF1000(os, minsize);
3231 writeF1000(os, maxsize);
3232 writeU8(os, collisiondetection);
3233 os<<serializeLongString(texture);
3235 writeU8(os, vertical);
3238 std::string s = os.str();
3239 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3241 if (peer_id != PEER_ID_INEXISTENT)
3244 m_clients.send(peer_id, 0, data, true);
3247 m_clients.sendToAll(0,data,true);
3251 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3253 DSTACK(__FUNCTION_NAME);
3255 std::ostringstream os(std::ios_base::binary);
3256 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3261 std::string s = os.str();
3262 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3264 if (peer_id != PEER_ID_INEXISTENT) {
3266 m_clients.send(peer_id, 0, data, true);
3269 m_clients.sendToAll(0,data,true);
3274 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3276 std::ostringstream os(std::ios_base::binary);
3279 writeU16(os, TOCLIENT_HUDADD);
3281 writeU8(os, (u8)form->type);
3282 writeV2F1000(os, form->pos);
3283 os << serializeString(form->name);
3284 writeV2F1000(os, form->scale);
3285 os << serializeString(form->text);
3286 writeU32(os, form->number);
3287 writeU32(os, form->item);
3288 writeU32(os, form->dir);
3289 writeV2F1000(os, form->align);
3290 writeV2F1000(os, form->offset);
3291 writeV3F1000(os, form->world_pos);
3294 std::string s = os.str();
3295 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3297 m_clients.send(peer_id, 1, data, true);
3300 void Server::SendHUDRemove(u16 peer_id, u32 id)
3302 std::ostringstream os(std::ios_base::binary);
3305 writeU16(os, TOCLIENT_HUDRM);
3309 std::string s = os.str();
3310 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3313 m_clients.send(peer_id, 1, data, true);
3316 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3318 std::ostringstream os(std::ios_base::binary);
3321 writeU16(os, TOCLIENT_HUDCHANGE);
3323 writeU8(os, (u8)stat);
3326 case HUD_STAT_SCALE:
3327 case HUD_STAT_ALIGN:
3328 case HUD_STAT_OFFSET:
3329 writeV2F1000(os, *(v2f *)value);
3333 os << serializeString(*(std::string *)value);
3335 case HUD_STAT_WORLD_POS:
3336 writeV3F1000(os, *(v3f *)value);
3338 case HUD_STAT_NUMBER:
3342 writeU32(os, *(u32 *)value);
3347 std::string s = os.str();
3348 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3350 m_clients.send(peer_id, 0, data, true);
3353 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3355 std::ostringstream os(std::ios_base::binary);
3358 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3359 writeU32(os, flags);
3363 std::string s = os.str();
3364 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3366 m_clients.send(peer_id, 0, data, true);
3369 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3371 std::ostringstream os(std::ios_base::binary);
3374 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3375 writeU16(os, param);
3376 os<<serializeString(value);
3379 std::string s = os.str();
3380 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3382 m_clients.send(peer_id, 0, data, true);
3385 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3386 const std::string &type, const std::vector<std::string> ¶ms)
3388 std::ostringstream os(std::ios_base::binary);
3391 writeU16(os, TOCLIENT_SET_SKY);
3392 writeARGB8(os, bgcolor);
3393 os<<serializeString(type);
3394 writeU16(os, params.size());
3395 for(size_t i=0; i<params.size(); i++)
3396 os<<serializeString(params[i]);
3399 std::string s = os.str();
3400 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3402 m_clients.send(peer_id, 0, data, true);
3405 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
3408 std::ostringstream os(std::ios_base::binary);
3411 writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
3412 writeU8(os, do_override);
3413 writeU16(os, ratio*65535);
3416 std::string s = os.str();
3417 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3419 m_clients.send(peer_id, 0, data, true);
3422 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3424 DSTACK(__FUNCTION_NAME);
3427 SharedBuffer<u8> data(2+2+4);
3428 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3429 writeU16(&data[2], time);
3430 writeF1000(&data[4], time_speed);
3432 if (peer_id == PEER_ID_INEXISTENT) {
3433 m_clients.sendToAll(0,data,true);
3437 m_clients.send(peer_id, 0, data, true);
3441 void Server::SendPlayerHP(u16 peer_id)
3443 DSTACK(__FUNCTION_NAME);
3444 PlayerSAO *playersao = getPlayerSAO(peer_id);
3446 playersao->m_hp_not_sent = false;
3447 SendHP(peer_id, playersao->getHP());
3449 // Send to other clients
3450 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3451 ActiveObjectMessage aom(playersao->getId(), true, str);
3452 playersao->m_messages_out.push_back(aom);
3455 void Server::SendPlayerBreath(u16 peer_id)
3457 DSTACK(__FUNCTION_NAME);
3458 PlayerSAO *playersao = getPlayerSAO(peer_id);
3460 playersao->m_breath_not_sent = false;
3461 SendBreath(peer_id, playersao->getBreath());
3464 void Server::SendMovePlayer(u16 peer_id)
3466 DSTACK(__FUNCTION_NAME);
3467 Player *player = m_env->getPlayer(peer_id);
3470 std::ostringstream os(std::ios_base::binary);
3471 writeU16(os, TOCLIENT_MOVE_PLAYER);
3472 writeV3F1000(os, player->getPosition());
3473 writeF1000(os, player->getPitch());
3474 writeF1000(os, player->getYaw());
3477 v3f pos = player->getPosition();
3478 f32 pitch = player->getPitch();
3479 f32 yaw = player->getYaw();
3480 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3481 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3488 std::string s = os.str();
3489 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3491 m_clients.send(peer_id, 0, data, true);
3494 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
3496 std::ostringstream os(std::ios_base::binary);
3498 writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS);
3499 writeV2S32(os, animation_frames[0]);
3500 writeV2S32(os, animation_frames[1]);
3501 writeV2S32(os, animation_frames[2]);
3502 writeV2S32(os, animation_frames[3]);
3503 writeF1000(os, animation_speed);
3506 std::string s = os.str();
3507 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3509 m_clients.send(peer_id, 0, data, true);
3512 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
3514 std::ostringstream os(std::ios_base::binary);
3516 writeU16(os, TOCLIENT_EYE_OFFSET);
3517 writeV3F1000(os, first);
3518 writeV3F1000(os, third);
3521 std::string s = os.str();
3522 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3524 m_clients.send(peer_id, 0, data, true);
3526 void Server::SendPlayerPrivileges(u16 peer_id)
3528 Player *player = m_env->getPlayer(peer_id);
3530 if(player->peer_id == PEER_ID_INEXISTENT)
3533 std::set<std::string> privs;
3534 m_script->getAuth(player->getName(), NULL, &privs);
3536 std::ostringstream os(std::ios_base::binary);
3537 writeU16(os, TOCLIENT_PRIVILEGES);
3538 writeU16(os, privs.size());
3539 for(std::set<std::string>::const_iterator i = privs.begin();
3540 i != privs.end(); i++){
3541 os<<serializeString(*i);
3545 std::string s = os.str();
3546 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3548 m_clients.send(peer_id, 0, data, true);
3551 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3553 Player *player = m_env->getPlayer(peer_id);
3555 if(player->peer_id == PEER_ID_INEXISTENT)
3558 std::ostringstream os(std::ios_base::binary);
3559 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3560 os<<serializeLongString(player->inventory_formspec);
3563 std::string s = os.str();
3564 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3566 m_clients.send(peer_id, 0, data, true);
3569 s32 Server::playSound(const SimpleSoundSpec &spec,
3570 const ServerSoundParams ¶ms)
3572 // Find out initial position of sound
3573 bool pos_exists = false;
3574 v3f pos = params.getPos(m_env, &pos_exists);
3575 // If position is not found while it should be, cancel sound
3576 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3579 // Filter destination clients
3580 std::list<u16> dst_clients;
3581 if(params.to_player != "")
3583 Player *player = m_env->getPlayer(params.to_player.c_str());
3585 infostream<<"Server::playSound: Player \""<<params.to_player
3586 <<"\" not found"<<std::endl;
3589 if(player->peer_id == PEER_ID_INEXISTENT){
3590 infostream<<"Server::playSound: Player \""<<params.to_player
3591 <<"\" not connected"<<std::endl;
3594 dst_clients.push_back(player->peer_id);
3598 std::list<u16> clients = m_clients.getClientIDs();
3600 for(std::list<u16>::iterator
3601 i = clients.begin(); i != clients.end(); ++i)
3603 Player *player = m_env->getPlayer(*i);
3607 if(player->getPosition().getDistanceFrom(pos) >
3608 params.max_hear_distance)
3611 dst_clients.push_back(*i);
3614 if(dst_clients.size() == 0)
3618 s32 id = m_next_sound_id++;
3619 // The sound will exist as a reference in m_playing_sounds
3620 m_playing_sounds[id] = ServerPlayingSound();
3621 ServerPlayingSound &psound = m_playing_sounds[id];
3622 psound.params = params;
3623 for(std::list<u16>::iterator i = dst_clients.begin();
3624 i != dst_clients.end(); i++)
3625 psound.clients.insert(*i);
3627 std::ostringstream os(std::ios_base::binary);
3628 writeU16(os, TOCLIENT_PLAY_SOUND);
3630 os<<serializeString(spec.name);
3631 writeF1000(os, spec.gain * params.gain);
3632 writeU8(os, params.type);
3633 writeV3F1000(os, pos);
3634 writeU16(os, params.object);
3635 writeU8(os, params.loop);
3637 std::string s = os.str();
3638 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3640 for(std::list<u16>::iterator i = dst_clients.begin();
3641 i != dst_clients.end(); i++){
3643 m_clients.send(*i, 0, data, true);
3647 void Server::stopSound(s32 handle)
3649 // Get sound reference
3650 std::map<s32, ServerPlayingSound>::iterator i =
3651 m_playing_sounds.find(handle);
3652 if(i == m_playing_sounds.end())
3654 ServerPlayingSound &psound = i->second;
3656 std::ostringstream os(std::ios_base::binary);
3657 writeU16(os, TOCLIENT_STOP_SOUND);
3658 writeS32(os, handle);
3660 std::string s = os.str();
3661 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3663 for(std::set<u16>::iterator i = psound.clients.begin();
3664 i != psound.clients.end(); i++){
3666 m_clients.send(*i, 0, data, true);
3668 // Remove sound reference
3669 m_playing_sounds.erase(i);
3672 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3673 std::list<u16> *far_players, float far_d_nodes)
3675 float maxd = far_d_nodes*BS;
3676 v3f p_f = intToFloat(p, BS);
3680 SharedBuffer<u8> reply(replysize);
3681 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3682 writeS16(&reply[2], p.X);
3683 writeS16(&reply[4], p.Y);
3684 writeS16(&reply[6], p.Z);
3686 std::list<u16> clients = m_clients.getClientIDs();
3687 for(std::list<u16>::iterator
3688 i = clients.begin();
3689 i != clients.end(); ++i)
3694 Player *player = m_env->getPlayer(*i);
3697 // If player is far away, only set modified blocks not sent
3698 v3f player_pos = player->getPosition();
3699 if(player_pos.getDistanceFrom(p_f) > maxd)
3701 far_players->push_back(*i);
3708 m_clients.send(*i, 0, reply, true);
3712 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3713 std::list<u16> *far_players, float far_d_nodes,
3714 bool remove_metadata)
3716 float maxd = far_d_nodes*BS;
3717 v3f p_f = intToFloat(p, BS);
3719 std::list<u16> clients = m_clients.getClientIDs();
3720 for(std::list<u16>::iterator
3721 i = clients.begin();
3722 i != clients.end(); ++i)
3728 Player *player = m_env->getPlayer(*i);
3731 // If player is far away, only set modified blocks not sent
3732 v3f player_pos = player->getPosition();
3733 if(player_pos.getDistanceFrom(p_f) > maxd)
3735 far_players->push_back(*i);
3740 SharedBuffer<u8> reply(0);
3742 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3746 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3747 reply = SharedBuffer<u8>(replysize);
3748 writeU16(&reply[0], TOCLIENT_ADDNODE);
3749 writeS16(&reply[2], p.X);
3750 writeS16(&reply[4], p.Y);
3751 writeS16(&reply[6], p.Z);
3752 n.serialize(&reply[8], client->serialization_version);
3753 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3754 writeU8(&reply[index], remove_metadata ? 0 : 1);
3756 if (!remove_metadata) {
3757 if (client->net_proto_version <= 21) {
3758 // Old clients always clear metadata; fix it
3759 // by sending the full block again.
3760 client->SetBlockNotSent(p);
3767 if (reply.getSize() > 0)
3768 m_clients.send(*i, 0, reply, true);
3772 void Server::setBlockNotSent(v3s16 p)
3774 std::list<u16> clients = m_clients.getClientIDs();
3776 for(std::list<u16>::iterator
3777 i = clients.begin();
3778 i != clients.end(); ++i)
3780 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3781 client->SetBlockNotSent(p);
3786 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3788 DSTACK(__FUNCTION_NAME);
3790 v3s16 p = block->getPos();
3794 bool completely_air = true;
3795 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3796 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3797 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3799 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3801 completely_air = false;
3802 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3807 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3809 infostream<<"[completely air] ";
3810 infostream<<std::endl;
3814 Create a packet with the block in the right format
3817 std::ostringstream os(std::ios_base::binary);
3818 block->serialize(os, ver, false);
3819 block->serializeNetworkSpecific(os, net_proto_version);
3820 std::string s = os.str();
3821 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3823 u32 replysize = 8 + blockdata.getSize();
3824 SharedBuffer<u8> reply(replysize);
3825 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3826 writeS16(&reply[2], p.X);
3827 writeS16(&reply[4], p.Y);
3828 writeS16(&reply[6], p.Z);
3829 memcpy(&reply[8], *blockdata, blockdata.getSize());
3831 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3832 <<": \tpacket size: "<<replysize<<std::endl;*/
3837 m_clients.send(peer_id, 2, reply, true);
3840 void Server::SendBlocks(float dtime)
3842 DSTACK(__FUNCTION_NAME);
3844 JMutexAutoLock envlock(m_env_mutex);
3845 //TODO check if one big lock could be faster then multiple small ones
3847 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3849 std::vector<PrioritySortedBlockTransfer> queue;
3851 s32 total_sending = 0;
3854 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3856 std::list<u16> clients = m_clients.getClientIDs();
3859 for(std::list<u16>::iterator
3860 i = clients.begin();
3861 i != clients.end(); ++i)
3863 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3868 total_sending += client->SendingCount();
3869 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3875 // Lowest priority number comes first.
3876 // Lowest is most important.
3877 std::sort(queue.begin(), queue.end());
3880 for(u32 i=0; i<queue.size(); i++)
3882 //TODO: Calculate limit dynamically
3883 if(total_sending >= g_settings->getS32
3884 ("max_simultaneous_block_sends_server_total"))
3887 PrioritySortedBlockTransfer q = queue[i];
3889 MapBlock *block = NULL;
3892 block = m_env->getMap().getBlockNoCreate(q.pos);
3894 catch(InvalidPositionException &e)
3899 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3904 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3906 client->SentBlock(q.pos);
3912 void Server::fillMediaCache()
3914 DSTACK(__FUNCTION_NAME);
3916 infostream<<"Server: Calculating media file checksums"<<std::endl;
3918 // Collect all media file paths
3919 std::list<std::string> paths;
3920 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3921 i != m_mods.end(); i++){
3922 const ModSpec &mod = *i;
3923 paths.push_back(mod.path + DIR_DELIM + "textures");
3924 paths.push_back(mod.path + DIR_DELIM + "sounds");
3925 paths.push_back(mod.path + DIR_DELIM + "media");
3926 paths.push_back(mod.path + DIR_DELIM + "models");
3928 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3930 // Collect media file information from paths into cache
3931 for(std::list<std::string>::iterator i = paths.begin();
3932 i != paths.end(); i++)
3934 std::string mediapath = *i;
3935 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3936 for(u32 j=0; j<dirlist.size(); j++){
3937 if(dirlist[j].dir) // Ignode dirs
3939 std::string filename = dirlist[j].name;
3940 // If name contains illegal characters, ignore the file
3941 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3942 infostream<<"Server: ignoring illegal file name: \""
3943 <<filename<<"\""<<std::endl;
3946 // If name is not in a supported format, ignore it
3947 const char *supported_ext[] = {
3948 ".png", ".jpg", ".bmp", ".tga",
3949 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3951 ".x", ".b3d", ".md2", ".obj",
3954 if(removeStringEnd(filename, supported_ext) == ""){
3955 infostream<<"Server: ignoring unsupported file extension: \""
3956 <<filename<<"\""<<std::endl;
3959 // Ok, attempt to load the file and add to cache
3960 std::string filepath = mediapath + DIR_DELIM + filename;
3962 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3963 if(fis.good() == false){
3964 errorstream<<"Server::fillMediaCache(): Could not open \""
3965 <<filename<<"\" for reading"<<std::endl;
3968 std::ostringstream tmp_os(std::ios_base::binary);
3972 fis.read(buf, 1024);
3973 std::streamsize len = fis.gcount();
3974 tmp_os.write(buf, len);
3983 errorstream<<"Server::fillMediaCache(): Failed to read \""
3984 <<filename<<"\""<<std::endl;
3987 if(tmp_os.str().length() == 0){
3988 errorstream<<"Server::fillMediaCache(): Empty file \""
3989 <<filepath<<"\""<<std::endl;
3994 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3996 unsigned char *digest = sha1.getDigest();
3997 std::string sha1_base64 = base64_encode(digest, 20);
3998 std::string sha1_hex = hex_encode((char*)digest, 20);
4002 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4003 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4008 struct SendableMediaAnnouncement
4011 std::string sha1_digest;
4013 SendableMediaAnnouncement(const std::string &name_="",
4014 const std::string &sha1_digest_=""):
4016 sha1_digest(sha1_digest_)
4020 void Server::sendMediaAnnouncement(u16 peer_id)
4022 DSTACK(__FUNCTION_NAME);
4024 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4027 std::list<SendableMediaAnnouncement> file_announcements;
4029 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4030 i != m_media.end(); i++){
4032 file_announcements.push_back(
4033 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4037 std::ostringstream os(std::ios_base::binary);
4045 u16 length of sha1_digest
4050 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4051 writeU16(os, file_announcements.size());
4053 for(std::list<SendableMediaAnnouncement>::iterator
4054 j = file_announcements.begin();
4055 j != file_announcements.end(); ++j){
4056 os<<serializeString(j->name);
4057 os<<serializeString(j->sha1_digest);
4059 os<<serializeString(g_settings->get("remote_media"));
4062 std::string s = os.str();
4063 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4066 m_clients.send(peer_id, 0, data, true);
4069 struct SendableMedia
4075 SendableMedia(const std::string &name_="", const std::string &path_="",
4076 const std::string &data_=""):
4083 void Server::sendRequestedMedia(u16 peer_id,
4084 const std::list<std::string> &tosend)
4086 DSTACK(__FUNCTION_NAME);
4088 verbosestream<<"Server::sendRequestedMedia(): "
4089 <<"Sending files to client"<<std::endl;
4093 // Put 5kB in one bunch (this is not accurate)
4094 u32 bytes_per_bunch = 5000;
4096 std::vector< std::list<SendableMedia> > file_bunches;
4097 file_bunches.push_back(std::list<SendableMedia>());
4099 u32 file_size_bunch_total = 0;
4101 for(std::list<std::string>::const_iterator i = tosend.begin();
4102 i != tosend.end(); ++i)
4104 const std::string &name = *i;
4106 if(m_media.find(name) == m_media.end()){
4107 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4108 <<"unknown file \""<<(name)<<"\""<<std::endl;
4112 //TODO get path + name
4113 std::string tpath = m_media[name].path;
4116 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4117 if(fis.good() == false){
4118 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4119 <<tpath<<"\" for reading"<<std::endl;
4122 std::ostringstream tmp_os(std::ios_base::binary);
4126 fis.read(buf, 1024);
4127 std::streamsize len = fis.gcount();
4128 tmp_os.write(buf, len);
4129 file_size_bunch_total += len;
4138 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4139 <<name<<"\""<<std::endl;
4142 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4143 <<tname<<"\""<<std::endl;*/
4145 file_bunches[file_bunches.size()-1].push_back(
4146 SendableMedia(name, tpath, tmp_os.str()));
4148 // Start next bunch if got enough data
4149 if(file_size_bunch_total >= bytes_per_bunch){
4150 file_bunches.push_back(std::list<SendableMedia>());
4151 file_size_bunch_total = 0;
4156 /* Create and send packets */
4158 u32 num_bunches = file_bunches.size();
4159 for(u32 i=0; i<num_bunches; i++)
4161 std::ostringstream os(std::ios_base::binary);
4165 u16 total number of texture bunches
4166 u16 index of this bunch
4167 u32 number of files in this bunch
4176 writeU16(os, TOCLIENT_MEDIA);
4177 writeU16(os, num_bunches);
4179 writeU32(os, file_bunches[i].size());
4181 for(std::list<SendableMedia>::iterator
4182 j = file_bunches[i].begin();
4183 j != file_bunches[i].end(); ++j){
4184 os<<serializeString(j->name);
4185 os<<serializeLongString(j->data);
4189 std::string s = os.str();
4190 verbosestream<<"Server::sendRequestedMedia(): bunch "
4191 <<i<<"/"<<num_bunches
4192 <<" files="<<file_bunches[i].size()
4193 <<" size=" <<s.size()<<std::endl;
4194 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4196 m_clients.send(peer_id, 2, data, true);
4200 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4202 if(m_detached_inventories.count(name) == 0){
4203 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4206 Inventory *inv = m_detached_inventories[name];
4208 std::ostringstream os(std::ios_base::binary);
4209 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4210 os<<serializeString(name);
4214 std::string s = os.str();
4215 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4217 if (peer_id != PEER_ID_INEXISTENT)
4220 m_clients.send(peer_id, 0, data, true);
4224 m_clients.sendToAll(0,data,true);
4228 void Server::sendDetachedInventories(u16 peer_id)
4230 DSTACK(__FUNCTION_NAME);
4232 for(std::map<std::string, Inventory*>::iterator
4233 i = m_detached_inventories.begin();
4234 i != m_detached_inventories.end(); i++){
4235 const std::string &name = i->first;
4236 //Inventory *inv = i->second;
4237 sendDetachedInventory(name, peer_id);
4245 void Server::DiePlayer(u16 peer_id)
4247 DSTACK(__FUNCTION_NAME);
4249 PlayerSAO *playersao = getPlayerSAO(peer_id);
4252 infostream<<"Server::DiePlayer(): Player "
4253 <<playersao->getPlayer()->getName()
4254 <<" dies"<<std::endl;
4256 playersao->setHP(0);
4258 // Trigger scripted stuff
4259 m_script->on_dieplayer(playersao);
4261 SendPlayerHP(peer_id);
4262 SendDeathscreen(peer_id, false, v3f(0,0,0));
4265 void Server::RespawnPlayer(u16 peer_id)
4267 DSTACK(__FUNCTION_NAME);
4269 PlayerSAO *playersao = getPlayerSAO(peer_id);
4272 infostream<<"Server::RespawnPlayer(): Player "
4273 <<playersao->getPlayer()->getName()
4274 <<" respawns"<<std::endl;
4276 playersao->setHP(PLAYER_MAX_HP);
4278 bool repositioned = m_script->on_respawnplayer(playersao);
4280 v3f pos = findSpawnPos(m_env->getServerMap());
4281 playersao->setPos(pos);
4285 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4287 DSTACK(__FUNCTION_NAME);
4289 SendAccessDenied(peer_id, reason);
4290 m_clients.event(peer_id,SetDenied);
4291 m_con.DisconnectPeer(peer_id);
4294 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4296 DSTACK(__FUNCTION_NAME);
4297 std::wstring message;
4300 Clear references to playing sounds
4302 for(std::map<s32, ServerPlayingSound>::iterator
4303 i = m_playing_sounds.begin();
4304 i != m_playing_sounds.end();)
4306 ServerPlayingSound &psound = i->second;
4307 psound.clients.erase(peer_id);
4308 if(psound.clients.size() == 0)
4309 m_playing_sounds.erase(i++);
4314 Player *player = m_env->getPlayer(peer_id);
4316 // Collect information about leaving in chat
4318 if(player != NULL && reason != CDR_DENY)
4320 std::wstring name = narrow_to_wide(player->getName());
4323 message += L" left the game.";
4324 if(reason == CDR_TIMEOUT)
4325 message += L" (timed out)";
4329 /* Run scripts and remove from environment */
4333 PlayerSAO *playersao = player->getPlayerSAO();
4336 m_script->on_leaveplayer(playersao);
4338 playersao->disconnected();
4346 if(player != NULL && reason != CDR_DENY)
4348 std::ostringstream os(std::ios_base::binary);
4349 std::list<u16> clients = m_clients.getClientIDs();
4351 for(std::list<u16>::iterator
4352 i = clients.begin();
4353 i != clients.end(); ++i)
4356 Player *player = m_env->getPlayer(*i);
4359 // Get name of player
4360 os<<player->getName()<<" ";
4363 actionstream<<player->getName()<<" "
4364 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4365 <<" List of players: "<<os.str()<<std::endl;
4369 JMutexAutoLock env_lock(m_env_mutex);
4370 m_clients.DeleteClient(peer_id);
4374 // Send leave chat message to all remaining clients
4375 if(message.length() != 0)
4376 SendChatMessage(PEER_ID_INEXISTENT,message);
4379 void Server::UpdateCrafting(u16 peer_id)
4381 DSTACK(__FUNCTION_NAME);
4383 Player* player = m_env->getPlayer(peer_id);
4386 // Get a preview for crafting
4388 InventoryLocation loc;
4389 loc.setPlayer(player->getName());
4390 getCraftingResult(&player->inventory, preview, false, this);
4391 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4393 // Put the new preview in
4394 InventoryList *plist = player->inventory.getList("craftpreview");
4396 assert(plist->getSize() >= 1);
4397 plist->changeItem(0, preview);
4400 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4402 RemoteClient *client = getClientNoEx(peer_id,state_min);
4404 throw ClientNotFoundException("Client not found");
4408 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4410 return m_clients.getClientNoEx(peer_id, state_min);
4413 std::string Server::getPlayerName(u16 peer_id)
4415 Player *player = m_env->getPlayer(peer_id);
4417 return "[id="+itos(peer_id)+"]";
4418 return player->getName();
4421 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4423 Player *player = m_env->getPlayer(peer_id);
4426 return player->getPlayerSAO();
4429 std::wstring Server::getStatusString()
4431 std::wostringstream os(std::ios_base::binary);
4434 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4436 os<<L", uptime="<<m_uptime.get();
4438 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4439 // Information about clients
4442 std::list<u16> clients = m_clients.getClientIDs();
4443 for(std::list<u16>::iterator i = clients.begin();
4444 i != clients.end(); ++i)
4447 Player *player = m_env->getPlayer(*i);
4448 // Get name of player
4449 std::wstring name = L"unknown";
4451 name = narrow_to_wide(player->getName());
4452 // Add name to information string
4460 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4461 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4462 if(g_settings->get("motd") != "")
4463 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4467 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4469 std::set<std::string> privs;
4470 m_script->getAuth(name, NULL, &privs);
4474 bool Server::checkPriv(const std::string &name, const std::string &priv)
4476 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4477 return (privs.count(priv) != 0);
4480 void Server::reportPrivsModified(const std::string &name)
4483 std::list<u16> clients = m_clients.getClientIDs();
4484 for(std::list<u16>::iterator
4485 i = clients.begin();
4486 i != clients.end(); ++i){
4487 Player *player = m_env->getPlayer(*i);
4488 reportPrivsModified(player->getName());
4491 Player *player = m_env->getPlayer(name.c_str());
4494 SendPlayerPrivileges(player->peer_id);
4495 PlayerSAO *sao = player->getPlayerSAO();
4498 sao->updatePrivileges(
4499 getPlayerEffectivePrivs(name),
4504 void Server::reportInventoryFormspecModified(const std::string &name)
4506 Player *player = m_env->getPlayer(name.c_str());
4509 SendPlayerInventoryFormspec(player->peer_id);
4512 void Server::setIpBanned(const std::string &ip, const std::string &name)
4514 m_banmanager->add(ip, name);
4517 void Server::unsetIpBanned(const std::string &ip_or_name)
4519 m_banmanager->remove(ip_or_name);
4522 std::string Server::getBanDescription(const std::string &ip_or_name)
4524 return m_banmanager->getBanDescription(ip_or_name);
4527 void Server::notifyPlayer(const char *name, const std::wstring &msg)
4529 Player *player = m_env->getPlayer(name);
4533 if (player->peer_id == PEER_ID_INEXISTENT)
4536 SendChatMessage(player->peer_id, msg);
4539 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4541 Player *player = m_env->getPlayer(playername);
4545 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4549 SendShowFormspecMessage(player->peer_id, formspec, formname);
4553 u32 Server::hudAdd(Player *player, HudElement *form) {
4557 u32 id = player->getFreeHudID();
4558 if (id < player->hud.size())
4559 player->hud[id] = form;
4561 player->hud.push_back(form);
4563 SendHUDAdd(player->peer_id, id, form);
4567 bool Server::hudRemove(Player *player, u32 id) {
4568 if (!player || id >= player->hud.size() || !player->hud[id])
4571 delete player->hud[id];
4572 player->hud[id] = NULL;
4574 SendHUDRemove(player->peer_id, id);
4578 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4582 SendHUDChange(player->peer_id, id, stat, data);
4586 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4590 SendHUDSetFlags(player->peer_id, flags, mask);
4594 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4597 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4600 std::ostringstream os(std::ios::binary);
4601 writeS32(os, hotbar_itemcount);
4602 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4606 void Server::hudSetHotbarImage(Player *player, std::string name) {
4610 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4613 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4617 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4620 bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed)
4625 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
4629 bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
4634 SendEyeOffset(player->peer_id, first, third);
4638 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4639 const std::string &type, const std::vector<std::string> ¶ms)
4644 SendSetSky(player->peer_id, bgcolor, type, params);
4648 bool Server::overrideDayNightRatio(Player *player, bool do_override,
4654 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
4658 void Server::notifyPlayers(const std::wstring &msg)
4660 SendChatMessage(PEER_ID_INEXISTENT,msg);
4663 void Server::spawnParticle(const char *playername, v3f pos,
4664 v3f velocity, v3f acceleration,
4665 float expirationtime, float size, bool
4666 collisiondetection, bool vertical, std::string texture)
4668 Player *player = m_env->getPlayer(playername);
4671 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4672 expirationtime, size, collisiondetection, vertical, texture);
4675 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4676 float expirationtime, float size,
4677 bool collisiondetection, bool vertical, std::string texture)
4679 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4680 expirationtime, size, collisiondetection, vertical, texture);
4683 u32 Server::addParticleSpawner(const char *playername,
4684 u16 amount, float spawntime,
4685 v3f minpos, v3f maxpos,
4686 v3f minvel, v3f maxvel,
4687 v3f minacc, v3f maxacc,
4688 float minexptime, float maxexptime,
4689 float minsize, float maxsize,
4690 bool collisiondetection, bool vertical, std::string texture)
4692 Player *player = m_env->getPlayer(playername);
4697 for(;;) // look for unused particlespawner id
4700 if (std::find(m_particlespawner_ids.begin(),
4701 m_particlespawner_ids.end(), id)
4702 == m_particlespawner_ids.end())
4704 m_particlespawner_ids.push_back(id);
4709 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4710 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4711 minexptime, maxexptime, minsize, maxsize,
4712 collisiondetection, vertical, texture, id);
4717 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4718 v3f minpos, v3f maxpos,
4719 v3f minvel, v3f maxvel,
4720 v3f minacc, v3f maxacc,
4721 float minexptime, float maxexptime,
4722 float minsize, float maxsize,
4723 bool collisiondetection, bool vertical, std::string texture)
4726 for(;;) // look for unused particlespawner id
4729 if (std::find(m_particlespawner_ids.begin(),
4730 m_particlespawner_ids.end(), id)
4731 == m_particlespawner_ids.end())
4733 m_particlespawner_ids.push_back(id);
4738 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4739 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4740 minexptime, maxexptime, minsize, maxsize,
4741 collisiondetection, vertical, texture, id);
4746 void Server::deleteParticleSpawner(const char *playername, u32 id)
4748 Player *player = m_env->getPlayer(playername);
4752 m_particlespawner_ids.erase(
4753 std::remove(m_particlespawner_ids.begin(),
4754 m_particlespawner_ids.end(), id),
4755 m_particlespawner_ids.end());
4756 SendDeleteParticleSpawner(player->peer_id, id);
4759 void Server::deleteParticleSpawnerAll(u32 id)
4761 m_particlespawner_ids.erase(
4762 std::remove(m_particlespawner_ids.begin(),
4763 m_particlespawner_ids.end(), id),
4764 m_particlespawner_ids.end());
4765 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4768 Inventory* Server::createDetachedInventory(const std::string &name)
4770 if(m_detached_inventories.count(name) > 0){
4771 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4772 delete m_detached_inventories[name];
4774 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4776 Inventory *inv = new Inventory(m_itemdef);
4778 m_detached_inventories[name] = inv;
4779 //TODO find a better way to do this
4780 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4787 BoolScopeSet(bool *dst, bool val):
4790 m_orig_state = *m_dst;
4795 *m_dst = m_orig_state;
4802 // actions: time-reversed list
4803 // Return value: success/failure
4804 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4805 std::list<std::string> *log)
4807 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4808 ServerMap *map = (ServerMap*)(&m_env->getMap());
4809 // Disable rollback report sink while reverting
4810 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4812 // Fail if no actions to handle
4813 if(actions.empty()){
4814 log->push_back("Nothing to do.");
4821 for(std::list<RollbackAction>::const_iterator
4822 i = actions.begin();
4823 i != actions.end(); i++)
4825 const RollbackAction &action = *i;
4827 bool success = action.applyRevert(map, this, this);
4830 std::ostringstream os;
4831 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4832 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4834 log->push_back(os.str());
4836 std::ostringstream os;
4837 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4838 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4840 log->push_back(os.str());
4844 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4845 <<" failed"<<std::endl;
4847 // Call it done if less than half failed
4848 return num_failed <= num_tried/2;
4851 // IGameDef interface
4853 IItemDefManager* Server::getItemDefManager()
4857 INodeDefManager* Server::getNodeDefManager()
4861 ICraftDefManager* Server::getCraftDefManager()
4865 ITextureSource* Server::getTextureSource()
4869 IShaderSource* Server::getShaderSource()
4873 u16 Server::allocateUnknownNodeId(const std::string &name)
4875 return m_nodedef->allocateDummy(name);
4877 ISoundManager* Server::getSoundManager()
4879 return &dummySoundManager;
4881 MtEventManager* Server::getEventManager()
4885 IRollbackReportSink* Server::getRollbackReportSink()
4887 if(!m_enable_rollback_recording)
4889 if(!m_rollback_sink_enabled)
4894 IWritableItemDefManager* Server::getWritableItemDefManager()
4898 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4902 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4907 const ModSpec* Server::getModSpec(const std::string &modname)
4909 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4910 i != m_mods.end(); i++){
4911 const ModSpec &mod = *i;
4912 if(mod.name == modname)
4917 void Server::getModNames(std::list<std::string> &modlist)
4919 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4921 modlist.push_back(i->name);
4924 std::string Server::getBuiltinLuaPath()
4926 return porting::path_share + DIR_DELIM + "builtin";
4929 v3f findSpawnPos(ServerMap &map)
4931 //return v3f(50,50,50)*BS;
4936 nodepos = v2s16(0,0);
4941 s16 water_level = map.getWaterLevel();
4943 // Try to find a good place a few times
4944 for(s32 i=0; i<1000; i++)
4947 // We're going to try to throw the player to this position
4948 v2s16 nodepos2d = v2s16(
4949 -range + (myrand() % (range * 2)),
4950 -range + (myrand() % (range * 2)));
4952 // Get ground height at point
4953 s16 groundheight = map.findGroundLevel(nodepos2d);
4954 if (groundheight <= water_level) // Don't go underwater
4956 if (groundheight > water_level + 6) // Don't go to high places
4959 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4960 bool is_good = false;
4962 for (s32 i = 0; i < 10; i++) {
4963 v3s16 blockpos = getNodeBlockPos(nodepos);
4964 map.emergeBlock(blockpos, true);
4965 content_t c = map.getNodeNoEx(nodepos).getContent();
4966 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4968 if (air_count >= 2){
4976 // Found a good place
4977 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4983 return intToFloat(nodepos, BS);
4986 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4988 RemotePlayer *player = NULL;
4989 bool newplayer = false;
4992 Try to get an existing player
4994 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4996 // If player is already connected, cancel
4997 if(player != NULL && player->peer_id != 0)
4999 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5004 If player with the wanted peer_id already exists, cancel.
5006 if(m_env->getPlayer(peer_id) != NULL)
5008 infostream<<"emergePlayer(): Player with wrong name but same"
5009 " peer_id already exists"<<std::endl;
5014 Create a new player if it doesn't exist yet
5019 player = new RemotePlayer(this);
5020 player->updateName(name);
5022 /* Set player position */
5023 infostream<<"Server: Finding spawn place for player \""
5024 <<name<<"\""<<std::endl;
5025 v3f pos = findSpawnPos(m_env->getServerMap());
5026 player->setPosition(pos);
5028 /* Add player to environment */
5029 m_env->addPlayer(player);
5033 Create a new player active object
5035 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5036 getPlayerEffectivePrivs(player->getName()),
5039 /* Clean up old HUD elements from previous sessions */
5040 player->hud.clear();
5042 /* Add object to environment */
5043 m_env->addActiveObject(playersao);
5047 m_script->on_newplayer(playersao);
5052 void dedicated_server_loop(Server &server, bool &kill)
5054 DSTACK(__FUNCTION_NAME);
5056 verbosestream<<"dedicated_server_loop()"<<std::endl;
5058 IntervalLimiter m_profiler_interval;
5062 float steplen = g_settings->getFloat("dedicated_server_step");
5063 // This is kind of a hack but can be done like this
5064 // because server.step() is very light
5066 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5067 sleep_ms((int)(steplen*1000.0));
5069 server.step(steplen);
5071 if(server.getShutdownRequested() || kill)
5073 infostream<<"Dedicated server quitting"<<std::endl;
5075 if(g_settings->getBool("server_announce") == true)
5076 ServerList::sendAnnounce("delete");
5084 float profiler_print_interval =
5085 g_settings->getFloat("profiler_print_interval");
5086 if(profiler_print_interval != 0)
5088 if(m_profiler_interval.step(steplen, profiler_print_interval))
5090 infostream<<"Profiler:"<<std::endl;
5091 g_profiler->print(infostream);
5092 g_profiler->clear();