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;
298 JMutexAutoLock envlock(m_env_mutex);
300 // Initialize scripting
301 infostream<<"Server: Initializing Lua"<<std::endl;
303 m_script = new GameScripting(this);
305 std::string scriptpath = getBuiltinLuaPath() + DIR_DELIM "init.lua";
307 if (!m_script->loadScript(scriptpath)) {
308 throw ModError("Failed to load and run " + scriptpath);
313 infostream<<"Server: Loading mods: ";
314 for(std::vector<ModSpec>::iterator i = m_mods.begin();
315 i != m_mods.end(); i++){
316 const ModSpec &mod = *i;
317 infostream<<mod.name<<" ";
319 infostream<<std::endl;
320 // Load and run "mod" scripts
321 for(std::vector<ModSpec>::iterator i = m_mods.begin();
322 i != m_mods.end(); i++){
323 const ModSpec &mod = *i;
324 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
325 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
326 <<scriptpath<<"\"]"<<std::endl;
327 bool success = m_script->loadMod(scriptpath, mod.name);
329 errorstream<<"Server: Failed to load and run "
330 <<scriptpath<<std::endl;
331 throw ModError("Failed to load and run "+scriptpath);
335 // Read Textures and calculate sha1 sums
338 // Apply item aliases in the node definition manager
339 m_nodedef->updateAliases(m_itemdef);
341 // Load the mapgen params from global settings now after any
342 // initial overrides have been set by the mods
343 m_emerge->loadMapgenParams();
345 // Initialize Environment
346 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
347 m_env = new ServerEnvironment(servermap, m_script, this);
349 m_clients.setEnv(m_env);
351 // Run some callbacks after the MG params have been set up but before activation
352 m_script->environment_OnMapgenInit(&m_emerge->params);
354 // Initialize mapgens
355 m_emerge->initMapgens();
357 // Give environment reference to scripting api
358 m_script->initializeEnvironment(m_env);
360 // Register us to receive map edit events
361 servermap->addEventReceiver(this);
363 // If file exists, load environment metadata
364 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
366 infostream<<"Server: Loading environment metadata"<<std::endl;
367 m_env->loadMeta(m_path_world);
371 infostream<<"Server: Loading players"<<std::endl;
372 m_env->deSerializePlayers(m_path_world);
375 Add some test ActiveBlockModifiers to environment
377 add_legacy_abms(m_env, m_nodedef);
379 m_liquid_transform_every = g_settings->getFloat("liquid_update");
384 infostream<<"Server destructing"<<std::endl;
387 Send shutdown message
390 std::wstring line = L"*** Server shutting down";
391 SendChatMessage(PEER_ID_INEXISTENT, line);
395 JMutexAutoLock envlock(m_env_mutex);
398 Execute script shutdown hooks
400 m_script->on_shutdown();
404 JMutexAutoLock envlock(m_env_mutex);
409 infostream<<"Server: Saving players"<<std::endl;
410 m_env->serializePlayers(m_path_world);
413 Save environment metadata
415 infostream<<"Server: Saving environment metadata"<<std::endl;
416 m_env->saveMeta(m_path_world);
425 // stop all emerge threads before deleting players that may have
426 // requested blocks to be emerged
427 m_emerge->stopThreads();
429 // Delete things in the reverse order of creation
432 // N.B. the EmergeManager should be deleted after the Environment since Map
433 // depends on EmergeManager to write its current params to the map meta
442 // Deinitialize scripting
443 infostream<<"Server: Deinitializing scripting"<<std::endl;
446 // Delete detached inventories
448 for(std::map<std::string, Inventory*>::iterator
449 i = m_detached_inventories.begin();
450 i != m_detached_inventories.end(); i++){
456 void Server::start(Address bind_addr)
458 DSTACK(__FUNCTION_NAME);
459 infostream<<"Starting server on "
460 << bind_addr.serializeString() <<"..."<<std::endl;
462 // Stop thread if already running
465 // Initialize connection
466 m_con.SetTimeoutMs(30);
467 m_con.Serve(bind_addr);
472 // ASCII art for the win!
474 <<" .__ __ __ "<<std::endl
475 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
476 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
477 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
478 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
479 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
480 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
481 actionstream<<"Server for gameid=\""<<m_gamespec.id
482 <<"\" listening on "<<bind_addr.serializeString()<<":"
483 <<bind_addr.getPort() << "."<<std::endl;
488 DSTACK(__FUNCTION_NAME);
490 infostream<<"Server: Stopping and waiting threads"<<std::endl;
492 // Stop threads (set run=false first so both start stopping)
494 //m_emergethread.setRun(false);
496 //m_emergethread.stop();
498 infostream<<"Server: Threads stopped"<<std::endl;
501 void Server::step(float dtime)
503 DSTACK(__FUNCTION_NAME);
508 JMutexAutoLock lock(m_step_dtime_mutex);
509 m_step_dtime += dtime;
511 // Throw if fatal error occurred in thread
512 std::string async_err = m_async_fatal_error.get();
514 throw ServerError(async_err);
518 void Server::AsyncRunStep(bool initial_step)
520 DSTACK(__FUNCTION_NAME);
522 g_profiler->add("Server::AsyncRunStep (num)", 1);
526 JMutexAutoLock lock1(m_step_dtime_mutex);
527 dtime = m_step_dtime;
531 // Send blocks to clients
535 if((dtime < 0.001) && (initial_step == false))
538 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
540 //infostream<<"Server steps "<<dtime<<std::endl;
541 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
544 JMutexAutoLock lock1(m_step_dtime_mutex);
545 m_step_dtime -= dtime;
552 m_uptime.set(m_uptime.get() + dtime);
558 Update time of day and overall game time
561 JMutexAutoLock envlock(m_env_mutex);
563 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
566 Send to clients at constant intervals
569 m_time_of_day_send_timer -= dtime;
570 if(m_time_of_day_send_timer < 0.0)
572 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
573 u16 time = m_env->getTimeOfDay();
574 float time_speed = g_settings->getFloat("time_speed");
575 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
580 JMutexAutoLock lock(m_env_mutex);
581 // Figure out and report maximum lag to environment
582 float max_lag = m_env->getMaxLagEstimate();
583 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
585 if(dtime > 0.1 && dtime > max_lag * 2.0)
586 infostream<<"Server: Maximum lag peaked to "<<dtime
590 m_env->reportMaxLagEstimate(max_lag);
592 ScopeProfiler sp(g_profiler, "SEnv step");
593 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
597 const float map_timer_and_unload_dtime = 2.92;
598 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
600 JMutexAutoLock lock(m_env_mutex);
601 // Run Map's timers and unload unused data
602 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
603 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
604 g_settings->getFloat("server_unload_unused_data_timeout"));
615 JMutexAutoLock lock(m_env_mutex);
617 std::list<u16> clientids = m_clients.getClientIDs();
619 ScopeProfiler sp(g_profiler, "Server: handle players");
621 for(std::list<u16>::iterator
622 i = clientids.begin();
623 i != clientids.end(); ++i)
625 PlayerSAO *playersao = getPlayerSAO(*i);
626 if(playersao == NULL)
630 Handle player HPs (die if hp=0)
632 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
634 if(playersao->getHP() == 0)
641 Send player breath if changed
643 if(playersao->m_breath_not_sent) {
644 SendPlayerBreath(*i);
648 Send player inventories if necessary
650 if(playersao->m_moved){
652 playersao->m_moved = false;
654 if(playersao->m_inventory_not_sent){
661 /* Transform liquids */
662 m_liquid_transform_timer += dtime;
663 if(m_liquid_transform_timer >= m_liquid_transform_every)
665 m_liquid_transform_timer -= m_liquid_transform_every;
667 JMutexAutoLock lock(m_env_mutex);
669 ScopeProfiler sp(g_profiler, "Server: liquid transform");
671 std::map<v3s16, MapBlock*> modified_blocks;
672 m_env->getMap().transformLiquids(modified_blocks);
677 core::map<v3s16, MapBlock*> lighting_modified_blocks;
678 ServerMap &map = ((ServerMap&)m_env->getMap());
679 map.updateLighting(modified_blocks, lighting_modified_blocks);
681 // Add blocks modified by lighting to modified_blocks
682 for(core::map<v3s16, MapBlock*>::Iterator
683 i = lighting_modified_blocks.getIterator();
684 i.atEnd() == false; i++)
686 MapBlock *block = i.getNode()->getValue();
687 modified_blocks.insert(block->getPos(), block);
691 Set the modified blocks unsent for all the clients
693 if(modified_blocks.size() > 0)
695 SetBlocksNotSent(modified_blocks);
698 m_clients.step(dtime);
700 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
702 // send masterserver announce
704 float &counter = m_masterserver_timer;
705 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
706 g_settings->getBool("server_announce") == true)
708 ServerList::sendAnnounce(!counter ? "start" : "update",
709 m_clients.getPlayerNames(),
711 m_env->getGameTime(),
722 Check added and deleted active objects
725 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
726 JMutexAutoLock envlock(m_env_mutex);
729 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
730 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
732 // Radius inside which objects are active
733 s16 radius = g_settings->getS16("active_object_send_range_blocks");
734 radius *= MAP_BLOCKSIZE;
736 for(std::map<u16, RemoteClient*>::iterator
738 i != clients.end(); ++i)
740 RemoteClient *client = i->second;
742 // If definitions and textures have not been sent, don't
743 // send objects either
744 if (client->getState() < DefinitionsSent)
747 Player *player = m_env->getPlayer(client->peer_id);
750 // This can happen if the client timeouts somehow
751 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
753 <<" has no associated player"<<std::endl;*/
756 v3s16 pos = floatToInt(player->getPosition(), BS);
758 std::set<u16> removed_objects;
759 std::set<u16> added_objects;
760 m_env->getRemovedActiveObjects(pos, radius,
761 client->m_known_objects, removed_objects);
762 m_env->getAddedActiveObjects(pos, radius,
763 client->m_known_objects, added_objects);
765 // Ignore if nothing happened
766 if(removed_objects.size() == 0 && added_objects.size() == 0)
768 //infostream<<"active objects: none changed"<<std::endl;
772 std::string data_buffer;
776 // Handle removed objects
777 writeU16((u8*)buf, removed_objects.size());
778 data_buffer.append(buf, 2);
779 for(std::set<u16>::iterator
780 i = removed_objects.begin();
781 i != removed_objects.end(); ++i)
785 ServerActiveObject* obj = m_env->getActiveObject(id);
787 // Add to data buffer for sending
788 writeU16((u8*)buf, id);
789 data_buffer.append(buf, 2);
791 // Remove from known objects
792 client->m_known_objects.erase(id);
794 if(obj && obj->m_known_by_count > 0)
795 obj->m_known_by_count--;
798 // Handle added objects
799 writeU16((u8*)buf, added_objects.size());
800 data_buffer.append(buf, 2);
801 for(std::set<u16>::iterator
802 i = added_objects.begin();
803 i != added_objects.end(); ++i)
807 ServerActiveObject* obj = m_env->getActiveObject(id);
810 u8 type = ACTIVEOBJECT_TYPE_INVALID;
812 infostream<<"WARNING: "<<__FUNCTION_NAME
813 <<": NULL object"<<std::endl;
815 type = obj->getSendType();
817 // Add to data buffer for sending
818 writeU16((u8*)buf, id);
819 data_buffer.append(buf, 2);
820 writeU8((u8*)buf, type);
821 data_buffer.append(buf, 1);
824 data_buffer.append(serializeLongString(
825 obj->getClientInitializationData(client->net_proto_version)));
827 data_buffer.append(serializeLongString(""));
829 // Add to known objects
830 client->m_known_objects.insert(id);
833 obj->m_known_by_count++;
837 SharedBuffer<u8> reply(2 + data_buffer.size());
838 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
839 memcpy((char*)&reply[2], data_buffer.c_str(),
842 m_clients.send(client->peer_id, 0, reply, true);
844 verbosestream<<"Server: Sent object remove/add: "
845 <<removed_objects.size()<<" removed, "
846 <<added_objects.size()<<" added, "
847 <<"packet size is "<<reply.getSize()<<std::endl;
852 Collect a list of all the objects known by the clients
853 and report it back to the environment.
856 core::map<u16, bool> all_known_objects;
858 for(core::map<u16, RemoteClient*>::Iterator
859 i = m_clients.getIterator();
860 i.atEnd() == false; i++)
862 RemoteClient *client = i.getNode()->getValue();
863 // Go through all known objects of client
864 for(core::map<u16, bool>::Iterator
865 i = client->m_known_objects.getIterator();
866 i.atEnd()==false; i++)
868 u16 id = i.getNode()->getKey();
869 all_known_objects[id] = true;
873 m_env->setKnownActiveObjects(whatever);
882 JMutexAutoLock envlock(m_env_mutex);
883 ScopeProfiler sp(g_profiler, "Server: sending object messages");
886 // Value = data sent by object
887 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
889 // Get active object messages from environment
892 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
896 std::list<ActiveObjectMessage>* message_list = NULL;
897 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
898 n = buffered_messages.find(aom.id);
899 if(n == buffered_messages.end())
901 message_list = new std::list<ActiveObjectMessage>;
902 buffered_messages[aom.id] = message_list;
906 message_list = n->second;
908 message_list->push_back(aom);
912 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
913 // Route data to every client
914 for(std::map<u16, RemoteClient*>::iterator
916 i != clients.end(); ++i)
918 RemoteClient *client = i->second;
919 std::string reliable_data;
920 std::string unreliable_data;
921 // Go through all objects in message buffer
922 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
923 j = buffered_messages.begin();
924 j != buffered_messages.end(); ++j)
926 // If object is not known by client, skip it
928 if(client->m_known_objects.find(id) == client->m_known_objects.end())
930 // Get message list of object
931 std::list<ActiveObjectMessage>* list = j->second;
932 // Go through every message
933 for(std::list<ActiveObjectMessage>::iterator
934 k = list->begin(); k != list->end(); ++k)
936 // Compose the full new data with header
937 ActiveObjectMessage aom = *k;
938 std::string new_data;
941 writeU16((u8*)&buf[0], aom.id);
942 new_data.append(buf, 2);
944 new_data += serializeString(aom.datastring);
945 // Add data to buffer
947 reliable_data += new_data;
949 unreliable_data += new_data;
953 reliable_data and unreliable_data are now ready.
956 if(reliable_data.size() > 0)
958 SharedBuffer<u8> reply(2 + reliable_data.size());
959 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
960 memcpy((char*)&reply[2], reliable_data.c_str(),
961 reliable_data.size());
963 m_clients.send(client->peer_id, 0, reply, true);
965 if(unreliable_data.size() > 0)
967 SharedBuffer<u8> reply(2 + unreliable_data.size());
968 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
969 memcpy((char*)&reply[2], unreliable_data.c_str(),
970 unreliable_data.size());
971 // Send as unreliable
972 m_clients.send(client->peer_id, 1, reply, false);
975 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
977 infostream<<"Server: Size of object message data: "
978 <<"reliable: "<<reliable_data.size()
979 <<", unreliable: "<<unreliable_data.size()
985 // Clear buffered_messages
986 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
987 i = buffered_messages.begin();
988 i != buffered_messages.end(); ++i)
995 Send queued-for-sending map edit events.
998 // We will be accessing the environment
999 JMutexAutoLock lock(m_env_mutex);
1001 // Don't send too many at a time
1004 // Single change sending is disabled if queue size is not small
1005 bool disable_single_change_sending = false;
1006 if(m_unsent_map_edit_queue.size() >= 4)
1007 disable_single_change_sending = true;
1009 int event_count = m_unsent_map_edit_queue.size();
1011 // We'll log the amount of each
1014 while(m_unsent_map_edit_queue.size() != 0)
1016 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1018 // Players far away from the change are stored here.
1019 // Instead of sending the changes, MapBlocks are set not sent
1021 std::list<u16> far_players;
1023 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
1025 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1026 prof.add("MEET_ADDNODE", 1);
1027 if(disable_single_change_sending)
1028 sendAddNode(event->p, event->n, event->already_known_by_peer,
1029 &far_players, 5, event->type == MEET_ADDNODE);
1031 sendAddNode(event->p, event->n, event->already_known_by_peer,
1032 &far_players, 30, event->type == MEET_ADDNODE);
1034 else if(event->type == MEET_REMOVENODE)
1036 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1037 prof.add("MEET_REMOVENODE", 1);
1038 if(disable_single_change_sending)
1039 sendRemoveNode(event->p, event->already_known_by_peer,
1042 sendRemoveNode(event->p, event->already_known_by_peer,
1045 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1047 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1048 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1049 setBlockNotSent(event->p);
1051 else if(event->type == MEET_OTHER)
1053 infostream<<"Server: MEET_OTHER"<<std::endl;
1054 prof.add("MEET_OTHER", 1);
1055 for(std::set<v3s16>::iterator
1056 i = event->modified_blocks.begin();
1057 i != event->modified_blocks.end(); ++i)
1059 setBlockNotSent(*i);
1064 prof.add("unknown", 1);
1065 infostream<<"WARNING: Server: Unknown MapEditEvent "
1066 <<((u32)event->type)<<std::endl;
1070 Set blocks not sent to far players
1072 if(far_players.size() > 0)
1074 // Convert list format to that wanted by SetBlocksNotSent
1075 std::map<v3s16, MapBlock*> modified_blocks2;
1076 for(std::set<v3s16>::iterator
1077 i = event->modified_blocks.begin();
1078 i != event->modified_blocks.end(); ++i)
1080 modified_blocks2[*i] =
1081 m_env->getMap().getBlockNoCreateNoEx(*i);
1083 // Set blocks not sent
1084 for(std::list<u16>::iterator
1085 i = far_players.begin();
1086 i != far_players.end(); ++i)
1089 RemoteClient *client = getClient(peer_id);
1092 client->SetBlocksNotSent(modified_blocks2);
1098 /*// Don't send too many at a time
1100 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1104 if(event_count >= 5){
1105 infostream<<"Server: MapEditEvents:"<<std::endl;
1106 prof.print(infostream);
1107 } else if(event_count != 0){
1108 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1109 prof.print(verbosestream);
1115 Trigger emergethread (it somehow gets to a non-triggered but
1116 bysy state sometimes)
1119 float &counter = m_emergethread_trigger_timer;
1125 m_emerge->startThreads();
1127 // Update m_enable_rollback_recording here too
1128 m_enable_rollback_recording =
1129 g_settings->getBool("enable_rollback_recording");
1133 // Save map, players and auth stuff
1135 float &counter = m_savemap_timer;
1137 if(counter >= g_settings->getFloat("server_map_save_interval"))
1140 JMutexAutoLock lock(m_env_mutex);
1142 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1145 if(m_banmanager->isModified())
1146 m_banmanager->save();
1148 // Save changed parts of map
1149 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1152 m_env->serializePlayers(m_path_world);
1154 // Save environment metadata
1155 m_env->saveMeta(m_path_world);
1160 void Server::Receive()
1162 DSTACK(__FUNCTION_NAME);
1163 SharedBuffer<u8> data;
1167 datasize = m_con.Receive(peer_id,data);
1168 ProcessData(*data, datasize, peer_id);
1170 catch(con::InvalidIncomingDataException &e)
1172 infostream<<"Server::Receive(): "
1173 "InvalidIncomingDataException: what()="
1174 <<e.what()<<std::endl;
1176 catch(con::PeerNotFoundException &e)
1178 //NOTE: This is not needed anymore
1180 // The peer has been disconnected.
1181 // Find the associated player and remove it.
1183 /*JMutexAutoLock envlock(m_env_mutex);
1185 infostream<<"ServerThread: peer_id="<<peer_id
1186 <<" has apparently closed connection. "
1187 <<"Removing player."<<std::endl;
1189 m_env->removePlayer(peer_id);*/
1191 catch(ClientStateError &e)
1193 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1194 DenyAccess(peer_id, L"Your client sent something server didn't expect."
1195 L"Try reconnecting or updating your client");
1199 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1201 std::string playername = "";
1202 PlayerSAO *playersao = NULL;
1204 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,InitDone);
1205 if (client != NULL) {
1206 playername = client->getName();
1207 playersao = emergePlayer(playername.c_str(), peer_id);
1211 RemotePlayer *player =
1212 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1214 // If failed, cancel
1215 if((playersao == NULL) || (player == NULL))
1217 if(player && player->peer_id != 0){
1218 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1219 <<" (player allocated to an another client)"<<std::endl;
1220 DenyAccess(peer_id, L"Another client is connected with this "
1221 L"name. If your client closed unexpectedly, try again in "
1224 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1226 DenyAccess(peer_id, L"Could not allocate player.");
1232 Send complete position information
1234 SendMovePlayer(peer_id);
1237 SendPlayerPrivileges(peer_id);
1239 // Send inventory formspec
1240 SendPlayerInventoryFormspec(peer_id);
1243 UpdateCrafting(peer_id);
1244 SendInventory(peer_id);
1247 if(g_settings->getBool("enable_damage"))
1248 SendPlayerHP(peer_id);
1251 SendPlayerBreath(peer_id);
1253 // Show death screen if necessary
1255 SendDeathscreen(peer_id, false, v3f(0,0,0));
1257 // Note things in chat if not in simple singleplayer mode
1258 if(!m_simple_singleplayer_mode)
1260 // Send information about server to player in chat
1261 SendChatMessage(peer_id, getStatusString());
1263 // Send information about joining in chat
1265 std::wstring name = L"unknown";
1266 Player *player = m_env->getPlayer(peer_id);
1268 name = narrow_to_wide(player->getName());
1270 std::wstring message;
1273 message += L" joined the game.";
1274 SendChatMessage(PEER_ID_INEXISTENT,message);
1277 Address addr = getPeerAddress(player->peer_id);
1278 std::string ip_str = addr.serializeString();
1279 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1284 std::vector<std::string> names = m_clients.getPlayerNames();
1286 actionstream<<player->getName() <<" joins game. List of players: ";
1288 for (std::vector<std::string>::iterator i = names.begin();
1289 i != names.end(); i++)
1291 actionstream << *i << " ";
1294 actionstream << player->getName() <<std::endl;
1299 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1301 DSTACK(__FUNCTION_NAME);
1302 // Environment is locked first.
1303 JMutexAutoLock envlock(m_env_mutex);
1305 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1309 Address address = getPeerAddress(peer_id);
1310 addr_s = address.serializeString();
1312 // drop player if is ip is banned
1313 if(m_banmanager->isIpBanned(addr_s)){
1314 std::string ban_name = m_banmanager->getBanName(addr_s);
1315 infostream<<"Server: A banned client tried to connect from "
1316 <<addr_s<<"; banned name was "
1317 <<ban_name<<std::endl;
1318 // This actually doesn't seem to transfer to the client
1319 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1320 +narrow_to_wide(ban_name));
1324 catch(con::PeerNotFoundException &e)
1327 * no peer for this packet found
1328 * most common reason is peer timeout, e.g. peer didn't
1329 * respond for some time, your server was overloaded or
1332 infostream<<"Server::ProcessData(): Cancelling: peer "
1333 <<peer_id<<" not found"<<std::endl;
1343 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1345 if(command == TOSERVER_INIT)
1347 // [0] u16 TOSERVER_INIT
1348 // [2] u8 SER_FMT_VER_HIGHEST_READ
1349 // [3] u8[20] player_name
1350 // [23] u8[28] password <--- can be sent without this, from old versions
1352 if(datasize < 2+1+PLAYERNAME_SIZE)
1355 RemoteClient* client = getClient(peer_id,Created);
1357 // If net_proto_version is set, this client has already been handled
1358 if(client->getState() > Created)
1360 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1361 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1365 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1366 <<peer_id<<")"<<std::endl;
1368 // Do not allow multiple players in simple singleplayer mode.
1369 // This isn't a perfect way to do it, but will suffice for now
1370 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1371 infostream<<"Server: Not allowing another client ("<<addr_s
1372 <<") to connect in simple singleplayer mode"<<std::endl;
1373 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1377 // First byte after command is maximum supported
1378 // serialization version
1379 u8 client_max = data[2];
1380 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1381 // Use the highest version supported by both
1382 u8 deployed = std::min(client_max, our_max);
1383 // If it's lower than the lowest supported, give up.
1384 if(deployed < SER_FMT_VER_LOWEST)
1385 deployed = SER_FMT_VER_INVALID;
1387 if(deployed == SER_FMT_VER_INVALID)
1389 actionstream<<"Server: A mismatched client tried to connect from "
1390 <<addr_s<<std::endl;
1391 infostream<<"Server: Cannot negotiate serialization version with "
1392 <<addr_s<<std::endl;
1393 DenyAccess(peer_id, std::wstring(
1394 L"Your client's version is not supported.\n"
1395 L"Server version is ")
1396 + narrow_to_wide(minetest_version_simple) + L"."
1401 client->setPendingSerializationVersion(deployed);
1404 Read and check network protocol version
1407 u16 min_net_proto_version = 0;
1408 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1409 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1411 // Use same version as minimum and maximum if maximum version field
1412 // doesn't exist (backwards compatibility)
1413 u16 max_net_proto_version = min_net_proto_version;
1414 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1415 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1417 // Start with client's maximum version
1418 u16 net_proto_version = max_net_proto_version;
1420 // Figure out a working version if it is possible at all
1421 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1422 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1424 // If maximum is larger than our maximum, go with our maximum
1425 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1426 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1427 // Else go with client's maximum
1429 net_proto_version = max_net_proto_version;
1432 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1433 <<min_net_proto_version<<", max: "<<max_net_proto_version
1434 <<", chosen: "<<net_proto_version<<std::endl;
1436 client->net_proto_version = net_proto_version;
1438 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1439 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1441 actionstream<<"Server: A mismatched client tried to connect from "
1442 <<addr_s<<std::endl;
1443 DenyAccess(peer_id, std::wstring(
1444 L"Your client's version is not supported.\n"
1445 L"Server version is ")
1446 + narrow_to_wide(minetest_version_simple) + L",\n"
1447 + L"server's PROTOCOL_VERSION is "
1448 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1450 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1451 + L", client's PROTOCOL_VERSION is "
1452 + narrow_to_wide(itos(min_net_proto_version))
1454 + narrow_to_wide(itos(max_net_proto_version))
1459 if(g_settings->getBool("strict_protocol_version_checking"))
1461 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1463 actionstream<<"Server: A mismatched (strict) client tried to "
1464 <<"connect from "<<addr_s<<std::endl;
1465 DenyAccess(peer_id, std::wstring(
1466 L"Your client's version is not supported.\n"
1467 L"Server version is ")
1468 + narrow_to_wide(minetest_version_simple) + L",\n"
1469 + L"server's PROTOCOL_VERSION (strict) is "
1470 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1471 + L", client's PROTOCOL_VERSION is "
1472 + narrow_to_wide(itos(min_net_proto_version))
1474 + narrow_to_wide(itos(max_net_proto_version))
1485 char playername[PLAYERNAME_SIZE];
1486 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1488 playername[i] = data[3+i];
1490 playername[PLAYERNAME_SIZE-1] = 0;
1492 if(playername[0]=='\0')
1494 actionstream<<"Server: Player with an empty name "
1495 <<"tried to connect from "<<addr_s<<std::endl;
1496 DenyAccess(peer_id, L"Empty name");
1500 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1502 actionstream<<"Server: Player with an invalid name "
1503 <<"tried to connect from "<<addr_s<<std::endl;
1504 DenyAccess(peer_id, L"Name contains unallowed characters");
1508 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1510 actionstream<<"Server: Player with the name \"singleplayer\" "
1511 <<"tried to connect from "<<addr_s<<std::endl;
1512 DenyAccess(peer_id, L"Name is not allowed");
1518 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1520 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1521 <<"tried to connect from "<<addr_s<<" "
1522 <<"but it was disallowed for the following reason: "
1523 <<reason<<std::endl;
1524 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1529 infostream<<"Server: New connection: \""<<playername<<"\" from "
1530 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1533 char given_password[PASSWORD_SIZE];
1534 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1536 // old version - assume blank password
1537 given_password[0] = 0;
1541 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1543 given_password[i] = data[23+i];
1545 given_password[PASSWORD_SIZE-1] = 0;
1548 if(!base64_is_valid(given_password)){
1549 actionstream<<"Server: "<<playername
1550 <<" supplied invalid password hash"<<std::endl;
1551 DenyAccess(peer_id, L"Invalid password hash");
1555 // Enforce user limit.
1556 // Don't enforce for users that have some admin right
1557 if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") &&
1558 !checkPriv(playername, "server") &&
1559 !checkPriv(playername, "ban") &&
1560 !checkPriv(playername, "privs") &&
1561 !checkPriv(playername, "password") &&
1562 playername != g_settings->get("name"))
1564 actionstream<<"Server: "<<playername<<" tried to join, but there"
1565 <<" are already max_users="
1566 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1567 DenyAccess(peer_id, L"Too many users.");
1571 std::string checkpwd; // Password hash to check against
1572 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1574 // If no authentication info exists for user, create it
1576 if(!isSingleplayer() &&
1577 g_settings->getBool("disallow_empty_password") &&
1578 std::string(given_password) == ""){
1579 actionstream<<"Server: "<<playername
1580 <<" supplied empty password"<<std::endl;
1581 DenyAccess(peer_id, L"Empty passwords are "
1582 L"disallowed. Set a password and try again.");
1585 std::wstring raw_default_password =
1586 narrow_to_wide(g_settings->get("default_password"));
1587 std::string initial_password =
1588 translatePassword(playername, raw_default_password);
1590 // If default_password is empty, allow any initial password
1591 if (raw_default_password.length() == 0)
1592 initial_password = given_password;
1594 m_script->createAuth(playername, initial_password);
1597 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1600 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1601 <<" (auth handler does not work?)"<<std::endl;
1602 DenyAccess(peer_id, L"Not allowed to login");
1606 if(given_password != checkpwd){
1607 actionstream<<"Server: "<<playername<<" supplied wrong password"
1609 DenyAccess(peer_id, L"Wrong password");
1613 RemotePlayer *player =
1614 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1616 if(player && player->peer_id != 0){
1617 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1618 <<" (player allocated to an another client)"<<std::endl;
1619 DenyAccess(peer_id, L"Another client is connected with this "
1620 L"name. If your client closed unexpectedly, try again in "
1624 m_clients.setPlayerName(peer_id,playername);
1627 Answer with a TOCLIENT_INIT
1630 SharedBuffer<u8> reply(2+1+6+8+4);
1631 writeU16(&reply[0], TOCLIENT_INIT);
1632 writeU8(&reply[2], deployed);
1633 //send dummy pos for legacy reasons only
1634 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1635 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1636 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1639 m_clients.send(peer_id, 0, reply, true);
1640 m_clients.event(peer_id, Init);
1646 if(command == TOSERVER_INIT2)
1649 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1650 <<peer_id<<std::endl;
1652 m_clients.event(peer_id, GotInit2);
1653 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1656 ///// begin compatibility code
1657 PlayerSAO* playersao = NULL;
1658 if (protocol_version <= 22) {
1659 playersao = StageTwoClientInit(peer_id);
1661 if (playersao == NULL) {
1663 << "TOSERVER_INIT2 stage 2 client init failed for peer "
1664 << peer_id << std::endl;
1668 ///// end compatibility code
1671 Send some initialization data
1674 infostream<<"Server: Sending content to "
1675 <<getPlayerName(peer_id)<<std::endl;
1677 // Send player movement settings
1678 SendMovement(peer_id);
1680 // Send item definitions
1681 SendItemDef(peer_id, m_itemdef, protocol_version);
1683 // Send node definitions
1684 SendNodeDef(peer_id, m_nodedef, protocol_version);
1686 m_clients.event(peer_id, SetDefinitionsSent);
1688 // Send media announcement
1689 sendMediaAnnouncement(peer_id);
1691 // Send detached inventories
1692 sendDetachedInventories(peer_id);
1695 u16 time = m_env->getTimeOfDay();
1696 float time_speed = g_settings->getFloat("time_speed");
1697 SendTimeOfDay(peer_id, time, time_speed);
1699 ///// begin compatibility code
1700 if (protocol_version <= 22) {
1701 m_clients.event(peer_id, SetClientReady);
1702 m_script->on_joinplayer(playersao);
1704 ///// end compatibility code
1706 // Warnings about protocol version can be issued here
1707 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1709 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1710 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1716 u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version;
1717 u16 peer_proto_ver = getClient(peer_id,InitDone)->net_proto_version;
1719 if(peer_ser_ver == SER_FMT_VER_INVALID)
1721 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1722 " serialization format invalid or not initialized."
1723 " Skipping incoming command="<<command<<std::endl;
1727 /* Handle commands relate to client startup */
1728 if(command == TOSERVER_REQUEST_MEDIA) {
1729 std::string datastring((char*)&data[2], datasize-2);
1730 std::istringstream is(datastring, std::ios_base::binary);
1732 std::list<std::string> tosend;
1733 u16 numfiles = readU16(is);
1735 infostream<<"Sending "<<numfiles<<" files to "
1736 <<getPlayerName(peer_id)<<std::endl;
1737 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1739 for(int i = 0; i < numfiles; i++) {
1740 std::string name = deSerializeString(is);
1741 tosend.push_back(name);
1742 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1746 sendRequestedMedia(peer_id, tosend);
1749 else if(command == TOSERVER_RECEIVED_MEDIA) {
1752 else if(command == TOSERVER_CLIENT_READY) {
1753 // clients <= protocol version 22 did not send ready message,
1754 // they're already initialized
1755 if (peer_proto_ver <= 22) {
1756 infostream << "Client sent message not expected by a "
1757 << "client using protocol version <= 22,"
1758 << "disconnecing peer_id: " << peer_id << std::endl;
1759 m_con.DisconnectPeer(peer_id);
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);
2205 m_script->player_event(playersao,"breath_changed");
2207 else if(command == TOSERVER_PASSWORD)
2210 [0] u16 TOSERVER_PASSWORD
2211 [2] u8[28] old password
2212 [30] u8[28] new password
2215 if(datasize != 2+PASSWORD_SIZE*2)
2217 /*char password[PASSWORD_SIZE];
2218 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2219 password[i] = data[2+i];
2220 password[PASSWORD_SIZE-1] = 0;*/
2222 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2230 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2232 char c = data[2+PASSWORD_SIZE+i];
2238 if(!base64_is_valid(newpwd)){
2239 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2240 // Wrong old password supplied!!
2241 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2245 infostream<<"Server: Client requests a password change from "
2246 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2248 std::string playername = player->getName();
2250 std::string checkpwd;
2251 m_script->getAuth(playername, &checkpwd, NULL);
2253 if(oldpwd != checkpwd)
2255 infostream<<"Server: invalid old password"<<std::endl;
2256 // Wrong old password supplied!!
2257 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2261 bool success = m_script->setPassword(playername, newpwd);
2263 actionstream<<player->getName()<<" changes password"<<std::endl;
2264 SendChatMessage(peer_id, L"Password change successful.");
2266 actionstream<<player->getName()<<" tries to change password but "
2267 <<"it fails"<<std::endl;
2268 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2271 else if(command == TOSERVER_PLAYERITEM)
2276 u16 item = readU16(&data[2]);
2277 playersao->setWieldIndex(item);
2279 else if(command == TOSERVER_RESPAWN)
2281 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2284 RespawnPlayer(peer_id);
2286 actionstream<<player->getName()<<" respawns at "
2287 <<PP(player->getPosition()/BS)<<std::endl;
2289 // ActiveObject is added to environment in AsyncRunStep after
2290 // the previous addition has been succesfully removed
2292 else if(command == TOSERVER_INTERACT)
2294 std::string datastring((char*)&data[2], datasize-2);
2295 std::istringstream is(datastring, std::ios_base::binary);
2301 [5] u32 length of the next item
2302 [9] serialized PointedThing
2304 0: start digging (from undersurface) or use
2305 1: stop digging (all parameters ignored)
2306 2: digging completed
2307 3: place block or item (to abovesurface)
2310 u8 action = readU8(is);
2311 u16 item_i = readU16(is);
2312 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2313 PointedThing pointed;
2314 pointed.deSerialize(tmp_is);
2316 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2317 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2321 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2322 <<" tried to interact, but is dead!"<<std::endl;
2326 v3f player_pos = playersao->getLastGoodPosition();
2328 // Update wielded item
2329 playersao->setWieldIndex(item_i);
2331 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2332 v3s16 p_under = pointed.node_undersurface;
2333 v3s16 p_above = pointed.node_abovesurface;
2335 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2336 ServerActiveObject *pointed_object = NULL;
2337 if(pointed.type == POINTEDTHING_OBJECT)
2339 pointed_object = m_env->getActiveObject(pointed.object_id);
2340 if(pointed_object == NULL)
2342 verbosestream<<"TOSERVER_INTERACT: "
2343 "pointed object is NULL"<<std::endl;
2349 v3f pointed_pos_under = player_pos;
2350 v3f pointed_pos_above = player_pos;
2351 if(pointed.type == POINTEDTHING_NODE)
2353 pointed_pos_under = intToFloat(p_under, BS);
2354 pointed_pos_above = intToFloat(p_above, BS);
2356 else if(pointed.type == POINTEDTHING_OBJECT)
2358 pointed_pos_under = pointed_object->getBasePosition();
2359 pointed_pos_above = pointed_pos_under;
2363 Check that target is reasonably close
2364 (only when digging or placing things)
2366 if(action == 0 || action == 2 || action == 3)
2368 float d = player_pos.getDistanceFrom(pointed_pos_under);
2369 float max_d = BS * 14; // Just some large enough value
2371 actionstream<<"Player "<<player->getName()
2372 <<" tried to access "<<pointed.dump()
2374 <<"d="<<d<<", max_d="<<max_d
2375 <<". ignoring."<<std::endl;
2376 // Re-send block to revert change on client-side
2377 RemoteClient *client = getClient(peer_id);
2378 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2379 client->SetBlockNotSent(blockpos);
2381 m_script->on_cheat(playersao, "interacted_too_far");
2388 Make sure the player is allowed to do it
2390 if(!checkPriv(player->getName(), "interact"))
2392 actionstream<<player->getName()<<" attempted to interact with "
2393 <<pointed.dump()<<" without 'interact' privilege"
2395 // Re-send block to revert change on client-side
2396 RemoteClient *client = getClient(peer_id);
2397 // Digging completed -> under
2399 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2400 client->SetBlockNotSent(blockpos);
2402 // Placement -> above
2404 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2405 client->SetBlockNotSent(blockpos);
2411 If something goes wrong, this player is to blame
2413 RollbackScopeActor rollback_scope(m_rollback,
2414 std::string("player:")+player->getName());
2417 0: start digging or punch object
2421 if(pointed.type == POINTEDTHING_NODE)
2424 NOTE: This can be used in the future to check if
2425 somebody is cheating, by checking the timing.
2427 MapNode n(CONTENT_IGNORE);
2430 n = m_env->getMap().getNode(p_under);
2432 catch(InvalidPositionException &e)
2434 infostream<<"Server: Not punching: Node not found."
2435 <<" Adding block to emerge queue."
2437 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2439 if(n.getContent() != CONTENT_IGNORE)
2440 m_script->node_on_punch(p_under, n, playersao, pointed);
2442 playersao->noCheatDigStart(p_under);
2444 else if(pointed.type == POINTEDTHING_OBJECT)
2446 // Skip if object has been removed
2447 if(pointed_object->m_removed)
2450 actionstream<<player->getName()<<" punches object "
2451 <<pointed.object_id<<": "
2452 <<pointed_object->getDescription()<<std::endl;
2454 ItemStack punchitem = playersao->getWieldedItem();
2455 ToolCapabilities toolcap =
2456 punchitem.getToolCapabilities(m_itemdef);
2457 v3f dir = (pointed_object->getBasePosition() -
2458 (player->getPosition() + player->getEyeOffset())
2460 float time_from_last_punch =
2461 playersao->resetTimeFromLastPunch();
2462 pointed_object->punch(dir, &toolcap, playersao,
2463 time_from_last_punch);
2471 else if(action == 1)
2476 2: Digging completed
2478 else if(action == 2)
2480 // Only digging of nodes
2481 if(pointed.type == POINTEDTHING_NODE)
2483 MapNode n(CONTENT_IGNORE);
2486 n = m_env->getMap().getNode(p_under);
2488 catch(InvalidPositionException &e)
2490 infostream<<"Server: Not finishing digging: Node not found."
2491 <<" Adding block to emerge queue."
2493 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2496 /* Cheat prevention */
2497 bool is_valid_dig = true;
2498 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2500 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2501 float nocheat_t = playersao->getNoCheatDigTime();
2502 playersao->noCheatDigEnd();
2503 // If player didn't start digging this, ignore dig
2504 if(nocheat_p != p_under){
2505 infostream<<"Server: NoCheat: "<<player->getName()
2506 <<" started digging "
2507 <<PP(nocheat_p)<<" and completed digging "
2508 <<PP(p_under)<<"; not digging."<<std::endl;
2509 is_valid_dig = false;
2511 m_script->on_cheat(playersao, "finished_unknown_dig");
2513 // Get player's wielded item
2514 ItemStack playeritem;
2515 InventoryList *mlist = playersao->getInventory()->getList("main");
2517 playeritem = mlist->getItem(playersao->getWieldIndex());
2518 ToolCapabilities playeritem_toolcap =
2519 playeritem.getToolCapabilities(m_itemdef);
2520 // Get diggability and expected digging time
2521 DigParams params = getDigParams(m_nodedef->get(n).groups,
2522 &playeritem_toolcap);
2523 // If can't dig, try hand
2524 if(!params.diggable){
2525 const ItemDefinition &hand = m_itemdef->get("");
2526 const ToolCapabilities *tp = hand.tool_capabilities;
2528 params = getDigParams(m_nodedef->get(n).groups, tp);
2530 // If can't dig, ignore dig
2531 if(!params.diggable){
2532 infostream<<"Server: NoCheat: "<<player->getName()
2533 <<" completed digging "<<PP(p_under)
2534 <<", which is not diggable with tool. not digging."
2536 is_valid_dig = false;
2538 m_script->on_cheat(playersao, "dug_unbreakable");
2540 // Check digging time
2541 // If already invalidated, we don't have to
2543 // Well not our problem then
2545 // Clean and long dig
2546 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2547 // All is good, but grab time from pool; don't care if
2548 // it's actually available
2549 playersao->getDigPool().grab(params.time);
2551 // Short or laggy dig
2552 // Try getting the time from pool
2553 else if(playersao->getDigPool().grab(params.time)){
2558 infostream<<"Server: NoCheat: "<<player->getName()
2559 <<" completed digging "<<PP(p_under)
2560 <<"too fast; not digging."<<std::endl;
2561 is_valid_dig = false;
2563 m_script->on_cheat(playersao, "dug_too_fast");
2567 /* Actually dig node */
2569 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2570 m_script->node_on_dig(p_under, n, playersao);
2572 // Send unusual result (that is, node not being removed)
2573 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2575 // Re-send block to revert change on client-side
2576 RemoteClient *client = getClient(peer_id);
2577 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2578 client->SetBlockNotSent(blockpos);
2584 3: place block or right-click object
2586 else if(action == 3)
2588 ItemStack item = playersao->getWieldedItem();
2590 // Reset build time counter
2591 if(pointed.type == POINTEDTHING_NODE &&
2592 item.getDefinition(m_itemdef).type == ITEM_NODE)
2593 getClient(peer_id)->m_time_from_building = 0.0;
2595 if(pointed.type == POINTEDTHING_OBJECT)
2597 // Right click object
2599 // Skip if object has been removed
2600 if(pointed_object->m_removed)
2603 actionstream<<player->getName()<<" right-clicks object "
2604 <<pointed.object_id<<": "
2605 <<pointed_object->getDescription()<<std::endl;
2608 pointed_object->rightClick(playersao);
2610 else if(m_script->item_OnPlace(
2611 item, playersao, pointed))
2613 // Placement was handled in lua
2615 // Apply returned ItemStack
2616 playersao->setWieldedItem(item);
2619 // If item has node placement prediction, always send the
2620 // blocks to make sure the client knows what exactly happened
2621 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2622 RemoteClient *client = getClient(peer_id);
2623 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2624 client->SetBlockNotSent(blockpos);
2625 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2626 if(blockpos2 != blockpos){
2627 client->SetBlockNotSent(blockpos2);
2635 else if(action == 4)
2637 ItemStack item = playersao->getWieldedItem();
2639 actionstream<<player->getName()<<" uses "<<item.name
2640 <<", pointing at "<<pointed.dump()<<std::endl;
2642 if(m_script->item_OnUse(
2643 item, playersao, pointed))
2645 // Apply returned ItemStack
2646 playersao->setWieldedItem(item);
2653 Catch invalid actions
2657 infostream<<"WARNING: Server: Invalid action "
2658 <<action<<std::endl;
2661 else if(command == TOSERVER_REMOVED_SOUNDS)
2663 std::string datastring((char*)&data[2], datasize-2);
2664 std::istringstream is(datastring, std::ios_base::binary);
2666 int num = readU16(is);
2667 for(int k=0; k<num; k++){
2668 s32 id = readS32(is);
2669 std::map<s32, ServerPlayingSound>::iterator i =
2670 m_playing_sounds.find(id);
2671 if(i == m_playing_sounds.end())
2673 ServerPlayingSound &psound = i->second;
2674 psound.clients.erase(peer_id);
2675 if(psound.clients.size() == 0)
2676 m_playing_sounds.erase(i++);
2679 else if(command == TOSERVER_NODEMETA_FIELDS)
2681 std::string datastring((char*)&data[2], datasize-2);
2682 std::istringstream is(datastring, std::ios_base::binary);
2684 v3s16 p = readV3S16(is);
2685 std::string formname = deSerializeString(is);
2686 int num = readU16(is);
2687 std::map<std::string, std::string> fields;
2688 for(int k=0; k<num; k++){
2689 std::string fieldname = deSerializeString(is);
2690 std::string fieldvalue = deSerializeLongString(is);
2691 fields[fieldname] = fieldvalue;
2694 // If something goes wrong, this player is to blame
2695 RollbackScopeActor rollback_scope(m_rollback,
2696 std::string("player:")+player->getName());
2698 // Check the target node for rollback data; leave others unnoticed
2699 RollbackNode rn_old(&m_env->getMap(), p, this);
2701 m_script->node_on_receive_fields(p, formname, fields,playersao);
2703 // Report rollback data
2704 RollbackNode rn_new(&m_env->getMap(), p, this);
2705 if(rollback() && rn_new != rn_old){
2706 RollbackAction action;
2707 action.setSetNode(p, rn_old, rn_new);
2708 rollback()->reportAction(action);
2711 else if(command == TOSERVER_INVENTORY_FIELDS)
2713 std::string datastring((char*)&data[2], datasize-2);
2714 std::istringstream is(datastring, std::ios_base::binary);
2716 std::string formname = deSerializeString(is);
2717 int num = readU16(is);
2718 std::map<std::string, std::string> fields;
2719 for(int k=0; k<num; k++){
2720 std::string fieldname = deSerializeString(is);
2721 std::string fieldvalue = deSerializeLongString(is);
2722 fields[fieldname] = fieldvalue;
2725 m_script->on_playerReceiveFields(playersao, formname, fields);
2729 infostream<<"Server::ProcessData(): Ignoring "
2730 "unknown command "<<command<<std::endl;
2734 catch(SendFailedException &e)
2736 errorstream<<"Server::ProcessData(): SendFailedException: "
2742 void Server::setTimeOfDay(u32 time)
2744 m_env->setTimeOfDay(time);
2745 m_time_of_day_send_timer = 0;
2748 void Server::onMapEditEvent(MapEditEvent *event)
2750 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2751 if(m_ignore_map_edit_events)
2753 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2755 MapEditEvent *e = event->clone();
2756 m_unsent_map_edit_queue.push_back(e);
2759 Inventory* Server::getInventory(const InventoryLocation &loc)
2762 case InventoryLocation::UNDEFINED:
2765 case InventoryLocation::CURRENT_PLAYER:
2768 case InventoryLocation::PLAYER:
2770 Player *player = m_env->getPlayer(loc.name.c_str());
2773 PlayerSAO *playersao = player->getPlayerSAO();
2776 return playersao->getInventory();
2779 case InventoryLocation::NODEMETA:
2781 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2784 return meta->getInventory();
2787 case InventoryLocation::DETACHED:
2789 if(m_detached_inventories.count(loc.name) == 0)
2791 return m_detached_inventories[loc.name];
2799 void Server::setInventoryModified(const InventoryLocation &loc)
2802 case InventoryLocation::UNDEFINED:
2805 case InventoryLocation::PLAYER:
2807 Player *player = m_env->getPlayer(loc.name.c_str());
2810 PlayerSAO *playersao = player->getPlayerSAO();
2813 playersao->m_inventory_not_sent = true;
2814 playersao->m_wielded_item_not_sent = true;
2817 case InventoryLocation::NODEMETA:
2819 v3s16 blockpos = getNodeBlockPos(loc.p);
2821 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2823 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2825 setBlockNotSent(blockpos);
2828 case InventoryLocation::DETACHED:
2830 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2838 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2840 std::list<u16> clients = m_clients.getClientIDs();
2842 // Set the modified blocks unsent for all the clients
2843 for (std::list<u16>::iterator
2844 i = clients.begin();
2845 i != clients.end(); ++i) {
2846 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2848 client->SetBlocksNotSent(block);
2853 void Server::peerAdded(con::Peer *peer)
2855 DSTACK(__FUNCTION_NAME);
2856 verbosestream<<"Server::peerAdded(): peer->id="
2857 <<peer->id<<std::endl;
2860 c.type = con::PEER_ADDED;
2861 c.peer_id = peer->id;
2863 m_peer_change_queue.push_back(c);
2866 void Server::deletingPeer(con::Peer *peer, bool timeout)
2868 DSTACK(__FUNCTION_NAME);
2869 verbosestream<<"Server::deletingPeer(): peer->id="
2870 <<peer->id<<", timeout="<<timeout<<std::endl;
2872 m_clients.event(peer->id,Disconnect);
2874 c.type = con::PEER_REMOVED;
2875 c.peer_id = peer->id;
2876 c.timeout = timeout;
2877 m_peer_change_queue.push_back(c);
2880 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
2882 *retval = m_con.getPeerStat(peer_id,type);
2883 if (*retval == -1) return false;
2887 bool Server::getClientInfo(
2896 std::string* vers_string
2899 *state = m_clients.getClientState(peer_id);
2901 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,Invalid);
2903 if (client == NULL) {
2908 *uptime = client->uptime();
2909 *ser_vers = client->serialization_version;
2910 *prot_vers = client->net_proto_version;
2912 *major = client->getMajor();
2913 *minor = client->getMinor();
2914 *patch = client->getPatch();
2915 *vers_string = client->getPatch();
2922 void Server::handlePeerChanges()
2924 while(m_peer_change_queue.size() > 0)
2926 con::PeerChange c = m_peer_change_queue.pop_front();
2928 verbosestream<<"Server: Handling peer change: "
2929 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2934 case con::PEER_ADDED:
2935 m_clients.CreateClient(c.peer_id);
2938 case con::PEER_REMOVED:
2939 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2943 assert("Invalid peer change event received!" == 0);
2949 void Server::SendMovement(u16 peer_id)
2951 DSTACK(__FUNCTION_NAME);
2952 std::ostringstream os(std::ios_base::binary);
2954 writeU16(os, TOCLIENT_MOVEMENT);
2955 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2956 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2957 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2958 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2959 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2960 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2961 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2962 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2963 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2964 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2965 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2966 writeF1000(os, g_settings->getFloat("movement_gravity"));
2969 std::string s = os.str();
2970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2972 m_clients.send(peer_id, 0, data, true);
2975 void Server::SendHP(u16 peer_id, u8 hp)
2977 DSTACK(__FUNCTION_NAME);
2978 std::ostringstream os(std::ios_base::binary);
2980 writeU16(os, TOCLIENT_HP);
2984 std::string s = os.str();
2985 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2987 m_clients.send(peer_id, 0, data, true);
2990 void Server::SendBreath(u16 peer_id, u16 breath)
2992 DSTACK(__FUNCTION_NAME);
2993 std::ostringstream os(std::ios_base::binary);
2995 writeU16(os, TOCLIENT_BREATH);
2996 writeU16(os, breath);
2999 std::string s = os.str();
3000 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3002 m_clients.send(peer_id, 0, data, true);
3005 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
3007 DSTACK(__FUNCTION_NAME);
3008 std::ostringstream os(std::ios_base::binary);
3010 writeU16(os, TOCLIENT_ACCESS_DENIED);
3011 os<<serializeWideString(reason);
3014 std::string s = os.str();
3015 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3017 m_clients.send(peer_id, 0, data, true);
3020 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
3021 v3f camera_point_target)
3023 DSTACK(__FUNCTION_NAME);
3024 std::ostringstream os(std::ios_base::binary);
3026 writeU16(os, TOCLIENT_DEATHSCREEN);
3027 writeU8(os, set_camera_point_target);
3028 writeV3F1000(os, camera_point_target);
3031 std::string s = os.str();
3032 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3034 m_clients.send(peer_id, 0, data, true);
3037 void Server::SendItemDef(u16 peer_id,
3038 IItemDefManager *itemdef, u16 protocol_version)
3040 DSTACK(__FUNCTION_NAME);
3041 std::ostringstream os(std::ios_base::binary);
3045 u32 length of the next item
3046 zlib-compressed serialized ItemDefManager
3048 writeU16(os, TOCLIENT_ITEMDEF);
3049 std::ostringstream tmp_os(std::ios::binary);
3050 itemdef->serialize(tmp_os, protocol_version);
3051 std::ostringstream tmp_os2(std::ios::binary);
3052 compressZlib(tmp_os.str(), tmp_os2);
3053 os<<serializeLongString(tmp_os2.str());
3056 std::string s = os.str();
3057 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3058 <<"): size="<<s.size()<<std::endl;
3059 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3061 m_clients.send(peer_id, 0, data, true);
3064 void Server::SendNodeDef(u16 peer_id,
3065 INodeDefManager *nodedef, u16 protocol_version)
3067 DSTACK(__FUNCTION_NAME);
3068 std::ostringstream os(std::ios_base::binary);
3072 u32 length of the next item
3073 zlib-compressed serialized NodeDefManager
3075 writeU16(os, TOCLIENT_NODEDEF);
3076 std::ostringstream tmp_os(std::ios::binary);
3077 nodedef->serialize(tmp_os, protocol_version);
3078 std::ostringstream tmp_os2(std::ios::binary);
3079 compressZlib(tmp_os.str(), tmp_os2);
3080 os<<serializeLongString(tmp_os2.str());
3083 std::string s = os.str();
3084 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3085 <<"): size="<<s.size()<<std::endl;
3086 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3088 m_clients.send(peer_id, 0, data, true);
3092 Non-static send methods
3095 void Server::SendInventory(u16 peer_id)
3097 DSTACK(__FUNCTION_NAME);
3099 PlayerSAO *playersao = getPlayerSAO(peer_id);
3102 playersao->m_inventory_not_sent = false;
3108 std::ostringstream os;
3109 playersao->getInventory()->serialize(os);
3111 std::string s = os.str();
3113 SharedBuffer<u8> data(s.size()+2);
3114 writeU16(&data[0], TOCLIENT_INVENTORY);
3115 memcpy(&data[2], s.c_str(), s.size());
3118 m_clients.send(peer_id, 0, data, true);
3121 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3123 DSTACK(__FUNCTION_NAME);
3125 std::ostringstream os(std::ios_base::binary);
3129 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3130 os.write((char*)buf, 2);
3133 writeU16(buf, message.size());
3134 os.write((char*)buf, 2);
3137 for(u32 i=0; i<message.size(); i++)
3141 os.write((char*)buf, 2);
3145 std::string s = os.str();
3146 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3148 if (peer_id != PEER_ID_INEXISTENT)
3151 m_clients.send(peer_id, 0, data, true);
3155 m_clients.sendToAll(0,data,true);
3159 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
3160 const std::string &formname)
3162 DSTACK(__FUNCTION_NAME);
3164 std::ostringstream os(std::ios_base::binary);
3168 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3169 os.write((char*)buf, 2);
3170 os<<serializeLongString(formspec);
3171 os<<serializeString(formname);
3174 std::string s = os.str();
3175 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3177 m_clients.send(peer_id, 0, data, true);
3180 // Spawns a particle on peer with peer_id
3181 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3182 float expirationtime, float size, bool collisiondetection,
3183 bool vertical, std::string texture)
3185 DSTACK(__FUNCTION_NAME);
3187 std::ostringstream os(std::ios_base::binary);
3188 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3189 writeV3F1000(os, pos);
3190 writeV3F1000(os, velocity);
3191 writeV3F1000(os, acceleration);
3192 writeF1000(os, expirationtime);
3193 writeF1000(os, size);
3194 writeU8(os, collisiondetection);
3195 os<<serializeLongString(texture);
3196 writeU8(os, vertical);
3199 std::string s = os.str();
3200 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3202 if (peer_id != PEER_ID_INEXISTENT)
3205 m_clients.send(peer_id, 0, data, true);
3209 m_clients.sendToAll(0,data,true);
3213 // Adds a ParticleSpawner on peer with peer_id
3214 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3215 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3216 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3218 DSTACK(__FUNCTION_NAME);
3220 std::ostringstream os(std::ios_base::binary);
3221 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3223 writeU16(os, amount);
3224 writeF1000(os, spawntime);
3225 writeV3F1000(os, minpos);
3226 writeV3F1000(os, maxpos);
3227 writeV3F1000(os, minvel);
3228 writeV3F1000(os, maxvel);
3229 writeV3F1000(os, minacc);
3230 writeV3F1000(os, maxacc);
3231 writeF1000(os, minexptime);
3232 writeF1000(os, maxexptime);
3233 writeF1000(os, minsize);
3234 writeF1000(os, maxsize);
3235 writeU8(os, collisiondetection);
3236 os<<serializeLongString(texture);
3238 writeU8(os, vertical);
3241 std::string s = os.str();
3242 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3244 if (peer_id != PEER_ID_INEXISTENT)
3247 m_clients.send(peer_id, 0, data, true);
3250 m_clients.sendToAll(0,data,true);
3254 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3256 DSTACK(__FUNCTION_NAME);
3258 std::ostringstream os(std::ios_base::binary);
3259 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3264 std::string s = os.str();
3265 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3267 if (peer_id != PEER_ID_INEXISTENT) {
3269 m_clients.send(peer_id, 0, data, true);
3272 m_clients.sendToAll(0,data,true);
3277 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3279 std::ostringstream os(std::ios_base::binary);
3282 writeU16(os, TOCLIENT_HUDADD);
3284 writeU8(os, (u8)form->type);
3285 writeV2F1000(os, form->pos);
3286 os << serializeString(form->name);
3287 writeV2F1000(os, form->scale);
3288 os << serializeString(form->text);
3289 writeU32(os, form->number);
3290 writeU32(os, form->item);
3291 writeU32(os, form->dir);
3292 writeV2F1000(os, form->align);
3293 writeV2F1000(os, form->offset);
3294 writeV3F1000(os, form->world_pos);
3295 writeV2S32(os,form->size);
3298 std::string s = os.str();
3299 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3301 m_clients.send(peer_id, 1, data, true);
3304 void Server::SendHUDRemove(u16 peer_id, u32 id)
3306 std::ostringstream os(std::ios_base::binary);
3309 writeU16(os, TOCLIENT_HUDRM);
3313 std::string s = os.str();
3314 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3317 m_clients.send(peer_id, 1, data, true);
3320 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3322 std::ostringstream os(std::ios_base::binary);
3325 writeU16(os, TOCLIENT_HUDCHANGE);
3327 writeU8(os, (u8)stat);
3330 case HUD_STAT_SCALE:
3331 case HUD_STAT_ALIGN:
3332 case HUD_STAT_OFFSET:
3333 writeV2F1000(os, *(v2f *)value);
3337 os << serializeString(*(std::string *)value);
3339 case HUD_STAT_WORLD_POS:
3340 writeV3F1000(os, *(v3f *)value);
3343 writeV2S32(os,*(v2s32 *)value);
3345 case HUD_STAT_NUMBER:
3349 writeU32(os, *(u32 *)value);
3354 std::string s = os.str();
3355 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3357 m_clients.send(peer_id, 0, data, true);
3360 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3362 std::ostringstream os(std::ios_base::binary);
3365 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3367 //////////////////////////// compatibility code to be removed //////////////
3368 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
3369 ////////////////////////////////////////////////////////////////////////////
3370 writeU32(os, flags);
3374 std::string s = os.str();
3375 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3377 m_clients.send(peer_id, 0, data, true);
3380 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3382 std::ostringstream os(std::ios_base::binary);
3385 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3386 writeU16(os, param);
3387 os<<serializeString(value);
3390 std::string s = os.str();
3391 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3393 m_clients.send(peer_id, 0, data, true);
3396 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3397 const std::string &type, const std::vector<std::string> ¶ms)
3399 std::ostringstream os(std::ios_base::binary);
3402 writeU16(os, TOCLIENT_SET_SKY);
3403 writeARGB8(os, bgcolor);
3404 os<<serializeString(type);
3405 writeU16(os, params.size());
3406 for(size_t i=0; i<params.size(); i++)
3407 os<<serializeString(params[i]);
3410 std::string s = os.str();
3411 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3413 m_clients.send(peer_id, 0, data, true);
3416 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
3419 std::ostringstream os(std::ios_base::binary);
3422 writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
3423 writeU8(os, do_override);
3424 writeU16(os, ratio*65535);
3427 std::string s = os.str();
3428 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3430 m_clients.send(peer_id, 0, data, true);
3433 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3435 DSTACK(__FUNCTION_NAME);
3438 SharedBuffer<u8> data(2+2+4);
3439 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3440 writeU16(&data[2], time);
3441 writeF1000(&data[4], time_speed);
3443 if (peer_id == PEER_ID_INEXISTENT) {
3444 m_clients.sendToAll(0,data,true);
3448 m_clients.send(peer_id, 0, data, true);
3452 void Server::SendPlayerHP(u16 peer_id)
3454 DSTACK(__FUNCTION_NAME);
3455 PlayerSAO *playersao = getPlayerSAO(peer_id);
3457 playersao->m_hp_not_sent = false;
3458 SendHP(peer_id, playersao->getHP());
3459 m_script->player_event(playersao,"health_changed");
3461 // Send to other clients
3462 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3463 ActiveObjectMessage aom(playersao->getId(), true, str);
3464 playersao->m_messages_out.push_back(aom);
3467 void Server::SendPlayerBreath(u16 peer_id)
3469 DSTACK(__FUNCTION_NAME);
3470 PlayerSAO *playersao = getPlayerSAO(peer_id);
3472 playersao->m_breath_not_sent = false;
3473 m_script->player_event(playersao,"breath_changed");
3474 SendBreath(peer_id, playersao->getBreath());
3477 void Server::SendMovePlayer(u16 peer_id)
3479 DSTACK(__FUNCTION_NAME);
3480 Player *player = m_env->getPlayer(peer_id);
3483 std::ostringstream os(std::ios_base::binary);
3484 writeU16(os, TOCLIENT_MOVE_PLAYER);
3485 writeV3F1000(os, player->getPosition());
3486 writeF1000(os, player->getPitch());
3487 writeF1000(os, player->getYaw());
3490 v3f pos = player->getPosition();
3491 f32 pitch = player->getPitch();
3492 f32 yaw = player->getYaw();
3493 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3494 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3501 std::string s = os.str();
3502 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504 m_clients.send(peer_id, 0, data, true);
3507 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
3509 std::ostringstream os(std::ios_base::binary);
3511 writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS);
3512 writeV2S32(os, animation_frames[0]);
3513 writeV2S32(os, animation_frames[1]);
3514 writeV2S32(os, animation_frames[2]);
3515 writeV2S32(os, animation_frames[3]);
3516 writeF1000(os, animation_speed);
3519 std::string s = os.str();
3520 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3522 m_clients.send(peer_id, 0, data, true);
3525 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
3527 std::ostringstream os(std::ios_base::binary);
3529 writeU16(os, TOCLIENT_EYE_OFFSET);
3530 writeV3F1000(os, first);
3531 writeV3F1000(os, third);
3534 std::string s = os.str();
3535 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3537 m_clients.send(peer_id, 0, data, true);
3539 void Server::SendPlayerPrivileges(u16 peer_id)
3541 Player *player = m_env->getPlayer(peer_id);
3543 if(player->peer_id == PEER_ID_INEXISTENT)
3546 std::set<std::string> privs;
3547 m_script->getAuth(player->getName(), NULL, &privs);
3549 std::ostringstream os(std::ios_base::binary);
3550 writeU16(os, TOCLIENT_PRIVILEGES);
3551 writeU16(os, privs.size());
3552 for(std::set<std::string>::const_iterator i = privs.begin();
3553 i != privs.end(); i++){
3554 os<<serializeString(*i);
3558 std::string s = os.str();
3559 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3561 m_clients.send(peer_id, 0, data, true);
3564 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3566 Player *player = m_env->getPlayer(peer_id);
3568 if(player->peer_id == PEER_ID_INEXISTENT)
3571 std::ostringstream os(std::ios_base::binary);
3572 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3573 os<<serializeLongString(player->inventory_formspec);
3576 std::string s = os.str();
3577 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3579 m_clients.send(peer_id, 0, data, true);
3582 s32 Server::playSound(const SimpleSoundSpec &spec,
3583 const ServerSoundParams ¶ms)
3585 // Find out initial position of sound
3586 bool pos_exists = false;
3587 v3f pos = params.getPos(m_env, &pos_exists);
3588 // If position is not found while it should be, cancel sound
3589 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3592 // Filter destination clients
3593 std::list<u16> dst_clients;
3594 if(params.to_player != "")
3596 Player *player = m_env->getPlayer(params.to_player.c_str());
3598 infostream<<"Server::playSound: Player \""<<params.to_player
3599 <<"\" not found"<<std::endl;
3602 if(player->peer_id == PEER_ID_INEXISTENT){
3603 infostream<<"Server::playSound: Player \""<<params.to_player
3604 <<"\" not connected"<<std::endl;
3607 dst_clients.push_back(player->peer_id);
3611 std::list<u16> clients = m_clients.getClientIDs();
3613 for(std::list<u16>::iterator
3614 i = clients.begin(); i != clients.end(); ++i)
3616 Player *player = m_env->getPlayer(*i);
3620 if(player->getPosition().getDistanceFrom(pos) >
3621 params.max_hear_distance)
3624 dst_clients.push_back(*i);
3627 if(dst_clients.size() == 0)
3631 s32 id = m_next_sound_id++;
3632 // The sound will exist as a reference in m_playing_sounds
3633 m_playing_sounds[id] = ServerPlayingSound();
3634 ServerPlayingSound &psound = m_playing_sounds[id];
3635 psound.params = params;
3636 for(std::list<u16>::iterator i = dst_clients.begin();
3637 i != dst_clients.end(); i++)
3638 psound.clients.insert(*i);
3640 std::ostringstream os(std::ios_base::binary);
3641 writeU16(os, TOCLIENT_PLAY_SOUND);
3643 os<<serializeString(spec.name);
3644 writeF1000(os, spec.gain * params.gain);
3645 writeU8(os, params.type);
3646 writeV3F1000(os, pos);
3647 writeU16(os, params.object);
3648 writeU8(os, params.loop);
3650 std::string s = os.str();
3651 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3653 for(std::list<u16>::iterator i = dst_clients.begin();
3654 i != dst_clients.end(); i++){
3656 m_clients.send(*i, 0, data, true);
3660 void Server::stopSound(s32 handle)
3662 // Get sound reference
3663 std::map<s32, ServerPlayingSound>::iterator i =
3664 m_playing_sounds.find(handle);
3665 if(i == m_playing_sounds.end())
3667 ServerPlayingSound &psound = i->second;
3669 std::ostringstream os(std::ios_base::binary);
3670 writeU16(os, TOCLIENT_STOP_SOUND);
3671 writeS32(os, handle);
3673 std::string s = os.str();
3674 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3676 for(std::set<u16>::iterator i = psound.clients.begin();
3677 i != psound.clients.end(); i++){
3679 m_clients.send(*i, 0, data, true);
3681 // Remove sound reference
3682 m_playing_sounds.erase(i);
3685 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3686 std::list<u16> *far_players, float far_d_nodes)
3688 float maxd = far_d_nodes*BS;
3689 v3f p_f = intToFloat(p, BS);
3693 SharedBuffer<u8> reply(replysize);
3694 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3695 writeS16(&reply[2], p.X);
3696 writeS16(&reply[4], p.Y);
3697 writeS16(&reply[6], p.Z);
3699 std::list<u16> clients = m_clients.getClientIDs();
3700 for(std::list<u16>::iterator
3701 i = clients.begin();
3702 i != clients.end(); ++i)
3707 Player *player = m_env->getPlayer(*i);
3710 // If player is far away, only set modified blocks not sent
3711 v3f player_pos = player->getPosition();
3712 if(player_pos.getDistanceFrom(p_f) > maxd)
3714 far_players->push_back(*i);
3721 m_clients.send(*i, 0, reply, true);
3725 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3726 std::list<u16> *far_players, float far_d_nodes,
3727 bool remove_metadata)
3729 float maxd = far_d_nodes*BS;
3730 v3f p_f = intToFloat(p, BS);
3732 std::list<u16> clients = m_clients.getClientIDs();
3733 for(std::list<u16>::iterator
3734 i = clients.begin();
3735 i != clients.end(); ++i)
3741 Player *player = m_env->getPlayer(*i);
3744 // If player is far away, only set modified blocks not sent
3745 v3f player_pos = player->getPosition();
3746 if(player_pos.getDistanceFrom(p_f) > maxd)
3748 far_players->push_back(*i);
3753 SharedBuffer<u8> reply(0);
3755 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3759 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3760 reply = SharedBuffer<u8>(replysize);
3761 writeU16(&reply[0], TOCLIENT_ADDNODE);
3762 writeS16(&reply[2], p.X);
3763 writeS16(&reply[4], p.Y);
3764 writeS16(&reply[6], p.Z);
3765 n.serialize(&reply[8], client->serialization_version);
3766 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3767 writeU8(&reply[index], remove_metadata ? 0 : 1);
3769 if (!remove_metadata) {
3770 if (client->net_proto_version <= 21) {
3771 // Old clients always clear metadata; fix it
3772 // by sending the full block again.
3773 client->SetBlockNotSent(p);
3780 if (reply.getSize() > 0)
3781 m_clients.send(*i, 0, reply, true);
3785 void Server::setBlockNotSent(v3s16 p)
3787 std::list<u16> clients = m_clients.getClientIDs();
3789 for(std::list<u16>::iterator
3790 i = clients.begin();
3791 i != clients.end(); ++i)
3793 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3794 client->SetBlockNotSent(p);
3799 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3801 DSTACK(__FUNCTION_NAME);
3803 v3s16 p = block->getPos();
3807 bool completely_air = true;
3808 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3809 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3810 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3812 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3814 completely_air = false;
3815 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3820 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3822 infostream<<"[completely air] ";
3823 infostream<<std::endl;
3827 Create a packet with the block in the right format
3830 std::ostringstream os(std::ios_base::binary);
3831 block->serialize(os, ver, false);
3832 block->serializeNetworkSpecific(os, net_proto_version);
3833 std::string s = os.str();
3834 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3836 u32 replysize = 8 + blockdata.getSize();
3837 SharedBuffer<u8> reply(replysize);
3838 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3839 writeS16(&reply[2], p.X);
3840 writeS16(&reply[4], p.Y);
3841 writeS16(&reply[6], p.Z);
3842 memcpy(&reply[8], *blockdata, blockdata.getSize());
3844 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3845 <<": \tpacket size: "<<replysize<<std::endl;*/
3850 m_clients.send(peer_id, 2, reply, true);
3853 void Server::SendBlocks(float dtime)
3855 DSTACK(__FUNCTION_NAME);
3857 JMutexAutoLock envlock(m_env_mutex);
3858 //TODO check if one big lock could be faster then multiple small ones
3860 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3862 std::vector<PrioritySortedBlockTransfer> queue;
3864 s32 total_sending = 0;
3867 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3869 std::list<u16> clients = m_clients.getClientIDs();
3872 for(std::list<u16>::iterator
3873 i = clients.begin();
3874 i != clients.end(); ++i)
3876 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3881 total_sending += client->SendingCount();
3882 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3888 // Lowest priority number comes first.
3889 // Lowest is most important.
3890 std::sort(queue.begin(), queue.end());
3893 for(u32 i=0; i<queue.size(); i++)
3895 //TODO: Calculate limit dynamically
3896 if(total_sending >= g_settings->getS32
3897 ("max_simultaneous_block_sends_server_total"))
3900 PrioritySortedBlockTransfer q = queue[i];
3902 MapBlock *block = NULL;
3905 block = m_env->getMap().getBlockNoCreate(q.pos);
3907 catch(InvalidPositionException &e)
3912 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3917 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3919 client->SentBlock(q.pos);
3925 void Server::fillMediaCache()
3927 DSTACK(__FUNCTION_NAME);
3929 infostream<<"Server: Calculating media file checksums"<<std::endl;
3931 // Collect all media file paths
3932 std::list<std::string> paths;
3933 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3934 i != m_mods.end(); i++){
3935 const ModSpec &mod = *i;
3936 paths.push_back(mod.path + DIR_DELIM + "textures");
3937 paths.push_back(mod.path + DIR_DELIM + "sounds");
3938 paths.push_back(mod.path + DIR_DELIM + "media");
3939 paths.push_back(mod.path + DIR_DELIM + "models");
3941 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3943 // Collect media file information from paths into cache
3944 for(std::list<std::string>::iterator i = paths.begin();
3945 i != paths.end(); i++)
3947 std::string mediapath = *i;
3948 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3949 for(u32 j=0; j<dirlist.size(); j++){
3950 if(dirlist[j].dir) // Ignode dirs
3952 std::string filename = dirlist[j].name;
3953 // If name contains illegal characters, ignore the file
3954 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3955 infostream<<"Server: ignoring illegal file name: \""
3956 <<filename<<"\""<<std::endl;
3959 // If name is not in a supported format, ignore it
3960 const char *supported_ext[] = {
3961 ".png", ".jpg", ".bmp", ".tga",
3962 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3964 ".x", ".b3d", ".md2", ".obj",
3967 if(removeStringEnd(filename, supported_ext) == ""){
3968 infostream<<"Server: ignoring unsupported file extension: \""
3969 <<filename<<"\""<<std::endl;
3972 // Ok, attempt to load the file and add to cache
3973 std::string filepath = mediapath + DIR_DELIM + filename;
3975 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3976 if(fis.good() == false){
3977 errorstream<<"Server::fillMediaCache(): Could not open \""
3978 <<filename<<"\" for reading"<<std::endl;
3981 std::ostringstream tmp_os(std::ios_base::binary);
3985 fis.read(buf, 1024);
3986 std::streamsize len = fis.gcount();
3987 tmp_os.write(buf, len);
3996 errorstream<<"Server::fillMediaCache(): Failed to read \""
3997 <<filename<<"\""<<std::endl;
4000 if(tmp_os.str().length() == 0){
4001 errorstream<<"Server::fillMediaCache(): Empty file \""
4002 <<filepath<<"\""<<std::endl;
4007 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4009 unsigned char *digest = sha1.getDigest();
4010 std::string sha1_base64 = base64_encode(digest, 20);
4011 std::string sha1_hex = hex_encode((char*)digest, 20);
4015 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4016 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4021 struct SendableMediaAnnouncement
4024 std::string sha1_digest;
4026 SendableMediaAnnouncement(const std::string &name_="",
4027 const std::string &sha1_digest_=""):
4029 sha1_digest(sha1_digest_)
4033 void Server::sendMediaAnnouncement(u16 peer_id)
4035 DSTACK(__FUNCTION_NAME);
4037 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4040 std::list<SendableMediaAnnouncement> file_announcements;
4042 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4043 i != m_media.end(); i++){
4045 file_announcements.push_back(
4046 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4050 std::ostringstream os(std::ios_base::binary);
4058 u16 length of sha1_digest
4063 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4064 writeU16(os, file_announcements.size());
4066 for(std::list<SendableMediaAnnouncement>::iterator
4067 j = file_announcements.begin();
4068 j != file_announcements.end(); ++j){
4069 os<<serializeString(j->name);
4070 os<<serializeString(j->sha1_digest);
4072 os<<serializeString(g_settings->get("remote_media"));
4075 std::string s = os.str();
4076 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4079 m_clients.send(peer_id, 0, data, true);
4082 struct SendableMedia
4088 SendableMedia(const std::string &name_="", const std::string &path_="",
4089 const std::string &data_=""):
4096 void Server::sendRequestedMedia(u16 peer_id,
4097 const std::list<std::string> &tosend)
4099 DSTACK(__FUNCTION_NAME);
4101 verbosestream<<"Server::sendRequestedMedia(): "
4102 <<"Sending files to client"<<std::endl;
4106 // Put 5kB in one bunch (this is not accurate)
4107 u32 bytes_per_bunch = 5000;
4109 std::vector< std::list<SendableMedia> > file_bunches;
4110 file_bunches.push_back(std::list<SendableMedia>());
4112 u32 file_size_bunch_total = 0;
4114 for(std::list<std::string>::const_iterator i = tosend.begin();
4115 i != tosend.end(); ++i)
4117 const std::string &name = *i;
4119 if(m_media.find(name) == m_media.end()){
4120 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4121 <<"unknown file \""<<(name)<<"\""<<std::endl;
4125 //TODO get path + name
4126 std::string tpath = m_media[name].path;
4129 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4130 if(fis.good() == false){
4131 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4132 <<tpath<<"\" for reading"<<std::endl;
4135 std::ostringstream tmp_os(std::ios_base::binary);
4139 fis.read(buf, 1024);
4140 std::streamsize len = fis.gcount();
4141 tmp_os.write(buf, len);
4142 file_size_bunch_total += len;
4151 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4152 <<name<<"\""<<std::endl;
4155 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4156 <<tname<<"\""<<std::endl;*/
4158 file_bunches[file_bunches.size()-1].push_back(
4159 SendableMedia(name, tpath, tmp_os.str()));
4161 // Start next bunch if got enough data
4162 if(file_size_bunch_total >= bytes_per_bunch){
4163 file_bunches.push_back(std::list<SendableMedia>());
4164 file_size_bunch_total = 0;
4169 /* Create and send packets */
4171 u32 num_bunches = file_bunches.size();
4172 for(u32 i=0; i<num_bunches; i++)
4174 std::ostringstream os(std::ios_base::binary);
4178 u16 total number of texture bunches
4179 u16 index of this bunch
4180 u32 number of files in this bunch
4189 writeU16(os, TOCLIENT_MEDIA);
4190 writeU16(os, num_bunches);
4192 writeU32(os, file_bunches[i].size());
4194 for(std::list<SendableMedia>::iterator
4195 j = file_bunches[i].begin();
4196 j != file_bunches[i].end(); ++j){
4197 os<<serializeString(j->name);
4198 os<<serializeLongString(j->data);
4202 std::string s = os.str();
4203 verbosestream<<"Server::sendRequestedMedia(): bunch "
4204 <<i<<"/"<<num_bunches
4205 <<" files="<<file_bunches[i].size()
4206 <<" size=" <<s.size()<<std::endl;
4207 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4209 m_clients.send(peer_id, 2, data, true);
4213 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4215 if(m_detached_inventories.count(name) == 0){
4216 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4219 Inventory *inv = m_detached_inventories[name];
4221 std::ostringstream os(std::ios_base::binary);
4222 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4223 os<<serializeString(name);
4227 std::string s = os.str();
4228 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4230 if (peer_id != PEER_ID_INEXISTENT)
4233 m_clients.send(peer_id, 0, data, true);
4237 m_clients.sendToAll(0,data,true);
4241 void Server::sendDetachedInventories(u16 peer_id)
4243 DSTACK(__FUNCTION_NAME);
4245 for(std::map<std::string, Inventory*>::iterator
4246 i = m_detached_inventories.begin();
4247 i != m_detached_inventories.end(); i++){
4248 const std::string &name = i->first;
4249 //Inventory *inv = i->second;
4250 sendDetachedInventory(name, peer_id);
4258 void Server::DiePlayer(u16 peer_id)
4260 DSTACK(__FUNCTION_NAME);
4262 PlayerSAO *playersao = getPlayerSAO(peer_id);
4265 infostream<<"Server::DiePlayer(): Player "
4266 <<playersao->getPlayer()->getName()
4267 <<" dies"<<std::endl;
4269 playersao->setHP(0);
4271 // Trigger scripted stuff
4272 m_script->on_dieplayer(playersao);
4274 SendPlayerHP(peer_id);
4275 SendDeathscreen(peer_id, false, v3f(0,0,0));
4278 void Server::RespawnPlayer(u16 peer_id)
4280 DSTACK(__FUNCTION_NAME);
4282 PlayerSAO *playersao = getPlayerSAO(peer_id);
4285 infostream<<"Server::RespawnPlayer(): Player "
4286 <<playersao->getPlayer()->getName()
4287 <<" respawns"<<std::endl;
4289 playersao->setHP(PLAYER_MAX_HP);
4291 bool repositioned = m_script->on_respawnplayer(playersao);
4293 v3f pos = findSpawnPos(m_env->getServerMap());
4294 playersao->setPos(pos);
4298 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4300 DSTACK(__FUNCTION_NAME);
4302 SendAccessDenied(peer_id, reason);
4303 m_clients.event(peer_id,SetDenied);
4304 m_con.DisconnectPeer(peer_id);
4307 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4309 DSTACK(__FUNCTION_NAME);
4310 std::wstring message;
4313 Clear references to playing sounds
4315 for(std::map<s32, ServerPlayingSound>::iterator
4316 i = m_playing_sounds.begin();
4317 i != m_playing_sounds.end();)
4319 ServerPlayingSound &psound = i->second;
4320 psound.clients.erase(peer_id);
4321 if(psound.clients.size() == 0)
4322 m_playing_sounds.erase(i++);
4327 Player *player = m_env->getPlayer(peer_id);
4329 // Collect information about leaving in chat
4331 if(player != NULL && reason != CDR_DENY)
4333 std::wstring name = narrow_to_wide(player->getName());
4336 message += L" left the game.";
4337 if(reason == CDR_TIMEOUT)
4338 message += L" (timed out)";
4342 /* Run scripts and remove from environment */
4346 PlayerSAO *playersao = player->getPlayerSAO();
4349 m_script->on_leaveplayer(playersao);
4351 playersao->disconnected();
4359 if(player != NULL && reason != CDR_DENY)
4361 std::ostringstream os(std::ios_base::binary);
4362 std::list<u16> clients = m_clients.getClientIDs();
4364 for(std::list<u16>::iterator
4365 i = clients.begin();
4366 i != clients.end(); ++i)
4369 Player *player = m_env->getPlayer(*i);
4372 // Get name of player
4373 os<<player->getName()<<" ";
4376 actionstream<<player->getName()<<" "
4377 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4378 <<" List of players: "<<os.str()<<std::endl;
4382 JMutexAutoLock env_lock(m_env_mutex);
4383 m_clients.DeleteClient(peer_id);
4387 // Send leave chat message to all remaining clients
4388 if(message.length() != 0)
4389 SendChatMessage(PEER_ID_INEXISTENT,message);
4392 void Server::UpdateCrafting(u16 peer_id)
4394 DSTACK(__FUNCTION_NAME);
4396 Player* player = m_env->getPlayer(peer_id);
4399 // Get a preview for crafting
4401 InventoryLocation loc;
4402 loc.setPlayer(player->getName());
4403 getCraftingResult(&player->inventory, preview, false, this);
4404 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4406 // Put the new preview in
4407 InventoryList *plist = player->inventory.getList("craftpreview");
4409 assert(plist->getSize() >= 1);
4410 plist->changeItem(0, preview);
4413 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4415 RemoteClient *client = getClientNoEx(peer_id,state_min);
4417 throw ClientNotFoundException("Client not found");
4421 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4423 return m_clients.getClientNoEx(peer_id, state_min);
4426 std::string Server::getPlayerName(u16 peer_id)
4428 Player *player = m_env->getPlayer(peer_id);
4430 return "[id="+itos(peer_id)+"]";
4431 return player->getName();
4434 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4436 Player *player = m_env->getPlayer(peer_id);
4439 return player->getPlayerSAO();
4442 std::wstring Server::getStatusString()
4444 std::wostringstream os(std::ios_base::binary);
4447 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4449 os<<L", uptime="<<m_uptime.get();
4451 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4452 // Information about clients
4455 std::list<u16> clients = m_clients.getClientIDs();
4456 for(std::list<u16>::iterator i = clients.begin();
4457 i != clients.end(); ++i)
4460 Player *player = m_env->getPlayer(*i);
4461 // Get name of player
4462 std::wstring name = L"unknown";
4464 name = narrow_to_wide(player->getName());
4465 // Add name to information string
4473 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4474 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4475 if(g_settings->get("motd") != "")
4476 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4480 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4482 std::set<std::string> privs;
4483 m_script->getAuth(name, NULL, &privs);
4487 bool Server::checkPriv(const std::string &name, const std::string &priv)
4489 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4490 return (privs.count(priv) != 0);
4493 void Server::reportPrivsModified(const std::string &name)
4496 std::list<u16> clients = m_clients.getClientIDs();
4497 for(std::list<u16>::iterator
4498 i = clients.begin();
4499 i != clients.end(); ++i){
4500 Player *player = m_env->getPlayer(*i);
4501 reportPrivsModified(player->getName());
4504 Player *player = m_env->getPlayer(name.c_str());
4507 SendPlayerPrivileges(player->peer_id);
4508 PlayerSAO *sao = player->getPlayerSAO();
4511 sao->updatePrivileges(
4512 getPlayerEffectivePrivs(name),
4517 void Server::reportInventoryFormspecModified(const std::string &name)
4519 Player *player = m_env->getPlayer(name.c_str());
4522 SendPlayerInventoryFormspec(player->peer_id);
4525 void Server::setIpBanned(const std::string &ip, const std::string &name)
4527 m_banmanager->add(ip, name);
4530 void Server::unsetIpBanned(const std::string &ip_or_name)
4532 m_banmanager->remove(ip_or_name);
4535 std::string Server::getBanDescription(const std::string &ip_or_name)
4537 return m_banmanager->getBanDescription(ip_or_name);
4540 void Server::notifyPlayer(const char *name, const std::wstring &msg)
4542 Player *player = m_env->getPlayer(name);
4546 if (player->peer_id == PEER_ID_INEXISTENT)
4549 SendChatMessage(player->peer_id, msg);
4552 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4554 Player *player = m_env->getPlayer(playername);
4558 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4562 SendShowFormspecMessage(player->peer_id, formspec, formname);
4566 u32 Server::hudAdd(Player *player, HudElement *form) {
4570 u32 id = player->addHud(form);
4572 SendHUDAdd(player->peer_id, id, form);
4577 bool Server::hudRemove(Player *player, u32 id) {
4581 HudElement* todel = player->removeHud(id);
4588 SendHUDRemove(player->peer_id, id);
4592 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4596 SendHUDChange(player->peer_id, id, stat, data);
4600 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4604 SendHUDSetFlags(player->peer_id, flags, mask);
4605 player->hud_flags = flags;
4607 m_script->player_event(player->getPlayerSAO(),"hud_changed");
4611 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4614 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4617 std::ostringstream os(std::ios::binary);
4618 writeS32(os, hotbar_itemcount);
4619 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4623 void Server::hudSetHotbarImage(Player *player, std::string name) {
4627 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4630 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4634 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4637 bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed)
4642 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
4646 bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
4651 SendEyeOffset(player->peer_id, first, third);
4655 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4656 const std::string &type, const std::vector<std::string> ¶ms)
4661 SendSetSky(player->peer_id, bgcolor, type, params);
4665 bool Server::overrideDayNightRatio(Player *player, bool do_override,
4671 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
4675 void Server::notifyPlayers(const std::wstring &msg)
4677 SendChatMessage(PEER_ID_INEXISTENT,msg);
4680 void Server::spawnParticle(const char *playername, v3f pos,
4681 v3f velocity, v3f acceleration,
4682 float expirationtime, float size, bool
4683 collisiondetection, bool vertical, std::string texture)
4685 Player *player = m_env->getPlayer(playername);
4688 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4689 expirationtime, size, collisiondetection, vertical, texture);
4692 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4693 float expirationtime, float size,
4694 bool collisiondetection, bool vertical, std::string texture)
4696 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4697 expirationtime, size, collisiondetection, vertical, texture);
4700 u32 Server::addParticleSpawner(const char *playername,
4701 u16 amount, float spawntime,
4702 v3f minpos, v3f maxpos,
4703 v3f minvel, v3f maxvel,
4704 v3f minacc, v3f maxacc,
4705 float minexptime, float maxexptime,
4706 float minsize, float maxsize,
4707 bool collisiondetection, bool vertical, std::string texture)
4709 Player *player = m_env->getPlayer(playername);
4714 for(;;) // look for unused particlespawner id
4717 if (std::find(m_particlespawner_ids.begin(),
4718 m_particlespawner_ids.end(), id)
4719 == m_particlespawner_ids.end())
4721 m_particlespawner_ids.push_back(id);
4726 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4727 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4728 minexptime, maxexptime, minsize, maxsize,
4729 collisiondetection, vertical, texture, id);
4734 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4735 v3f minpos, v3f maxpos,
4736 v3f minvel, v3f maxvel,
4737 v3f minacc, v3f maxacc,
4738 float minexptime, float maxexptime,
4739 float minsize, float maxsize,
4740 bool collisiondetection, bool vertical, std::string texture)
4743 for(;;) // look for unused particlespawner id
4746 if (std::find(m_particlespawner_ids.begin(),
4747 m_particlespawner_ids.end(), id)
4748 == m_particlespawner_ids.end())
4750 m_particlespawner_ids.push_back(id);
4755 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4756 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4757 minexptime, maxexptime, minsize, maxsize,
4758 collisiondetection, vertical, texture, id);
4763 void Server::deleteParticleSpawner(const char *playername, u32 id)
4765 Player *player = m_env->getPlayer(playername);
4769 m_particlespawner_ids.erase(
4770 std::remove(m_particlespawner_ids.begin(),
4771 m_particlespawner_ids.end(), id),
4772 m_particlespawner_ids.end());
4773 SendDeleteParticleSpawner(player->peer_id, id);
4776 void Server::deleteParticleSpawnerAll(u32 id)
4778 m_particlespawner_ids.erase(
4779 std::remove(m_particlespawner_ids.begin(),
4780 m_particlespawner_ids.end(), id),
4781 m_particlespawner_ids.end());
4782 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4785 Inventory* Server::createDetachedInventory(const std::string &name)
4787 if(m_detached_inventories.count(name) > 0){
4788 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4789 delete m_detached_inventories[name];
4791 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4793 Inventory *inv = new Inventory(m_itemdef);
4795 m_detached_inventories[name] = inv;
4796 //TODO find a better way to do this
4797 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4804 BoolScopeSet(bool *dst, bool val):
4807 m_orig_state = *m_dst;
4812 *m_dst = m_orig_state;
4819 // actions: time-reversed list
4820 // Return value: success/failure
4821 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4822 std::list<std::string> *log)
4824 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4825 ServerMap *map = (ServerMap*)(&m_env->getMap());
4826 // Disable rollback report sink while reverting
4827 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4829 // Fail if no actions to handle
4830 if(actions.empty()){
4831 log->push_back("Nothing to do.");
4838 for(std::list<RollbackAction>::const_iterator
4839 i = actions.begin();
4840 i != actions.end(); i++)
4842 const RollbackAction &action = *i;
4844 bool success = action.applyRevert(map, this, this);
4847 std::ostringstream os;
4848 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4849 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4851 log->push_back(os.str());
4853 std::ostringstream os;
4854 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4855 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4857 log->push_back(os.str());
4861 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4862 <<" failed"<<std::endl;
4864 // Call it done if less than half failed
4865 return num_failed <= num_tried/2;
4868 // IGameDef interface
4870 IItemDefManager* Server::getItemDefManager()
4874 INodeDefManager* Server::getNodeDefManager()
4878 ICraftDefManager* Server::getCraftDefManager()
4882 ITextureSource* Server::getTextureSource()
4886 IShaderSource* Server::getShaderSource()
4890 u16 Server::allocateUnknownNodeId(const std::string &name)
4892 return m_nodedef->allocateDummy(name);
4894 ISoundManager* Server::getSoundManager()
4896 return &dummySoundManager;
4898 MtEventManager* Server::getEventManager()
4902 IRollbackReportSink* Server::getRollbackReportSink()
4904 if(!m_enable_rollback_recording)
4906 if(!m_rollback_sink_enabled)
4911 IWritableItemDefManager* Server::getWritableItemDefManager()
4915 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4919 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4924 const ModSpec* Server::getModSpec(const std::string &modname)
4926 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4927 i != m_mods.end(); i++){
4928 const ModSpec &mod = *i;
4929 if(mod.name == modname)
4934 void Server::getModNames(std::list<std::string> &modlist)
4936 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4938 modlist.push_back(i->name);
4941 std::string Server::getBuiltinLuaPath()
4943 return porting::path_share + DIR_DELIM + "builtin";
4946 v3f findSpawnPos(ServerMap &map)
4948 //return v3f(50,50,50)*BS;
4953 nodepos = v2s16(0,0);
4958 s16 water_level = map.getWaterLevel();
4960 // Try to find a good place a few times
4961 for(s32 i=0; i<1000; i++)
4964 // We're going to try to throw the player to this position
4965 v2s16 nodepos2d = v2s16(
4966 -range + (myrand() % (range * 2)),
4967 -range + (myrand() % (range * 2)));
4969 // Get ground height at point
4970 s16 groundheight = map.findGroundLevel(nodepos2d);
4971 if (groundheight <= water_level) // Don't go underwater
4973 if (groundheight > water_level + 6) // Don't go to high places
4976 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4977 bool is_good = false;
4979 for (s32 i = 0; i < 10; i++) {
4980 v3s16 blockpos = getNodeBlockPos(nodepos);
4981 map.emergeBlock(blockpos, true);
4982 content_t c = map.getNodeNoEx(nodepos).getContent();
4983 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4985 if (air_count >= 2){
4993 // Found a good place
4994 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5000 return intToFloat(nodepos, BS);
5003 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5005 RemotePlayer *player = NULL;
5006 bool newplayer = false;
5009 Try to get an existing player
5011 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5013 // If player is already connected, cancel
5014 if(player != NULL && player->peer_id != 0)
5016 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5021 If player with the wanted peer_id already exists, cancel.
5023 if(m_env->getPlayer(peer_id) != NULL)
5025 infostream<<"emergePlayer(): Player with wrong name but same"
5026 " peer_id already exists"<<std::endl;
5031 Create a new player if it doesn't exist yet
5036 player = new RemotePlayer(this);
5037 player->updateName(name);
5039 /* Set player position */
5040 infostream<<"Server: Finding spawn place for player \""
5041 <<name<<"\""<<std::endl;
5042 v3f pos = findSpawnPos(m_env->getServerMap());
5043 player->setPosition(pos);
5045 /* Add player to environment */
5046 m_env->addPlayer(player);
5050 Create a new player active object
5052 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5053 getPlayerEffectivePrivs(player->getName()),
5056 /* Clean up old HUD elements from previous sessions */
5059 /* Add object to environment */
5060 m_env->addActiveObject(playersao);
5064 m_script->on_newplayer(playersao);
5069 void dedicated_server_loop(Server &server, bool &kill)
5071 DSTACK(__FUNCTION_NAME);
5073 verbosestream<<"dedicated_server_loop()"<<std::endl;
5075 IntervalLimiter m_profiler_interval;
5079 float steplen = g_settings->getFloat("dedicated_server_step");
5080 // This is kind of a hack but can be done like this
5081 // because server.step() is very light
5083 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5084 sleep_ms((int)(steplen*1000.0));
5086 server.step(steplen);
5088 if(server.getShutdownRequested() || kill)
5090 infostream<<"Dedicated server quitting"<<std::endl;
5092 if(g_settings->getBool("server_announce") == true)
5093 ServerList::sendAnnounce("delete");
5101 float profiler_print_interval =
5102 g_settings->getFloat("profiler_print_interval");
5103 if(profiler_print_interval != 0)
5105 if(m_profiler_interval.step(steplen, profiler_print_interval))
5107 infostream<<"Profiler:"<<std::endl;
5108 g_profiler->print(infostream);
5109 g_profiler->clear();