3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "environment.h"
28 #include "jthread/jmutexautolock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "scripting_game.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
56 #include "sound.h" // dummySoundManager
57 #include "event_manager.h"
59 #include "serverlist.h"
60 #include "util/string.h"
61 #include "util/pointedthing.h"
62 #include "util/mathconstants.h"
64 #include "util/serialize.h"
65 #include "util/thread.h"
66 #include "defaultsettings.h"
68 class ClientNotFoundException : public BaseException
71 ClientNotFoundException(const char *s):
76 class ServerThread : public JThread
82 ServerThread(Server *server):
91 void * ServerThread::Thread()
93 log_register_thread("ServerThread");
95 DSTACK(__FUNCTION_NAME);
96 BEGIN_DEBUG_EXCEPTION_HANDLER
98 m_server->AsyncRunStep(true);
102 while(!StopRequested())
105 //TimeTaker timer("AsyncRunStep() + Receive()");
107 m_server->AsyncRunStep();
112 catch(con::NoIncomingDataException &e)
115 catch(con::PeerNotFoundException &e)
117 infostream<<"Server: PeerNotFoundException"<<std::endl;
119 catch(ClientNotFoundException &e)
122 catch(con::ConnectionBindFailed &e)
124 m_server->setAsyncFatalError(e.what());
128 m_server->setAsyncFatalError(e.what());
132 END_DEBUG_EXCEPTION_HANDLER(errorstream)
137 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
139 if(pos_exists) *pos_exists = false;
144 if(pos_exists) *pos_exists = true;
149 ServerActiveObject *sao = env->getActiveObject(object);
152 if(pos_exists) *pos_exists = true;
153 return sao->getBasePosition(); }
165 const std::string &path_world,
166 const SubgameSpec &gamespec,
167 bool simple_singleplayer_mode
169 m_path_world(path_world),
170 m_gamespec(gamespec),
171 m_simple_singleplayer_mode(simple_singleplayer_mode),
172 m_async_fatal_error(""),
177 g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"),
181 m_rollback_sink_enabled(true),
182 m_enable_rollback_recording(false),
185 m_itemdef(createItemDefManager()),
186 m_nodedef(createNodeDefManager()),
187 m_craftdef(createCraftDefManager()),
188 m_event(new EventManager()),
190 m_time_of_day_send_timer(0),
193 m_shutdown_requested(false),
194 m_ignore_map_edit_events(false),
195 m_ignore_map_edit_events_peer_id(0)
198 m_liquid_transform_timer = 0.0;
199 m_liquid_transform_every = 1.0;
200 m_print_info_timer = 0.0;
201 m_masterserver_timer = 0.0;
202 m_objectdata_timer = 0.0;
203 m_emergethread_trigger_timer = 0.0;
204 m_savemap_timer = 0.0;
207 m_lag = g_settings->getFloat("dedicated_server_step");
210 throw ServerError("Supplied empty world path");
212 if(!gamespec.isValid())
213 throw ServerError("Supplied invalid gamespec");
215 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
216 if(m_simple_singleplayer_mode)
217 infostream<<" in simple singleplayer mode"<<std::endl;
219 infostream<<std::endl;
220 infostream<<"- world: "<<m_path_world<<std::endl;
221 infostream<<"- game: "<<m_gamespec.path<<std::endl;
223 // Initialize default settings and override defaults with those provided
225 set_default_settings(g_settings);
226 Settings gamedefaults;
227 getGameMinetestConfig(gamespec.path, gamedefaults);
228 override_default_settings(g_settings, &gamedefaults);
230 // Create server thread
231 m_thread = new ServerThread(this);
233 // Create emerge manager
234 m_emerge = new EmergeManager(this);
236 // Create world if it doesn't exist
237 if(!initializeWorld(m_path_world, m_gamespec.id))
238 throw ServerError("Failed to initialize world");
240 // Create ban manager
241 std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
242 m_banmanager = new BanManager(ban_path);
244 // Create rollback manager
245 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
246 m_rollback = createRollbackManager(rollback_path, this);
248 ModConfiguration modconf(m_path_world);
249 m_mods = modconf.getMods();
250 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
251 // complain about mods with unsatisfied dependencies
252 if(!modconf.isConsistent())
254 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
255 it != unsatisfied_mods.end(); ++it)
258 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
259 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
260 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
261 errorstream << " \"" << *dep_it << "\"";
262 errorstream << std::endl;
266 Settings worldmt_settings;
267 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
268 worldmt_settings.readConfigFile(worldmt.c_str());
269 std::vector<std::string> names = worldmt_settings.getNames();
270 std::set<std::string> load_mod_names;
271 for(std::vector<std::string>::iterator it = names.begin();
272 it != names.end(); ++it)
274 std::string name = *it;
275 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
276 load_mod_names.insert(name.substr(9));
278 // complain about mods declared to be loaded, but not found
279 for(std::vector<ModSpec>::iterator it = m_mods.begin();
280 it != m_mods.end(); ++it)
281 load_mod_names.erase((*it).name);
282 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
283 it != unsatisfied_mods.end(); ++it)
284 load_mod_names.erase((*it).name);
285 if(!load_mod_names.empty())
287 errorstream << "The following mods could not be found:";
288 for(std::set<std::string>::iterator it = load_mod_names.begin();
289 it != load_mod_names.end(); ++it)
290 errorstream << " \"" << (*it) << "\"";
291 errorstream << std::endl;
294 // Path to builtin.lua
295 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
298 JMutexAutoLock envlock(m_env_mutex);
300 // Initialize scripting
301 infostream<<"Server: Initializing Lua"<<std::endl;
303 m_script = new GameScripting(this);
306 // Load and run builtin.lua
307 infostream<<"Server: Loading builtin.lua [\""
308 <<builtinpath<<"\"]"<<std::endl;
309 bool success = m_script->loadMod(builtinpath, "__builtin");
311 errorstream<<"Server: Failed to load and run "
312 <<builtinpath<<std::endl;
313 throw ModError("Failed to load and run "+builtinpath);
316 infostream<<"Server: Loading mods: ";
317 for(std::vector<ModSpec>::iterator i = m_mods.begin();
318 i != m_mods.end(); i++){
319 const ModSpec &mod = *i;
320 infostream<<mod.name<<" ";
322 infostream<<std::endl;
323 // Load and run "mod" scripts
324 for(std::vector<ModSpec>::iterator i = m_mods.begin();
325 i != m_mods.end(); i++){
326 const ModSpec &mod = *i;
327 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
328 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
329 <<scriptpath<<"\"]"<<std::endl;
330 bool success = m_script->loadMod(scriptpath, mod.name);
332 errorstream<<"Server: Failed to load and run "
333 <<scriptpath<<std::endl;
334 throw ModError("Failed to load and run "+scriptpath);
338 // Read Textures and calculate sha1 sums
341 // Apply item aliases in the node definition manager
342 m_nodedef->updateAliases(m_itemdef);
344 // Initialize Environment
345 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
346 m_env = new ServerEnvironment(servermap, m_script, this, m_emerge);
348 m_clients.setEnv(m_env);
350 // Run some callbacks after the MG params have been set up but before activation
351 m_script->environment_OnMapgenInit(&m_emerge->params);
353 // Initialize mapgens
354 m_emerge->initMapgens();
356 // Give environment reference to scripting api
357 m_script->initializeEnvironment(m_env);
359 // Register us to receive map edit events
360 servermap->addEventReceiver(this);
362 // If file exists, load environment metadata
363 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
365 infostream<<"Server: Loading environment metadata"<<std::endl;
366 m_env->loadMeta(m_path_world);
370 infostream<<"Server: Loading players"<<std::endl;
371 m_env->deSerializePlayers(m_path_world);
374 Add some test ActiveBlockModifiers to environment
376 add_legacy_abms(m_env, m_nodedef);
378 m_liquid_transform_every = g_settings->getFloat("liquid_update");
383 infostream<<"Server destructing"<<std::endl;
386 Send shutdown message
389 std::wstring line = L"*** Server shutting down";
390 SendChatMessage(PEER_ID_INEXISTENT, line);
394 JMutexAutoLock envlock(m_env_mutex);
397 Execute script shutdown hooks
399 m_script->on_shutdown();
403 JMutexAutoLock envlock(m_env_mutex);
408 infostream<<"Server: Saving players"<<std::endl;
409 m_env->serializePlayers(m_path_world);
412 Save environment metadata
414 infostream<<"Server: Saving environment metadata"<<std::endl;
415 m_env->saveMeta(m_path_world);
424 // stop all emerge threads before deleting players that may have
425 // requested blocks to be emerged
426 m_emerge->stopThreads();
428 // Delete things in the reverse order of creation
431 // N.B. the EmergeManager should be deleted after the Environment since Map
432 // depends on EmergeManager to write its current params to the map meta
441 // Deinitialize scripting
442 infostream<<"Server: Deinitializing scripting"<<std::endl;
445 // Delete detached inventories
447 for(std::map<std::string, Inventory*>::iterator
448 i = m_detached_inventories.begin();
449 i != m_detached_inventories.end(); i++){
455 void Server::start(unsigned short port)
457 DSTACK(__FUNCTION_NAME);
458 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
460 // Stop thread if already running
463 // Initialize connection
464 m_con.SetTimeoutMs(30);
470 // ASCII art for the win!
472 <<" .__ __ __ "<<std::endl
473 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
474 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
475 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
476 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
477 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
478 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
479 actionstream<<"Server for gameid=\""<<m_gamespec.id
480 <<"\" listening on port "<<port<<"."<<std::endl;
485 DSTACK(__FUNCTION_NAME);
487 infostream<<"Server: Stopping and waiting threads"<<std::endl;
489 // Stop threads (set run=false first so both start stopping)
491 //m_emergethread.setRun(false);
493 //m_emergethread.stop();
495 infostream<<"Server: Threads stopped"<<std::endl;
498 void Server::step(float dtime)
500 DSTACK(__FUNCTION_NAME);
505 JMutexAutoLock lock(m_step_dtime_mutex);
506 m_step_dtime += dtime;
508 // Throw if fatal error occurred in thread
509 std::string async_err = m_async_fatal_error.get();
511 throw ServerError(async_err);
515 void Server::AsyncRunStep(bool initial_step)
517 DSTACK(__FUNCTION_NAME);
519 g_profiler->add("Server::AsyncRunStep (num)", 1);
523 JMutexAutoLock lock1(m_step_dtime_mutex);
524 dtime = m_step_dtime;
528 // Send blocks to clients
532 if((dtime < 0.001) && (initial_step == false))
535 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
537 //infostream<<"Server steps "<<dtime<<std::endl;
538 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
541 JMutexAutoLock lock1(m_step_dtime_mutex);
542 m_step_dtime -= dtime;
549 m_uptime.set(m_uptime.get() + dtime);
555 Update time of day and overall game time
558 JMutexAutoLock envlock(m_env_mutex);
560 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
563 Send to clients at constant intervals
566 m_time_of_day_send_timer -= dtime;
567 if(m_time_of_day_send_timer < 0.0)
569 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
570 u16 time = m_env->getTimeOfDay();
571 float time_speed = g_settings->getFloat("time_speed");
572 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
577 JMutexAutoLock lock(m_env_mutex);
578 // Figure out and report maximum lag to environment
579 float max_lag = m_env->getMaxLagEstimate();
580 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
582 if(dtime > 0.1 && dtime > max_lag * 2.0)
583 infostream<<"Server: Maximum lag peaked to "<<dtime
587 m_env->reportMaxLagEstimate(max_lag);
589 ScopeProfiler sp(g_profiler, "SEnv step");
590 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
594 const float map_timer_and_unload_dtime = 2.92;
595 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
597 JMutexAutoLock lock(m_env_mutex);
598 // Run Map's timers and unload unused data
599 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
600 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
601 g_settings->getFloat("server_unload_unused_data_timeout"));
612 JMutexAutoLock lock(m_env_mutex);
614 std::list<u16> clientids = m_clients.getClientIDs();
616 ScopeProfiler sp(g_profiler, "Server: handle players");
618 for(std::list<u16>::iterator
619 i = clientids.begin();
620 i != clientids.end(); ++i)
622 PlayerSAO *playersao = getPlayerSAO(*i);
623 if(playersao == NULL)
627 Handle player HPs (die if hp=0)
629 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
631 if(playersao->getHP() == 0)
638 Send player breath if changed
640 if(playersao->m_breath_not_sent){
641 SendPlayerBreath(*i);
645 Send player inventories if necessary
647 if(playersao->m_moved){
649 playersao->m_moved = false;
651 if(playersao->m_inventory_not_sent){
658 /* Transform liquids */
659 m_liquid_transform_timer += dtime;
660 if(m_liquid_transform_timer >= m_liquid_transform_every)
662 m_liquid_transform_timer -= m_liquid_transform_every;
664 JMutexAutoLock lock(m_env_mutex);
666 ScopeProfiler sp(g_profiler, "Server: liquid transform");
668 std::map<v3s16, MapBlock*> modified_blocks;
669 m_env->getMap().transformLiquids(modified_blocks);
674 core::map<v3s16, MapBlock*> lighting_modified_blocks;
675 ServerMap &map = ((ServerMap&)m_env->getMap());
676 map.updateLighting(modified_blocks, lighting_modified_blocks);
678 // Add blocks modified by lighting to modified_blocks
679 for(core::map<v3s16, MapBlock*>::Iterator
680 i = lighting_modified_blocks.getIterator();
681 i.atEnd() == false; i++)
683 MapBlock *block = i.getNode()->getValue();
684 modified_blocks.insert(block->getPos(), block);
688 Set the modified blocks unsent for all the clients
690 if(modified_blocks.size() > 0)
692 SetBlocksNotSent(modified_blocks);
695 m_clients.step(dtime);
697 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
699 // send masterserver announce
701 float &counter = m_masterserver_timer;
702 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
703 g_settings->getBool("server_announce") == true)
705 ServerList::sendAnnounce(!counter ? "start" : "update",
706 m_clients.getPlayerNames(),
708 m_env->getGameTime(),
719 Check added and deleted active objects
722 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
723 JMutexAutoLock envlock(m_env_mutex);
726 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
727 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
729 // Radius inside which objects are active
730 s16 radius = g_settings->getS16("active_object_send_range_blocks");
731 radius *= MAP_BLOCKSIZE;
733 for(std::map<u16, RemoteClient*>::iterator
735 i != clients.end(); ++i)
737 RemoteClient *client = i->second;
739 // If definitions and textures have not been sent, don't
740 // send objects either
741 if (client->getState() < DefinitionsSent)
744 Player *player = m_env->getPlayer(client->peer_id);
747 // This can happen if the client timeouts somehow
748 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
750 <<" has no associated player"<<std::endl;*/
753 v3s16 pos = floatToInt(player->getPosition(), BS);
755 std::set<u16> removed_objects;
756 std::set<u16> added_objects;
757 m_env->getRemovedActiveObjects(pos, radius,
758 client->m_known_objects, removed_objects);
759 m_env->getAddedActiveObjects(pos, radius,
760 client->m_known_objects, added_objects);
762 // Ignore if nothing happened
763 if(removed_objects.size() == 0 && added_objects.size() == 0)
765 //infostream<<"active objects: none changed"<<std::endl;
769 std::string data_buffer;
773 // Handle removed objects
774 writeU16((u8*)buf, removed_objects.size());
775 data_buffer.append(buf, 2);
776 for(std::set<u16>::iterator
777 i = removed_objects.begin();
778 i != removed_objects.end(); ++i)
782 ServerActiveObject* obj = m_env->getActiveObject(id);
784 // Add to data buffer for sending
785 writeU16((u8*)buf, id);
786 data_buffer.append(buf, 2);
788 // Remove from known objects
789 client->m_known_objects.erase(id);
791 if(obj && obj->m_known_by_count > 0)
792 obj->m_known_by_count--;
795 // Handle added objects
796 writeU16((u8*)buf, added_objects.size());
797 data_buffer.append(buf, 2);
798 for(std::set<u16>::iterator
799 i = added_objects.begin();
800 i != added_objects.end(); ++i)
804 ServerActiveObject* obj = m_env->getActiveObject(id);
807 u8 type = ACTIVEOBJECT_TYPE_INVALID;
809 infostream<<"WARNING: "<<__FUNCTION_NAME
810 <<": NULL object"<<std::endl;
812 type = obj->getSendType();
814 // Add to data buffer for sending
815 writeU16((u8*)buf, id);
816 data_buffer.append(buf, 2);
817 writeU8((u8*)buf, type);
818 data_buffer.append(buf, 1);
821 data_buffer.append(serializeLongString(
822 obj->getClientInitializationData(client->net_proto_version)));
824 data_buffer.append(serializeLongString(""));
826 // Add to known objects
827 client->m_known_objects.insert(id);
830 obj->m_known_by_count++;
834 SharedBuffer<u8> reply(2 + data_buffer.size());
835 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
836 memcpy((char*)&reply[2], data_buffer.c_str(),
839 m_clients.send(client->peer_id, 0, reply, true);
841 verbosestream<<"Server: Sent object remove/add: "
842 <<removed_objects.size()<<" removed, "
843 <<added_objects.size()<<" added, "
844 <<"packet size is "<<reply.getSize()<<std::endl;
849 Collect a list of all the objects known by the clients
850 and report it back to the environment.
853 core::map<u16, bool> all_known_objects;
855 for(core::map<u16, RemoteClient*>::Iterator
856 i = m_clients.getIterator();
857 i.atEnd() == false; i++)
859 RemoteClient *client = i.getNode()->getValue();
860 // Go through all known objects of client
861 for(core::map<u16, bool>::Iterator
862 i = client->m_known_objects.getIterator();
863 i.atEnd()==false; i++)
865 u16 id = i.getNode()->getKey();
866 all_known_objects[id] = true;
870 m_env->setKnownActiveObjects(whatever);
879 JMutexAutoLock envlock(m_env_mutex);
880 ScopeProfiler sp(g_profiler, "Server: sending object messages");
883 // Value = data sent by object
884 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
886 // Get active object messages from environment
889 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
893 std::list<ActiveObjectMessage>* message_list = NULL;
894 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
895 n = buffered_messages.find(aom.id);
896 if(n == buffered_messages.end())
898 message_list = new std::list<ActiveObjectMessage>;
899 buffered_messages[aom.id] = message_list;
903 message_list = n->second;
905 message_list->push_back(aom);
909 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
910 // Route data to every client
911 for(std::map<u16, RemoteClient*>::iterator
913 i != clients.end(); ++i)
915 RemoteClient *client = i->second;
916 std::string reliable_data;
917 std::string unreliable_data;
918 // Go through all objects in message buffer
919 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
920 j = buffered_messages.begin();
921 j != buffered_messages.end(); ++j)
923 // If object is not known by client, skip it
925 if(client->m_known_objects.find(id) == client->m_known_objects.end())
927 // Get message list of object
928 std::list<ActiveObjectMessage>* list = j->second;
929 // Go through every message
930 for(std::list<ActiveObjectMessage>::iterator
931 k = list->begin(); k != list->end(); ++k)
933 // Compose the full new data with header
934 ActiveObjectMessage aom = *k;
935 std::string new_data;
938 writeU16((u8*)&buf[0], aom.id);
939 new_data.append(buf, 2);
941 new_data += serializeString(aom.datastring);
942 // Add data to buffer
944 reliable_data += new_data;
946 unreliable_data += new_data;
950 reliable_data and unreliable_data are now ready.
953 if(reliable_data.size() > 0)
955 SharedBuffer<u8> reply(2 + reliable_data.size());
956 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
957 memcpy((char*)&reply[2], reliable_data.c_str(),
958 reliable_data.size());
960 m_clients.send(client->peer_id, 0, reply, true);
962 if(unreliable_data.size() > 0)
964 SharedBuffer<u8> reply(2 + unreliable_data.size());
965 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
966 memcpy((char*)&reply[2], unreliable_data.c_str(),
967 unreliable_data.size());
968 // Send as unreliable
969 m_clients.send(client->peer_id, 1, reply, false);
972 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
974 infostream<<"Server: Size of object message data: "
975 <<"reliable: "<<reliable_data.size()
976 <<", unreliable: "<<unreliable_data.size()
982 // Clear buffered_messages
983 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
984 i = buffered_messages.begin();
985 i != buffered_messages.end(); ++i)
992 Send queued-for-sending map edit events.
995 // We will be accessing the environment
996 JMutexAutoLock lock(m_env_mutex);
998 // Don't send too many at a time
1001 // Single change sending is disabled if queue size is not small
1002 bool disable_single_change_sending = false;
1003 if(m_unsent_map_edit_queue.size() >= 4)
1004 disable_single_change_sending = true;
1006 int event_count = m_unsent_map_edit_queue.size();
1008 // We'll log the amount of each
1011 while(m_unsent_map_edit_queue.size() != 0)
1013 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1015 // Players far away from the change are stored here.
1016 // Instead of sending the changes, MapBlocks are set not sent
1018 std::list<u16> far_players;
1020 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
1022 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1023 prof.add("MEET_ADDNODE", 1);
1024 if(disable_single_change_sending)
1025 sendAddNode(event->p, event->n, event->already_known_by_peer,
1026 &far_players, 5, event->type == MEET_ADDNODE);
1028 sendAddNode(event->p, event->n, event->already_known_by_peer,
1029 &far_players, 30, event->type == MEET_ADDNODE);
1031 else if(event->type == MEET_REMOVENODE)
1033 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1034 prof.add("MEET_REMOVENODE", 1);
1035 if(disable_single_change_sending)
1036 sendRemoveNode(event->p, event->already_known_by_peer,
1039 sendRemoveNode(event->p, event->already_known_by_peer,
1042 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1044 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1045 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1046 setBlockNotSent(event->p);
1048 else if(event->type == MEET_OTHER)
1050 infostream<<"Server: MEET_OTHER"<<std::endl;
1051 prof.add("MEET_OTHER", 1);
1052 for(std::set<v3s16>::iterator
1053 i = event->modified_blocks.begin();
1054 i != event->modified_blocks.end(); ++i)
1056 setBlockNotSent(*i);
1061 prof.add("unknown", 1);
1062 infostream<<"WARNING: Server: Unknown MapEditEvent "
1063 <<((u32)event->type)<<std::endl;
1067 Set blocks not sent to far players
1069 if(far_players.size() > 0)
1071 // Convert list format to that wanted by SetBlocksNotSent
1072 std::map<v3s16, MapBlock*> modified_blocks2;
1073 for(std::set<v3s16>::iterator
1074 i = event->modified_blocks.begin();
1075 i != event->modified_blocks.end(); ++i)
1077 modified_blocks2[*i] =
1078 m_env->getMap().getBlockNoCreateNoEx(*i);
1080 // Set blocks not sent
1081 for(std::list<u16>::iterator
1082 i = far_players.begin();
1083 i != far_players.end(); ++i)
1086 RemoteClient *client = getClient(peer_id);
1089 client->SetBlocksNotSent(modified_blocks2);
1095 /*// Don't send too many at a time
1097 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1101 if(event_count >= 5){
1102 infostream<<"Server: MapEditEvents:"<<std::endl;
1103 prof.print(infostream);
1104 } else if(event_count != 0){
1105 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1106 prof.print(verbosestream);
1112 Trigger emergethread (it somehow gets to a non-triggered but
1113 bysy state sometimes)
1116 float &counter = m_emergethread_trigger_timer;
1122 m_emerge->startThreads();
1124 // Update m_enable_rollback_recording here too
1125 m_enable_rollback_recording =
1126 g_settings->getBool("enable_rollback_recording");
1130 // Save map, players and auth stuff
1132 float &counter = m_savemap_timer;
1134 if(counter >= g_settings->getFloat("server_map_save_interval"))
1137 JMutexAutoLock lock(m_env_mutex);
1139 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1142 if(m_banmanager->isModified())
1143 m_banmanager->save();
1145 // Save changed parts of map
1146 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1149 m_env->serializePlayers(m_path_world);
1151 // Save environment metadata
1152 m_env->saveMeta(m_path_world);
1157 void Server::Receive()
1159 DSTACK(__FUNCTION_NAME);
1160 SharedBuffer<u8> data;
1164 datasize = m_con.Receive(peer_id,data);
1165 ProcessData(*data, datasize, peer_id);
1167 catch(con::InvalidIncomingDataException &e)
1169 infostream<<"Server::Receive(): "
1170 "InvalidIncomingDataException: what()="
1171 <<e.what()<<std::endl;
1173 catch(con::PeerNotFoundException &e)
1175 //NOTE: This is not needed anymore
1177 // The peer has been disconnected.
1178 // Find the associated player and remove it.
1180 /*JMutexAutoLock envlock(m_env_mutex);
1182 infostream<<"ServerThread: peer_id="<<peer_id
1183 <<" has apparently closed connection. "
1184 <<"Removing player."<<std::endl;
1186 m_env->removePlayer(peer_id);*/
1190 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1192 DSTACK(__FUNCTION_NAME);
1193 // Environment is locked first.
1194 JMutexAutoLock envlock(m_env_mutex);
1196 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1200 Address address = getPeerAddress(peer_id);
1201 addr_s = address.serializeString();
1203 // drop player if is ip is banned
1204 if(m_banmanager->isIpBanned(addr_s)){
1205 std::string ban_name = m_banmanager->getBanName(addr_s);
1206 infostream<<"Server: A banned client tried to connect from "
1207 <<addr_s<<"; banned name was "
1208 <<ban_name<<std::endl;
1209 // This actually doesn't seem to transfer to the client
1210 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1211 +narrow_to_wide(ban_name));
1215 catch(con::PeerNotFoundException &e)
1217 errorstream<<"Server::ProcessData(): Cancelling: peer "
1218 <<peer_id<<" not found"<<std::endl;
1228 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1230 if(command == TOSERVER_INIT)
1232 // [0] u16 TOSERVER_INIT
1233 // [2] u8 SER_FMT_VER_HIGHEST_READ
1234 // [3] u8[20] player_name
1235 // [23] u8[28] password <--- can be sent without this, from old versions
1237 if(datasize < 2+1+PLAYERNAME_SIZE)
1240 RemoteClient* client = getClient(peer_id,Created);
1242 // If net_proto_version is set, this client has already been handled
1243 if(client->getState() > Created)
1245 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1246 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1250 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1251 <<peer_id<<")"<<std::endl;
1253 // Do not allow multiple players in simple singleplayer mode.
1254 // This isn't a perfect way to do it, but will suffice for now
1255 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1256 infostream<<"Server: Not allowing another client ("<<addr_s
1257 <<") to connect in simple singleplayer mode"<<std::endl;
1258 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1262 // First byte after command is maximum supported
1263 // serialization version
1264 u8 client_max = data[2];
1265 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1266 // Use the highest version supported by both
1267 u8 deployed = std::min(client_max, our_max);
1268 // If it's lower than the lowest supported, give up.
1269 if(deployed < SER_FMT_VER_LOWEST)
1270 deployed = SER_FMT_VER_INVALID;
1272 if(deployed == SER_FMT_VER_INVALID)
1274 actionstream<<"Server: A mismatched client tried to connect from "
1275 <<addr_s<<std::endl;
1276 infostream<<"Server: Cannot negotiate serialization version with "
1277 <<addr_s<<std::endl;
1278 DenyAccess(peer_id, std::wstring(
1279 L"Your client's version is not supported.\n"
1280 L"Server version is ")
1281 + narrow_to_wide(minetest_version_simple) + L"."
1286 client->setPendingSerializationVersion(deployed);
1289 Read and check network protocol version
1292 u16 min_net_proto_version = 0;
1293 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1294 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1296 // Use same version as minimum and maximum if maximum version field
1297 // doesn't exist (backwards compatibility)
1298 u16 max_net_proto_version = min_net_proto_version;
1299 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1300 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1302 // Start with client's maximum version
1303 u16 net_proto_version = max_net_proto_version;
1305 // Figure out a working version if it is possible at all
1306 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1307 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1309 // If maximum is larger than our maximum, go with our maximum
1310 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1311 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1312 // Else go with client's maximum
1314 net_proto_version = max_net_proto_version;
1317 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1318 <<min_net_proto_version<<", max: "<<max_net_proto_version
1319 <<", chosen: "<<net_proto_version<<std::endl;
1321 client->net_proto_version = net_proto_version;
1323 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1324 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1326 actionstream<<"Server: A mismatched client tried to connect from "
1327 <<addr_s<<std::endl;
1328 DenyAccess(peer_id, std::wstring(
1329 L"Your client's version is not supported.\n"
1330 L"Server version is ")
1331 + narrow_to_wide(minetest_version_simple) + L",\n"
1332 + L"server's PROTOCOL_VERSION is "
1333 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1335 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1336 + L", client's PROTOCOL_VERSION is "
1337 + narrow_to_wide(itos(min_net_proto_version))
1339 + narrow_to_wide(itos(max_net_proto_version))
1344 if(g_settings->getBool("strict_protocol_version_checking"))
1346 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1348 actionstream<<"Server: A mismatched (strict) client tried to "
1349 <<"connect from "<<addr_s<<std::endl;
1350 DenyAccess(peer_id, std::wstring(
1351 L"Your client's version is not supported.\n"
1352 L"Server version is ")
1353 + narrow_to_wide(minetest_version_simple) + L",\n"
1354 + L"server's PROTOCOL_VERSION (strict) is "
1355 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1356 + L", client's PROTOCOL_VERSION is "
1357 + narrow_to_wide(itos(min_net_proto_version))
1359 + narrow_to_wide(itos(max_net_proto_version))
1370 char playername[PLAYERNAME_SIZE];
1371 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1373 playername[i] = data[3+i];
1375 playername[PLAYERNAME_SIZE-1] = 0;
1377 if(playername[0]=='\0')
1379 actionstream<<"Server: Player with an empty name "
1380 <<"tried to connect from "<<addr_s<<std::endl;
1381 DenyAccess(peer_id, L"Empty name");
1385 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1387 actionstream<<"Server: Player with an invalid name "
1388 <<"tried to connect from "<<addr_s<<std::endl;
1389 DenyAccess(peer_id, L"Name contains unallowed characters");
1393 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1395 actionstream<<"Server: Player with the name \"singleplayer\" "
1396 <<"tried to connect from "<<addr_s<<std::endl;
1397 DenyAccess(peer_id, L"Name is not allowed");
1403 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1405 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1406 <<"tried to connect from "<<addr_s<<" "
1407 <<"but it was disallowed for the following reason: "
1408 <<reason<<std::endl;
1409 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1414 infostream<<"Server: New connection: \""<<playername<<"\" from "
1415 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1418 char given_password[PASSWORD_SIZE];
1419 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1421 // old version - assume blank password
1422 given_password[0] = 0;
1426 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1428 given_password[i] = data[23+i];
1430 given_password[PASSWORD_SIZE-1] = 0;
1433 if(!base64_is_valid(given_password)){
1434 actionstream<<"Server: "<<playername
1435 <<" supplied invalid password hash"<<std::endl;
1436 DenyAccess(peer_id, L"Invalid password hash");
1440 // Enforce user limit.
1441 // Don't enforce for users that have some admin right
1442 if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") &&
1443 !checkPriv(playername, "server") &&
1444 !checkPriv(playername, "ban") &&
1445 !checkPriv(playername, "privs") &&
1446 !checkPriv(playername, "password") &&
1447 playername != g_settings->get("name"))
1449 actionstream<<"Server: "<<playername<<" tried to join, but there"
1450 <<" are already max_users="
1451 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1452 DenyAccess(peer_id, L"Too many users.");
1456 std::string checkpwd; // Password hash to check against
1457 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1459 // If no authentication info exists for user, create it
1461 if(!isSingleplayer() &&
1462 g_settings->getBool("disallow_empty_password") &&
1463 std::string(given_password) == ""){
1464 actionstream<<"Server: "<<playername
1465 <<" supplied empty password"<<std::endl;
1466 DenyAccess(peer_id, L"Empty passwords are "
1467 L"disallowed. Set a password and try again.");
1470 std::wstring raw_default_password =
1471 narrow_to_wide(g_settings->get("default_password"));
1472 std::string initial_password =
1473 translatePassword(playername, raw_default_password);
1475 // If default_password is empty, allow any initial password
1476 if (raw_default_password.length() == 0)
1477 initial_password = given_password;
1479 m_script->createAuth(playername, initial_password);
1482 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1485 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1486 <<" (auth handler does not work?)"<<std::endl;
1487 DenyAccess(peer_id, L"Not allowed to login");
1491 if(given_password != checkpwd){
1492 actionstream<<"Server: "<<playername<<" supplied wrong password"
1494 DenyAccess(peer_id, L"Wrong password");
1498 RemotePlayer *player =
1499 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1501 if(player && player->peer_id != 0){
1502 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1503 <<" (player allocated to an another client)"<<std::endl;
1504 DenyAccess(peer_id, L"Another client is connected with this "
1505 L"name. If your client closed unexpectedly, try again in "
1509 m_clients.setPlayerName(peer_id,playername);
1512 Answer with a TOCLIENT_INIT
1515 SharedBuffer<u8> reply(2+1+6+8+4);
1516 writeU16(&reply[0], TOCLIENT_INIT);
1517 writeU8(&reply[2], deployed);
1518 //send dummy pos for legacy reasons only
1519 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1520 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1521 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1524 m_clients.send(peer_id, 0, reply, true);
1525 m_clients.event(peer_id, Init);
1531 if(command == TOSERVER_INIT2)
1534 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1535 <<peer_id<<std::endl;
1537 m_clients.event(peer_id, GotInit2);
1538 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1541 Send some initialization data
1544 infostream<<"Server: Sending content to "
1545 <<getPlayerName(peer_id)<<std::endl;
1547 // Send player movement settings
1548 SendMovement(peer_id);
1550 // Send item definitions
1551 SendItemDef(peer_id, m_itemdef, protocol_version);
1553 // Send node definitions
1554 SendNodeDef(peer_id, m_nodedef, protocol_version);
1556 m_clients.event(peer_id, SetDefinitionsSent);
1558 // Send media announcement
1559 sendMediaAnnouncement(peer_id);
1561 // Send detached inventories
1562 sendDetachedInventories(peer_id);
1565 u16 time = m_env->getTimeOfDay();
1566 float time_speed = g_settings->getFloat("time_speed");
1567 SendTimeOfDay(peer_id, time, time_speed);
1569 // Warnings about protocol version can be issued here
1570 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1572 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1573 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1579 u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version;
1581 if(peer_ser_ver == SER_FMT_VER_INVALID)
1583 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1584 " serialization format invalid or not initialized."
1585 " Skipping incoming command="<<command<<std::endl;
1589 /* Handle commands relate to client startup */
1590 if(command == TOSERVER_REQUEST_MEDIA) {
1591 std::string datastring((char*)&data[2], datasize-2);
1592 std::istringstream is(datastring, std::ios_base::binary);
1594 std::list<std::string> tosend;
1595 u16 numfiles = readU16(is);
1597 infostream<<"Sending "<<numfiles<<" files to "
1598 <<getPlayerName(peer_id)<<std::endl;
1599 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1601 for(int i = 0; i < numfiles; i++) {
1602 std::string name = deSerializeString(is);
1603 tosend.push_back(name);
1604 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1608 sendRequestedMedia(peer_id, tosend);
1611 else if(command == TOSERVER_RECEIVED_MEDIA) {
1612 std::string playername = "";
1613 PlayerSAO *playersao = NULL;
1615 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,DefinitionsSent);
1616 if (client != NULL) {
1617 playername = client->getName();
1618 playersao = emergePlayer(playername.c_str(), peer_id);
1622 RemotePlayer *player =
1623 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1625 // If failed, cancel
1626 if((playersao == NULL) || (player == NULL))
1628 if(player && player->peer_id != 0){
1629 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1630 <<" (player allocated to an another client)"<<std::endl;
1631 DenyAccess(peer_id, L"Another client is connected with this "
1632 L"name. If your client closed unexpectedly, try again in "
1635 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1637 DenyAccess(peer_id, L"Could not allocate player.");
1643 Send complete position information
1645 SendMovePlayer(peer_id);
1648 SendPlayerPrivileges(peer_id);
1650 // Send inventory formspec
1651 SendPlayerInventoryFormspec(peer_id);
1654 UpdateCrafting(peer_id);
1655 SendInventory(peer_id);
1658 if(g_settings->getBool("enable_damage"))
1659 SendPlayerHP(peer_id);
1662 SendPlayerBreath(peer_id);
1664 // Show death screen if necessary
1666 SendDeathscreen(peer_id, false, v3f(0,0,0));
1668 // Note things in chat if not in simple singleplayer mode
1669 if(!m_simple_singleplayer_mode)
1671 // Send information about server to player in chat
1672 SendChatMessage(peer_id, getStatusString());
1674 // Send information about joining in chat
1676 std::wstring name = L"unknown";
1677 Player *player = m_env->getPlayer(peer_id);
1679 name = narrow_to_wide(player->getName());
1681 std::wstring message;
1684 message += L" joined the game.";
1685 SendChatMessage(PEER_ID_INEXISTENT,message);
1689 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. " << std::endl;
1694 std::vector<std::string> names = m_clients.getPlayerNames();
1696 actionstream<<player->getName()<<" ["<<addr_s<<"] "
1697 <<"joins game. List of players: ";
1699 for (std::vector<std::string>::iterator i = names.begin();
1700 i != names.end(); i++)
1702 actionstream << *i << " ";
1705 actionstream<<std::endl;
1708 m_clients.event(peer_id,SetMediaSent);
1709 m_script->on_joinplayer(playersao);
1712 else if(command == TOSERVER_GOTBLOCKS)
1725 u16 count = data[2];
1726 for(u16 i=0; i<count; i++)
1728 if((s16)datasize < 2+1+(i+1)*6)
1729 throw con::InvalidIncomingDataException
1730 ("GOTBLOCKS length is too short");
1731 v3s16 p = readV3S16(&data[2+1+i*6]);
1732 /*infostream<<"Server: GOTBLOCKS ("
1733 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1734 RemoteClient *client = getClient(peer_id);
1735 client->GotBlock(p);
1740 if (m_clients.getClientState(peer_id) < Active)
1742 if (command == TOSERVER_PLAYERPOS) return;
1744 errorstream<<"Got packet command: " << command << " for peer id "
1745 << peer_id << " but client isn't active yet. Dropping packet "
1750 Player *player = m_env->getPlayer(peer_id);
1752 errorstream<<"Server::ProcessData(): Cancelling: "
1753 "No player for peer_id="<<peer_id
1758 PlayerSAO *playersao = player->getPlayerSAO();
1759 if(playersao == NULL){
1760 errorstream<<"Server::ProcessData(): Cancelling: "
1761 "No player object for peer_id="<<peer_id
1766 if(command == TOSERVER_PLAYERPOS)
1768 if(datasize < 2+12+12+4+4)
1772 v3s32 ps = readV3S32(&data[start+2]);
1773 v3s32 ss = readV3S32(&data[start+2+12]);
1774 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1775 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1777 if(datasize >= 2+12+12+4+4+4)
1778 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
1779 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1780 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1781 pitch = wrapDegrees(pitch);
1782 yaw = wrapDegrees(yaw);
1784 player->setPosition(position);
1785 player->setSpeed(speed);
1786 player->setPitch(pitch);
1787 player->setYaw(yaw);
1788 player->keyPressed=keyPressed;
1789 player->control.up = (bool)(keyPressed&1);
1790 player->control.down = (bool)(keyPressed&2);
1791 player->control.left = (bool)(keyPressed&4);
1792 player->control.right = (bool)(keyPressed&8);
1793 player->control.jump = (bool)(keyPressed&16);
1794 player->control.aux1 = (bool)(keyPressed&32);
1795 player->control.sneak = (bool)(keyPressed&64);
1796 player->control.LMB = (bool)(keyPressed&128);
1797 player->control.RMB = (bool)(keyPressed&256);
1799 bool cheated = playersao->checkMovementCheat();
1802 m_script->on_cheat(playersao, "moved_too_fast");
1805 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1806 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1807 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1809 else if(command == TOSERVER_DELETEDBLOCKS)
1822 u16 count = data[2];
1823 for(u16 i=0; i<count; i++)
1825 if((s16)datasize < 2+1+(i+1)*6)
1826 throw con::InvalidIncomingDataException
1827 ("DELETEDBLOCKS length is too short");
1828 v3s16 p = readV3S16(&data[2+1+i*6]);
1829 /*infostream<<"Server: DELETEDBLOCKS ("
1830 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1831 RemoteClient *client = getClient(peer_id);
1832 client->SetBlockNotSent(p);
1835 else if(command == TOSERVER_CLICK_OBJECT)
1837 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
1840 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
1842 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
1845 else if(command == TOSERVER_GROUND_ACTION)
1847 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
1851 else if(command == TOSERVER_RELEASE)
1853 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
1856 else if(command == TOSERVER_SIGNTEXT)
1858 infostream<<"Server: SIGNTEXT not supported anymore"
1862 else if(command == TOSERVER_SIGNNODETEXT)
1864 infostream<<"Server: SIGNNODETEXT not supported anymore"
1868 else if(command == TOSERVER_INVENTORY_ACTION)
1870 // Strip command and create a stream
1871 std::string datastring((char*)&data[2], datasize-2);
1872 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
1873 std::istringstream is(datastring, std::ios_base::binary);
1875 InventoryAction *a = InventoryAction::deSerialize(is);
1878 infostream<<"TOSERVER_INVENTORY_ACTION: "
1879 <<"InventoryAction::deSerialize() returned NULL"
1884 // If something goes wrong, this player is to blame
1885 RollbackScopeActor rollback_scope(m_rollback,
1886 std::string("player:")+player->getName());
1889 Note: Always set inventory not sent, to repair cases
1890 where the client made a bad prediction.
1894 Handle restrictions and special cases of the move action
1896 if(a->getType() == IACTION_MOVE)
1898 IMoveAction *ma = (IMoveAction*)a;
1900 ma->from_inv.applyCurrentPlayer(player->getName());
1901 ma->to_inv.applyCurrentPlayer(player->getName());
1903 setInventoryModified(ma->from_inv);
1904 setInventoryModified(ma->to_inv);
1906 bool from_inv_is_current_player =
1907 (ma->from_inv.type == InventoryLocation::PLAYER) &&
1908 (ma->from_inv.name == player->getName());
1910 bool to_inv_is_current_player =
1911 (ma->to_inv.type == InventoryLocation::PLAYER) &&
1912 (ma->to_inv.name == player->getName());
1915 Disable moving items out of craftpreview
1917 if(ma->from_list == "craftpreview")
1919 infostream<<"Ignoring IMoveAction from "
1920 <<(ma->from_inv.dump())<<":"<<ma->from_list
1921 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1922 <<" because src is "<<ma->from_list<<std::endl;
1928 Disable moving items into craftresult and craftpreview
1930 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
1932 infostream<<"Ignoring IMoveAction from "
1933 <<(ma->from_inv.dump())<<":"<<ma->from_list
1934 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1935 <<" because dst is "<<ma->to_list<<std::endl;
1940 // Disallow moving items in elsewhere than player's inventory
1941 // if not allowed to interact
1942 if(!checkPriv(player->getName(), "interact") &&
1943 (!from_inv_is_current_player ||
1944 !to_inv_is_current_player))
1946 infostream<<"Cannot move outside of player's inventory: "
1947 <<"No interact privilege"<<std::endl;
1953 Handle restrictions and special cases of the drop action
1955 else if(a->getType() == IACTION_DROP)
1957 IDropAction *da = (IDropAction*)a;
1959 da->from_inv.applyCurrentPlayer(player->getName());
1961 setInventoryModified(da->from_inv);
1964 Disable dropping items out of craftpreview
1966 if(da->from_list == "craftpreview")
1968 infostream<<"Ignoring IDropAction from "
1969 <<(da->from_inv.dump())<<":"<<da->from_list
1970 <<" because src is "<<da->from_list<<std::endl;
1975 // Disallow dropping items if not allowed to interact
1976 if(!checkPriv(player->getName(), "interact"))
1983 Handle restrictions and special cases of the craft action
1985 else if(a->getType() == IACTION_CRAFT)
1987 ICraftAction *ca = (ICraftAction*)a;
1989 ca->craft_inv.applyCurrentPlayer(player->getName());
1991 setInventoryModified(ca->craft_inv);
1993 //bool craft_inv_is_current_player =
1994 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
1995 // (ca->craft_inv.name == player->getName());
1997 // Disallow crafting if not allowed to interact
1998 if(!checkPriv(player->getName(), "interact"))
2000 infostream<<"Cannot craft: "
2001 <<"No interact privilege"<<std::endl;
2008 a->apply(this, playersao, this);
2012 else if(command == TOSERVER_CHAT_MESSAGE)
2020 std::string datastring((char*)&data[2], datasize-2);
2021 std::istringstream is(datastring, std::ios_base::binary);
2024 is.read((char*)buf, 2);
2025 u16 len = readU16(buf);
2027 std::wstring message;
2028 for(u16 i=0; i<len; i++)
2030 is.read((char*)buf, 2);
2031 message += (wchar_t)readU16(buf);
2034 // If something goes wrong, this player is to blame
2035 RollbackScopeActor rollback_scope(m_rollback,
2036 std::string("player:")+player->getName());
2038 // Get player name of this client
2039 std::wstring name = narrow_to_wide(player->getName());
2042 bool ate = m_script->on_chat_message(player->getName(),
2043 wide_to_narrow(message));
2044 // If script ate the message, don't proceed
2048 // Line to send to players
2050 // Whether to send to the player that sent the line
2051 bool send_to_sender_only = false;
2053 // Commands are implemented in Lua, so only catch invalid
2054 // commands that were not "eaten" and send an error back
2055 if(message[0] == L'/')
2057 message = message.substr(1);
2058 send_to_sender_only = true;
2059 if(message.length() == 0)
2060 line += L"-!- Empty command";
2062 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2066 if(checkPriv(player->getName(), "shout")){
2072 line += L"-!- You don't have permission to shout.";
2073 send_to_sender_only = true;
2080 Send the message to sender
2082 if (send_to_sender_only)
2084 SendChatMessage(peer_id, line);
2087 Send the message to others
2091 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2093 std::list<u16> clients = m_clients.getClientIDs();
2095 for(std::list<u16>::iterator
2096 i = clients.begin();
2097 i != clients.end(); ++i)
2100 SendChatMessage(*i, line);
2105 else if(command == TOSERVER_DAMAGE)
2107 std::string datastring((char*)&data[2], datasize-2);
2108 std::istringstream is(datastring, std::ios_base::binary);
2109 u8 damage = readU8(is);
2111 if(g_settings->getBool("enable_damage"))
2113 actionstream<<player->getName()<<" damaged by "
2114 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2117 playersao->setHP(playersao->getHP() - damage);
2119 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2122 if(playersao->m_hp_not_sent)
2123 SendPlayerHP(peer_id);
2126 else if(command == TOSERVER_BREATH)
2128 std::string datastring((char*)&data[2], datasize-2);
2129 std::istringstream is(datastring, std::ios_base::binary);
2130 u16 breath = readU16(is);
2131 playersao->setBreath(breath);
2133 else if(command == TOSERVER_PASSWORD)
2136 [0] u16 TOSERVER_PASSWORD
2137 [2] u8[28] old password
2138 [30] u8[28] new password
2141 if(datasize != 2+PASSWORD_SIZE*2)
2143 /*char password[PASSWORD_SIZE];
2144 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2145 password[i] = data[2+i];
2146 password[PASSWORD_SIZE-1] = 0;*/
2148 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2156 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2158 char c = data[2+PASSWORD_SIZE+i];
2164 if(!base64_is_valid(newpwd)){
2165 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2166 // Wrong old password supplied!!
2167 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2171 infostream<<"Server: Client requests a password change from "
2172 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2174 std::string playername = player->getName();
2176 std::string checkpwd;
2177 m_script->getAuth(playername, &checkpwd, NULL);
2179 if(oldpwd != checkpwd)
2181 infostream<<"Server: invalid old password"<<std::endl;
2182 // Wrong old password supplied!!
2183 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2187 bool success = m_script->setPassword(playername, newpwd);
2189 actionstream<<player->getName()<<" changes password"<<std::endl;
2190 SendChatMessage(peer_id, L"Password change successful.");
2192 actionstream<<player->getName()<<" tries to change password but "
2193 <<"it fails"<<std::endl;
2194 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2197 else if(command == TOSERVER_PLAYERITEM)
2202 u16 item = readU16(&data[2]);
2203 playersao->setWieldIndex(item);
2205 else if(command == TOSERVER_RESPAWN)
2207 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2210 RespawnPlayer(peer_id);
2212 actionstream<<player->getName()<<" respawns at "
2213 <<PP(player->getPosition()/BS)<<std::endl;
2215 // ActiveObject is added to environment in AsyncRunStep after
2216 // the previous addition has been succesfully removed
2218 else if(command == TOSERVER_INTERACT)
2220 std::string datastring((char*)&data[2], datasize-2);
2221 std::istringstream is(datastring, std::ios_base::binary);
2227 [5] u32 length of the next item
2228 [9] serialized PointedThing
2230 0: start digging (from undersurface) or use
2231 1: stop digging (all parameters ignored)
2232 2: digging completed
2233 3: place block or item (to abovesurface)
2236 u8 action = readU8(is);
2237 u16 item_i = readU16(is);
2238 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2239 PointedThing pointed;
2240 pointed.deSerialize(tmp_is);
2242 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2243 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2247 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2248 <<" tried to interact, but is dead!"<<std::endl;
2252 v3f player_pos = playersao->getLastGoodPosition();
2254 // Update wielded item
2255 playersao->setWieldIndex(item_i);
2257 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2258 v3s16 p_under = pointed.node_undersurface;
2259 v3s16 p_above = pointed.node_abovesurface;
2261 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2262 ServerActiveObject *pointed_object = NULL;
2263 if(pointed.type == POINTEDTHING_OBJECT)
2265 pointed_object = m_env->getActiveObject(pointed.object_id);
2266 if(pointed_object == NULL)
2268 verbosestream<<"TOSERVER_INTERACT: "
2269 "pointed object is NULL"<<std::endl;
2275 v3f pointed_pos_under = player_pos;
2276 v3f pointed_pos_above = player_pos;
2277 if(pointed.type == POINTEDTHING_NODE)
2279 pointed_pos_under = intToFloat(p_under, BS);
2280 pointed_pos_above = intToFloat(p_above, BS);
2282 else if(pointed.type == POINTEDTHING_OBJECT)
2284 pointed_pos_under = pointed_object->getBasePosition();
2285 pointed_pos_above = pointed_pos_under;
2289 Check that target is reasonably close
2290 (only when digging or placing things)
2292 if(action == 0 || action == 2 || action == 3)
2294 float d = player_pos.getDistanceFrom(pointed_pos_under);
2295 float max_d = BS * 14; // Just some large enough value
2297 actionstream<<"Player "<<player->getName()
2298 <<" tried to access "<<pointed.dump()
2300 <<"d="<<d<<", max_d="<<max_d
2301 <<". ignoring."<<std::endl;
2302 // Re-send block to revert change on client-side
2303 RemoteClient *client = getClient(peer_id);
2304 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2305 client->SetBlockNotSent(blockpos);
2307 m_script->on_cheat(playersao, "interacted_too_far");
2314 Make sure the player is allowed to do it
2316 if(!checkPriv(player->getName(), "interact"))
2318 actionstream<<player->getName()<<" attempted to interact with "
2319 <<pointed.dump()<<" without 'interact' privilege"
2321 // Re-send block to revert change on client-side
2322 RemoteClient *client = getClient(peer_id);
2323 // Digging completed -> under
2325 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2326 client->SetBlockNotSent(blockpos);
2328 // Placement -> above
2330 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2331 client->SetBlockNotSent(blockpos);
2337 If something goes wrong, this player is to blame
2339 RollbackScopeActor rollback_scope(m_rollback,
2340 std::string("player:")+player->getName());
2343 0: start digging or punch object
2347 if(pointed.type == POINTEDTHING_NODE)
2350 NOTE: This can be used in the future to check if
2351 somebody is cheating, by checking the timing.
2353 MapNode n(CONTENT_IGNORE);
2356 n = m_env->getMap().getNode(p_under);
2358 catch(InvalidPositionException &e)
2360 infostream<<"Server: Not punching: Node not found."
2361 <<" Adding block to emerge queue."
2363 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2365 if(n.getContent() != CONTENT_IGNORE)
2366 m_script->node_on_punch(p_under, n, playersao, pointed);
2368 playersao->noCheatDigStart(p_under);
2370 else if(pointed.type == POINTEDTHING_OBJECT)
2372 // Skip if object has been removed
2373 if(pointed_object->m_removed)
2376 actionstream<<player->getName()<<" punches object "
2377 <<pointed.object_id<<": "
2378 <<pointed_object->getDescription()<<std::endl;
2380 ItemStack punchitem = playersao->getWieldedItem();
2381 ToolCapabilities toolcap =
2382 punchitem.getToolCapabilities(m_itemdef);
2383 v3f dir = (pointed_object->getBasePosition() -
2384 (player->getPosition() + player->getEyeOffset())
2386 float time_from_last_punch =
2387 playersao->resetTimeFromLastPunch();
2388 pointed_object->punch(dir, &toolcap, playersao,
2389 time_from_last_punch);
2397 else if(action == 1)
2402 2: Digging completed
2404 else if(action == 2)
2406 // Only digging of nodes
2407 if(pointed.type == POINTEDTHING_NODE)
2409 MapNode n(CONTENT_IGNORE);
2412 n = m_env->getMap().getNode(p_under);
2414 catch(InvalidPositionException &e)
2416 infostream<<"Server: Not finishing digging: Node not found."
2417 <<" Adding block to emerge queue."
2419 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2422 /* Cheat prevention */
2423 bool is_valid_dig = true;
2424 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2426 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2427 float nocheat_t = playersao->getNoCheatDigTime();
2428 playersao->noCheatDigEnd();
2429 // If player didn't start digging this, ignore dig
2430 if(nocheat_p != p_under){
2431 infostream<<"Server: NoCheat: "<<player->getName()
2432 <<" started digging "
2433 <<PP(nocheat_p)<<" and completed digging "
2434 <<PP(p_under)<<"; not digging."<<std::endl;
2435 is_valid_dig = false;
2437 m_script->on_cheat(playersao, "finished_unknown_dig");
2439 // Get player's wielded item
2440 ItemStack playeritem;
2441 InventoryList *mlist = playersao->getInventory()->getList("main");
2443 playeritem = mlist->getItem(playersao->getWieldIndex());
2444 ToolCapabilities playeritem_toolcap =
2445 playeritem.getToolCapabilities(m_itemdef);
2446 // Get diggability and expected digging time
2447 DigParams params = getDigParams(m_nodedef->get(n).groups,
2448 &playeritem_toolcap);
2449 // If can't dig, try hand
2450 if(!params.diggable){
2451 const ItemDefinition &hand = m_itemdef->get("");
2452 const ToolCapabilities *tp = hand.tool_capabilities;
2454 params = getDigParams(m_nodedef->get(n).groups, tp);
2456 // If can't dig, ignore dig
2457 if(!params.diggable){
2458 infostream<<"Server: NoCheat: "<<player->getName()
2459 <<" completed digging "<<PP(p_under)
2460 <<", which is not diggable with tool. not digging."
2462 is_valid_dig = false;
2464 m_script->on_cheat(playersao, "dug_unbreakable");
2466 // Check digging time
2467 // If already invalidated, we don't have to
2469 // Well not our problem then
2471 // Clean and long dig
2472 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2473 // All is good, but grab time from pool; don't care if
2474 // it's actually available
2475 playersao->getDigPool().grab(params.time);
2477 // Short or laggy dig
2478 // Try getting the time from pool
2479 else if(playersao->getDigPool().grab(params.time)){
2484 infostream<<"Server: NoCheat: "<<player->getName()
2485 <<" completed digging "<<PP(p_under)
2486 <<"too fast; not digging."<<std::endl;
2487 is_valid_dig = false;
2489 m_script->on_cheat(playersao, "dug_too_fast");
2493 /* Actually dig node */
2495 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2496 m_script->node_on_dig(p_under, n, playersao);
2498 // Send unusual result (that is, node not being removed)
2499 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2501 // Re-send block to revert change on client-side
2502 RemoteClient *client = getClient(peer_id);
2503 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2504 client->SetBlockNotSent(blockpos);
2510 3: place block or right-click object
2512 else if(action == 3)
2514 ItemStack item = playersao->getWieldedItem();
2516 // Reset build time counter
2517 if(pointed.type == POINTEDTHING_NODE &&
2518 item.getDefinition(m_itemdef).type == ITEM_NODE)
2519 getClient(peer_id)->m_time_from_building = 0.0;
2521 if(pointed.type == POINTEDTHING_OBJECT)
2523 // Right click object
2525 // Skip if object has been removed
2526 if(pointed_object->m_removed)
2529 actionstream<<player->getName()<<" right-clicks object "
2530 <<pointed.object_id<<": "
2531 <<pointed_object->getDescription()<<std::endl;
2534 pointed_object->rightClick(playersao);
2536 else if(m_script->item_OnPlace(
2537 item, playersao, pointed))
2539 // Placement was handled in lua
2541 // Apply returned ItemStack
2542 playersao->setWieldedItem(item);
2545 // If item has node placement prediction, always send the
2546 // blocks to make sure the client knows what exactly happened
2547 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2548 RemoteClient *client = getClient(peer_id);
2549 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2550 client->SetBlockNotSent(blockpos);
2551 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2552 if(blockpos2 != blockpos){
2553 client->SetBlockNotSent(blockpos2);
2561 else if(action == 4)
2563 ItemStack item = playersao->getWieldedItem();
2565 actionstream<<player->getName()<<" uses "<<item.name
2566 <<", pointing at "<<pointed.dump()<<std::endl;
2568 if(m_script->item_OnUse(
2569 item, playersao, pointed))
2571 // Apply returned ItemStack
2572 playersao->setWieldedItem(item);
2579 Catch invalid actions
2583 infostream<<"WARNING: Server: Invalid action "
2584 <<action<<std::endl;
2587 else if(command == TOSERVER_REMOVED_SOUNDS)
2589 std::string datastring((char*)&data[2], datasize-2);
2590 std::istringstream is(datastring, std::ios_base::binary);
2592 int num = readU16(is);
2593 for(int k=0; k<num; k++){
2594 s32 id = readS32(is);
2595 std::map<s32, ServerPlayingSound>::iterator i =
2596 m_playing_sounds.find(id);
2597 if(i == m_playing_sounds.end())
2599 ServerPlayingSound &psound = i->second;
2600 psound.clients.erase(peer_id);
2601 if(psound.clients.size() == 0)
2602 m_playing_sounds.erase(i++);
2605 else if(command == TOSERVER_NODEMETA_FIELDS)
2607 std::string datastring((char*)&data[2], datasize-2);
2608 std::istringstream is(datastring, std::ios_base::binary);
2610 v3s16 p = readV3S16(is);
2611 std::string formname = deSerializeString(is);
2612 int num = readU16(is);
2613 std::map<std::string, std::string> fields;
2614 for(int k=0; k<num; k++){
2615 std::string fieldname = deSerializeString(is);
2616 std::string fieldvalue = deSerializeLongString(is);
2617 fields[fieldname] = fieldvalue;
2620 // If something goes wrong, this player is to blame
2621 RollbackScopeActor rollback_scope(m_rollback,
2622 std::string("player:")+player->getName());
2624 // Check the target node for rollback data; leave others unnoticed
2625 RollbackNode rn_old(&m_env->getMap(), p, this);
2627 m_script->node_on_receive_fields(p, formname, fields,playersao);
2629 // Report rollback data
2630 RollbackNode rn_new(&m_env->getMap(), p, this);
2631 if(rollback() && rn_new != rn_old){
2632 RollbackAction action;
2633 action.setSetNode(p, rn_old, rn_new);
2634 rollback()->reportAction(action);
2637 else if(command == TOSERVER_INVENTORY_FIELDS)
2639 std::string datastring((char*)&data[2], datasize-2);
2640 std::istringstream is(datastring, std::ios_base::binary);
2642 std::string formname = deSerializeString(is);
2643 int num = readU16(is);
2644 std::map<std::string, std::string> fields;
2645 for(int k=0; k<num; k++){
2646 std::string fieldname = deSerializeString(is);
2647 std::string fieldvalue = deSerializeLongString(is);
2648 fields[fieldname] = fieldvalue;
2651 m_script->on_playerReceiveFields(playersao, formname, fields);
2655 infostream<<"Server::ProcessData(): Ignoring "
2656 "unknown command "<<command<<std::endl;
2660 catch(SendFailedException &e)
2662 errorstream<<"Server::ProcessData(): SendFailedException: "
2668 void Server::setTimeOfDay(u32 time)
2670 m_env->setTimeOfDay(time);
2671 m_time_of_day_send_timer = 0;
2674 void Server::onMapEditEvent(MapEditEvent *event)
2676 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2677 if(m_ignore_map_edit_events)
2679 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2681 MapEditEvent *e = event->clone();
2682 m_unsent_map_edit_queue.push_back(e);
2685 Inventory* Server::getInventory(const InventoryLocation &loc)
2688 case InventoryLocation::UNDEFINED:
2691 case InventoryLocation::CURRENT_PLAYER:
2694 case InventoryLocation::PLAYER:
2696 Player *player = m_env->getPlayer(loc.name.c_str());
2699 PlayerSAO *playersao = player->getPlayerSAO();
2702 return playersao->getInventory();
2705 case InventoryLocation::NODEMETA:
2707 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2710 return meta->getInventory();
2713 case InventoryLocation::DETACHED:
2715 if(m_detached_inventories.count(loc.name) == 0)
2717 return m_detached_inventories[loc.name];
2725 void Server::setInventoryModified(const InventoryLocation &loc)
2728 case InventoryLocation::UNDEFINED:
2731 case InventoryLocation::PLAYER:
2733 Player *player = m_env->getPlayer(loc.name.c_str());
2736 PlayerSAO *playersao = player->getPlayerSAO();
2739 playersao->m_inventory_not_sent = true;
2740 playersao->m_wielded_item_not_sent = true;
2743 case InventoryLocation::NODEMETA:
2745 v3s16 blockpos = getNodeBlockPos(loc.p);
2747 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2749 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2751 setBlockNotSent(blockpos);
2754 case InventoryLocation::DETACHED:
2756 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2764 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2766 std::list<u16> clients = m_clients.getClientIDs();
2768 // Set the modified blocks unsent for all the clients
2769 for (std::list<u16>::iterator
2770 i = clients.begin();
2771 i != clients.end(); ++i) {
2772 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2774 client->SetBlocksNotSent(block);
2779 void Server::peerAdded(con::Peer *peer)
2781 DSTACK(__FUNCTION_NAME);
2782 verbosestream<<"Server::peerAdded(): peer->id="
2783 <<peer->id<<std::endl;
2786 c.type = con::PEER_ADDED;
2787 c.peer_id = peer->id;
2789 m_peer_change_queue.push_back(c);
2792 void Server::deletingPeer(con::Peer *peer, bool timeout)
2794 DSTACK(__FUNCTION_NAME);
2795 verbosestream<<"Server::deletingPeer(): peer->id="
2796 <<peer->id<<", timeout="<<timeout<<std::endl;
2798 m_clients.event(peer->id,Disconnect);
2800 c.type = con::PEER_REMOVED;
2801 c.peer_id = peer->id;
2802 c.timeout = timeout;
2803 m_peer_change_queue.push_back(c);
2806 void Server::handlePeerChanges()
2808 while(m_peer_change_queue.size() > 0)
2810 con::PeerChange c = m_peer_change_queue.pop_front();
2812 verbosestream<<"Server: Handling peer change: "
2813 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2818 case con::PEER_ADDED:
2819 m_clients.CreateClient(c.peer_id);
2822 case con::PEER_REMOVED:
2823 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2827 assert("Invalid peer change event received!" == 0);
2833 void Server::SendMovement(u16 peer_id)
2835 DSTACK(__FUNCTION_NAME);
2836 std::ostringstream os(std::ios_base::binary);
2838 writeU16(os, TOCLIENT_MOVEMENT);
2839 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2840 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2841 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2842 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2843 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2844 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2845 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2846 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2847 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2848 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2849 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2850 writeF1000(os, g_settings->getFloat("movement_gravity"));
2853 std::string s = os.str();
2854 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2856 m_clients.send(peer_id, 0, data, true);
2859 void Server::SendHP(u16 peer_id, u8 hp)
2861 DSTACK(__FUNCTION_NAME);
2862 std::ostringstream os(std::ios_base::binary);
2864 writeU16(os, TOCLIENT_HP);
2868 std::string s = os.str();
2869 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2871 m_clients.send(peer_id, 0, data, true);
2874 void Server::SendBreath(u16 peer_id, u16 breath)
2876 DSTACK(__FUNCTION_NAME);
2877 std::ostringstream os(std::ios_base::binary);
2879 writeU16(os, TOCLIENT_BREATH);
2880 writeU16(os, breath);
2883 std::string s = os.str();
2884 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2886 m_clients.send(peer_id, 0, data, true);
2889 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
2891 DSTACK(__FUNCTION_NAME);
2892 std::ostringstream os(std::ios_base::binary);
2894 writeU16(os, TOCLIENT_ACCESS_DENIED);
2895 os<<serializeWideString(reason);
2898 std::string s = os.str();
2899 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2901 m_clients.send(peer_id, 0, data, true);
2904 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
2905 v3f camera_point_target)
2907 DSTACK(__FUNCTION_NAME);
2908 std::ostringstream os(std::ios_base::binary);
2910 writeU16(os, TOCLIENT_DEATHSCREEN);
2911 writeU8(os, set_camera_point_target);
2912 writeV3F1000(os, camera_point_target);
2915 std::string s = os.str();
2916 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2918 m_clients.send(peer_id, 0, data, true);
2921 void Server::SendItemDef(u16 peer_id,
2922 IItemDefManager *itemdef, u16 protocol_version)
2924 DSTACK(__FUNCTION_NAME);
2925 std::ostringstream os(std::ios_base::binary);
2929 u32 length of the next item
2930 zlib-compressed serialized ItemDefManager
2932 writeU16(os, TOCLIENT_ITEMDEF);
2933 std::ostringstream tmp_os(std::ios::binary);
2934 itemdef->serialize(tmp_os, protocol_version);
2935 std::ostringstream tmp_os2(std::ios::binary);
2936 compressZlib(tmp_os.str(), tmp_os2);
2937 os<<serializeLongString(tmp_os2.str());
2940 std::string s = os.str();
2941 verbosestream<<"Server: Sending item definitions to id("<<peer_id
2942 <<"): size="<<s.size()<<std::endl;
2943 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2945 m_clients.send(peer_id, 0, data, true);
2948 void Server::SendNodeDef(u16 peer_id,
2949 INodeDefManager *nodedef, u16 protocol_version)
2951 DSTACK(__FUNCTION_NAME);
2952 std::ostringstream os(std::ios_base::binary);
2956 u32 length of the next item
2957 zlib-compressed serialized NodeDefManager
2959 writeU16(os, TOCLIENT_NODEDEF);
2960 std::ostringstream tmp_os(std::ios::binary);
2961 nodedef->serialize(tmp_os, protocol_version);
2962 std::ostringstream tmp_os2(std::ios::binary);
2963 compressZlib(tmp_os.str(), tmp_os2);
2964 os<<serializeLongString(tmp_os2.str());
2967 std::string s = os.str();
2968 verbosestream<<"Server: Sending node definitions to id("<<peer_id
2969 <<"): size="<<s.size()<<std::endl;
2970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2972 m_clients.send(peer_id, 0, data, true);
2976 Non-static send methods
2979 void Server::SendInventory(u16 peer_id)
2981 DSTACK(__FUNCTION_NAME);
2983 PlayerSAO *playersao = getPlayerSAO(peer_id);
2986 playersao->m_inventory_not_sent = false;
2992 std::ostringstream os;
2993 playersao->getInventory()->serialize(os);
2995 std::string s = os.str();
2997 SharedBuffer<u8> data(s.size()+2);
2998 writeU16(&data[0], TOCLIENT_INVENTORY);
2999 memcpy(&data[2], s.c_str(), s.size());
3002 m_clients.send(peer_id, 0, data, true);
3005 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3007 DSTACK(__FUNCTION_NAME);
3009 std::ostringstream os(std::ios_base::binary);
3013 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3014 os.write((char*)buf, 2);
3017 writeU16(buf, message.size());
3018 os.write((char*)buf, 2);
3021 for(u32 i=0; i<message.size(); i++)
3025 os.write((char*)buf, 2);
3029 std::string s = os.str();
3030 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3032 if (peer_id != PEER_ID_INEXISTENT)
3035 m_clients.send(peer_id, 0, data, true);
3039 m_clients.sendToAll(0,data,true);
3043 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3044 const std::string formname)
3046 DSTACK(__FUNCTION_NAME);
3048 std::ostringstream os(std::ios_base::binary);
3052 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3053 os.write((char*)buf, 2);
3054 os<<serializeLongString(formspec);
3055 os<<serializeString(formname);
3058 std::string s = os.str();
3059 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3061 m_clients.send(peer_id, 0, data, true);
3064 // Spawns a particle on peer with peer_id
3065 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3066 float expirationtime, float size, bool collisiondetection,
3067 bool vertical, std::string texture)
3069 DSTACK(__FUNCTION_NAME);
3071 std::ostringstream os(std::ios_base::binary);
3072 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3073 writeV3F1000(os, pos);
3074 writeV3F1000(os, velocity);
3075 writeV3F1000(os, acceleration);
3076 writeF1000(os, expirationtime);
3077 writeF1000(os, size);
3078 writeU8(os, collisiondetection);
3079 os<<serializeLongString(texture);
3080 writeU8(os, vertical);
3083 std::string s = os.str();
3084 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3086 if (peer_id != PEER_ID_INEXISTENT)
3089 m_clients.send(peer_id, 0, data, true);
3093 m_clients.sendToAll(0,data,true);
3097 // Adds a ParticleSpawner on peer with peer_id
3098 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3099 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3100 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3102 DSTACK(__FUNCTION_NAME);
3104 std::ostringstream os(std::ios_base::binary);
3105 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3107 writeU16(os, amount);
3108 writeF1000(os, spawntime);
3109 writeV3F1000(os, minpos);
3110 writeV3F1000(os, maxpos);
3111 writeV3F1000(os, minvel);
3112 writeV3F1000(os, maxvel);
3113 writeV3F1000(os, minacc);
3114 writeV3F1000(os, maxacc);
3115 writeF1000(os, minexptime);
3116 writeF1000(os, maxexptime);
3117 writeF1000(os, minsize);
3118 writeF1000(os, maxsize);
3119 writeU8(os, collisiondetection);
3120 os<<serializeLongString(texture);
3122 writeU8(os, vertical);
3125 std::string s = os.str();
3126 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3128 if (peer_id != PEER_ID_INEXISTENT)
3131 m_clients.send(peer_id, 0, data, true);
3134 m_clients.sendToAll(0,data,true);
3138 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3140 DSTACK(__FUNCTION_NAME);
3142 std::ostringstream os(std::ios_base::binary);
3143 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3148 std::string s = os.str();
3149 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3151 if (peer_id != PEER_ID_INEXISTENT) {
3153 m_clients.send(peer_id, 0, data, true);
3156 m_clients.sendToAll(0,data,true);
3161 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3163 std::ostringstream os(std::ios_base::binary);
3166 writeU16(os, TOCLIENT_HUDADD);
3168 writeU8(os, (u8)form->type);
3169 writeV2F1000(os, form->pos);
3170 os << serializeString(form->name);
3171 writeV2F1000(os, form->scale);
3172 os << serializeString(form->text);
3173 writeU32(os, form->number);
3174 writeU32(os, form->item);
3175 writeU32(os, form->dir);
3176 writeV2F1000(os, form->align);
3177 writeV2F1000(os, form->offset);
3178 writeV3F1000(os, form->world_pos);
3181 std::string s = os.str();
3182 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3184 m_clients.send(peer_id, 1, data, true);
3187 void Server::SendHUDRemove(u16 peer_id, u32 id)
3189 std::ostringstream os(std::ios_base::binary);
3192 writeU16(os, TOCLIENT_HUDRM);
3196 std::string s = os.str();
3197 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3200 m_clients.send(peer_id, 1, data, true);
3203 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3205 std::ostringstream os(std::ios_base::binary);
3208 writeU16(os, TOCLIENT_HUDCHANGE);
3210 writeU8(os, (u8)stat);
3213 case HUD_STAT_SCALE:
3214 case HUD_STAT_ALIGN:
3215 case HUD_STAT_OFFSET:
3216 writeV2F1000(os, *(v2f *)value);
3220 os << serializeString(*(std::string *)value);
3222 case HUD_STAT_WORLD_POS:
3223 writeV3F1000(os, *(v3f *)value);
3225 case HUD_STAT_NUMBER:
3229 writeU32(os, *(u32 *)value);
3234 std::string s = os.str();
3235 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3237 m_clients.send(peer_id, 0, data, true);
3240 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3242 std::ostringstream os(std::ios_base::binary);
3245 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3246 writeU32(os, flags);
3250 std::string s = os.str();
3251 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3253 m_clients.send(peer_id, 0, data, true);
3256 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3258 std::ostringstream os(std::ios_base::binary);
3261 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3262 writeU16(os, param);
3263 os<<serializeString(value);
3266 std::string s = os.str();
3267 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3269 m_clients.send(peer_id, 0, data, true);
3272 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3273 const std::string &type, const std::vector<std::string> ¶ms)
3275 std::ostringstream os(std::ios_base::binary);
3278 writeU16(os, TOCLIENT_SET_SKY);
3279 writeARGB8(os, bgcolor);
3280 os<<serializeString(type);
3281 writeU16(os, params.size());
3282 for(size_t i=0; i<params.size(); i++)
3283 os<<serializeString(params[i]);
3286 std::string s = os.str();
3287 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3289 m_clients.send(peer_id, 0, data, true);
3292 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
3295 std::ostringstream os(std::ios_base::binary);
3298 writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
3299 writeU8(os, do_override);
3300 writeU16(os, ratio*65535);
3303 std::string s = os.str();
3304 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3306 m_clients.send(peer_id, 0, data, true);
3309 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3311 DSTACK(__FUNCTION_NAME);
3314 SharedBuffer<u8> data(2+2+4);
3315 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3316 writeU16(&data[2], time);
3317 writeF1000(&data[4], time_speed);
3319 if (peer_id == PEER_ID_INEXISTENT) {
3320 m_clients.sendToAll(0,data,true);
3324 m_clients.send(peer_id, 0, data, true);
3328 void Server::SendPlayerHP(u16 peer_id)
3330 DSTACK(__FUNCTION_NAME);
3331 PlayerSAO *playersao = getPlayerSAO(peer_id);
3333 playersao->m_hp_not_sent = false;
3334 SendHP(peer_id, playersao->getHP());
3336 // Send to other clients
3337 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3338 ActiveObjectMessage aom(playersao->getId(), true, str);
3339 playersao->m_messages_out.push_back(aom);
3342 void Server::SendPlayerBreath(u16 peer_id)
3344 DSTACK(__FUNCTION_NAME);
3345 PlayerSAO *playersao = getPlayerSAO(peer_id);
3347 playersao->m_breath_not_sent = false;
3348 SendBreath(peer_id, playersao->getBreath());
3351 void Server::SendMovePlayer(u16 peer_id)
3353 DSTACK(__FUNCTION_NAME);
3354 Player *player = m_env->getPlayer(peer_id);
3357 std::ostringstream os(std::ios_base::binary);
3358 writeU16(os, TOCLIENT_MOVE_PLAYER);
3359 writeV3F1000(os, player->getPosition());
3360 writeF1000(os, player->getPitch());
3361 writeF1000(os, player->getYaw());
3364 v3f pos = player->getPosition();
3365 f32 pitch = player->getPitch();
3366 f32 yaw = player->getYaw();
3367 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3368 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3375 std::string s = os.str();
3376 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3378 m_clients.send(peer_id, 0, data, true);
3381 void Server::SendPlayerPrivileges(u16 peer_id)
3383 Player *player = m_env->getPlayer(peer_id);
3385 if(player->peer_id == PEER_ID_INEXISTENT)
3388 std::set<std::string> privs;
3389 m_script->getAuth(player->getName(), NULL, &privs);
3391 std::ostringstream os(std::ios_base::binary);
3392 writeU16(os, TOCLIENT_PRIVILEGES);
3393 writeU16(os, privs.size());
3394 for(std::set<std::string>::const_iterator i = privs.begin();
3395 i != privs.end(); i++){
3396 os<<serializeString(*i);
3400 std::string s = os.str();
3401 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3403 m_clients.send(peer_id, 0, data, true);
3406 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3408 Player *player = m_env->getPlayer(peer_id);
3410 if(player->peer_id == PEER_ID_INEXISTENT)
3413 std::ostringstream os(std::ios_base::binary);
3414 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3415 os<<serializeLongString(player->inventory_formspec);
3418 std::string s = os.str();
3419 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3421 m_clients.send(peer_id, 0, data, true);
3424 s32 Server::playSound(const SimpleSoundSpec &spec,
3425 const ServerSoundParams ¶ms)
3427 // Find out initial position of sound
3428 bool pos_exists = false;
3429 v3f pos = params.getPos(m_env, &pos_exists);
3430 // If position is not found while it should be, cancel sound
3431 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3434 // Filter destination clients
3435 std::list<u16> dst_clients;
3436 if(params.to_player != "")
3438 Player *player = m_env->getPlayer(params.to_player.c_str());
3440 infostream<<"Server::playSound: Player \""<<params.to_player
3441 <<"\" not found"<<std::endl;
3444 if(player->peer_id == PEER_ID_INEXISTENT){
3445 infostream<<"Server::playSound: Player \""<<params.to_player
3446 <<"\" not connected"<<std::endl;
3449 dst_clients.push_back(player->peer_id);
3453 std::list<u16> clients = m_clients.getClientIDs();
3455 for(std::list<u16>::iterator
3456 i = clients.begin(); i != clients.end(); ++i)
3458 Player *player = m_env->getPlayer(*i);
3462 if(player->getPosition().getDistanceFrom(pos) >
3463 params.max_hear_distance)
3466 dst_clients.push_back(*i);
3469 if(dst_clients.size() == 0)
3473 s32 id = m_next_sound_id++;
3474 // The sound will exist as a reference in m_playing_sounds
3475 m_playing_sounds[id] = ServerPlayingSound();
3476 ServerPlayingSound &psound = m_playing_sounds[id];
3477 psound.params = params;
3478 for(std::list<u16>::iterator i = dst_clients.begin();
3479 i != dst_clients.end(); i++)
3480 psound.clients.insert(*i);
3482 std::ostringstream os(std::ios_base::binary);
3483 writeU16(os, TOCLIENT_PLAY_SOUND);
3485 os<<serializeString(spec.name);
3486 writeF1000(os, spec.gain * params.gain);
3487 writeU8(os, params.type);
3488 writeV3F1000(os, pos);
3489 writeU16(os, params.object);
3490 writeU8(os, params.loop);
3492 std::string s = os.str();
3493 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3495 for(std::list<u16>::iterator i = dst_clients.begin();
3496 i != dst_clients.end(); i++){
3498 m_clients.send(*i, 0, data, true);
3502 void Server::stopSound(s32 handle)
3504 // Get sound reference
3505 std::map<s32, ServerPlayingSound>::iterator i =
3506 m_playing_sounds.find(handle);
3507 if(i == m_playing_sounds.end())
3509 ServerPlayingSound &psound = i->second;
3511 std::ostringstream os(std::ios_base::binary);
3512 writeU16(os, TOCLIENT_STOP_SOUND);
3513 writeS32(os, handle);
3515 std::string s = os.str();
3516 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3518 for(std::set<u16>::iterator i = psound.clients.begin();
3519 i != psound.clients.end(); i++){
3521 m_clients.send(*i, 0, data, true);
3523 // Remove sound reference
3524 m_playing_sounds.erase(i);
3527 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3528 std::list<u16> *far_players, float far_d_nodes)
3530 float maxd = far_d_nodes*BS;
3531 v3f p_f = intToFloat(p, BS);
3535 SharedBuffer<u8> reply(replysize);
3536 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3537 writeS16(&reply[2], p.X);
3538 writeS16(&reply[4], p.Y);
3539 writeS16(&reply[6], p.Z);
3541 std::list<u16> clients = m_clients.getClientIDs();
3542 for(std::list<u16>::iterator
3543 i = clients.begin();
3544 i != clients.end(); ++i)
3549 Player *player = m_env->getPlayer(*i);
3552 // If player is far away, only set modified blocks not sent
3553 v3f player_pos = player->getPosition();
3554 if(player_pos.getDistanceFrom(p_f) > maxd)
3556 far_players->push_back(*i);
3563 m_clients.send(*i, 0, reply, true);
3567 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3568 std::list<u16> *far_players, float far_d_nodes,
3569 bool remove_metadata)
3571 float maxd = far_d_nodes*BS;
3572 v3f p_f = intToFloat(p, BS);
3574 std::list<u16> clients = m_clients.getClientIDs();
3575 for(std::list<u16>::iterator
3576 i = clients.begin();
3577 i != clients.end(); ++i)
3583 Player *player = m_env->getPlayer(*i);
3586 // If player is far away, only set modified blocks not sent
3587 v3f player_pos = player->getPosition();
3588 if(player_pos.getDistanceFrom(p_f) > maxd)
3590 far_players->push_back(*i);
3595 SharedBuffer<u8> reply(0);
3597 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3601 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3602 reply = SharedBuffer<u8>(replysize);
3603 writeU16(&reply[0], TOCLIENT_ADDNODE);
3604 writeS16(&reply[2], p.X);
3605 writeS16(&reply[4], p.Y);
3606 writeS16(&reply[6], p.Z);
3607 n.serialize(&reply[8], client->serialization_version);
3608 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3609 writeU8(&reply[index], remove_metadata ? 0 : 1);
3611 if (!remove_metadata) {
3612 if (client->net_proto_version <= 21) {
3613 // Old clients always clear metadata; fix it
3614 // by sending the full block again.
3615 client->SetBlockNotSent(p);
3622 if (reply.getSize() > 0)
3623 m_clients.send(*i, 0, reply, true);
3627 void Server::setBlockNotSent(v3s16 p)
3629 std::list<u16> clients = m_clients.getClientIDs();
3631 for(std::list<u16>::iterator
3632 i = clients.begin();
3633 i != clients.end(); ++i)
3635 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3636 client->SetBlockNotSent(p);
3641 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3643 DSTACK(__FUNCTION_NAME);
3645 v3s16 p = block->getPos();
3649 bool completely_air = true;
3650 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3651 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3652 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3654 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3656 completely_air = false;
3657 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3662 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3664 infostream<<"[completely air] ";
3665 infostream<<std::endl;
3669 Create a packet with the block in the right format
3672 std::ostringstream os(std::ios_base::binary);
3673 block->serialize(os, ver, false);
3674 block->serializeNetworkSpecific(os, net_proto_version);
3675 std::string s = os.str();
3676 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3678 u32 replysize = 8 + blockdata.getSize();
3679 SharedBuffer<u8> reply(replysize);
3680 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3681 writeS16(&reply[2], p.X);
3682 writeS16(&reply[4], p.Y);
3683 writeS16(&reply[6], p.Z);
3684 memcpy(&reply[8], *blockdata, blockdata.getSize());
3686 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3687 <<": \tpacket size: "<<replysize<<std::endl;*/
3692 m_clients.send(peer_id, 2, reply, true);
3695 void Server::SendBlocks(float dtime)
3697 DSTACK(__FUNCTION_NAME);
3699 JMutexAutoLock envlock(m_env_mutex);
3700 //TODO check if one big lock could be faster then multiple small ones
3702 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3704 std::vector<PrioritySortedBlockTransfer> queue;
3706 s32 total_sending = 0;
3709 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3711 std::list<u16> clients = m_clients.getClientIDs();
3714 for(std::list<u16>::iterator
3715 i = clients.begin();
3716 i != clients.end(); ++i)
3718 RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active);
3723 total_sending += client->SendingCount();
3724 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3730 // Lowest priority number comes first.
3731 // Lowest is most important.
3732 std::sort(queue.begin(), queue.end());
3735 for(u32 i=0; i<queue.size(); i++)
3737 //TODO: Calculate limit dynamically
3738 if(total_sending >= g_settings->getS32
3739 ("max_simultaneous_block_sends_server_total"))
3742 PrioritySortedBlockTransfer q = queue[i];
3744 MapBlock *block = NULL;
3747 block = m_env->getMap().getBlockNoCreate(q.pos);
3749 catch(InvalidPositionException &e)
3754 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active);
3759 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3761 client->SentBlock(q.pos);
3767 void Server::fillMediaCache()
3769 DSTACK(__FUNCTION_NAME);
3771 infostream<<"Server: Calculating media file checksums"<<std::endl;
3773 // Collect all media file paths
3774 std::list<std::string> paths;
3775 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3776 i != m_mods.end(); i++){
3777 const ModSpec &mod = *i;
3778 paths.push_back(mod.path + DIR_DELIM + "textures");
3779 paths.push_back(mod.path + DIR_DELIM + "sounds");
3780 paths.push_back(mod.path + DIR_DELIM + "media");
3781 paths.push_back(mod.path + DIR_DELIM + "models");
3783 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3785 // Collect media file information from paths into cache
3786 for(std::list<std::string>::iterator i = paths.begin();
3787 i != paths.end(); i++)
3789 std::string mediapath = *i;
3790 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3791 for(u32 j=0; j<dirlist.size(); j++){
3792 if(dirlist[j].dir) // Ignode dirs
3794 std::string filename = dirlist[j].name;
3795 // If name contains illegal characters, ignore the file
3796 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3797 infostream<<"Server: ignoring illegal file name: \""
3798 <<filename<<"\""<<std::endl;
3801 // If name is not in a supported format, ignore it
3802 const char *supported_ext[] = {
3803 ".png", ".jpg", ".bmp", ".tga",
3804 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3806 ".x", ".b3d", ".md2", ".obj",
3809 if(removeStringEnd(filename, supported_ext) == ""){
3810 infostream<<"Server: ignoring unsupported file extension: \""
3811 <<filename<<"\""<<std::endl;
3814 // Ok, attempt to load the file and add to cache
3815 std::string filepath = mediapath + DIR_DELIM + filename;
3817 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3818 if(fis.good() == false){
3819 errorstream<<"Server::fillMediaCache(): Could not open \""
3820 <<filename<<"\" for reading"<<std::endl;
3823 std::ostringstream tmp_os(std::ios_base::binary);
3827 fis.read(buf, 1024);
3828 std::streamsize len = fis.gcount();
3829 tmp_os.write(buf, len);
3838 errorstream<<"Server::fillMediaCache(): Failed to read \""
3839 <<filename<<"\""<<std::endl;
3842 if(tmp_os.str().length() == 0){
3843 errorstream<<"Server::fillMediaCache(): Empty file \""
3844 <<filepath<<"\""<<std::endl;
3849 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3851 unsigned char *digest = sha1.getDigest();
3852 std::string sha1_base64 = base64_encode(digest, 20);
3853 std::string sha1_hex = hex_encode((char*)digest, 20);
3857 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3858 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3863 struct SendableMediaAnnouncement
3866 std::string sha1_digest;
3868 SendableMediaAnnouncement(const std::string name_="",
3869 const std::string sha1_digest_=""):
3871 sha1_digest(sha1_digest_)
3875 void Server::sendMediaAnnouncement(u16 peer_id)
3877 DSTACK(__FUNCTION_NAME);
3879 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
3882 std::list<SendableMediaAnnouncement> file_announcements;
3884 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
3885 i != m_media.end(); i++){
3887 file_announcements.push_back(
3888 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
3892 std::ostringstream os(std::ios_base::binary);
3900 u16 length of sha1_digest
3905 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
3906 writeU16(os, file_announcements.size());
3908 for(std::list<SendableMediaAnnouncement>::iterator
3909 j = file_announcements.begin();
3910 j != file_announcements.end(); ++j){
3911 os<<serializeString(j->name);
3912 os<<serializeString(j->sha1_digest);
3914 os<<serializeString(g_settings->get("remote_media"));
3917 std::string s = os.str();
3918 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3921 m_clients.send(peer_id, 0, data, true);
3924 struct SendableMedia
3930 SendableMedia(const std::string &name_="", const std::string path_="",
3931 const std::string &data_=""):
3938 void Server::sendRequestedMedia(u16 peer_id,
3939 const std::list<std::string> &tosend)
3941 DSTACK(__FUNCTION_NAME);
3943 verbosestream<<"Server::sendRequestedMedia(): "
3944 <<"Sending files to client"<<std::endl;
3948 // Put 5kB in one bunch (this is not accurate)
3949 u32 bytes_per_bunch = 5000;
3951 std::vector< std::list<SendableMedia> > file_bunches;
3952 file_bunches.push_back(std::list<SendableMedia>());
3954 u32 file_size_bunch_total = 0;
3956 for(std::list<std::string>::const_iterator i = tosend.begin();
3957 i != tosend.end(); ++i)
3959 const std::string &name = *i;
3961 if(m_media.find(name) == m_media.end()){
3962 errorstream<<"Server::sendRequestedMedia(): Client asked for "
3963 <<"unknown file \""<<(name)<<"\""<<std::endl;
3967 //TODO get path + name
3968 std::string tpath = m_media[name].path;
3971 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
3972 if(fis.good() == false){
3973 errorstream<<"Server::sendRequestedMedia(): Could not open \""
3974 <<tpath<<"\" for reading"<<std::endl;
3977 std::ostringstream tmp_os(std::ios_base::binary);
3981 fis.read(buf, 1024);
3982 std::streamsize len = fis.gcount();
3983 tmp_os.write(buf, len);
3984 file_size_bunch_total += len;
3993 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
3994 <<name<<"\""<<std::endl;
3997 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
3998 <<tname<<"\""<<std::endl;*/
4000 file_bunches[file_bunches.size()-1].push_back(
4001 SendableMedia(name, tpath, tmp_os.str()));
4003 // Start next bunch if got enough data
4004 if(file_size_bunch_total >= bytes_per_bunch){
4005 file_bunches.push_back(std::list<SendableMedia>());
4006 file_size_bunch_total = 0;
4011 /* Create and send packets */
4013 u32 num_bunches = file_bunches.size();
4014 for(u32 i=0; i<num_bunches; i++)
4016 std::ostringstream os(std::ios_base::binary);
4020 u16 total number of texture bunches
4021 u16 index of this bunch
4022 u32 number of files in this bunch
4031 writeU16(os, TOCLIENT_MEDIA);
4032 writeU16(os, num_bunches);
4034 writeU32(os, file_bunches[i].size());
4036 for(std::list<SendableMedia>::iterator
4037 j = file_bunches[i].begin();
4038 j != file_bunches[i].end(); ++j){
4039 os<<serializeString(j->name);
4040 os<<serializeLongString(j->data);
4044 std::string s = os.str();
4045 verbosestream<<"Server::sendRequestedMedia(): bunch "
4046 <<i<<"/"<<num_bunches
4047 <<" files="<<file_bunches[i].size()
4048 <<" size=" <<s.size()<<std::endl;
4049 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4051 m_clients.send(peer_id, 2, data, true);
4055 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4057 if(m_detached_inventories.count(name) == 0){
4058 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4061 Inventory *inv = m_detached_inventories[name];
4063 std::ostringstream os(std::ios_base::binary);
4064 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4065 os<<serializeString(name);
4069 std::string s = os.str();
4070 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4072 if (peer_id != PEER_ID_INEXISTENT)
4075 m_clients.send(peer_id, 0, data, true);
4079 m_clients.sendToAll(0,data,true);
4083 void Server::sendDetachedInventories(u16 peer_id)
4085 DSTACK(__FUNCTION_NAME);
4087 for(std::map<std::string, Inventory*>::iterator
4088 i = m_detached_inventories.begin();
4089 i != m_detached_inventories.end(); i++){
4090 const std::string &name = i->first;
4091 //Inventory *inv = i->second;
4092 sendDetachedInventory(name, peer_id);
4100 void Server::DiePlayer(u16 peer_id)
4102 DSTACK(__FUNCTION_NAME);
4104 PlayerSAO *playersao = getPlayerSAO(peer_id);
4107 infostream<<"Server::DiePlayer(): Player "
4108 <<playersao->getPlayer()->getName()
4109 <<" dies"<<std::endl;
4111 playersao->setHP(0);
4113 // Trigger scripted stuff
4114 m_script->on_dieplayer(playersao);
4116 SendPlayerHP(peer_id);
4117 SendDeathscreen(peer_id, false, v3f(0,0,0));
4120 void Server::RespawnPlayer(u16 peer_id)
4122 DSTACK(__FUNCTION_NAME);
4124 PlayerSAO *playersao = getPlayerSAO(peer_id);
4127 infostream<<"Server::RespawnPlayer(): Player "
4128 <<playersao->getPlayer()->getName()
4129 <<" respawns"<<std::endl;
4131 playersao->setHP(PLAYER_MAX_HP);
4133 bool repositioned = m_script->on_respawnplayer(playersao);
4135 v3f pos = findSpawnPos(m_env->getServerMap());
4136 playersao->setPos(pos);
4140 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4142 DSTACK(__FUNCTION_NAME);
4144 SendAccessDenied(peer_id, reason);
4145 m_clients.event(peer_id,SetDenied);
4146 m_con.DisconnectPeer(peer_id);
4149 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4151 DSTACK(__FUNCTION_NAME);
4152 std::wstring message;
4155 Clear references to playing sounds
4157 for(std::map<s32, ServerPlayingSound>::iterator
4158 i = m_playing_sounds.begin();
4159 i != m_playing_sounds.end();)
4161 ServerPlayingSound &psound = i->second;
4162 psound.clients.erase(peer_id);
4163 if(psound.clients.size() == 0)
4164 m_playing_sounds.erase(i++);
4169 Player *player = m_env->getPlayer(peer_id);
4171 // Collect information about leaving in chat
4173 if(player != NULL && reason != CDR_DENY)
4175 std::wstring name = narrow_to_wide(player->getName());
4178 message += L" left the game.";
4179 if(reason == CDR_TIMEOUT)
4180 message += L" (timed out)";
4184 /* Run scripts and remove from environment */
4188 PlayerSAO *playersao = player->getPlayerSAO();
4191 m_script->on_leaveplayer(playersao);
4193 playersao->disconnected();
4201 if(player != NULL && reason != CDR_DENY)
4203 std::ostringstream os(std::ios_base::binary);
4204 std::list<u16> clients = m_clients.getClientIDs();
4206 for(std::list<u16>::iterator
4207 i = clients.begin();
4208 i != clients.end(); ++i)
4211 Player *player = m_env->getPlayer(*i);
4214 // Get name of player
4215 os<<player->getName()<<" ";
4218 actionstream<<player->getName()<<" "
4219 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4220 <<" List of players: "<<os.str()<<std::endl;
4224 m_clients.DeleteClient(peer_id);
4225 m_env_mutex.Unlock();
4228 // Send leave chat message to all remaining clients
4229 if(message.length() != 0)
4230 SendChatMessage(PEER_ID_INEXISTENT,message);
4233 void Server::UpdateCrafting(u16 peer_id)
4235 DSTACK(__FUNCTION_NAME);
4237 Player* player = m_env->getPlayer(peer_id);
4240 // Get a preview for crafting
4242 InventoryLocation loc;
4243 loc.setPlayer(player->getName());
4244 getCraftingResult(&player->inventory, preview, false, this);
4245 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4247 // Put the new preview in
4248 InventoryList *plist = player->inventory.getList("craftpreview");
4250 assert(plist->getSize() >= 1);
4251 plist->changeItem(0, preview);
4254 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4256 RemoteClient *client = getClientNoEx(peer_id,state_min);
4258 throw ClientNotFoundException("Client not found");
4262 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4264 return m_clients.getClientNoEx(peer_id, state_min);
4267 std::string Server::getPlayerName(u16 peer_id)
4269 Player *player = m_env->getPlayer(peer_id);
4271 return "[id="+itos(peer_id)+"]";
4272 return player->getName();
4275 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4277 Player *player = m_env->getPlayer(peer_id);
4280 return player->getPlayerSAO();
4283 std::wstring Server::getStatusString()
4285 std::wostringstream os(std::ios_base::binary);
4288 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4290 os<<L", uptime="<<m_uptime.get();
4292 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4293 // Information about clients
4296 std::list<u16> clients = m_clients.getClientIDs();
4297 for(std::list<u16>::iterator i = clients.begin();
4298 i != clients.end(); ++i)
4301 Player *player = m_env->getPlayer(*i);
4302 // Get name of player
4303 std::wstring name = L"unknown";
4305 name = narrow_to_wide(player->getName());
4306 // Add name to information string
4314 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4315 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4316 if(g_settings->get("motd") != "")
4317 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4321 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4323 std::set<std::string> privs;
4324 m_script->getAuth(name, NULL, &privs);
4328 bool Server::checkPriv(const std::string &name, const std::string &priv)
4330 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4331 return (privs.count(priv) != 0);
4334 void Server::reportPrivsModified(const std::string &name)
4337 std::list<u16> clients = m_clients.getClientIDs();
4338 for(std::list<u16>::iterator
4339 i = clients.begin();
4340 i != clients.end(); ++i){
4341 Player *player = m_env->getPlayer(*i);
4342 reportPrivsModified(player->getName());
4345 Player *player = m_env->getPlayer(name.c_str());
4348 SendPlayerPrivileges(player->peer_id);
4349 PlayerSAO *sao = player->getPlayerSAO();
4352 sao->updatePrivileges(
4353 getPlayerEffectivePrivs(name),
4358 void Server::reportInventoryFormspecModified(const std::string &name)
4360 Player *player = m_env->getPlayer(name.c_str());
4363 SendPlayerInventoryFormspec(player->peer_id);
4366 void Server::setIpBanned(const std::string &ip, const std::string &name)
4368 m_banmanager->add(ip, name);
4371 void Server::unsetIpBanned(const std::string &ip_or_name)
4373 m_banmanager->remove(ip_or_name);
4376 std::string Server::getBanDescription(const std::string &ip_or_name)
4378 return m_banmanager->getBanDescription(ip_or_name);
4381 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4383 Player *player = m_env->getPlayer(name);
4387 if (player->peer_id == PEER_ID_INEXISTENT)
4391 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4393 SendChatMessage(player->peer_id, msg);
4396 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4398 Player *player = m_env->getPlayer(playername);
4402 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4406 SendShowFormspecMessage(player->peer_id, formspec, formname);
4410 u32 Server::hudAdd(Player *player, HudElement *form) {
4414 u32 id = player->getFreeHudID();
4415 if (id < player->hud.size())
4416 player->hud[id] = form;
4418 player->hud.push_back(form);
4420 SendHUDAdd(player->peer_id, id, form);
4424 bool Server::hudRemove(Player *player, u32 id) {
4425 if (!player || id >= player->hud.size() || !player->hud[id])
4428 delete player->hud[id];
4429 player->hud[id] = NULL;
4431 SendHUDRemove(player->peer_id, id);
4435 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4439 SendHUDChange(player->peer_id, id, stat, data);
4443 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4447 SendHUDSetFlags(player->peer_id, flags, mask);
4451 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4454 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4457 std::ostringstream os(std::ios::binary);
4458 writeS32(os, hotbar_itemcount);
4459 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4463 void Server::hudSetHotbarImage(Player *player, std::string name) {
4467 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4470 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4474 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4477 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4478 const std::string &type, const std::vector<std::string> ¶ms)
4483 SendSetSky(player->peer_id, bgcolor, type, params);
4487 bool Server::overrideDayNightRatio(Player *player, bool do_override,
4493 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
4497 void Server::notifyPlayers(const std::wstring msg)
4499 SendChatMessage(PEER_ID_INEXISTENT,msg);
4502 void Server::spawnParticle(const char *playername, v3f pos,
4503 v3f velocity, v3f acceleration,
4504 float expirationtime, float size, bool
4505 collisiondetection, bool vertical, std::string texture)
4507 Player *player = m_env->getPlayer(playername);
4510 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4511 expirationtime, size, collisiondetection, vertical, texture);
4514 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4515 float expirationtime, float size,
4516 bool collisiondetection, bool vertical, std::string texture)
4518 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4519 expirationtime, size, collisiondetection, vertical, texture);
4522 u32 Server::addParticleSpawner(const char *playername,
4523 u16 amount, float spawntime,
4524 v3f minpos, v3f maxpos,
4525 v3f minvel, v3f maxvel,
4526 v3f minacc, v3f maxacc,
4527 float minexptime, float maxexptime,
4528 float minsize, float maxsize,
4529 bool collisiondetection, bool vertical, std::string texture)
4531 Player *player = m_env->getPlayer(playername);
4536 for(;;) // look for unused particlespawner id
4539 if (std::find(m_particlespawner_ids.begin(),
4540 m_particlespawner_ids.end(), id)
4541 == m_particlespawner_ids.end())
4543 m_particlespawner_ids.push_back(id);
4548 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4549 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4550 minexptime, maxexptime, minsize, maxsize,
4551 collisiondetection, vertical, texture, id);
4556 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4557 v3f minpos, v3f maxpos,
4558 v3f minvel, v3f maxvel,
4559 v3f minacc, v3f maxacc,
4560 float minexptime, float maxexptime,
4561 float minsize, float maxsize,
4562 bool collisiondetection, bool vertical, std::string texture)
4565 for(;;) // look for unused particlespawner id
4568 if (std::find(m_particlespawner_ids.begin(),
4569 m_particlespawner_ids.end(), id)
4570 == m_particlespawner_ids.end())
4572 m_particlespawner_ids.push_back(id);
4577 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4578 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4579 minexptime, maxexptime, minsize, maxsize,
4580 collisiondetection, vertical, texture, id);
4585 void Server::deleteParticleSpawner(const char *playername, u32 id)
4587 Player *player = m_env->getPlayer(playername);
4591 m_particlespawner_ids.erase(
4592 std::remove(m_particlespawner_ids.begin(),
4593 m_particlespawner_ids.end(), id),
4594 m_particlespawner_ids.end());
4595 SendDeleteParticleSpawner(player->peer_id, id);
4598 void Server::deleteParticleSpawnerAll(u32 id)
4600 m_particlespawner_ids.erase(
4601 std::remove(m_particlespawner_ids.begin(),
4602 m_particlespawner_ids.end(), id),
4603 m_particlespawner_ids.end());
4604 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4607 Inventory* Server::createDetachedInventory(const std::string &name)
4609 if(m_detached_inventories.count(name) > 0){
4610 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4611 delete m_detached_inventories[name];
4613 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4615 Inventory *inv = new Inventory(m_itemdef);
4617 m_detached_inventories[name] = inv;
4618 //TODO find a better way to do this
4619 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4626 BoolScopeSet(bool *dst, bool val):
4629 m_orig_state = *m_dst;
4634 *m_dst = m_orig_state;
4641 // actions: time-reversed list
4642 // Return value: success/failure
4643 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4644 std::list<std::string> *log)
4646 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4647 ServerMap *map = (ServerMap*)(&m_env->getMap());
4648 // Disable rollback report sink while reverting
4649 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4651 // Fail if no actions to handle
4652 if(actions.empty()){
4653 log->push_back("Nothing to do.");
4660 for(std::list<RollbackAction>::const_iterator
4661 i = actions.begin();
4662 i != actions.end(); i++)
4664 const RollbackAction &action = *i;
4666 bool success = action.applyRevert(map, this, this);
4669 std::ostringstream os;
4670 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4671 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4673 log->push_back(os.str());
4675 std::ostringstream os;
4676 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4677 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4679 log->push_back(os.str());
4683 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4684 <<" failed"<<std::endl;
4686 // Call it done if less than half failed
4687 return num_failed <= num_tried/2;
4690 // IGameDef interface
4692 IItemDefManager* Server::getItemDefManager()
4696 INodeDefManager* Server::getNodeDefManager()
4700 ICraftDefManager* Server::getCraftDefManager()
4704 ITextureSource* Server::getTextureSource()
4708 IShaderSource* Server::getShaderSource()
4712 u16 Server::allocateUnknownNodeId(const std::string &name)
4714 return m_nodedef->allocateDummy(name);
4716 ISoundManager* Server::getSoundManager()
4718 return &dummySoundManager;
4720 MtEventManager* Server::getEventManager()
4724 IRollbackReportSink* Server::getRollbackReportSink()
4726 if(!m_enable_rollback_recording)
4728 if(!m_rollback_sink_enabled)
4733 IWritableItemDefManager* Server::getWritableItemDefManager()
4737 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4741 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4746 const ModSpec* Server::getModSpec(const std::string &modname)
4748 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4749 i != m_mods.end(); i++){
4750 const ModSpec &mod = *i;
4751 if(mod.name == modname)
4756 void Server::getModNames(std::list<std::string> &modlist)
4758 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4760 modlist.push_back(i->name);
4763 std::string Server::getBuiltinLuaPath()
4765 return porting::path_share + DIR_DELIM + "builtin";
4768 v3f findSpawnPos(ServerMap &map)
4770 //return v3f(50,50,50)*BS;
4775 nodepos = v2s16(0,0);
4780 s16 water_level = map.getWaterLevel();
4782 // Try to find a good place a few times
4783 for(s32 i=0; i<1000; i++)
4786 // We're going to try to throw the player to this position
4787 v2s16 nodepos2d = v2s16(
4788 -range + (myrand() % (range * 2)),
4789 -range + (myrand() % (range * 2)));
4791 // Get ground height at point
4792 s16 groundheight = map.findGroundLevel(nodepos2d);
4793 if (groundheight <= water_level) // Don't go underwater
4795 if (groundheight > water_level + 6) // Don't go to high places
4798 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4799 bool is_good = false;
4801 for (s32 i = 0; i < 10; i++) {
4802 v3s16 blockpos = getNodeBlockPos(nodepos);
4803 map.emergeBlock(blockpos, true);
4804 content_t c = map.getNodeNoEx(nodepos).getContent();
4805 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4807 if (air_count >= 2){
4815 // Found a good place
4816 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4822 return intToFloat(nodepos, BS);
4825 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4827 RemotePlayer *player = NULL;
4828 bool newplayer = false;
4831 Try to get an existing player
4833 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4835 // If player is already connected, cancel
4836 if(player != NULL && player->peer_id != 0)
4838 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4843 If player with the wanted peer_id already exists, cancel.
4845 if(m_env->getPlayer(peer_id) != NULL)
4847 infostream<<"emergePlayer(): Player with wrong name but same"
4848 " peer_id already exists"<<std::endl;
4853 Create a new player if it doesn't exist yet
4858 player = new RemotePlayer(this);
4859 player->updateName(name);
4861 /* Set player position */
4862 infostream<<"Server: Finding spawn place for player \""
4863 <<name<<"\""<<std::endl;
4864 v3f pos = findSpawnPos(m_env->getServerMap());
4865 player->setPosition(pos);
4867 /* Add player to environment */
4868 m_env->addPlayer(player);
4872 Create a new player active object
4874 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4875 getPlayerEffectivePrivs(player->getName()),
4878 /* Clean up old HUD elements from previous sessions */
4879 player->hud.clear();
4881 /* Add object to environment */
4882 m_env->addActiveObject(playersao);
4886 m_script->on_newplayer(playersao);
4891 void dedicated_server_loop(Server &server, bool &kill)
4893 DSTACK(__FUNCTION_NAME);
4895 verbosestream<<"dedicated_server_loop()"<<std::endl;
4897 IntervalLimiter m_profiler_interval;
4901 float steplen = g_settings->getFloat("dedicated_server_step");
4902 // This is kind of a hack but can be done like this
4903 // because server.step() is very light
4905 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4906 sleep_ms((int)(steplen*1000.0));
4908 server.step(steplen);
4910 if(server.getShutdownRequested() || kill)
4912 infostream<<"Dedicated server quitting"<<std::endl;
4914 if(g_settings->getBool("server_announce") == true)
4915 ServerList::sendAnnounce("delete");
4923 float profiler_print_interval =
4924 g_settings->getFloat("profiler_print_interval");
4925 if(profiler_print_interval != 0)
4927 if(m_profiler_interval.step(steplen, profiler_print_interval))
4929 infostream<<"Profiler:"<<std::endl;
4930 g_profiler->print(infostream);
4931 g_profiler->clear();