/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "server.h"
#include <iostream>
#include <queue>
#include <algorithm>
#include "network/networkprotocol.h"
#include "network/serveropcodes.h"
#include "ban.h"
#include "environment.h"
#include "map.h"
#include "jthread/jmutexautolock.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
#include "version.h"
#include "filesys.h"
#include "mapblock.h"
#include "serverobject.h"
#include "genericobject.h"
#include "settings.h"
#include "profiler.h"
#include "log.h"
#include "scripting_game.h"
#include "nodedef.h"
#include "itemdef.h"
#include "craftdef.h"
#include "emerge.h"
#include "mapgen.h"
#include "mg_biome.h"
#include "content_mapnode.h"
#include "content_nodemeta.h"
#include "content_abm.h"
#include "content_sao.h"
#include "mods.h"
#include "sound.h" // dummySoundManager
#include "event_manager.h"
#include "serverlist.h"
#include "util/string.h"
#include "util/mathconstants.h"
#include "rollback.h"
#include "util/serialize.h"
#include "util/thread.h"
#include "defaultsettings.h"
#include "util/base64.h"
#include "util/sha1.h"
#include "util/hex.h"
class ClientNotFoundException : public BaseException
{
public:
ClientNotFoundException(const char *s):
BaseException(s)
{}
};
class ServerThread : public JThread
{
Server *m_server;
public:
ServerThread(Server *server):
JThread(),
m_server(server)
{
}
void * Thread();
};
void * ServerThread::Thread()
{
log_register_thread("ServerThread");
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
m_server->AsyncRunStep(true);
ThreadStarted();
porting::setThreadName("ServerThread");
while(!StopRequested())
{
try{
//TimeTaker timer("AsyncRunStep() + Receive()");
m_server->AsyncRunStep();
m_server->Receive();
}
catch(con::NoIncomingDataException &e)
{
}
catch(con::PeerNotFoundException &e)
{
infostream<<"Server: PeerNotFoundException"<<std::endl;
}
catch(ClientNotFoundException &e)
{
}
catch(con::ConnectionBindFailed &e)
{
m_server->setAsyncFatalError(e.what());
}
catch(LuaError &e)
{
m_server->setAsyncFatalError(e.what());
}
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
}
v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
{
if(pos_exists) *pos_exists = false;
switch(type){
case SSP_LOCAL:
return v3f(0,0,0);
case SSP_POSITIONAL:
if(pos_exists) *pos_exists = true;
return pos;
case SSP_OBJECT: {
if(object == 0)
return v3f(0,0,0);
ServerActiveObject *sao = env->getActiveObject(object);
if(!sao)
return v3f(0,0,0);
if(pos_exists) *pos_exists = true;
return sao->getBasePosition(); }
}
return v3f(0,0,0);
}
/*
Server
*/
Server::Server(
const std::string &path_world,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode,
bool ipv6
):
m_path_world(path_world),
m_gamespec(gamespec),
m_simple_singleplayer_mode(simple_singleplayer_mode),
m_async_fatal_error(""),
m_env(NULL),
m_con(PROTOCOL_ID,
512,
CONNECTION_TIMEOUT,
ipv6,
this),
m_banmanager(NULL),
m_rollback(NULL),
m_enable_rollback_recording(false),
m_emerge(NULL),
m_script(NULL),
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
m_craftdef(createCraftDefManager()),
m_event(new EventManager()),
m_thread(NULL),
m_time_of_day_send_timer(0),
m_uptime(0),
m_clients(&m_con),
m_shutdown_requested(false),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0),
m_next_sound_id(0)
{
m_liquid_transform_timer = 0.0;
m_liquid_transform_every = 1.0;
m_print_info_timer = 0.0;
m_masterserver_timer = 0.0;
m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0;
m_savemap_timer = 0.0;
m_step_dtime = 0.0;
m_lag = g_settings->getFloat("dedicated_server_step");
if(path_world == "")
throw ServerError("Supplied empty world path");
if(!gamespec.isValid())
throw ServerError("Supplied invalid gamespec");
infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
if(m_simple_singleplayer_mode)
infostream<<" in simple singleplayer mode"<<std::endl;
else
infostream<<std::endl;
infostream<<"- world: "<<m_path_world<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
// Create world if it doesn't exist
if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
throw ServerError("Failed to initialize world");
// Create server thread
m_thread = new ServerThread(this);
// Create emerge manager
m_emerge = new EmergeManager(this);
// Create ban manager
std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
m_banmanager = new BanManager(ban_path);
ModConfiguration modconf(m_path_world);
m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
if(!modconf.isConsistent())
{
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
{
ModSpec mod = *it;
errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
dep_it != mod.unsatisfied_depends.end(); ++dep_it)
errorstream << " \"" << *dep_it << "\"";
errorstream << std::endl;
}
}
Settings worldmt_settings;
std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
worldmt_settings.readConfigFile(worldmt.c_str());
std::vector<std::string> names = worldmt_settings.getNames();
std::set<std::string> load_mod_names;
for(std::vector<std::string>::iterator it = names.begin();
it != names.end(); ++it)
{
std::string name = *it;
if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
|