3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "environment.h"
28 #include "jthread/jmutexautolock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "scripting_game.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
56 #include "sound.h" // dummySoundManager
57 #include "event_manager.h"
59 #include "serverlist.h"
60 #include "util/string.h"
61 #include "util/pointedthing.h"
62 #include "util/mathconstants.h"
64 #include "util/serialize.h"
65 #include "util/thread.h"
66 #include "defaultsettings.h"
68 class ClientNotFoundException : public BaseException
71 ClientNotFoundException(const char *s):
76 class ServerThread : public JThread
82 ServerThread(Server *server):
91 void * ServerThread::Thread()
93 log_register_thread("ServerThread");
95 DSTACK(__FUNCTION_NAME);
96 BEGIN_DEBUG_EXCEPTION_HANDLER
98 m_server->AsyncRunStep(true);
102 porting::setThreadName("ServerThread");
104 while(!StopRequested())
107 //TimeTaker timer("AsyncRunStep() + Receive()");
109 m_server->AsyncRunStep();
114 catch(con::NoIncomingDataException &e)
117 catch(con::PeerNotFoundException &e)
119 infostream<<"Server: PeerNotFoundException"<<std::endl;
121 catch(ClientNotFoundException &e)
124 catch(con::ConnectionBindFailed &e)
126 m_server->setAsyncFatalError(e.what());
130 m_server->setAsyncFatalError(e.what());
134 END_DEBUG_EXCEPTION_HANDLER(errorstream)
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
167 const std::string &path_world,
168 const SubgameSpec &gamespec,
169 bool simple_singleplayer_mode,
172 m_path_world(path_world),
173 m_gamespec(gamespec),
174 m_simple_singleplayer_mode(simple_singleplayer_mode),
175 m_async_fatal_error(""),
184 m_rollback_sink_enabled(true),
185 m_enable_rollback_recording(false),
188 m_itemdef(createItemDefManager()),
189 m_nodedef(createNodeDefManager()),
190 m_craftdef(createCraftDefManager()),
191 m_event(new EventManager()),
193 m_time_of_day_send_timer(0),
196 m_shutdown_requested(false),
197 m_ignore_map_edit_events(false),
198 m_ignore_map_edit_events_peer_id(0)
201 m_liquid_transform_timer = 0.0;
202 m_liquid_transform_every = 1.0;
203 m_print_info_timer = 0.0;
204 m_masterserver_timer = 0.0;
205 m_objectdata_timer = 0.0;
206 m_emergethread_trigger_timer = 0.0;
207 m_savemap_timer = 0.0;
210 m_lag = g_settings->getFloat("dedicated_server_step");
213 throw ServerError("Supplied empty world path");
215 if(!gamespec.isValid())
216 throw ServerError("Supplied invalid gamespec");
218 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
219 if(m_simple_singleplayer_mode)
220 infostream<<" in simple singleplayer mode"<<std::endl;
222 infostream<<std::endl;
223 infostream<<"- world: "<<m_path_world<<std::endl;
224 infostream<<"- game: "<<m_gamespec.path<<std::endl;
226 // Initialize default settings and override defaults with those provided
228 set_default_settings(g_settings);
229 Settings gamedefaults;
230 getGameMinetestConfig(gamespec.path, gamedefaults);
231 override_default_settings(g_settings, &gamedefaults);
233 // Create server thread
234 m_thread = new ServerThread(this);
236 // Create emerge manager
237 m_emerge = new EmergeManager(this);
239 // Create world if it doesn't exist
240 if(!initializeWorld(m_path_world, m_gamespec.id))
241 throw ServerError("Failed to initialize world");
243 // Create ban manager
244 std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
245 m_banmanager = new BanManager(ban_path);
247 // Create rollback manager
248 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
249 m_rollback = createRollbackManager(rollback_path, this);
251 ModConfiguration modconf(m_path_world);
252 m_mods = modconf.getMods();
253 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
254 // complain about mods with unsatisfied dependencies
255 if(!modconf.isConsistent())
257 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
258 it != unsatisfied_mods.end(); ++it)
261 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
262 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
263 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
264 errorstream << " \"" << *dep_it << "\"";
265 errorstream << std::endl;
269 Settings worldmt_settings;
270 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
271 worldmt_settings.readConfigFile(worldmt.c_str());
272 std::vector<std::string> names = worldmt_settings.getNames();
273 std::set<std::string> load_mod_names;
274 for(std::vector<std::string>::iterator it = names.begin();
275 it != names.end(); ++it)
277 std::string name = *it;
278 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
279 load_mod_names.insert(name.substr(9));
281 // complain about mods declared to be loaded, but not found
282 for(std::vector<ModSpec>::iterator it = m_mods.begin();
283 it != m_mods.end(); ++it)
284 load_mod_names.erase((*it).name);
285 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
286 it != unsatisfied_mods.end(); ++it)
287 load_mod_names.erase((*it).name);
288 if(!load_mod_names.empty())
290 errorstream << "The following mods could not be found:";
291 for(std::set<std::string>::iterator it = load_mod_names.begin();
292 it != load_mod_names.end(); ++it)
293 errorstream << " \"" << (*it) << "\"";
294 errorstream << std::endl;
298 JMutexAutoLock envlock(m_env_mutex);
300 // Initialize scripting
301 infostream<<"Server: Initializing Lua"<<std::endl;
303 m_script = new GameScripting(this);
305 std::string scriptpath = getBuiltinLuaPath() + DIR_DELIM "init.lua";
307 if (!m_script->loadScript(scriptpath)) {
308 throw ModError("Failed to load and run " + scriptpath);
313 infostream<<"Server: Loading mods: ";
314 for(std::vector<ModSpec>::iterator i = m_mods.begin();
315 i != m_mods.end(); i++){
316 const ModSpec &mod = *i;
317 infostream<<mod.name<<" ";
319 infostream<<std::endl;
320 // Load and run "mod" scripts
321 for(std::vector<ModSpec>::iterator i = m_mods.begin();
322 i != m_mods.end(); i++){
323 const ModSpec &mod = *i;
324 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
325 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
326 <<scriptpath<<"\"]"<<std::endl;
327 bool success = m_script->loadMod(scriptpath, mod.name);
329 errorstream<<"Server: Failed to load and run "
330 <<scriptpath<<std::endl;
331 throw ModError("Failed to load and run "+scriptpath);
335 // Read Textures and calculate sha1 sums
338 // Apply item aliases in the node definition manager
339 m_nodedef->updateAliases(m_itemdef);
341 // Load the mapgen params from global settings now after any
342 // initial overrides have been set by the mods
343 m_emerge->loadMapgenParams();
345 // Initialize Environment
346 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
347 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
349 m_clients.setEnv(m_env);
351 // Run some callbacks after the MG params have been set up but before activation
352 m_script->environment_OnMapgenInit(&m_emerge->params);
354 // Initialize mapgens
355 m_emerge->initMapgens();
357 // Give environment reference to scripting api
358 m_script->initializeEnvironment(m_env);
360 // Register us to receive map edit events
361 servermap->addEventReceiver(this);
363 // If file exists, load environment metadata
364 if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt"))
366 infostream<<"Server: Loading environment metadata"<<std::endl;
370 // Add some test ActiveBlockModifiers to environment
371 add_legacy_abms(m_env, m_nodedef);
373 m_liquid_transform_every = g_settings->getFloat("liquid_update");
378 infostream<<"Server destructing"<<std::endl;
380 // Send shutdown message
381 SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
384 JMutexAutoLock envlock(m_env_mutex);
386 // Execute script shutdown hooks
387 m_script->on_shutdown();
389 infostream<<"Server: Saving players"<<std::endl;
390 m_env->saveLoadedPlayers();
392 infostream<<"Server: Saving environment metadata"<<std::endl;
400 // stop all emerge threads before deleting players that may have
401 // requested blocks to be emerged
402 m_emerge->stopThreads();
404 // Delete things in the reverse order of creation
407 // N.B. the EmergeManager should be deleted after the Environment since Map
408 // depends on EmergeManager to write its current params to the map meta
417 // Deinitialize scripting
418 infostream<<"Server: Deinitializing scripting"<<std::endl;
421 // Delete detached inventories
422 for (std::map<std::string, Inventory*>::iterator
423 i = m_detached_inventories.begin();
424 i != m_detached_inventories.end(); i++) {
429 void Server::start(Address bind_addr)
431 DSTACK(__FUNCTION_NAME);
432 infostream<<"Starting server on "
433 << bind_addr.serializeString() <<"..."<<std::endl;
435 // Stop thread if already running
438 // Initialize connection
439 m_con.SetTimeoutMs(30);
440 m_con.Serve(bind_addr);
445 // ASCII art for the win!
447 <<" .__ __ __ "<<std::endl
448 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
449 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
450 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
451 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
452 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
453 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
454 actionstream<<"Server for gameid=\""<<m_gamespec.id
455 <<"\" listening on "<<bind_addr.serializeString()<<":"
456 <<bind_addr.getPort() << "."<<std::endl;
461 DSTACK(__FUNCTION_NAME);
463 infostream<<"Server: Stopping and waiting threads"<<std::endl;
465 // Stop threads (set run=false first so both start stopping)
467 //m_emergethread.setRun(false);
469 //m_emergethread.stop();
471 infostream<<"Server: Threads stopped"<<std::endl;
474 void Server::step(float dtime)
476 DSTACK(__FUNCTION_NAME);
481 JMutexAutoLock lock(m_step_dtime_mutex);
482 m_step_dtime += dtime;
484 // Throw if fatal error occurred in thread
485 std::string async_err = m_async_fatal_error.get();
487 throw ServerError(async_err);
491 void Server::AsyncRunStep(bool initial_step)
493 DSTACK(__FUNCTION_NAME);
495 g_profiler->add("Server::AsyncRunStep (num)", 1);
499 JMutexAutoLock lock1(m_step_dtime_mutex);
500 dtime = m_step_dtime;
504 // Send blocks to clients
508 if((dtime < 0.001) && (initial_step == false))
511 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
513 //infostream<<"Server steps "<<dtime<<std::endl;
514 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
517 JMutexAutoLock lock1(m_step_dtime_mutex);
518 m_step_dtime -= dtime;
525 m_uptime.set(m_uptime.get() + dtime);
531 Update time of day and overall game time
534 JMutexAutoLock envlock(m_env_mutex);
536 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
539 Send to clients at constant intervals
542 m_time_of_day_send_timer -= dtime;
543 if(m_time_of_day_send_timer < 0.0)
545 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
546 u16 time = m_env->getTimeOfDay();
547 float time_speed = g_settings->getFloat("time_speed");
548 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
553 JMutexAutoLock lock(m_env_mutex);
554 // Figure out and report maximum lag to environment
555 float max_lag = m_env->getMaxLagEstimate();
556 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
558 if(dtime > 0.1 && dtime > max_lag * 2.0)
559 infostream<<"Server: Maximum lag peaked to "<<dtime
563 m_env->reportMaxLagEstimate(max_lag);
565 ScopeProfiler sp(g_profiler, "SEnv step");
566 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
570 const float map_timer_and_unload_dtime = 2.92;
571 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
573 JMutexAutoLock lock(m_env_mutex);
574 // Run Map's timers and unload unused data
575 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
576 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
577 g_settings->getFloat("server_unload_unused_data_timeout"));
588 JMutexAutoLock lock(m_env_mutex);
590 std::list<u16> clientids = m_clients.getClientIDs();
592 ScopeProfiler sp(g_profiler, "Server: handle players");
594 for(std::list<u16>::iterator
595 i = clientids.begin();
596 i != clientids.end(); ++i)
598 PlayerSAO *playersao = getPlayerSAO(*i);
599 if(playersao == NULL)
603 Handle player HPs (die if hp=0)
605 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
607 if(playersao->getHP() == 0)
614 Send player breath if changed
616 if(playersao->m_breath_not_sent) {
617 SendPlayerBreath(*i);
621 Send player inventories if necessary
623 if(playersao->m_moved){
625 playersao->m_moved = false;
627 if(playersao->m_inventory_not_sent){
634 /* Transform liquids */
635 m_liquid_transform_timer += dtime;
636 if(m_liquid_transform_timer >= m_liquid_transform_every)
638 m_liquid_transform_timer -= m_liquid_transform_every;
640 JMutexAutoLock lock(m_env_mutex);
642 ScopeProfiler sp(g_profiler, "Server: liquid transform");
644 std::map<v3s16, MapBlock*> modified_blocks;
645 m_env->getMap().transformLiquids(modified_blocks);
650 core::map<v3s16, MapBlock*> lighting_modified_blocks;
651 ServerMap &map = ((ServerMap&)m_env->getMap());
652 map.updateLighting(modified_blocks, lighting_modified_blocks);
654 // Add blocks modified by lighting to modified_blocks
655 for(core::map<v3s16, MapBlock*>::Iterator
656 i = lighting_modified_blocks.getIterator();
657 i.atEnd() == false; i++)
659 MapBlock *block = i.getNode()->getValue();
660 modified_blocks.insert(block->getPos(), block);
664 Set the modified blocks unsent for all the clients
666 if(modified_blocks.size() > 0)
668 SetBlocksNotSent(modified_blocks);
671 m_clients.step(dtime);
673 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
675 // send masterserver announce
677 float &counter = m_masterserver_timer;
678 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
679 g_settings->getBool("server_announce"))
681 ServerList::sendAnnounce(counter ? "update" : "start",
682 m_clients.getPlayerNames(),
684 m_env->getGameTime(),
695 Check added and deleted active objects
698 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
699 JMutexAutoLock envlock(m_env_mutex);
702 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
703 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
705 // Radius inside which objects are active
706 s16 radius = g_settings->getS16("active_object_send_range_blocks");
707 radius *= MAP_BLOCKSIZE;
709 for(std::map<u16, RemoteClient*>::iterator
711 i != clients.end(); ++i)
713 RemoteClient *client = i->second;
715 // If definitions and textures have not been sent, don't
716 // send objects either
717 if (client->getState() < CS_DefinitionsSent)
720 Player *player = m_env->getPlayer(client->peer_id);
723 // This can happen if the client timeouts somehow
724 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
726 <<" has no associated player"<<std::endl;*/
729 v3s16 pos = floatToInt(player->getPosition(), BS);
731 std::set<u16> removed_objects;
732 std::set<u16> added_objects;
733 m_env->getRemovedActiveObjects(pos, radius,
734 client->m_known_objects, removed_objects);
735 m_env->getAddedActiveObjects(pos, radius,
736 client->m_known_objects, added_objects);
738 // Ignore if nothing happened
739 if(removed_objects.size() == 0 && added_objects.size() == 0)
741 //infostream<<"active objects: none changed"<<std::endl;
745 std::string data_buffer;
749 // Handle removed objects
750 writeU16((u8*)buf, removed_objects.size());
751 data_buffer.append(buf, 2);
752 for(std::set<u16>::iterator
753 i = removed_objects.begin();
754 i != removed_objects.end(); ++i)
758 ServerActiveObject* obj = m_env->getActiveObject(id);
760 // Add to data buffer for sending
761 writeU16((u8*)buf, id);
762 data_buffer.append(buf, 2);
764 // Remove from known objects
765 client->m_known_objects.erase(id);
767 if(obj && obj->m_known_by_count > 0)
768 obj->m_known_by_count--;
771 // Handle added objects
772 writeU16((u8*)buf, added_objects.size());
773 data_buffer.append(buf, 2);
774 for(std::set<u16>::iterator
775 i = added_objects.begin();
776 i != added_objects.end(); ++i)
780 ServerActiveObject* obj = m_env->getActiveObject(id);
783 u8 type = ACTIVEOBJECT_TYPE_INVALID;
785 infostream<<"WARNING: "<<__FUNCTION_NAME
786 <<": NULL object"<<std::endl;
788 type = obj->getSendType();
790 // Add to data buffer for sending
791 writeU16((u8*)buf, id);
792 data_buffer.append(buf, 2);
793 writeU8((u8*)buf, type);
794 data_buffer.append(buf, 1);
797 data_buffer.append(serializeLongString(
798 obj->getClientInitializationData(client->net_proto_version)));
800 data_buffer.append(serializeLongString(""));
802 // Add to known objects
803 client->m_known_objects.insert(id);
806 obj->m_known_by_count++;
810 SharedBuffer<u8> reply(2 + data_buffer.size());
811 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
812 memcpy((char*)&reply[2], data_buffer.c_str(),
815 m_clients.send(client->peer_id, 0, reply, true);
817 verbosestream<<"Server: Sent object remove/add: "
818 <<removed_objects.size()<<" removed, "
819 <<added_objects.size()<<" added, "
820 <<"packet size is "<<reply.getSize()<<std::endl;
825 Collect a list of all the objects known by the clients
826 and report it back to the environment.
829 core::map<u16, bool> all_known_objects;
831 for(core::map<u16, RemoteClient*>::Iterator
832 i = m_clients.getIterator();
833 i.atEnd() == false; i++)
835 RemoteClient *client = i.getNode()->getValue();
836 // Go through all known objects of client
837 for(core::map<u16, bool>::Iterator
838 i = client->m_known_objects.getIterator();
839 i.atEnd()==false; i++)
841 u16 id = i.getNode()->getKey();
842 all_known_objects[id] = true;
846 m_env->setKnownActiveObjects(whatever);
855 JMutexAutoLock envlock(m_env_mutex);
856 ScopeProfiler sp(g_profiler, "Server: sending object messages");
859 // Value = data sent by object
860 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
862 // Get active object messages from environment
865 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
869 std::list<ActiveObjectMessage>* message_list = NULL;
870 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
871 n = buffered_messages.find(aom.id);
872 if(n == buffered_messages.end())
874 message_list = new std::list<ActiveObjectMessage>;
875 buffered_messages[aom.id] = message_list;
879 message_list = n->second;
881 message_list->push_back(aom);
885 std::map<u16, RemoteClient*> clients = m_clients.getClientList();
886 // Route data to every client
887 for(std::map<u16, RemoteClient*>::iterator
889 i != clients.end(); ++i)
891 RemoteClient *client = i->second;
892 std::string reliable_data;
893 std::string unreliable_data;
894 // Go through all objects in message buffer
895 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
896 j = buffered_messages.begin();
897 j != buffered_messages.end(); ++j)
899 // If object is not known by client, skip it
901 if(client->m_known_objects.find(id) == client->m_known_objects.end())
903 // Get message list of object
904 std::list<ActiveObjectMessage>* list = j->second;
905 // Go through every message
906 for(std::list<ActiveObjectMessage>::iterator
907 k = list->begin(); k != list->end(); ++k)
909 // Compose the full new data with header
910 ActiveObjectMessage aom = *k;
911 std::string new_data;
914 writeU16((u8*)&buf[0], aom.id);
915 new_data.append(buf, 2);
917 new_data += serializeString(aom.datastring);
918 // Add data to buffer
920 reliable_data += new_data;
922 unreliable_data += new_data;
926 reliable_data and unreliable_data are now ready.
929 if(reliable_data.size() > 0)
931 SharedBuffer<u8> reply(2 + reliable_data.size());
932 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
933 memcpy((char*)&reply[2], reliable_data.c_str(),
934 reliable_data.size());
936 m_clients.send(client->peer_id, 0, reply, true);
938 if(unreliable_data.size() > 0)
940 SharedBuffer<u8> reply(2 + unreliable_data.size());
941 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
942 memcpy((char*)&reply[2], unreliable_data.c_str(),
943 unreliable_data.size());
944 // Send as unreliable
945 m_clients.send(client->peer_id, 1, reply, false);
948 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
950 infostream<<"Server: Size of object message data: "
951 <<"reliable: "<<reliable_data.size()
952 <<", unreliable: "<<unreliable_data.size()
958 // Clear buffered_messages
959 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
960 i = buffered_messages.begin();
961 i != buffered_messages.end(); ++i)
968 Send queued-for-sending map edit events.
971 // We will be accessing the environment
972 JMutexAutoLock lock(m_env_mutex);
974 // Don't send too many at a time
977 // Single change sending is disabled if queue size is not small
978 bool disable_single_change_sending = false;
979 if(m_unsent_map_edit_queue.size() >= 4)
980 disable_single_change_sending = true;
982 int event_count = m_unsent_map_edit_queue.size();
984 // We'll log the amount of each
987 while(m_unsent_map_edit_queue.size() != 0)
989 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
991 // Players far away from the change are stored here.
992 // Instead of sending the changes, MapBlocks are set not sent
994 std::list<u16> far_players;
996 if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
998 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
999 prof.add("MEET_ADDNODE", 1);
1000 if(disable_single_change_sending)
1001 sendAddNode(event->p, event->n, event->already_known_by_peer,
1002 &far_players, 5, event->type == MEET_ADDNODE);
1004 sendAddNode(event->p, event->n, event->already_known_by_peer,
1005 &far_players, 30, event->type == MEET_ADDNODE);
1007 else if(event->type == MEET_REMOVENODE)
1009 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1010 prof.add("MEET_REMOVENODE", 1);
1011 if(disable_single_change_sending)
1012 sendRemoveNode(event->p, event->already_known_by_peer,
1015 sendRemoveNode(event->p, event->already_known_by_peer,
1018 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1020 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1021 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1022 setBlockNotSent(event->p);
1024 else if(event->type == MEET_OTHER)
1026 infostream<<"Server: MEET_OTHER"<<std::endl;
1027 prof.add("MEET_OTHER", 1);
1028 for(std::set<v3s16>::iterator
1029 i = event->modified_blocks.begin();
1030 i != event->modified_blocks.end(); ++i)
1032 setBlockNotSent(*i);
1037 prof.add("unknown", 1);
1038 infostream<<"WARNING: Server: Unknown MapEditEvent "
1039 <<((u32)event->type)<<std::endl;
1043 Set blocks not sent to far players
1045 if(far_players.size() > 0)
1047 // Convert list format to that wanted by SetBlocksNotSent
1048 std::map<v3s16, MapBlock*> modified_blocks2;
1049 for(std::set<v3s16>::iterator
1050 i = event->modified_blocks.begin();
1051 i != event->modified_blocks.end(); ++i)
1053 modified_blocks2[*i] =
1054 m_env->getMap().getBlockNoCreateNoEx(*i);
1056 // Set blocks not sent
1057 for(std::list<u16>::iterator
1058 i = far_players.begin();
1059 i != far_players.end(); ++i)
1062 RemoteClient *client = getClient(peer_id);
1065 client->SetBlocksNotSent(modified_blocks2);
1071 /*// Don't send too many at a time
1073 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1077 if(event_count >= 5){
1078 infostream<<"Server: MapEditEvents:"<<std::endl;
1079 prof.print(infostream);
1080 } else if(event_count != 0){
1081 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1082 prof.print(verbosestream);
1088 Trigger emergethread (it somehow gets to a non-triggered but
1089 bysy state sometimes)
1092 float &counter = m_emergethread_trigger_timer;
1098 m_emerge->startThreads();
1100 // Update m_enable_rollback_recording here too
1101 m_enable_rollback_recording =
1102 g_settings->getBool("enable_rollback_recording");
1106 // Save map, players and auth stuff
1108 float &counter = m_savemap_timer;
1110 if(counter >= g_settings->getFloat("server_map_save_interval"))
1113 JMutexAutoLock lock(m_env_mutex);
1115 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1118 if (m_banmanager->isModified()) {
1119 m_banmanager->save();
1122 // Save changed parts of map
1123 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1126 m_env->saveLoadedPlayers();
1128 // Save environment metadata
1134 void Server::Receive()
1136 DSTACK(__FUNCTION_NAME);
1137 SharedBuffer<u8> data;
1141 datasize = m_con.Receive(peer_id,data);
1142 ProcessData(*data, datasize, peer_id);
1144 catch(con::InvalidIncomingDataException &e)
1146 infostream<<"Server::Receive(): "
1147 "InvalidIncomingDataException: what()="
1148 <<e.what()<<std::endl;
1150 catch(SerializationError &e) {
1151 infostream<<"Server::Receive(): "
1152 "SerializationError: what()="
1153 <<e.what()<<std::endl;
1155 catch(ClientStateError &e)
1157 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1158 DenyAccess(peer_id, L"Your client sent something server didn't expect."
1159 L"Try reconnecting or updating your client");
1161 catch(con::PeerNotFoundException &e)
1167 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1169 std::string playername = "";
1170 PlayerSAO *playersao = NULL;
1173 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1174 if (client != NULL) {
1175 playername = client->getName();
1176 playersao = emergePlayer(playername.c_str(), peer_id);
1178 } catch (std::exception &e) {
1184 RemotePlayer *player =
1185 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1187 // If failed, cancel
1188 if((playersao == NULL) || (player == NULL))
1190 if(player && player->peer_id != 0){
1191 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1192 <<" (player allocated to an another client)"<<std::endl;
1193 DenyAccess(peer_id, L"Another client is connected with this "
1194 L"name. If your client closed unexpectedly, try again in "
1197 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1199 DenyAccess(peer_id, L"Could not allocate player.");
1205 Send complete position information
1207 SendMovePlayer(peer_id);
1210 SendPlayerPrivileges(peer_id);
1212 // Send inventory formspec
1213 SendPlayerInventoryFormspec(peer_id);
1216 UpdateCrafting(peer_id);
1217 SendInventory(peer_id);
1220 if(g_settings->getBool("enable_damage"))
1221 SendPlayerHP(peer_id);
1224 SendPlayerBreath(peer_id);
1226 // Show death screen if necessary
1228 SendDeathscreen(peer_id, false, v3f(0,0,0));
1230 // Note things in chat if not in simple singleplayer mode
1231 if(!m_simple_singleplayer_mode)
1233 // Send information about server to player in chat
1234 SendChatMessage(peer_id, getStatusString());
1236 // Send information about joining in chat
1238 std::wstring name = L"unknown";
1239 Player *player = m_env->getPlayer(peer_id);
1241 name = narrow_to_wide(player->getName());
1243 std::wstring message;
1246 message += L" joined the game.";
1247 SendChatMessage(PEER_ID_INEXISTENT,message);
1250 Address addr = getPeerAddress(player->peer_id);
1251 std::string ip_str = addr.serializeString();
1252 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1257 std::vector<std::string> names = m_clients.getPlayerNames();
1259 actionstream<<player->getName() <<" joins game. List of players: ";
1261 for (std::vector<std::string>::iterator i = names.begin();
1262 i != names.end(); i++)
1264 actionstream << *i << " ";
1267 actionstream << player->getName() <<std::endl;
1272 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1274 DSTACK(__FUNCTION_NAME);
1275 // Environment is locked first.
1276 JMutexAutoLock envlock(m_env_mutex);
1278 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1282 Address address = getPeerAddress(peer_id);
1283 addr_s = address.serializeString();
1285 // drop player if is ip is banned
1286 if(m_banmanager->isIpBanned(addr_s)){
1287 std::string ban_name = m_banmanager->getBanName(addr_s);
1288 infostream<<"Server: A banned client tried to connect from "
1289 <<addr_s<<"; banned name was "
1290 <<ban_name<<std::endl;
1291 // This actually doesn't seem to transfer to the client
1292 DenyAccess(peer_id, L"Your ip is banned. Banned name was "
1293 +narrow_to_wide(ban_name));
1297 catch(con::PeerNotFoundException &e)
1300 * no peer for this packet found
1301 * most common reason is peer timeout, e.g. peer didn't
1302 * respond for some time, your server was overloaded or
1305 infostream<<"Server::ProcessData(): Cancelling: peer "
1306 <<peer_id<<" not found"<<std::endl;
1316 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1318 if(command == TOSERVER_INIT)
1320 // [0] u16 TOSERVER_INIT
1321 // [2] u8 SER_FMT_VER_HIGHEST_READ
1322 // [3] u8[20] player_name
1323 // [23] u8[28] password <--- can be sent without this, from old versions
1325 if(datasize < 2+1+PLAYERNAME_SIZE)
1328 RemoteClient* client = getClient(peer_id, CS_Created);
1330 // If net_proto_version is set, this client has already been handled
1331 if(client->getState() > CS_Created)
1333 verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
1334 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1338 verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
1339 <<peer_id<<")"<<std::endl;
1341 // Do not allow multiple players in simple singleplayer mode.
1342 // This isn't a perfect way to do it, but will suffice for now
1343 if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
1344 infostream<<"Server: Not allowing another client ("<<addr_s
1345 <<") to connect in simple singleplayer mode"<<std::endl;
1346 DenyAccess(peer_id, L"Running in simple singleplayer mode.");
1350 // First byte after command is maximum supported
1351 // serialization version
1352 u8 client_max = data[2];
1353 u8 our_max = SER_FMT_VER_HIGHEST_READ;
1354 // Use the highest version supported by both
1355 u8 deployed = std::min(client_max, our_max);
1356 // If it's lower than the lowest supported, give up.
1357 if(deployed < SER_FMT_VER_LOWEST)
1358 deployed = SER_FMT_VER_INVALID;
1360 if(deployed == SER_FMT_VER_INVALID)
1362 actionstream<<"Server: A mismatched client tried to connect from "
1363 <<addr_s<<std::endl;
1364 infostream<<"Server: Cannot negotiate serialization version with "
1365 <<addr_s<<std::endl;
1366 DenyAccess(peer_id, std::wstring(
1367 L"Your client's version is not supported.\n"
1368 L"Server version is ")
1369 + narrow_to_wide(minetest_version_simple) + L"."
1374 client->setPendingSerializationVersion(deployed);
1377 Read and check network protocol version
1380 u16 min_net_proto_version = 0;
1381 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1382 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1384 // Use same version as minimum and maximum if maximum version field
1385 // doesn't exist (backwards compatibility)
1386 u16 max_net_proto_version = min_net_proto_version;
1387 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1388 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1390 // Start with client's maximum version
1391 u16 net_proto_version = max_net_proto_version;
1393 // Figure out a working version if it is possible at all
1394 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1395 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1397 // If maximum is larger than our maximum, go with our maximum
1398 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1399 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1400 // Else go with client's maximum
1402 net_proto_version = max_net_proto_version;
1405 verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
1406 <<min_net_proto_version<<", max: "<<max_net_proto_version
1407 <<", chosen: "<<net_proto_version<<std::endl;
1409 client->net_proto_version = net_proto_version;
1411 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1412 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1414 actionstream<<"Server: A mismatched client tried to connect from "
1415 <<addr_s<<std::endl;
1416 DenyAccess(peer_id, std::wstring(
1417 L"Your client's version is not supported.\n"
1418 L"Server version is ")
1419 + narrow_to_wide(minetest_version_simple) + L",\n"
1420 + L"server's PROTOCOL_VERSION is "
1421 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1423 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1424 + L", client's PROTOCOL_VERSION is "
1425 + narrow_to_wide(itos(min_net_proto_version))
1427 + narrow_to_wide(itos(max_net_proto_version))
1432 if(g_settings->getBool("strict_protocol_version_checking"))
1434 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1436 actionstream<<"Server: A mismatched (strict) client tried to "
1437 <<"connect from "<<addr_s<<std::endl;
1438 DenyAccess(peer_id, std::wstring(
1439 L"Your client's version is not supported.\n"
1440 L"Server version is ")
1441 + narrow_to_wide(minetest_version_simple) + L",\n"
1442 + L"server's PROTOCOL_VERSION (strict) is "
1443 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1444 + L", client's PROTOCOL_VERSION is "
1445 + narrow_to_wide(itos(min_net_proto_version))
1447 + narrow_to_wide(itos(max_net_proto_version))
1456 char playername[PLAYERNAME_SIZE];
1457 unsigned int playername_length = 0;
1458 for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
1459 playername[playername_length] = data[3+playername_length];
1460 if (data[3+playername_length] == 0)
1464 if (playername_length == PLAYERNAME_SIZE) {
1465 actionstream<<"Server: Player with name exceeding max length "
1466 <<"tried to connect from "<<addr_s<<std::endl;
1467 DenyAccess(peer_id, L"Name too long");
1472 if(playername[0]=='\0')
1474 actionstream<<"Server: Player with an empty name "
1475 <<"tried to connect from "<<addr_s<<std::endl;
1476 DenyAccess(peer_id, L"Empty name");
1480 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1482 actionstream<<"Server: Player with an invalid name "
1483 <<"tried to connect from "<<addr_s<<std::endl;
1484 DenyAccess(peer_id, L"Name contains unallowed characters");
1488 if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
1490 actionstream<<"Server: Player with the name \"singleplayer\" "
1491 <<"tried to connect from "<<addr_s<<std::endl;
1492 DenyAccess(peer_id, L"Name is not allowed");
1498 if(m_script->on_prejoinplayer(playername, addr_s, reason))
1500 actionstream<<"Server: Player with the name \""<<playername<<"\" "
1501 <<"tried to connect from "<<addr_s<<" "
1502 <<"but it was disallowed for the following reason: "
1503 <<reason<<std::endl;
1504 DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
1509 infostream<<"Server: New connection: \""<<playername<<"\" from "
1510 <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
1513 char given_password[PASSWORD_SIZE];
1514 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1516 // old version - assume blank password
1517 given_password[0] = 0;
1521 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1523 given_password[i] = data[23+i];
1525 given_password[PASSWORD_SIZE-1] = 0;
1528 if(!base64_is_valid(given_password)){
1529 actionstream<<"Server: "<<playername
1530 <<" supplied invalid password hash"<<std::endl;
1531 DenyAccess(peer_id, L"Invalid password hash");
1535 // Enforce user limit.
1536 // Don't enforce for users that have some admin right
1537 if(m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
1538 !checkPriv(playername, "server") &&
1539 !checkPriv(playername, "ban") &&
1540 !checkPriv(playername, "privs") &&
1541 !checkPriv(playername, "password") &&
1542 playername != g_settings->get("name"))
1544 actionstream<<"Server: "<<playername<<" tried to join, but there"
1545 <<" are already max_users="
1546 <<g_settings->getU16("max_users")<<" players."<<std::endl;
1547 DenyAccess(peer_id, L"Too many users.");
1551 std::string checkpwd; // Password hash to check against
1552 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1554 // If no authentication info exists for user, create it
1556 if(!isSingleplayer() &&
1557 g_settings->getBool("disallow_empty_password") &&
1558 std::string(given_password) == ""){
1559 actionstream<<"Server: "<<playername
1560 <<" supplied empty password"<<std::endl;
1561 DenyAccess(peer_id, L"Empty passwords are "
1562 L"disallowed. Set a password and try again.");
1565 std::wstring raw_default_password =
1566 narrow_to_wide(g_settings->get("default_password"));
1567 std::string initial_password =
1568 translatePassword(playername, raw_default_password);
1570 // If default_password is empty, allow any initial password
1571 if (raw_default_password.length() == 0)
1572 initial_password = given_password;
1574 m_script->createAuth(playername, initial_password);
1577 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1580 actionstream<<"Server: "<<playername<<" cannot be authenticated"
1581 <<" (auth handler does not work?)"<<std::endl;
1582 DenyAccess(peer_id, L"Not allowed to login");
1586 if(given_password != checkpwd){
1587 actionstream<<"Server: "<<playername<<" supplied wrong password"
1589 DenyAccess(peer_id, L"Wrong password");
1593 RemotePlayer *player =
1594 static_cast<RemotePlayer*>(m_env->getPlayer(playername));
1596 if(player && player->peer_id != 0){
1597 errorstream<<"Server: "<<playername<<": Failed to emerge player"
1598 <<" (player allocated to an another client)"<<std::endl;
1599 DenyAccess(peer_id, L"Another client is connected with this "
1600 L"name. If your client closed unexpectedly, try again in "
1604 m_clients.setPlayerName(peer_id,playername);
1607 Answer with a TOCLIENT_INIT
1610 SharedBuffer<u8> reply(2+1+6+8+4);
1611 writeU16(&reply[0], TOCLIENT_INIT);
1612 writeU8(&reply[2], deployed);
1613 //send dummy pos for legacy reasons only
1614 writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
1615 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
1616 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
1619 m_clients.send(peer_id, 0, reply, true);
1620 m_clients.event(peer_id, CSE_Init);
1626 if(command == TOSERVER_INIT2)
1629 verbosestream<<"Server: Got TOSERVER_INIT2 from "
1630 <<peer_id<<std::endl;
1632 m_clients.event(peer_id, CSE_GotInit2);
1633 u16 protocol_version = m_clients.getProtocolVersion(peer_id);
1636 ///// begin compatibility code
1637 PlayerSAO* playersao = NULL;
1638 if (protocol_version <= 22) {
1639 playersao = StageTwoClientInit(peer_id);
1641 if (playersao == NULL) {
1643 << "TOSERVER_INIT2 stage 2 client init failed for peer "
1644 << peer_id << std::endl;
1648 ///// end compatibility code
1651 Send some initialization data
1654 infostream<<"Server: Sending content to "
1655 <<getPlayerName(peer_id)<<std::endl;
1657 // Send player movement settings
1658 SendMovement(peer_id);
1660 // Send item definitions
1661 SendItemDef(peer_id, m_itemdef, protocol_version);
1663 // Send node definitions
1664 SendNodeDef(peer_id, m_nodedef, protocol_version);
1666 m_clients.event(peer_id, CSE_SetDefinitionsSent);
1668 // Send media announcement
1669 sendMediaAnnouncement(peer_id);
1671 // Send detached inventories
1672 sendDetachedInventories(peer_id);
1675 u16 time = m_env->getTimeOfDay();
1676 float time_speed = g_settings->getFloat("time_speed");
1677 SendTimeOfDay(peer_id, time, time_speed);
1679 ///// begin compatibility code
1680 if (protocol_version <= 22) {
1681 m_clients.event(peer_id, CSE_SetClientReady);
1682 m_script->on_joinplayer(playersao);
1684 ///// end compatibility code
1686 // Warnings about protocol version can be issued here
1687 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
1689 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
1690 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
1696 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1697 u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
1699 if(peer_ser_ver == SER_FMT_VER_INVALID)
1701 errorstream<<"Server::ProcessData(): Cancelling: Peer"
1702 " serialization format invalid or not initialized."
1703 " Skipping incoming command="<<command<<std::endl;
1707 /* Handle commands relate to client startup */
1708 if(command == TOSERVER_REQUEST_MEDIA) {
1709 std::string datastring((char*)&data[2], datasize-2);
1710 std::istringstream is(datastring, std::ios_base::binary);
1712 std::list<std::string> tosend;
1713 u16 numfiles = readU16(is);
1715 infostream<<"Sending "<<numfiles<<" files to "
1716 <<getPlayerName(peer_id)<<std::endl;
1717 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
1719 for(int i = 0; i < numfiles; i++) {
1720 std::string name = deSerializeString(is);
1721 tosend.push_back(name);
1722 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
1726 sendRequestedMedia(peer_id, tosend);
1729 else if(command == TOSERVER_RECEIVED_MEDIA) {
1732 else if(command == TOSERVER_CLIENT_READY) {
1733 // clients <= protocol version 22 did not send ready message,
1734 // they're already initialized
1735 if (peer_proto_ver <= 22) {
1736 infostream << "Client sent message not expected by a "
1737 << "client using protocol version <= 22,"
1738 << "disconnecing peer_id: " << peer_id << std::endl;
1739 m_con.DisconnectPeer(peer_id);
1743 PlayerSAO* playersao = StageTwoClientInit(peer_id);
1745 if (playersao == NULL) {
1747 << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
1748 << peer_id << std::endl;
1749 m_con.DisconnectPeer(peer_id);
1754 if(datasize < 2+8) {
1756 << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
1757 << peer_id << std::endl;
1758 m_con.DisconnectPeer(peer_id);
1762 m_clients.setClientVersion(
1764 data[2], data[3], data[4],
1765 std::string((char*) &data[8],(u16) data[6]));
1767 m_clients.event(peer_id, CSE_SetClientReady);
1768 m_script->on_joinplayer(playersao);
1771 else if(command == TOSERVER_GOTBLOCKS)
1784 u16 count = data[2];
1785 for(u16 i=0; i<count; i++)
1787 if((s16)datasize < 2+1+(i+1)*6)
1788 throw con::InvalidIncomingDataException
1789 ("GOTBLOCKS length is too short");
1790 v3s16 p = readV3S16(&data[2+1+i*6]);
1791 /*infostream<<"Server: GOTBLOCKS ("
1792 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1793 RemoteClient *client = getClient(peer_id);
1794 client->GotBlock(p);
1799 if (m_clients.getClientState(peer_id) < CS_Active)
1801 if (command == TOSERVER_PLAYERPOS) return;
1803 errorstream<<"Got packet command: " << command << " for peer id "
1804 << peer_id << " but client isn't active yet. Dropping packet "
1809 Player *player = m_env->getPlayer(peer_id);
1810 if(player == NULL) {
1811 errorstream<<"Server::ProcessData(): Cancelling: "
1812 "No player for peer_id="<<peer_id
1813 << " disconnecting peer!" <<std::endl;
1814 m_con.DisconnectPeer(peer_id);
1818 PlayerSAO *playersao = player->getPlayerSAO();
1819 if(playersao == NULL) {
1820 errorstream<<"Server::ProcessData(): Cancelling: "
1821 "No player object for peer_id="<<peer_id
1822 << " disconnecting peer!" <<std::endl;
1823 m_con.DisconnectPeer(peer_id);
1827 if(command == TOSERVER_PLAYERPOS)
1829 if(datasize < 2+12+12+4+4)
1833 v3s32 ps = readV3S32(&data[start+2]);
1834 v3s32 ss = readV3S32(&data[start+2+12]);
1835 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1836 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1838 if(datasize >= 2+12+12+4+4+4)
1839 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
1840 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1841 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1842 pitch = wrapDegrees(pitch);
1843 yaw = wrapDegrees(yaw);
1845 player->setPosition(position);
1846 player->setSpeed(speed);
1847 player->setPitch(pitch);
1848 player->setYaw(yaw);
1849 player->keyPressed=keyPressed;
1850 player->control.up = (bool)(keyPressed&1);
1851 player->control.down = (bool)(keyPressed&2);
1852 player->control.left = (bool)(keyPressed&4);
1853 player->control.right = (bool)(keyPressed&8);
1854 player->control.jump = (bool)(keyPressed&16);
1855 player->control.aux1 = (bool)(keyPressed&32);
1856 player->control.sneak = (bool)(keyPressed&64);
1857 player->control.LMB = (bool)(keyPressed&128);
1858 player->control.RMB = (bool)(keyPressed&256);
1860 bool cheated = playersao->checkMovementCheat();
1863 m_script->on_cheat(playersao, "moved_too_fast");
1866 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1867 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1868 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1870 else if(command == TOSERVER_DELETEDBLOCKS)
1883 u16 count = data[2];
1884 for(u16 i=0; i<count; i++)
1886 if((s16)datasize < 2+1+(i+1)*6)
1887 throw con::InvalidIncomingDataException
1888 ("DELETEDBLOCKS length is too short");
1889 v3s16 p = readV3S16(&data[2+1+i*6]);
1890 /*infostream<<"Server: DELETEDBLOCKS ("
1891 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1892 RemoteClient *client = getClient(peer_id);
1893 client->SetBlockNotSent(p);
1896 else if(command == TOSERVER_CLICK_OBJECT)
1898 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
1901 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
1903 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
1906 else if(command == TOSERVER_GROUND_ACTION)
1908 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
1912 else if(command == TOSERVER_RELEASE)
1914 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
1917 else if(command == TOSERVER_SIGNTEXT)
1919 infostream<<"Server: SIGNTEXT not supported anymore"
1923 else if(command == TOSERVER_SIGNNODETEXT)
1925 infostream<<"Server: SIGNNODETEXT not supported anymore"
1929 else if(command == TOSERVER_INVENTORY_ACTION)
1931 // Strip command and create a stream
1932 std::string datastring((char*)&data[2], datasize-2);
1933 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
1934 std::istringstream is(datastring, std::ios_base::binary);
1936 InventoryAction *a = InventoryAction::deSerialize(is);
1939 infostream<<"TOSERVER_INVENTORY_ACTION: "
1940 <<"InventoryAction::deSerialize() returned NULL"
1945 // If something goes wrong, this player is to blame
1946 RollbackScopeActor rollback_scope(m_rollback,
1947 std::string("player:")+player->getName());
1950 Note: Always set inventory not sent, to repair cases
1951 where the client made a bad prediction.
1955 Handle restrictions and special cases of the move action
1957 if(a->getType() == IACTION_MOVE)
1959 IMoveAction *ma = (IMoveAction*)a;
1961 ma->from_inv.applyCurrentPlayer(player->getName());
1962 ma->to_inv.applyCurrentPlayer(player->getName());
1964 setInventoryModified(ma->from_inv);
1965 setInventoryModified(ma->to_inv);
1967 bool from_inv_is_current_player =
1968 (ma->from_inv.type == InventoryLocation::PLAYER) &&
1969 (ma->from_inv.name == player->getName());
1971 bool to_inv_is_current_player =
1972 (ma->to_inv.type == InventoryLocation::PLAYER) &&
1973 (ma->to_inv.name == player->getName());
1976 Disable moving items out of craftpreview
1978 if(ma->from_list == "craftpreview")
1980 infostream<<"Ignoring IMoveAction from "
1981 <<(ma->from_inv.dump())<<":"<<ma->from_list
1982 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1983 <<" because src is "<<ma->from_list<<std::endl;
1989 Disable moving items into craftresult and craftpreview
1991 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
1993 infostream<<"Ignoring IMoveAction from "
1994 <<(ma->from_inv.dump())<<":"<<ma->from_list
1995 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
1996 <<" because dst is "<<ma->to_list<<std::endl;
2001 // Disallow moving items in elsewhere than player's inventory
2002 // if not allowed to interact
2003 if(!checkPriv(player->getName(), "interact") &&
2004 (!from_inv_is_current_player ||
2005 !to_inv_is_current_player))
2007 infostream<<"Cannot move outside of player's inventory: "
2008 <<"No interact privilege"<<std::endl;
2014 Handle restrictions and special cases of the drop action
2016 else if(a->getType() == IACTION_DROP)
2018 IDropAction *da = (IDropAction*)a;
2020 da->from_inv.applyCurrentPlayer(player->getName());
2022 setInventoryModified(da->from_inv);
2025 Disable dropping items out of craftpreview
2027 if(da->from_list == "craftpreview")
2029 infostream<<"Ignoring IDropAction from "
2030 <<(da->from_inv.dump())<<":"<<da->from_list
2031 <<" because src is "<<da->from_list<<std::endl;
2036 // Disallow dropping items if not allowed to interact
2037 if(!checkPriv(player->getName(), "interact"))
2044 Handle restrictions and special cases of the craft action
2046 else if(a->getType() == IACTION_CRAFT)
2048 ICraftAction *ca = (ICraftAction*)a;
2050 ca->craft_inv.applyCurrentPlayer(player->getName());
2052 setInventoryModified(ca->craft_inv);
2054 //bool craft_inv_is_current_player =
2055 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2056 // (ca->craft_inv.name == player->getName());
2058 // Disallow crafting if not allowed to interact
2059 if(!checkPriv(player->getName(), "interact"))
2061 infostream<<"Cannot craft: "
2062 <<"No interact privilege"<<std::endl;
2069 a->apply(this, playersao, this);
2073 else if(command == TOSERVER_CHAT_MESSAGE)
2081 std::string datastring((char*)&data[2], datasize-2);
2082 std::istringstream is(datastring, std::ios_base::binary);
2085 is.read((char*)buf, 2);
2086 u16 len = readU16(buf);
2088 std::wstring message;
2089 for(u16 i=0; i<len; i++)
2091 is.read((char*)buf, 2);
2092 message += (wchar_t)readU16(buf);
2095 // If something goes wrong, this player is to blame
2096 RollbackScopeActor rollback_scope(m_rollback,
2097 std::string("player:")+player->getName());
2099 // Get player name of this client
2100 std::wstring name = narrow_to_wide(player->getName());
2103 bool ate = m_script->on_chat_message(player->getName(),
2104 wide_to_narrow(message));
2105 // If script ate the message, don't proceed
2109 // Line to send to players
2111 // Whether to send to the player that sent the line
2112 bool send_to_sender_only = false;
2114 // Commands are implemented in Lua, so only catch invalid
2115 // commands that were not "eaten" and send an error back
2116 if(message[0] == L'/')
2118 message = message.substr(1);
2119 send_to_sender_only = true;
2120 if(message.length() == 0)
2121 line += L"-!- Empty command";
2123 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2127 if(checkPriv(player->getName(), "shout")){
2133 line += L"-!- You don't have permission to shout.";
2134 send_to_sender_only = true;
2141 Send the message to sender
2143 if (send_to_sender_only)
2145 SendChatMessage(peer_id, line);
2148 Send the message to others
2152 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2154 std::list<u16> clients = m_clients.getClientIDs();
2156 for(std::list<u16>::iterator
2157 i = clients.begin();
2158 i != clients.end(); ++i)
2161 SendChatMessage(*i, line);
2166 else if(command == TOSERVER_DAMAGE)
2168 std::string datastring((char*)&data[2], datasize-2);
2169 std::istringstream is(datastring, std::ios_base::binary);
2170 u8 damage = readU8(is);
2172 if(g_settings->getBool("enable_damage"))
2174 actionstream<<player->getName()<<" damaged by "
2175 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2178 playersao->setHP(playersao->getHP() - damage);
2180 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2183 if(playersao->m_hp_not_sent)
2184 SendPlayerHP(peer_id);
2187 else if(command == TOSERVER_BREATH)
2189 std::string datastring((char*)&data[2], datasize-2);
2190 std::istringstream is(datastring, std::ios_base::binary);
2191 u16 breath = readU16(is);
2192 playersao->setBreath(breath);
2193 m_script->player_event(playersao,"breath_changed");
2195 else if(command == TOSERVER_PASSWORD)
2198 [0] u16 TOSERVER_PASSWORD
2199 [2] u8[28] old password
2200 [30] u8[28] new password
2203 if(datasize != 2+PASSWORD_SIZE*2)
2205 /*char password[PASSWORD_SIZE];
2206 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2207 password[i] = data[2+i];
2208 password[PASSWORD_SIZE-1] = 0;*/
2210 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2218 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2220 char c = data[2+PASSWORD_SIZE+i];
2226 if(!base64_is_valid(newpwd)){
2227 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2228 // Wrong old password supplied!!
2229 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2233 infostream<<"Server: Client requests a password change from "
2234 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2236 std::string playername = player->getName();
2238 std::string checkpwd;
2239 m_script->getAuth(playername, &checkpwd, NULL);
2241 if(oldpwd != checkpwd)
2243 infostream<<"Server: invalid old password"<<std::endl;
2244 // Wrong old password supplied!!
2245 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2249 bool success = m_script->setPassword(playername, newpwd);
2251 actionstream<<player->getName()<<" changes password"<<std::endl;
2252 SendChatMessage(peer_id, L"Password change successful.");
2254 actionstream<<player->getName()<<" tries to change password but "
2255 <<"it fails"<<std::endl;
2256 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2259 else if(command == TOSERVER_PLAYERITEM)
2264 u16 item = readU16(&data[2]);
2265 playersao->setWieldIndex(item);
2267 else if(command == TOSERVER_RESPAWN)
2269 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2272 RespawnPlayer(peer_id);
2274 actionstream<<player->getName()<<" respawns at "
2275 <<PP(player->getPosition()/BS)<<std::endl;
2277 // ActiveObject is added to environment in AsyncRunStep after
2278 // the previous addition has been succesfully removed
2280 else if(command == TOSERVER_INTERACT)
2282 std::string datastring((char*)&data[2], datasize-2);
2283 std::istringstream is(datastring, std::ios_base::binary);
2289 [5] u32 length of the next item
2290 [9] serialized PointedThing
2292 0: start digging (from undersurface) or use
2293 1: stop digging (all parameters ignored)
2294 2: digging completed
2295 3: place block or item (to abovesurface)
2298 u8 action = readU8(is);
2299 u16 item_i = readU16(is);
2300 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2301 PointedThing pointed;
2302 pointed.deSerialize(tmp_is);
2304 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2305 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2309 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2310 <<" tried to interact, but is dead!"<<std::endl;
2314 v3f player_pos = playersao->getLastGoodPosition();
2316 // Update wielded item
2317 playersao->setWieldIndex(item_i);
2319 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2320 v3s16 p_under = pointed.node_undersurface;
2321 v3s16 p_above = pointed.node_abovesurface;
2323 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2324 ServerActiveObject *pointed_object = NULL;
2325 if(pointed.type == POINTEDTHING_OBJECT)
2327 pointed_object = m_env->getActiveObject(pointed.object_id);
2328 if(pointed_object == NULL)
2330 verbosestream<<"TOSERVER_INTERACT: "
2331 "pointed object is NULL"<<std::endl;
2337 v3f pointed_pos_under = player_pos;
2338 v3f pointed_pos_above = player_pos;
2339 if(pointed.type == POINTEDTHING_NODE)
2341 pointed_pos_under = intToFloat(p_under, BS);
2342 pointed_pos_above = intToFloat(p_above, BS);
2344 else if(pointed.type == POINTEDTHING_OBJECT)
2346 pointed_pos_under = pointed_object->getBasePosition();
2347 pointed_pos_above = pointed_pos_under;
2351 Check that target is reasonably close
2352 (only when digging or placing things)
2354 if(action == 0 || action == 2 || action == 3)
2356 float d = player_pos.getDistanceFrom(pointed_pos_under);
2357 float max_d = BS * 14; // Just some large enough value
2359 actionstream<<"Player "<<player->getName()
2360 <<" tried to access "<<pointed.dump()
2362 <<"d="<<d<<", max_d="<<max_d
2363 <<". ignoring."<<std::endl;
2364 // Re-send block to revert change on client-side
2365 RemoteClient *client = getClient(peer_id);
2366 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2367 client->SetBlockNotSent(blockpos);
2369 m_script->on_cheat(playersao, "interacted_too_far");
2376 Make sure the player is allowed to do it
2378 if(!checkPriv(player->getName(), "interact"))
2380 actionstream<<player->getName()<<" attempted to interact with "
2381 <<pointed.dump()<<" without 'interact' privilege"
2383 // Re-send block to revert change on client-side
2384 RemoteClient *client = getClient(peer_id);
2385 // Digging completed -> under
2387 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2388 client->SetBlockNotSent(blockpos);
2390 // Placement -> above
2392 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2393 client->SetBlockNotSent(blockpos);
2399 If something goes wrong, this player is to blame
2401 RollbackScopeActor rollback_scope(m_rollback,
2402 std::string("player:")+player->getName());
2405 0: start digging or punch object
2409 if(pointed.type == POINTEDTHING_NODE)
2412 NOTE: This can be used in the future to check if
2413 somebody is cheating, by checking the timing.
2415 MapNode n(CONTENT_IGNORE);
2418 n = m_env->getMap().getNode(p_under);
2420 catch(InvalidPositionException &e)
2422 infostream<<"Server: Not punching: Node not found."
2423 <<" Adding block to emerge queue."
2425 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2427 if(n.getContent() != CONTENT_IGNORE)
2428 m_script->node_on_punch(p_under, n, playersao, pointed);
2430 playersao->noCheatDigStart(p_under);
2432 else if(pointed.type == POINTEDTHING_OBJECT)
2434 // Skip if object has been removed
2435 if(pointed_object->m_removed)
2438 actionstream<<player->getName()<<" punches object "
2439 <<pointed.object_id<<": "
2440 <<pointed_object->getDescription()<<std::endl;
2442 ItemStack punchitem = playersao->getWieldedItem();
2443 ToolCapabilities toolcap =
2444 punchitem.getToolCapabilities(m_itemdef);
2445 v3f dir = (pointed_object->getBasePosition() -
2446 (player->getPosition() + player->getEyeOffset())
2448 float time_from_last_punch =
2449 playersao->resetTimeFromLastPunch();
2450 pointed_object->punch(dir, &toolcap, playersao,
2451 time_from_last_punch);
2459 else if(action == 1)
2464 2: Digging completed
2466 else if(action == 2)
2468 // Only digging of nodes
2469 if(pointed.type == POINTEDTHING_NODE)
2471 MapNode n(CONTENT_IGNORE);
2474 n = m_env->getMap().getNode(p_under);
2476 catch(InvalidPositionException &e)
2478 infostream<<"Server: Not finishing digging: Node not found."
2479 <<" Adding block to emerge queue."
2481 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2484 /* Cheat prevention */
2485 bool is_valid_dig = true;
2486 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2488 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2489 float nocheat_t = playersao->getNoCheatDigTime();
2490 playersao->noCheatDigEnd();
2491 // If player didn't start digging this, ignore dig
2492 if(nocheat_p != p_under){
2493 infostream<<"Server: NoCheat: "<<player->getName()
2494 <<" started digging "
2495 <<PP(nocheat_p)<<" and completed digging "
2496 <<PP(p_under)<<"; not digging."<<std::endl;
2497 is_valid_dig = false;
2499 m_script->on_cheat(playersao, "finished_unknown_dig");
2501 // Get player's wielded item
2502 ItemStack playeritem;
2503 InventoryList *mlist = playersao->getInventory()->getList("main");
2505 playeritem = mlist->getItem(playersao->getWieldIndex());
2506 ToolCapabilities playeritem_toolcap =
2507 playeritem.getToolCapabilities(m_itemdef);
2508 // Get diggability and expected digging time
2509 DigParams params = getDigParams(m_nodedef->get(n).groups,
2510 &playeritem_toolcap);
2511 // If can't dig, try hand
2512 if(!params.diggable){
2513 const ItemDefinition &hand = m_itemdef->get("");
2514 const ToolCapabilities *tp = hand.tool_capabilities;
2516 params = getDigParams(m_nodedef->get(n).groups, tp);
2518 // If can't dig, ignore dig
2519 if(!params.diggable){
2520 infostream<<"Server: NoCheat: "<<player->getName()
2521 <<" completed digging "<<PP(p_under)
2522 <<", which is not diggable with tool. not digging."
2524 is_valid_dig = false;
2526 m_script->on_cheat(playersao, "dug_unbreakable");
2528 // Check digging time
2529 // If already invalidated, we don't have to
2531 // Well not our problem then
2533 // Clean and long dig
2534 else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
2535 // All is good, but grab time from pool; don't care if
2536 // it's actually available
2537 playersao->getDigPool().grab(params.time);
2539 // Short or laggy dig
2540 // Try getting the time from pool
2541 else if(playersao->getDigPool().grab(params.time)){
2546 infostream<<"Server: NoCheat: "<<player->getName()
2547 <<" completed digging "<<PP(p_under)
2548 <<"too fast; not digging."<<std::endl;
2549 is_valid_dig = false;
2551 m_script->on_cheat(playersao, "dug_too_fast");
2555 /* Actually dig node */
2557 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2558 m_script->node_on_dig(p_under, n, playersao);
2560 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2561 RemoteClient *client = getClient(peer_id);
2562 // Send unusual result (that is, node not being removed)
2563 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2565 // Re-send block to revert change on client-side
2566 client->SetBlockNotSent(blockpos);
2569 client->ResendBlockIfOnWire(blockpos);
2575 3: place block or right-click object
2577 else if(action == 3)
2579 ItemStack item = playersao->getWieldedItem();
2581 // Reset build time counter
2582 if(pointed.type == POINTEDTHING_NODE &&
2583 item.getDefinition(m_itemdef).type == ITEM_NODE)
2584 getClient(peer_id)->m_time_from_building = 0.0;
2586 if(pointed.type == POINTEDTHING_OBJECT)
2588 // Right click object
2590 // Skip if object has been removed
2591 if(pointed_object->m_removed)
2594 actionstream<<player->getName()<<" right-clicks object "
2595 <<pointed.object_id<<": "
2596 <<pointed_object->getDescription()<<std::endl;
2599 pointed_object->rightClick(playersao);
2601 else if(m_script->item_OnPlace(
2602 item, playersao, pointed))
2604 // Placement was handled in lua
2606 // Apply returned ItemStack
2607 playersao->setWieldedItem(item);
2610 // If item has node placement prediction, always send the
2611 // blocks to make sure the client knows what exactly happened
2612 RemoteClient *client = getClient(peer_id);
2613 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2614 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2615 if(item.getDefinition(m_itemdef).node_placement_prediction != "") {
2616 client->SetBlockNotSent(blockpos);
2617 if(blockpos2 != blockpos) {
2618 client->SetBlockNotSent(blockpos2);
2622 client->ResendBlockIfOnWire(blockpos);
2623 if(blockpos2 != blockpos) {
2624 client->ResendBlockIfOnWire(blockpos2);
2632 else if(action == 4)
2634 ItemStack item = playersao->getWieldedItem();
2636 actionstream<<player->getName()<<" uses "<<item.name
2637 <<", pointing at "<<pointed.dump()<<std::endl;
2639 if(m_script->item_OnUse(
2640 item, playersao, pointed))
2642 // Apply returned ItemStack
2643 playersao->setWieldedItem(item);
2650 Catch invalid actions
2654 infostream<<"WARNING: Server: Invalid action "
2655 <<action<<std::endl;
2658 else if(command == TOSERVER_REMOVED_SOUNDS)
2660 std::string datastring((char*)&data[2], datasize-2);
2661 std::istringstream is(datastring, std::ios_base::binary);
2663 int num = readU16(is);
2664 for(int k=0; k<num; k++){
2665 s32 id = readS32(is);
2666 std::map<s32, ServerPlayingSound>::iterator i =
2667 m_playing_sounds.find(id);
2668 if(i == m_playing_sounds.end())
2670 ServerPlayingSound &psound = i->second;
2671 psound.clients.erase(peer_id);
2672 if(psound.clients.size() == 0)
2673 m_playing_sounds.erase(i++);
2676 else if(command == TOSERVER_NODEMETA_FIELDS)
2678 std::string datastring((char*)&data[2], datasize-2);
2679 std::istringstream is(datastring, std::ios_base::binary);
2681 v3s16 p = readV3S16(is);
2682 std::string formname = deSerializeString(is);
2683 int num = readU16(is);
2684 std::map<std::string, std::string> fields;
2685 for(int k=0; k<num; k++){
2686 std::string fieldname = deSerializeString(is);
2687 std::string fieldvalue = deSerializeLongString(is);
2688 fields[fieldname] = fieldvalue;
2691 // If something goes wrong, this player is to blame
2692 RollbackScopeActor rollback_scope(m_rollback,
2693 std::string("player:")+player->getName());
2695 // Check the target node for rollback data; leave others unnoticed
2696 RollbackNode rn_old(&m_env->getMap(), p, this);
2698 m_script->node_on_receive_fields(p, formname, fields,playersao);
2700 // Report rollback data
2701 RollbackNode rn_new(&m_env->getMap(), p, this);
2702 if(rollback() && rn_new != rn_old){
2703 RollbackAction action;
2704 action.setSetNode(p, rn_old, rn_new);
2705 rollback()->reportAction(action);
2708 else if(command == TOSERVER_INVENTORY_FIELDS)
2710 std::string datastring((char*)&data[2], datasize-2);
2711 std::istringstream is(datastring, std::ios_base::binary);
2713 std::string formname = deSerializeString(is);
2714 int num = readU16(is);
2715 std::map<std::string, std::string> fields;
2716 for(int k=0; k<num; k++){
2717 std::string fieldname = deSerializeString(is);
2718 std::string fieldvalue = deSerializeLongString(is);
2719 fields[fieldname] = fieldvalue;
2722 m_script->on_playerReceiveFields(playersao, formname, fields);
2726 infostream<<"Server::ProcessData(): Ignoring "
2727 "unknown command "<<command<<std::endl;
2731 catch(SendFailedException &e)
2733 errorstream<<"Server::ProcessData(): SendFailedException: "
2739 void Server::setTimeOfDay(u32 time)
2741 m_env->setTimeOfDay(time);
2742 m_time_of_day_send_timer = 0;
2745 void Server::onMapEditEvent(MapEditEvent *event)
2747 //infostream<<"Server::onMapEditEvent()"<<std::endl;
2748 if(m_ignore_map_edit_events)
2750 if(m_ignore_map_edit_events_area.contains(event->getArea()))
2752 MapEditEvent *e = event->clone();
2753 m_unsent_map_edit_queue.push_back(e);
2756 Inventory* Server::getInventory(const InventoryLocation &loc)
2759 case InventoryLocation::UNDEFINED:
2762 case InventoryLocation::CURRENT_PLAYER:
2765 case InventoryLocation::PLAYER:
2767 Player *player = m_env->getPlayer(loc.name.c_str());
2770 PlayerSAO *playersao = player->getPlayerSAO();
2773 return playersao->getInventory();
2776 case InventoryLocation::NODEMETA:
2778 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
2781 return meta->getInventory();
2784 case InventoryLocation::DETACHED:
2786 if(m_detached_inventories.count(loc.name) == 0)
2788 return m_detached_inventories[loc.name];
2796 void Server::setInventoryModified(const InventoryLocation &loc)
2799 case InventoryLocation::UNDEFINED:
2802 case InventoryLocation::PLAYER:
2804 Player *player = m_env->getPlayer(loc.name.c_str());
2807 PlayerSAO *playersao = player->getPlayerSAO();
2810 playersao->m_inventory_not_sent = true;
2811 playersao->m_wielded_item_not_sent = true;
2814 case InventoryLocation::NODEMETA:
2816 v3s16 blockpos = getNodeBlockPos(loc.p);
2818 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2820 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2822 setBlockNotSent(blockpos);
2825 case InventoryLocation::DETACHED:
2827 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
2835 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
2837 std::list<u16> clients = m_clients.getClientIDs();
2839 // Set the modified blocks unsent for all the clients
2840 for (std::list<u16>::iterator
2841 i = clients.begin();
2842 i != clients.end(); ++i) {
2843 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2845 client->SetBlocksNotSent(block);
2850 void Server::peerAdded(con::Peer *peer)
2852 DSTACK(__FUNCTION_NAME);
2853 verbosestream<<"Server::peerAdded(): peer->id="
2854 <<peer->id<<std::endl;
2857 c.type = con::PEER_ADDED;
2858 c.peer_id = peer->id;
2860 m_peer_change_queue.push_back(c);
2863 void Server::deletingPeer(con::Peer *peer, bool timeout)
2865 DSTACK(__FUNCTION_NAME);
2866 verbosestream<<"Server::deletingPeer(): peer->id="
2867 <<peer->id<<", timeout="<<timeout<<std::endl;
2869 m_clients.event(peer->id, CSE_Disconnect);
2871 c.type = con::PEER_REMOVED;
2872 c.peer_id = peer->id;
2873 c.timeout = timeout;
2874 m_peer_change_queue.push_back(c);
2877 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
2879 *retval = m_con.getPeerStat(peer_id,type);
2880 if (*retval == -1) return false;
2884 bool Server::getClientInfo(
2893 std::string* vers_string
2896 *state = m_clients.getClientState(peer_id);
2898 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
2900 if (client == NULL) {
2905 *uptime = client->uptime();
2906 *ser_vers = client->serialization_version;
2907 *prot_vers = client->net_proto_version;
2909 *major = client->getMajor();
2910 *minor = client->getMinor();
2911 *patch = client->getPatch();
2912 *vers_string = client->getPatch();
2919 void Server::handlePeerChanges()
2921 while(m_peer_change_queue.size() > 0)
2923 con::PeerChange c = m_peer_change_queue.pop_front();
2925 verbosestream<<"Server: Handling peer change: "
2926 <<"id="<<c.peer_id<<", timeout="<<c.timeout
2931 case con::PEER_ADDED:
2932 m_clients.CreateClient(c.peer_id);
2935 case con::PEER_REMOVED:
2936 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
2940 assert("Invalid peer change event received!" == 0);
2946 void Server::SendMovement(u16 peer_id)
2948 DSTACK(__FUNCTION_NAME);
2949 std::ostringstream os(std::ios_base::binary);
2951 writeU16(os, TOCLIENT_MOVEMENT);
2952 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
2953 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
2954 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
2955 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
2956 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
2957 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
2958 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
2959 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
2960 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
2961 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
2962 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
2963 writeF1000(os, g_settings->getFloat("movement_gravity"));
2966 std::string s = os.str();
2967 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2969 m_clients.send(peer_id, 0, data, true);
2972 void Server::SendHP(u16 peer_id, u8 hp)
2974 DSTACK(__FUNCTION_NAME);
2975 std::ostringstream os(std::ios_base::binary);
2977 writeU16(os, TOCLIENT_HP);
2981 std::string s = os.str();
2982 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2984 m_clients.send(peer_id, 0, data, true);
2987 void Server::SendBreath(u16 peer_id, u16 breath)
2989 DSTACK(__FUNCTION_NAME);
2990 std::ostringstream os(std::ios_base::binary);
2992 writeU16(os, TOCLIENT_BREATH);
2993 writeU16(os, breath);
2996 std::string s = os.str();
2997 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2999 m_clients.send(peer_id, 0, data, true);
3002 void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
3004 DSTACK(__FUNCTION_NAME);
3005 std::ostringstream os(std::ios_base::binary);
3007 writeU16(os, TOCLIENT_ACCESS_DENIED);
3008 os<<serializeWideString(reason);
3011 std::string s = os.str();
3012 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3014 m_clients.send(peer_id, 0, data, true);
3017 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
3018 v3f camera_point_target)
3020 DSTACK(__FUNCTION_NAME);
3021 std::ostringstream os(std::ios_base::binary);
3023 writeU16(os, TOCLIENT_DEATHSCREEN);
3024 writeU8(os, set_camera_point_target);
3025 writeV3F1000(os, camera_point_target);
3028 std::string s = os.str();
3029 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3031 m_clients.send(peer_id, 0, data, true);
3034 void Server::SendItemDef(u16 peer_id,
3035 IItemDefManager *itemdef, u16 protocol_version)
3037 DSTACK(__FUNCTION_NAME);
3038 std::ostringstream os(std::ios_base::binary);
3042 u32 length of the next item
3043 zlib-compressed serialized ItemDefManager
3045 writeU16(os, TOCLIENT_ITEMDEF);
3046 std::ostringstream tmp_os(std::ios::binary);
3047 itemdef->serialize(tmp_os, protocol_version);
3048 std::ostringstream tmp_os2(std::ios::binary);
3049 compressZlib(tmp_os.str(), tmp_os2);
3050 os<<serializeLongString(tmp_os2.str());
3053 std::string s = os.str();
3054 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3055 <<"): size="<<s.size()<<std::endl;
3056 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3058 m_clients.send(peer_id, 0, data, true);
3061 void Server::SendNodeDef(u16 peer_id,
3062 INodeDefManager *nodedef, u16 protocol_version)
3064 DSTACK(__FUNCTION_NAME);
3065 std::ostringstream os(std::ios_base::binary);
3069 u32 length of the next item
3070 zlib-compressed serialized NodeDefManager
3072 writeU16(os, TOCLIENT_NODEDEF);
3073 std::ostringstream tmp_os(std::ios::binary);
3074 nodedef->serialize(tmp_os, protocol_version);
3075 std::ostringstream tmp_os2(std::ios::binary);
3076 compressZlib(tmp_os.str(), tmp_os2);
3077 os<<serializeLongString(tmp_os2.str());
3080 std::string s = os.str();
3081 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3082 <<"): size="<<s.size()<<std::endl;
3083 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3085 m_clients.send(peer_id, 0, data, true);
3089 Non-static send methods
3092 void Server::SendInventory(u16 peer_id)
3094 DSTACK(__FUNCTION_NAME);
3096 PlayerSAO *playersao = getPlayerSAO(peer_id);
3099 playersao->m_inventory_not_sent = false;
3105 std::ostringstream os;
3106 playersao->getInventory()->serialize(os);
3108 std::string s = os.str();
3110 SharedBuffer<u8> data(s.size()+2);
3111 writeU16(&data[0], TOCLIENT_INVENTORY);
3112 memcpy(&data[2], s.c_str(), s.size());
3115 m_clients.send(peer_id, 0, data, true);
3118 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3120 DSTACK(__FUNCTION_NAME);
3122 std::ostringstream os(std::ios_base::binary);
3126 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3127 os.write((char*)buf, 2);
3130 writeU16(buf, message.size());
3131 os.write((char*)buf, 2);
3134 for(u32 i=0; i<message.size(); i++)
3138 os.write((char*)buf, 2);
3142 std::string s = os.str();
3143 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3145 if (peer_id != PEER_ID_INEXISTENT)
3148 m_clients.send(peer_id, 0, data, true);
3152 m_clients.sendToAll(0,data,true);
3156 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
3157 const std::string &formname)
3159 DSTACK(__FUNCTION_NAME);
3161 std::ostringstream os(std::ios_base::binary);
3166 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3167 os.write((char*)buf, 2);
3168 os<<serializeLongString(FORMSPEC_VERSION_STRING + formspec);
3169 os<<serializeString(formname);
3172 std::string s = os.str();
3173 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3175 m_clients.send(peer_id, 0, data, true);
3178 // Spawns a particle on peer with peer_id
3179 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3180 float expirationtime, float size, bool collisiondetection,
3181 bool vertical, std::string texture)
3183 DSTACK(__FUNCTION_NAME);
3185 std::ostringstream os(std::ios_base::binary);
3186 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3187 writeV3F1000(os, pos);
3188 writeV3F1000(os, velocity);
3189 writeV3F1000(os, acceleration);
3190 writeF1000(os, expirationtime);
3191 writeF1000(os, size);
3192 writeU8(os, collisiondetection);
3193 os<<serializeLongString(texture);
3194 writeU8(os, vertical);
3197 std::string s = os.str();
3198 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3200 if (peer_id != PEER_ID_INEXISTENT)
3203 m_clients.send(peer_id, 0, data, true);
3207 m_clients.sendToAll(0,data,true);
3211 // Adds a ParticleSpawner on peer with peer_id
3212 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3213 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3214 float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
3216 DSTACK(__FUNCTION_NAME);
3218 std::ostringstream os(std::ios_base::binary);
3219 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3221 writeU16(os, amount);
3222 writeF1000(os, spawntime);
3223 writeV3F1000(os, minpos);
3224 writeV3F1000(os, maxpos);
3225 writeV3F1000(os, minvel);
3226 writeV3F1000(os, maxvel);
3227 writeV3F1000(os, minacc);
3228 writeV3F1000(os, maxacc);
3229 writeF1000(os, minexptime);
3230 writeF1000(os, maxexptime);
3231 writeF1000(os, minsize);
3232 writeF1000(os, maxsize);
3233 writeU8(os, collisiondetection);
3234 os<<serializeLongString(texture);
3236 writeU8(os, vertical);
3239 std::string s = os.str();
3240 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3242 if (peer_id != PEER_ID_INEXISTENT)
3245 m_clients.send(peer_id, 0, data, true);
3248 m_clients.sendToAll(0,data,true);
3252 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3254 DSTACK(__FUNCTION_NAME);
3256 std::ostringstream os(std::ios_base::binary);
3257 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3262 std::string s = os.str();
3263 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3265 if (peer_id != PEER_ID_INEXISTENT) {
3267 m_clients.send(peer_id, 0, data, true);
3270 m_clients.sendToAll(0,data,true);
3275 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3277 std::ostringstream os(std::ios_base::binary);
3280 writeU16(os, TOCLIENT_HUDADD);
3282 writeU8(os, (u8)form->type);
3283 writeV2F1000(os, form->pos);
3284 os << serializeString(form->name);
3285 writeV2F1000(os, form->scale);
3286 os << serializeString(form->text);
3287 writeU32(os, form->number);
3288 writeU32(os, form->item);
3289 writeU32(os, form->dir);
3290 writeV2F1000(os, form->align);
3291 writeV2F1000(os, form->offset);
3292 writeV3F1000(os, form->world_pos);
3293 writeV2S32(os,form->size);
3296 std::string s = os.str();
3297 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3299 m_clients.send(peer_id, 1, data, true);
3302 void Server::SendHUDRemove(u16 peer_id, u32 id)
3304 std::ostringstream os(std::ios_base::binary);
3307 writeU16(os, TOCLIENT_HUDRM);
3311 std::string s = os.str();
3312 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3315 m_clients.send(peer_id, 1, data, true);
3318 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3320 std::ostringstream os(std::ios_base::binary);
3323 writeU16(os, TOCLIENT_HUDCHANGE);
3325 writeU8(os, (u8)stat);
3328 case HUD_STAT_SCALE:
3329 case HUD_STAT_ALIGN:
3330 case HUD_STAT_OFFSET:
3331 writeV2F1000(os, *(v2f *)value);
3335 os << serializeString(*(std::string *)value);
3337 case HUD_STAT_WORLD_POS:
3338 writeV3F1000(os, *(v3f *)value);
3341 writeV2S32(os,*(v2s32 *)value);
3343 case HUD_STAT_NUMBER:
3347 writeU32(os, *(u32 *)value);
3352 std::string s = os.str();
3353 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3355 m_clients.send(peer_id, 0, data, true);
3358 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3360 std::ostringstream os(std::ios_base::binary);
3363 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3365 //////////////////////////// compatibility code to be removed //////////////
3366 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
3367 ////////////////////////////////////////////////////////////////////////////
3368 writeU32(os, flags);
3372 std::string s = os.str();
3373 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3375 m_clients.send(peer_id, 0, data, true);
3378 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3380 std::ostringstream os(std::ios_base::binary);
3383 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3384 writeU16(os, param);
3385 os<<serializeString(value);
3388 std::string s = os.str();
3389 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3391 m_clients.send(peer_id, 0, data, true);
3394 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
3395 const std::string &type, const std::vector<std::string> ¶ms)
3397 std::ostringstream os(std::ios_base::binary);
3400 writeU16(os, TOCLIENT_SET_SKY);
3401 writeARGB8(os, bgcolor);
3402 os<<serializeString(type);
3403 writeU16(os, params.size());
3404 for(size_t i=0; i<params.size(); i++)
3405 os<<serializeString(params[i]);
3408 std::string s = os.str();
3409 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3411 m_clients.send(peer_id, 0, data, true);
3414 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
3417 std::ostringstream os(std::ios_base::binary);
3420 writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
3421 writeU8(os, do_override);
3422 writeU16(os, ratio*65535);
3425 std::string s = os.str();
3426 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3428 m_clients.send(peer_id, 0, data, true);
3431 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
3433 DSTACK(__FUNCTION_NAME);
3436 SharedBuffer<u8> data(2+2+4);
3437 writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
3438 writeU16(&data[2], time);
3439 writeF1000(&data[4], time_speed);
3441 if (peer_id == PEER_ID_INEXISTENT) {
3442 m_clients.sendToAll(0,data,true);
3446 m_clients.send(peer_id, 0, data, true);
3450 void Server::SendPlayerHP(u16 peer_id)
3452 DSTACK(__FUNCTION_NAME);
3453 PlayerSAO *playersao = getPlayerSAO(peer_id);
3455 playersao->m_hp_not_sent = false;
3456 SendHP(peer_id, playersao->getHP());
3457 m_script->player_event(playersao,"health_changed");
3459 // Send to other clients
3460 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
3461 ActiveObjectMessage aom(playersao->getId(), true, str);
3462 playersao->m_messages_out.push_back(aom);
3465 void Server::SendPlayerBreath(u16 peer_id)
3467 DSTACK(__FUNCTION_NAME);
3468 PlayerSAO *playersao = getPlayerSAO(peer_id);
3470 playersao->m_breath_not_sent = false;
3471 m_script->player_event(playersao,"breath_changed");
3472 SendBreath(peer_id, playersao->getBreath());
3475 void Server::SendMovePlayer(u16 peer_id)
3477 DSTACK(__FUNCTION_NAME);
3478 Player *player = m_env->getPlayer(peer_id);
3481 std::ostringstream os(std::ios_base::binary);
3482 writeU16(os, TOCLIENT_MOVE_PLAYER);
3483 writeV3F1000(os, player->getPosition());
3484 writeF1000(os, player->getPitch());
3485 writeF1000(os, player->getYaw());
3488 v3f pos = player->getPosition();
3489 f32 pitch = player->getPitch();
3490 f32 yaw = player->getYaw();
3491 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3492 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3499 std::string s = os.str();
3500 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3502 m_clients.send(peer_id, 0, data, true);
3505 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
3507 std::ostringstream os(std::ios_base::binary);
3509 writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS);
3510 writeV2S32(os, animation_frames[0]);
3511 writeV2S32(os, animation_frames[1]);
3512 writeV2S32(os, animation_frames[2]);
3513 writeV2S32(os, animation_frames[3]);
3514 writeF1000(os, animation_speed);
3517 std::string s = os.str();
3518 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3520 m_clients.send(peer_id, 0, data, true);
3523 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
3525 std::ostringstream os(std::ios_base::binary);
3527 writeU16(os, TOCLIENT_EYE_OFFSET);
3528 writeV3F1000(os, first);
3529 writeV3F1000(os, third);
3532 std::string s = os.str();
3533 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3535 m_clients.send(peer_id, 0, data, true);
3537 void Server::SendPlayerPrivileges(u16 peer_id)
3539 Player *player = m_env->getPlayer(peer_id);
3541 if(player->peer_id == PEER_ID_INEXISTENT)
3544 std::set<std::string> privs;
3545 m_script->getAuth(player->getName(), NULL, &privs);
3547 std::ostringstream os(std::ios_base::binary);
3548 writeU16(os, TOCLIENT_PRIVILEGES);
3549 writeU16(os, privs.size());
3550 for(std::set<std::string>::const_iterator i = privs.begin();
3551 i != privs.end(); i++){
3552 os<<serializeString(*i);
3556 std::string s = os.str();
3557 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3559 m_clients.send(peer_id, 0, data, true);
3562 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3564 Player *player = m_env->getPlayer(peer_id);
3566 if(player->peer_id == PEER_ID_INEXISTENT)
3569 std::ostringstream os(std::ios_base::binary);
3570 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3571 os<<serializeLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
3574 std::string s = os.str();
3575 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3577 m_clients.send(peer_id, 0, data, true);
3580 s32 Server::playSound(const SimpleSoundSpec &spec,
3581 const ServerSoundParams ¶ms)
3583 // Find out initial position of sound
3584 bool pos_exists = false;
3585 v3f pos = params.getPos(m_env, &pos_exists);
3586 // If position is not found while it should be, cancel sound
3587 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3590 // Filter destination clients
3591 std::list<u16> dst_clients;
3592 if(params.to_player != "")
3594 Player *player = m_env->getPlayer(params.to_player.c_str());
3596 infostream<<"Server::playSound: Player \""<<params.to_player
3597 <<"\" not found"<<std::endl;
3600 if(player->peer_id == PEER_ID_INEXISTENT){
3601 infostream<<"Server::playSound: Player \""<<params.to_player
3602 <<"\" not connected"<<std::endl;
3605 dst_clients.push_back(player->peer_id);
3609 std::list<u16> clients = m_clients.getClientIDs();
3611 for(std::list<u16>::iterator
3612 i = clients.begin(); i != clients.end(); ++i)
3614 Player *player = m_env->getPlayer(*i);
3618 if(player->getPosition().getDistanceFrom(pos) >
3619 params.max_hear_distance)
3622 dst_clients.push_back(*i);
3625 if(dst_clients.size() == 0)
3629 s32 id = m_next_sound_id++;
3630 // The sound will exist as a reference in m_playing_sounds
3631 m_playing_sounds[id] = ServerPlayingSound();
3632 ServerPlayingSound &psound = m_playing_sounds[id];
3633 psound.params = params;
3634 for(std::list<u16>::iterator i = dst_clients.begin();
3635 i != dst_clients.end(); i++)
3636 psound.clients.insert(*i);
3638 std::ostringstream os(std::ios_base::binary);
3639 writeU16(os, TOCLIENT_PLAY_SOUND);
3641 os<<serializeString(spec.name);
3642 writeF1000(os, spec.gain * params.gain);
3643 writeU8(os, params.type);
3644 writeV3F1000(os, pos);
3645 writeU16(os, params.object);
3646 writeU8(os, params.loop);
3648 std::string s = os.str();
3649 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3651 for(std::list<u16>::iterator i = dst_clients.begin();
3652 i != dst_clients.end(); i++){
3654 m_clients.send(*i, 0, data, true);
3658 void Server::stopSound(s32 handle)
3660 // Get sound reference
3661 std::map<s32, ServerPlayingSound>::iterator i =
3662 m_playing_sounds.find(handle);
3663 if(i == m_playing_sounds.end())
3665 ServerPlayingSound &psound = i->second;
3667 std::ostringstream os(std::ios_base::binary);
3668 writeU16(os, TOCLIENT_STOP_SOUND);
3669 writeS32(os, handle);
3671 std::string s = os.str();
3672 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3674 for(std::set<u16>::iterator i = psound.clients.begin();
3675 i != psound.clients.end(); i++){
3677 m_clients.send(*i, 0, data, true);
3679 // Remove sound reference
3680 m_playing_sounds.erase(i);
3683 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3684 std::list<u16> *far_players, float far_d_nodes)
3686 float maxd = far_d_nodes*BS;
3687 v3f p_f = intToFloat(p, BS);
3691 SharedBuffer<u8> reply(replysize);
3692 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3693 writeS16(&reply[2], p.X);
3694 writeS16(&reply[4], p.Y);
3695 writeS16(&reply[6], p.Z);
3697 std::list<u16> clients = m_clients.getClientIDs();
3698 for(std::list<u16>::iterator
3699 i = clients.begin();
3700 i != clients.end(); ++i)
3705 Player *player = m_env->getPlayer(*i);
3708 // If player is far away, only set modified blocks not sent
3709 v3f player_pos = player->getPosition();
3710 if(player_pos.getDistanceFrom(p_f) > maxd)
3712 far_players->push_back(*i);
3719 m_clients.send(*i, 0, reply, true);
3723 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3724 std::list<u16> *far_players, float far_d_nodes,
3725 bool remove_metadata)
3727 float maxd = far_d_nodes*BS;
3728 v3f p_f = intToFloat(p, BS);
3730 std::list<u16> clients = m_clients.getClientIDs();
3731 for(std::list<u16>::iterator
3732 i = clients.begin();
3733 i != clients.end(); ++i)
3739 Player *player = m_env->getPlayer(*i);
3742 // If player is far away, only set modified blocks not sent
3743 v3f player_pos = player->getPosition();
3744 if(player_pos.getDistanceFrom(p_f) > maxd)
3746 far_players->push_back(*i);
3751 SharedBuffer<u8> reply(0);
3753 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
3757 u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
3758 reply = SharedBuffer<u8>(replysize);
3759 writeU16(&reply[0], TOCLIENT_ADDNODE);
3760 writeS16(&reply[2], p.X);
3761 writeS16(&reply[4], p.Y);
3762 writeS16(&reply[6], p.Z);
3763 n.serialize(&reply[8], client->serialization_version);
3764 u32 index = 8 + MapNode::serializedLength(client->serialization_version);
3765 writeU8(&reply[index], remove_metadata ? 0 : 1);
3767 if (!remove_metadata) {
3768 if (client->net_proto_version <= 21) {
3769 // Old clients always clear metadata; fix it
3770 // by sending the full block again.
3771 client->SetBlockNotSent(p);
3778 if (reply.getSize() > 0)
3779 m_clients.send(*i, 0, reply, true);
3783 void Server::setBlockNotSent(v3s16 p)
3785 std::list<u16> clients = m_clients.getClientIDs();
3787 for(std::list<u16>::iterator
3788 i = clients.begin();
3789 i != clients.end(); ++i)
3791 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
3792 client->SetBlockNotSent(p);
3797 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
3799 DSTACK(__FUNCTION_NAME);
3801 v3s16 p = block->getPos();
3805 bool completely_air = true;
3806 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3807 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3808 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3810 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3812 completely_air = false;
3813 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3818 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3820 infostream<<"[completely air] ";
3821 infostream<<std::endl;
3825 Create a packet with the block in the right format
3828 std::ostringstream os(std::ios_base::binary);
3829 block->serialize(os, ver, false);
3830 block->serializeNetworkSpecific(os, net_proto_version);
3831 std::string s = os.str();
3832 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3834 u32 replysize = 8 + blockdata.getSize();
3835 SharedBuffer<u8> reply(replysize);
3836 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3837 writeS16(&reply[2], p.X);
3838 writeS16(&reply[4], p.Y);
3839 writeS16(&reply[6], p.Z);
3840 memcpy(&reply[8], *blockdata, blockdata.getSize());
3842 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3843 <<": \tpacket size: "<<replysize<<std::endl;*/
3848 m_clients.send(peer_id, 2, reply, true);
3851 void Server::SendBlocks(float dtime)
3853 DSTACK(__FUNCTION_NAME);
3855 JMutexAutoLock envlock(m_env_mutex);
3856 //TODO check if one big lock could be faster then multiple small ones
3858 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3860 std::vector<PrioritySortedBlockTransfer> queue;
3862 s32 total_sending = 0;
3865 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3867 std::list<u16> clients = m_clients.getClientIDs();
3870 for(std::list<u16>::iterator
3871 i = clients.begin();
3872 i != clients.end(); ++i)
3874 RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
3879 total_sending += client->SendingCount();
3880 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
3886 // Lowest priority number comes first.
3887 // Lowest is most important.
3888 std::sort(queue.begin(), queue.end());
3891 for(u32 i=0; i<queue.size(); i++)
3893 //TODO: Calculate limit dynamically
3894 if(total_sending >= g_settings->getS32
3895 ("max_simultaneous_block_sends_server_total"))
3898 PrioritySortedBlockTransfer q = queue[i];
3900 MapBlock *block = NULL;
3903 block = m_env->getMap().getBlockNoCreate(q.pos);
3905 catch(InvalidPositionException &e)
3910 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
3915 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
3917 client->SentBlock(q.pos);
3923 void Server::fillMediaCache()
3925 DSTACK(__FUNCTION_NAME);
3927 infostream<<"Server: Calculating media file checksums"<<std::endl;
3929 // Collect all media file paths
3930 std::list<std::string> paths;
3931 for(std::vector<ModSpec>::iterator i = m_mods.begin();
3932 i != m_mods.end(); i++){
3933 const ModSpec &mod = *i;
3934 paths.push_back(mod.path + DIR_DELIM + "textures");
3935 paths.push_back(mod.path + DIR_DELIM + "sounds");
3936 paths.push_back(mod.path + DIR_DELIM + "media");
3937 paths.push_back(mod.path + DIR_DELIM + "models");
3939 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
3941 // Collect media file information from paths into cache
3942 for(std::list<std::string>::iterator i = paths.begin();
3943 i != paths.end(); i++)
3945 std::string mediapath = *i;
3946 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3947 for(u32 j=0; j<dirlist.size(); j++){
3948 if(dirlist[j].dir) // Ignode dirs
3950 std::string filename = dirlist[j].name;
3951 // If name contains illegal characters, ignore the file
3952 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3953 infostream<<"Server: ignoring illegal file name: \""
3954 <<filename<<"\""<<std::endl;
3957 // If name is not in a supported format, ignore it
3958 const char *supported_ext[] = {
3959 ".png", ".jpg", ".bmp", ".tga",
3960 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3962 ".x", ".b3d", ".md2", ".obj",
3965 if(removeStringEnd(filename, supported_ext) == ""){
3966 infostream<<"Server: ignoring unsupported file extension: \""
3967 <<filename<<"\""<<std::endl;
3970 // Ok, attempt to load the file and add to cache
3971 std::string filepath = mediapath + DIR_DELIM + filename;
3973 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3974 if(fis.good() == false){
3975 errorstream<<"Server::fillMediaCache(): Could not open \""
3976 <<filename<<"\" for reading"<<std::endl;
3979 std::ostringstream tmp_os(std::ios_base::binary);
3983 fis.read(buf, 1024);
3984 std::streamsize len = fis.gcount();
3985 tmp_os.write(buf, len);
3994 errorstream<<"Server::fillMediaCache(): Failed to read \""
3995 <<filename<<"\""<<std::endl;
3998 if(tmp_os.str().length() == 0){
3999 errorstream<<"Server::fillMediaCache(): Empty file \""
4000 <<filepath<<"\""<<std::endl;
4005 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4007 unsigned char *digest = sha1.getDigest();
4008 std::string sha1_base64 = base64_encode(digest, 20);
4009 std::string sha1_hex = hex_encode((char*)digest, 20);
4013 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4014 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4019 struct SendableMediaAnnouncement
4022 std::string sha1_digest;
4024 SendableMediaAnnouncement(const std::string &name_="",
4025 const std::string &sha1_digest_=""):
4027 sha1_digest(sha1_digest_)
4031 void Server::sendMediaAnnouncement(u16 peer_id)
4033 DSTACK(__FUNCTION_NAME);
4035 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4038 std::list<SendableMediaAnnouncement> file_announcements;
4040 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4041 i != m_media.end(); i++){
4043 file_announcements.push_back(
4044 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4048 std::ostringstream os(std::ios_base::binary);
4056 u16 length of sha1_digest
4061 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4062 writeU16(os, file_announcements.size());
4064 for(std::list<SendableMediaAnnouncement>::iterator
4065 j = file_announcements.begin();
4066 j != file_announcements.end(); ++j){
4067 os<<serializeString(j->name);
4068 os<<serializeString(j->sha1_digest);
4070 os<<serializeString(g_settings->get("remote_media"));
4073 std::string s = os.str();
4074 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4077 m_clients.send(peer_id, 0, data, true);
4080 struct SendableMedia
4086 SendableMedia(const std::string &name_="", const std::string &path_="",
4087 const std::string &data_=""):
4094 void Server::sendRequestedMedia(u16 peer_id,
4095 const std::list<std::string> &tosend)
4097 DSTACK(__FUNCTION_NAME);
4099 verbosestream<<"Server::sendRequestedMedia(): "
4100 <<"Sending files to client"<<std::endl;
4104 // Put 5kB in one bunch (this is not accurate)
4105 u32 bytes_per_bunch = 5000;
4107 std::vector< std::list<SendableMedia> > file_bunches;
4108 file_bunches.push_back(std::list<SendableMedia>());
4110 u32 file_size_bunch_total = 0;
4112 for(std::list<std::string>::const_iterator i = tosend.begin();
4113 i != tosend.end(); ++i)
4115 const std::string &name = *i;
4117 if(m_media.find(name) == m_media.end()){
4118 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4119 <<"unknown file \""<<(name)<<"\""<<std::endl;
4123 //TODO get path + name
4124 std::string tpath = m_media[name].path;
4127 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4128 if(fis.good() == false){
4129 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4130 <<tpath<<"\" for reading"<<std::endl;
4133 std::ostringstream tmp_os(std::ios_base::binary);
4137 fis.read(buf, 1024);
4138 std::streamsize len = fis.gcount();
4139 tmp_os.write(buf, len);
4140 file_size_bunch_total += len;
4149 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4150 <<name<<"\""<<std::endl;
4153 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4154 <<tname<<"\""<<std::endl;*/
4156 file_bunches[file_bunches.size()-1].push_back(
4157 SendableMedia(name, tpath, tmp_os.str()));
4159 // Start next bunch if got enough data
4160 if(file_size_bunch_total >= bytes_per_bunch){
4161 file_bunches.push_back(std::list<SendableMedia>());
4162 file_size_bunch_total = 0;
4167 /* Create and send packets */
4169 u32 num_bunches = file_bunches.size();
4170 for(u32 i=0; i<num_bunches; i++)
4172 std::ostringstream os(std::ios_base::binary);
4176 u16 total number of texture bunches
4177 u16 index of this bunch
4178 u32 number of files in this bunch
4187 writeU16(os, TOCLIENT_MEDIA);
4188 writeU16(os, num_bunches);
4190 writeU32(os, file_bunches[i].size());
4192 for(std::list<SendableMedia>::iterator
4193 j = file_bunches[i].begin();
4194 j != file_bunches[i].end(); ++j){
4195 os<<serializeString(j->name);
4196 os<<serializeLongString(j->data);
4200 std::string s = os.str();
4201 verbosestream<<"Server::sendRequestedMedia(): bunch "
4202 <<i<<"/"<<num_bunches
4203 <<" files="<<file_bunches[i].size()
4204 <<" size=" <<s.size()<<std::endl;
4205 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4207 m_clients.send(peer_id, 2, data, true);
4211 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4213 if(m_detached_inventories.count(name) == 0){
4214 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4217 Inventory *inv = m_detached_inventories[name];
4219 std::ostringstream os(std::ios_base::binary);
4220 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4221 os<<serializeString(name);
4225 std::string s = os.str();
4226 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4228 if (peer_id != PEER_ID_INEXISTENT)
4231 m_clients.send(peer_id, 0, data, true);
4235 m_clients.sendToAll(0,data,true);
4239 void Server::sendDetachedInventories(u16 peer_id)
4241 DSTACK(__FUNCTION_NAME);
4243 for(std::map<std::string, Inventory*>::iterator
4244 i = m_detached_inventories.begin();
4245 i != m_detached_inventories.end(); i++){
4246 const std::string &name = i->first;
4247 //Inventory *inv = i->second;
4248 sendDetachedInventory(name, peer_id);
4256 void Server::DiePlayer(u16 peer_id)
4258 DSTACK(__FUNCTION_NAME);
4260 PlayerSAO *playersao = getPlayerSAO(peer_id);
4263 infostream<<"Server::DiePlayer(): Player "
4264 <<playersao->getPlayer()->getName()
4265 <<" dies"<<std::endl;
4267 playersao->setHP(0);
4269 // Trigger scripted stuff
4270 m_script->on_dieplayer(playersao);
4272 SendPlayerHP(peer_id);
4273 SendDeathscreen(peer_id, false, v3f(0,0,0));
4276 void Server::RespawnPlayer(u16 peer_id)
4278 DSTACK(__FUNCTION_NAME);
4280 PlayerSAO *playersao = getPlayerSAO(peer_id);
4283 infostream<<"Server::RespawnPlayer(): Player "
4284 <<playersao->getPlayer()->getName()
4285 <<" respawns"<<std::endl;
4287 playersao->setHP(PLAYER_MAX_HP);
4289 bool repositioned = m_script->on_respawnplayer(playersao);
4291 v3f pos = findSpawnPos(m_env->getServerMap());
4292 playersao->setPos(pos);
4296 void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
4298 DSTACK(__FUNCTION_NAME);
4300 SendAccessDenied(peer_id, reason);
4301 m_clients.event(peer_id, CSE_SetDenied);
4302 m_con.DisconnectPeer(peer_id);
4305 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
4307 DSTACK(__FUNCTION_NAME);
4308 std::wstring message;
4311 Clear references to playing sounds
4313 for(std::map<s32, ServerPlayingSound>::iterator
4314 i = m_playing_sounds.begin();
4315 i != m_playing_sounds.end();)
4317 ServerPlayingSound &psound = i->second;
4318 psound.clients.erase(peer_id);
4319 if(psound.clients.size() == 0)
4320 m_playing_sounds.erase(i++);
4325 Player *player = m_env->getPlayer(peer_id);
4327 // Collect information about leaving in chat
4329 if(player != NULL && reason != CDR_DENY)
4331 std::wstring name = narrow_to_wide(player->getName());
4334 message += L" left the game.";
4335 if(reason == CDR_TIMEOUT)
4336 message += L" (timed out)";
4340 /* Run scripts and remove from environment */
4344 PlayerSAO *playersao = player->getPlayerSAO();
4347 m_script->on_leaveplayer(playersao);
4349 playersao->disconnected();
4357 if(player != NULL && reason != CDR_DENY)
4359 std::ostringstream os(std::ios_base::binary);
4360 std::list<u16> clients = m_clients.getClientIDs();
4362 for(std::list<u16>::iterator
4363 i = clients.begin();
4364 i != clients.end(); ++i)
4367 Player *player = m_env->getPlayer(*i);
4370 // Get name of player
4371 os<<player->getName()<<" ";
4374 actionstream<<player->getName()<<" "
4375 <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
4376 <<" List of players: "<<os.str()<<std::endl;
4380 JMutexAutoLock env_lock(m_env_mutex);
4381 m_clients.DeleteClient(peer_id);
4385 // Send leave chat message to all remaining clients
4386 if(message.length() != 0)
4387 SendChatMessage(PEER_ID_INEXISTENT,message);
4390 void Server::UpdateCrafting(u16 peer_id)
4392 DSTACK(__FUNCTION_NAME);
4394 Player* player = m_env->getPlayer(peer_id);
4397 // Get a preview for crafting
4399 InventoryLocation loc;
4400 loc.setPlayer(player->getName());
4401 getCraftingResult(&player->inventory, preview, false, this);
4402 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
4404 // Put the new preview in
4405 InventoryList *plist = player->inventory.getList("craftpreview");
4407 assert(plist->getSize() >= 1);
4408 plist->changeItem(0, preview);
4411 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
4413 RemoteClient *client = getClientNoEx(peer_id,state_min);
4415 throw ClientNotFoundException("Client not found");
4419 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
4421 return m_clients.getClientNoEx(peer_id, state_min);
4424 std::string Server::getPlayerName(u16 peer_id)
4426 Player *player = m_env->getPlayer(peer_id);
4428 return "[id="+itos(peer_id)+"]";
4429 return player->getName();
4432 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
4434 Player *player = m_env->getPlayer(peer_id);
4437 return player->getPlayerSAO();
4440 std::wstring Server::getStatusString()
4442 std::wostringstream os(std::ios_base::binary);
4445 os<<L"version="<<narrow_to_wide(minetest_version_simple);
4447 os<<L", uptime="<<m_uptime.get();
4449 os<<L", max_lag="<<m_env->getMaxLagEstimate();
4450 // Information about clients
4453 std::list<u16> clients = m_clients.getClientIDs();
4454 for(std::list<u16>::iterator i = clients.begin();
4455 i != clients.end(); ++i)
4458 Player *player = m_env->getPlayer(*i);
4459 // Get name of player
4460 std::wstring name = L"unknown";
4462 name = narrow_to_wide(player->getName());
4463 // Add name to information string
4471 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4472 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4473 if(g_settings->get("motd") != "")
4474 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4478 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4480 std::set<std::string> privs;
4481 m_script->getAuth(name, NULL, &privs);
4485 bool Server::checkPriv(const std::string &name, const std::string &priv)
4487 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4488 return (privs.count(priv) != 0);
4491 void Server::reportPrivsModified(const std::string &name)
4494 std::list<u16> clients = m_clients.getClientIDs();
4495 for(std::list<u16>::iterator
4496 i = clients.begin();
4497 i != clients.end(); ++i){
4498 Player *player = m_env->getPlayer(*i);
4499 reportPrivsModified(player->getName());
4502 Player *player = m_env->getPlayer(name.c_str());
4505 SendPlayerPrivileges(player->peer_id);
4506 PlayerSAO *sao = player->getPlayerSAO();
4509 sao->updatePrivileges(
4510 getPlayerEffectivePrivs(name),
4515 void Server::reportInventoryFormspecModified(const std::string &name)
4517 Player *player = m_env->getPlayer(name.c_str());
4520 SendPlayerInventoryFormspec(player->peer_id);
4523 void Server::setIpBanned(const std::string &ip, const std::string &name)
4525 m_banmanager->add(ip, name);
4528 void Server::unsetIpBanned(const std::string &ip_or_name)
4530 m_banmanager->remove(ip_or_name);
4533 std::string Server::getBanDescription(const std::string &ip_or_name)
4535 return m_banmanager->getBanDescription(ip_or_name);
4538 void Server::notifyPlayer(const char *name, const std::wstring &msg)
4540 Player *player = m_env->getPlayer(name);
4544 if (player->peer_id == PEER_ID_INEXISTENT)
4547 SendChatMessage(player->peer_id, msg);
4550 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4552 Player *player = m_env->getPlayer(playername);
4556 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4560 SendShowFormspecMessage(player->peer_id, formspec, formname);
4564 u32 Server::hudAdd(Player *player, HudElement *form) {
4568 u32 id = player->addHud(form);
4570 SendHUDAdd(player->peer_id, id, form);
4575 bool Server::hudRemove(Player *player, u32 id) {
4579 HudElement* todel = player->removeHud(id);
4586 SendHUDRemove(player->peer_id, id);
4590 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4594 SendHUDChange(player->peer_id, id, stat, data);
4598 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4602 SendHUDSetFlags(player->peer_id, flags, mask);
4603 player->hud_flags = flags;
4605 PlayerSAO* playersao = player->getPlayerSAO();
4607 if (playersao == NULL)
4610 m_script->player_event(playersao, "hud_changed");
4614 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4617 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4620 std::ostringstream os(std::ios::binary);
4621 writeS32(os, hotbar_itemcount);
4622 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4626 void Server::hudSetHotbarImage(Player *player, std::string name) {
4630 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
4633 void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
4637 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
4640 bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed)
4645 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
4649 bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
4654 SendEyeOffset(player->peer_id, first, third);
4658 bool Server::setSky(Player *player, const video::SColor &bgcolor,
4659 const std::string &type, const std::vector<std::string> ¶ms)
4664 SendSetSky(player->peer_id, bgcolor, type, params);
4668 bool Server::overrideDayNightRatio(Player *player, bool do_override,
4674 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
4678 void Server::notifyPlayers(const std::wstring &msg)
4680 SendChatMessage(PEER_ID_INEXISTENT,msg);
4683 void Server::spawnParticle(const char *playername, v3f pos,
4684 v3f velocity, v3f acceleration,
4685 float expirationtime, float size, bool
4686 collisiondetection, bool vertical, std::string texture)
4688 Player *player = m_env->getPlayer(playername);
4691 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4692 expirationtime, size, collisiondetection, vertical, texture);
4695 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4696 float expirationtime, float size,
4697 bool collisiondetection, bool vertical, std::string texture)
4699 SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
4700 expirationtime, size, collisiondetection, vertical, texture);
4703 u32 Server::addParticleSpawner(const char *playername,
4704 u16 amount, float spawntime,
4705 v3f minpos, v3f maxpos,
4706 v3f minvel, v3f maxvel,
4707 v3f minacc, v3f maxacc,
4708 float minexptime, float maxexptime,
4709 float minsize, float maxsize,
4710 bool collisiondetection, bool vertical, std::string texture)
4712 Player *player = m_env->getPlayer(playername);
4717 for(;;) // look for unused particlespawner id
4720 if (std::find(m_particlespawner_ids.begin(),
4721 m_particlespawner_ids.end(), id)
4722 == m_particlespawner_ids.end())
4724 m_particlespawner_ids.push_back(id);
4729 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4730 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4731 minexptime, maxexptime, minsize, maxsize,
4732 collisiondetection, vertical, texture, id);
4737 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4738 v3f minpos, v3f maxpos,
4739 v3f minvel, v3f maxvel,
4740 v3f minacc, v3f maxacc,
4741 float minexptime, float maxexptime,
4742 float minsize, float maxsize,
4743 bool collisiondetection, bool vertical, std::string texture)
4746 for(;;) // look for unused particlespawner id
4749 if (std::find(m_particlespawner_ids.begin(),
4750 m_particlespawner_ids.end(), id)
4751 == m_particlespawner_ids.end())
4753 m_particlespawner_ids.push_back(id);
4758 SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
4759 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4760 minexptime, maxexptime, minsize, maxsize,
4761 collisiondetection, vertical, texture, id);
4766 void Server::deleteParticleSpawner(const char *playername, u32 id)
4768 Player *player = m_env->getPlayer(playername);
4772 m_particlespawner_ids.erase(
4773 std::remove(m_particlespawner_ids.begin(),
4774 m_particlespawner_ids.end(), id),
4775 m_particlespawner_ids.end());
4776 SendDeleteParticleSpawner(player->peer_id, id);
4779 void Server::deleteParticleSpawnerAll(u32 id)
4781 m_particlespawner_ids.erase(
4782 std::remove(m_particlespawner_ids.begin(),
4783 m_particlespawner_ids.end(), id),
4784 m_particlespawner_ids.end());
4785 SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
4788 Inventory* Server::createDetachedInventory(const std::string &name)
4790 if(m_detached_inventories.count(name) > 0){
4791 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4792 delete m_detached_inventories[name];
4794 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4796 Inventory *inv = new Inventory(m_itemdef);
4798 m_detached_inventories[name] = inv;
4799 //TODO find a better way to do this
4800 sendDetachedInventory(name,PEER_ID_INEXISTENT);
4807 BoolScopeSet(bool *dst, bool val):
4810 m_orig_state = *m_dst;
4815 *m_dst = m_orig_state;
4822 // actions: time-reversed list
4823 // Return value: success/failure
4824 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4825 std::list<std::string> *log)
4827 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4828 ServerMap *map = (ServerMap*)(&m_env->getMap());
4829 // Disable rollback report sink while reverting
4830 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4832 // Fail if no actions to handle
4833 if(actions.empty()){
4834 log->push_back("Nothing to do.");
4841 for(std::list<RollbackAction>::const_iterator
4842 i = actions.begin();
4843 i != actions.end(); i++)
4845 const RollbackAction &action = *i;
4847 bool success = action.applyRevert(map, this, this);
4850 std::ostringstream os;
4851 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4852 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4854 log->push_back(os.str());
4856 std::ostringstream os;
4857 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4858 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4860 log->push_back(os.str());
4864 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4865 <<" failed"<<std::endl;
4867 // Call it done if less than half failed
4868 return num_failed <= num_tried/2;
4871 // IGameDef interface
4873 IItemDefManager* Server::getItemDefManager()
4877 INodeDefManager* Server::getNodeDefManager()
4881 ICraftDefManager* Server::getCraftDefManager()
4885 ITextureSource* Server::getTextureSource()
4889 IShaderSource* Server::getShaderSource()
4893 scene::ISceneManager* Server::getSceneManager()
4898 u16 Server::allocateUnknownNodeId(const std::string &name)
4900 return m_nodedef->allocateDummy(name);
4902 ISoundManager* Server::getSoundManager()
4904 return &dummySoundManager;
4906 MtEventManager* Server::getEventManager()
4910 IRollbackReportSink* Server::getRollbackReportSink()
4912 if(!m_enable_rollback_recording)
4914 if(!m_rollback_sink_enabled)
4919 IWritableItemDefManager* Server::getWritableItemDefManager()
4923 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4927 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4932 const ModSpec* Server::getModSpec(const std::string &modname)
4934 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4935 i != m_mods.end(); i++){
4936 const ModSpec &mod = *i;
4937 if(mod.name == modname)
4942 void Server::getModNames(std::list<std::string> &modlist)
4944 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4946 modlist.push_back(i->name);
4949 std::string Server::getBuiltinLuaPath()
4951 return porting::path_share + DIR_DELIM + "builtin";
4954 v3f findSpawnPos(ServerMap &map)
4956 //return v3f(50,50,50)*BS;
4961 nodepos = v2s16(0,0);
4966 s16 water_level = map.getWaterLevel();
4968 // Try to find a good place a few times
4969 for(s32 i=0; i<1000; i++)
4972 // We're going to try to throw the player to this position
4973 v2s16 nodepos2d = v2s16(
4974 -range + (myrand() % (range * 2)),
4975 -range + (myrand() % (range * 2)));
4977 // Get ground height at point
4978 s16 groundheight = map.findGroundLevel(nodepos2d);
4979 if (groundheight <= water_level) // Don't go underwater
4981 if (groundheight > water_level + 6) // Don't go to high places
4984 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
4985 bool is_good = false;
4987 for (s32 i = 0; i < 10; i++) {
4988 v3s16 blockpos = getNodeBlockPos(nodepos);
4989 map.emergeBlock(blockpos, true);
4990 content_t c = map.getNodeNoEx(nodepos).getContent();
4991 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
4993 if (air_count >= 2){
5001 // Found a good place
5002 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5008 return intToFloat(nodepos, BS);
5011 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5013 RemotePlayer *player = NULL;
5014 bool newplayer = false;
5017 Try to get an existing player
5019 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5021 // If player is already connected, cancel
5022 if(player != NULL && player->peer_id != 0)
5024 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5029 If player with the wanted peer_id already exists, cancel.
5031 if(m_env->getPlayer(peer_id) != NULL)
5033 infostream<<"emergePlayer(): Player with wrong name but same"
5034 " peer_id already exists"<<std::endl;
5038 // Load player if it isn't already loaded
5040 player = static_cast<RemotePlayer*>(m_env->loadPlayer(name));
5043 // Create player if it doesn't exist
5046 player = new RemotePlayer(this, name);
5047 // Set player position
5048 infostream<<"Server: Finding spawn place for player \""
5049 <<name<<"\""<<std::endl;
5050 v3f pos = findSpawnPos(m_env->getServerMap());
5051 player->setPosition(pos);
5053 // Make sure the player is saved
5054 player->setModified(true);
5056 // Add player to environment
5057 m_env->addPlayer(player);
5060 // Create a new player active object
5061 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5062 getPlayerEffectivePrivs(player->getName()),
5065 /* Clean up old HUD elements from previous sessions */
5068 /* Add object to environment */
5069 m_env->addActiveObject(playersao);
5073 m_script->on_newplayer(playersao);
5079 void dedicated_server_loop(Server &server, bool &kill)
5081 DSTACK(__FUNCTION_NAME);
5083 verbosestream<<"dedicated_server_loop()"<<std::endl;
5085 IntervalLimiter m_profiler_interval;
5089 float steplen = g_settings->getFloat("dedicated_server_step");
5090 // This is kind of a hack but can be done like this
5091 // because server.step() is very light
5093 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5094 sleep_ms((int)(steplen*1000.0));
5096 server.step(steplen);
5098 if(server.getShutdownRequested() || kill)
5100 infostream<<"Dedicated server quitting"<<std::endl;
5102 if(g_settings->getBool("server_announce") == true)
5103 ServerList::sendAnnounce("delete");
5111 float profiler_print_interval =
5112 g_settings->getFloat("profiler_print_interval");
5113 if(profiler_print_interval != 0)
5115 if(m_profiler_interval.step(steplen, profiler_print_interval))
5117 infostream<<"Profiler:"<<std::endl;
5118 g_profiler->print(infostream);
5119 g_profiler->clear();