2010-11-29 19:13:04 +01:00
|
|
|
/*
|
2013-02-24 18:40:43 +01:00
|
|
|
Minetest
|
2013-02-24 19:38:45 +01:00
|
|
|
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
2010-11-29 19:13:04 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
2012-06-05 16:56:56 +02:00
|
|
|
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
|
2010-11-29 19:13:04 +01:00
|
|
|
(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
|
2012-06-05 16:56:56 +02:00
|
|
|
GNU Lesser General Public License for more details.
|
2010-11-29 19:13:04 +01:00
|
|
|
|
2012-06-05 16:56:56 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
2010-11-29 19:13:04 +01:00
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "server.h"
|
|
|
|
#include <iostream>
|
2011-11-27 10:44:05 +01:00
|
|
|
#include <queue>
|
2013-01-23 18:32:02 +01:00
|
|
|
#include <algorithm>
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "clientserver.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "ban.h"
|
|
|
|
#include "environment.h"
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "map.h"
|
2013-09-16 05:00:01 +02:00
|
|
|
#include "jthread/jmutexautolock.h"
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "main.h"
|
|
|
|
#include "constants.h"
|
2010-12-11 17:11:03 +01:00
|
|
|
#include "voxel.h"
|
2011-04-24 23:31:22 +02:00
|
|
|
#include "config.h"
|
2013-09-25 04:29:07 +02:00
|
|
|
#include "version.h"
|
2011-05-22 16:00:09 +02:00
|
|
|
#include "filesys.h"
|
2011-06-26 01:34:36 +02:00
|
|
|
#include "mapblock.h"
|
2011-06-26 14:48:56 +02:00
|
|
|
#include "serverobject.h"
|
2013-08-13 23:06:39 +02:00
|
|
|
#include "genericobject.h"
|
2011-10-12 12:53:38 +02:00
|
|
|
#include "settings.h"
|
|
|
|
#include "profiler.h"
|
2011-10-16 13:57:53 +02:00
|
|
|
#include "log.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "scripting_game.h"
|
2011-11-14 20:41:30 +01:00
|
|
|
#include "nodedef.h"
|
2012-01-12 06:10:39 +01:00
|
|
|
#include "itemdef.h"
|
2011-11-17 01:28:46 +01:00
|
|
|
#include "craftdef.h"
|
2013-02-14 04:43:15 +01:00
|
|
|
#include "emerge.h"
|
2011-11-26 14:53:52 +01:00
|
|
|
#include "mapgen.h"
|
2012-11-26 03:16:48 +01:00
|
|
|
#include "biome.h"
|
2012-03-19 03:04:16 +01:00
|
|
|
#include "content_mapnode.h"
|
|
|
|
#include "content_nodemeta.h"
|
2011-11-27 23:45:34 +01:00
|
|
|
#include "content_abm.h"
|
2012-03-19 03:04:16 +01:00
|
|
|
#include "content_sao.h"
|
2011-12-03 02:23:14 +01:00
|
|
|
#include "mods.h"
|
2012-01-02 12:31:50 +01:00
|
|
|
#include "sha1.h"
|
|
|
|
#include "base64.h"
|
2012-03-04 20:08:03 +01:00
|
|
|
#include "tool.h"
|
2012-03-23 11:05:17 +01:00
|
|
|
#include "sound.h" // dummySoundManager
|
2012-03-23 19:23:03 +01:00
|
|
|
#include "event_manager.h"
|
2012-03-25 13:47:51 +02:00
|
|
|
#include "hex.h"
|
2013-02-21 23:00:44 +01:00
|
|
|
#include "serverlist.h"
|
2012-06-17 01:40:36 +02:00
|
|
|
#include "util/string.h"
|
|
|
|
#include "util/pointedthing.h"
|
2012-06-23 15:06:03 +02:00
|
|
|
#include "util/mathconstants.h"
|
2012-07-26 21:06:45 +02:00
|
|
|
#include "rollback.h"
|
2012-11-26 10:18:34 +01:00
|
|
|
#include "util/serialize.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "util/thread.h"
|
2013-03-21 20:42:23 +01:00
|
|
|
#include "defaultsettings.h"
|
2011-10-16 13:57:53 +02:00
|
|
|
|
2013-08-04 07:17:07 +02:00
|
|
|
class ClientNotFoundException : public BaseException
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ClientNotFoundException(const char *s):
|
|
|
|
BaseException(s)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2013-12-03 23:32:03 +01:00
|
|
|
class ServerThread : public JThread
|
2013-08-11 04:09:45 +02:00
|
|
|
{
|
|
|
|
Server *m_server;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
ServerThread(Server *server):
|
2013-12-03 23:32:03 +01:00
|
|
|
JThread(),
|
2013-08-11 04:09:45 +02:00
|
|
|
m_server(server)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void * Thread();
|
|
|
|
};
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void * ServerThread::Thread()
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
log_register_thread("ServerThread");
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
2010-12-27 13:34:17 +01:00
|
|
|
BEGIN_DEBUG_EXCEPTION_HANDLER
|
|
|
|
|
2014-01-06 20:05:28 +01:00
|
|
|
m_server->AsyncRunStep(true);
|
|
|
|
|
|
|
|
ThreadStarted();
|
|
|
|
|
2014-04-05 15:27:33 +02:00
|
|
|
porting::setThreadName("ServerThread");
|
|
|
|
|
2013-12-03 23:32:03 +01:00
|
|
|
while(!StopRequested())
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
try{
|
2011-02-15 15:11:24 +01:00
|
|
|
//TimeTaker timer("AsyncRunStep() + Receive()");
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_server->AsyncRunStep();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
m_server->Receive();
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
catch(con::NoIncomingDataException &e)
|
|
|
|
{
|
|
|
|
}
|
2011-01-23 16:29:15 +01:00
|
|
|
catch(con::PeerNotFoundException &e)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: PeerNotFoundException"<<std::endl;
|
2011-01-23 16:29:15 +01:00
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
catch(ClientNotFoundException &e)
|
|
|
|
{
|
|
|
|
}
|
2012-03-11 19:45:43 +01:00
|
|
|
catch(con::ConnectionBindFailed &e)
|
|
|
|
{
|
|
|
|
m_server->setAsyncFatalError(e.what());
|
|
|
|
}
|
2012-03-30 17:42:18 +02:00
|
|
|
catch(LuaError &e)
|
|
|
|
{
|
|
|
|
m_server->setAsyncFatalError(e.what());
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Server
|
|
|
|
*/
|
|
|
|
|
|
|
|
Server::Server(
|
2012-03-11 13:54:23 +01:00
|
|
|
const std::string &path_world,
|
2012-03-15 14:20:20 +01:00
|
|
|
const SubgameSpec &gamespec,
|
2014-03-07 01:00:03 +01:00
|
|
|
bool simple_singleplayer_mode,
|
|
|
|
bool ipv6
|
2010-11-27 00:02:21 +01:00
|
|
|
):
|
2012-03-10 14:56:24 +01:00
|
|
|
m_path_world(path_world),
|
2012-03-11 13:54:23 +01:00
|
|
|
m_gamespec(gamespec),
|
2012-03-15 14:20:20 +01:00
|
|
|
m_simple_singleplayer_mode(simple_singleplayer_mode),
|
2012-03-11 19:45:43 +01:00
|
|
|
m_async_fatal_error(""),
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env(NULL),
|
2014-01-31 00:24:00 +01:00
|
|
|
m_con(PROTOCOL_ID,
|
|
|
|
512,
|
|
|
|
CONNECTION_TIMEOUT,
|
2014-03-07 01:00:03 +01:00
|
|
|
ipv6,
|
2014-01-31 00:24:00 +01:00
|
|
|
this),
|
2013-08-11 04:09:45 +02:00
|
|
|
m_banmanager(NULL),
|
2012-07-26 21:06:45 +02:00
|
|
|
m_rollback(NULL),
|
|
|
|
m_rollback_sink_enabled(true),
|
2012-07-28 02:08:09 +02:00
|
|
|
m_enable_rollback_recording(false),
|
2012-11-26 03:16:48 +01:00
|
|
|
m_emerge(NULL),
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script(NULL),
|
2012-01-12 06:10:39 +01:00
|
|
|
m_itemdef(createItemDefManager()),
|
2011-11-16 13:08:31 +01:00
|
|
|
m_nodedef(createNodeDefManager()),
|
2011-11-17 01:28:46 +01:00
|
|
|
m_craftdef(createCraftDefManager()),
|
2012-03-23 19:23:03 +01:00
|
|
|
m_event(new EventManager()),
|
2013-08-11 04:09:45 +02:00
|
|
|
m_thread(NULL),
|
2010-12-24 16:08:50 +01:00
|
|
|
m_time_of_day_send_timer(0),
|
2011-01-28 00:38:16 +01:00
|
|
|
m_uptime(0),
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients(&m_con),
|
2011-02-23 01:49:57 +01:00
|
|
|
m_shutdown_requested(false),
|
|
|
|
m_ignore_map_edit_events(false),
|
|
|
|
m_ignore_map_edit_events_peer_id(0)
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-01-17 20:15:31 +01:00
|
|
|
m_liquid_transform_timer = 0.0;
|
2013-02-24 15:39:07 +01:00
|
|
|
m_liquid_transform_every = 1.0;
|
2010-12-19 15:51:45 +01:00
|
|
|
m_print_info_timer = 0.0;
|
2013-02-21 23:00:44 +01:00
|
|
|
m_masterserver_timer = 0.0;
|
2010-12-19 15:51:45 +01:00
|
|
|
m_objectdata_timer = 0.0;
|
|
|
|
m_emergethread_trigger_timer = 0.0;
|
|
|
|
m_savemap_timer = 0.0;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
m_step_dtime = 0.0;
|
2014-01-06 23:50:45 +01:00
|
|
|
m_lag = g_settings->getFloat("dedicated_server_step");
|
2011-11-11 18:33:17 +01:00
|
|
|
|
2012-03-11 19:45:43 +01:00
|
|
|
if(path_world == "")
|
|
|
|
throw ServerError("Supplied empty world path");
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-11 13:54:23 +01:00
|
|
|
if(!gamespec.isValid())
|
|
|
|
throw ServerError("Supplied invalid gamespec");
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-15 14:20:20 +01:00
|
|
|
infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
|
|
|
|
if(m_simple_singleplayer_mode)
|
|
|
|
infostream<<" in simple singleplayer mode"<<std::endl;
|
|
|
|
else
|
|
|
|
infostream<<std::endl;
|
2012-03-11 13:54:23 +01:00
|
|
|
infostream<<"- world: "<<m_path_world<<std::endl;
|
|
|
|
infostream<<"- game: "<<m_gamespec.path<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
|
2013-03-21 20:42:23 +01:00
|
|
|
// Initialize default settings and override defaults with those provided
|
|
|
|
// by the game
|
|
|
|
set_default_settings(g_settings);
|
|
|
|
Settings gamedefaults;
|
|
|
|
getGameMinetestConfig(gamespec.path, gamedefaults);
|
|
|
|
override_default_settings(g_settings, &gamedefaults);
|
2013-08-11 04:09:45 +02:00
|
|
|
|
|
|
|
// Create server thread
|
|
|
|
m_thread = new ServerThread(this);
|
|
|
|
|
2013-03-24 06:43:38 +01:00
|
|
|
// Create emerge manager
|
2013-04-06 17:19:59 +02:00
|
|
|
m_emerge = new EmergeManager(this);
|
2013-08-11 04:09:45 +02:00
|
|
|
|
2013-12-30 20:29:33 +01:00
|
|
|
// Create world if it doesn't exist
|
|
|
|
if(!initializeWorld(m_path_world, m_gamespec.id))
|
|
|
|
throw ServerError("Failed to initialize world");
|
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
// Create ban manager
|
|
|
|
std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt";
|
|
|
|
m_banmanager = new BanManager(ban_path);
|
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
// Create rollback manager
|
|
|
|
std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
|
|
|
|
m_rollback = createRollbackManager(rollback_path, this);
|
|
|
|
|
2012-12-08 18:10:54 +01:00
|
|
|
ModConfiguration modconf(m_path_world);
|
|
|
|
m_mods = modconf.getMods();
|
2013-05-03 23:58:22 +02:00
|
|
|
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
|
2012-12-08 18:10:54 +01:00
|
|
|
// complain about mods with unsatisfied dependencies
|
2013-12-01 01:52:06 +01:00
|
|
|
if(!modconf.isConsistent())
|
2012-12-08 18:10:54 +01:00
|
|
|
{
|
2013-05-03 23:58:22 +02:00
|
|
|
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
2013-02-20 20:06:39 +01:00
|
|
|
it != unsatisfied_mods.end(); ++it)
|
2012-12-08 18:10:54 +01:00
|
|
|
{
|
2013-02-20 20:06:39 +01:00
|
|
|
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;
|
2012-12-08 18:10:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2013-12-01 01:52:06 +01:00
|
|
|
for(std::vector<std::string>::iterator it = names.begin();
|
2012-12-08 18:10:54 +01:00
|
|
|
it != names.end(); ++it)
|
2013-12-01 01:52:06 +01:00
|
|
|
{
|
|
|
|
std::string name = *it;
|
2013-05-19 19:46:50 +02:00
|
|
|
if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
|
|
|
|
load_mod_names.insert(name.substr(9));
|
2012-12-08 18:10:54 +01:00
|
|
|
}
|
|
|
|
// complain about mods declared to be loaded, but not found
|
|
|
|
for(std::vector<ModSpec>::iterator it = m_mods.begin();
|
2013-03-21 17:48:21 +01:00
|
|
|
it != m_mods.end(); ++it)
|
2012-12-08 18:10:54 +01:00
|
|
|
load_mod_names.erase((*it).name);
|
2013-05-03 23:58:22 +02:00
|
|
|
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
2013-03-21 17:48:21 +01:00
|
|
|
it != unsatisfied_mods.end(); ++it)
|
2013-02-20 20:06:39 +01:00
|
|
|
load_mod_names.erase((*it).name);
|
2012-12-08 18:10:54 +01:00
|
|
|
if(!load_mod_names.empty())
|
2013-12-01 01:52:06 +01:00
|
|
|
{
|
2013-02-20 20:06:39 +01:00
|
|
|
errorstream << "The following mods could not be found:";
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::set<std::string>::iterator it = load_mod_names.begin();
|
|
|
|
it != load_mod_names.end(); ++it)
|
2013-02-20 20:06:39 +01:00
|
|
|
errorstream << " \"" << (*it) << "\"";
|
2012-12-08 18:10:54 +01:00
|
|
|
errorstream << std::endl;
|
|
|
|
}
|
|
|
|
|
2012-03-10 14:56:24 +01:00
|
|
|
// Lock environment
|
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
// Initialize scripting
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"Server: Initializing Lua"<<std::endl;
|
2013-05-25 00:51:02 +02:00
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
m_script = new GameScripting(this);
|
2013-05-25 00:51:02 +02:00
|
|
|
|
2014-04-27 23:55:49 +02:00
|
|
|
std::string scriptpath = getBuiltinLuaPath() + DIR_DELIM "init.lua";
|
2013-05-25 00:51:02 +02:00
|
|
|
|
2014-04-27 23:55:49 +02:00
|
|
|
if (!m_script->loadScript(scriptpath)) {
|
|
|
|
throw ModError("Failed to load and run " + scriptpath);
|
2011-11-25 20:32:12 +01:00
|
|
|
}
|
2014-04-27 23:55:49 +02:00
|
|
|
|
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
// Print 'em
|
|
|
|
infostream<<"Server: Loading mods: ";
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::vector<ModSpec>::iterator i = m_mods.begin();
|
2012-03-11 03:15:45 +01:00
|
|
|
i != m_mods.end(); i++){
|
|
|
|
const ModSpec &mod = *i;
|
|
|
|
infostream<<mod.name<<" ";
|
|
|
|
}
|
|
|
|
infostream<<std::endl;
|
|
|
|
// Load and run "mod" scripts
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::vector<ModSpec>::iterator i = m_mods.begin();
|
2011-12-11 15:49:40 +01:00
|
|
|
i != m_mods.end(); i++){
|
|
|
|
const ModSpec &mod = *i;
|
2011-11-15 10:02:47 +01:00
|
|
|
std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
|
|
|
|
<<scriptpath<<"\"]"<<std::endl;
|
2013-05-25 00:51:02 +02:00
|
|
|
bool success = m_script->loadMod(scriptpath, mod.name);
|
2011-11-15 10:02:47 +01:00
|
|
|
if(!success){
|
|
|
|
errorstream<<"Server: Failed to load and run "
|
|
|
|
<<scriptpath<<std::endl;
|
2011-12-03 02:23:14 +01:00
|
|
|
throw ModError("Failed to load and run "+scriptpath);
|
2011-11-15 07:48:24 +01:00
|
|
|
}
|
2011-11-11 19:50:09 +01:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-01-02 12:31:50 +01:00
|
|
|
// Read Textures and calculate sha1 sums
|
2012-03-25 10:50:29 +02:00
|
|
|
fillMediaCache();
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Apply item aliases in the node definition manager
|
|
|
|
m_nodedef->updateAliases(m_itemdef);
|
|
|
|
|
2014-02-16 00:20:15 +01:00
|
|
|
// Load the mapgen params from global settings now after any
|
|
|
|
// initial overrides have been set by the mods
|
|
|
|
m_emerge->loadMapgenParams();
|
|
|
|
|
2013-01-23 04:32:30 +01:00
|
|
|
// Initialize Environment
|
|
|
|
ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
|
|
|
m_clients.setEnv(m_env);
|
|
|
|
|
2013-06-27 23:06:52 +02:00
|
|
|
// Run some callbacks after the MG params have been set up but before activation
|
2014-02-04 04:42:10 +01:00
|
|
|
m_script->environment_OnMapgenInit(&m_emerge->params);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2013-06-27 23:06:52 +02:00
|
|
|
// Initialize mapgens
|
2014-02-04 04:42:10 +01:00
|
|
|
m_emerge->initMapgens();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-12 01:25:30 +01:00
|
|
|
// Give environment reference to scripting api
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->initializeEnvironment(m_env);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-05-22 16:00:09 +02:00
|
|
|
// Register us to receive map edit events
|
2013-01-06 20:40:24 +01:00
|
|
|
servermap->addEventReceiver(this);
|
2011-02-23 01:49:57 +01:00
|
|
|
|
2011-05-22 16:00:09 +02:00
|
|
|
// If file exists, load environment metadata
|
2014-05-30 22:04:07 +02:00
|
|
|
if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt"))
|
2011-05-22 16:00:09 +02:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Loading environment metadata"<<std::endl;
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env->loadMeta();
|
2011-05-22 16:00:09 +02:00
|
|
|
}
|
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Add some test ActiveBlockModifiers to environment
|
2011-11-27 23:45:34 +01:00
|
|
|
add_legacy_abms(m_env, m_nodedef);
|
2013-02-24 15:39:07 +01:00
|
|
|
|
|
|
|
m_liquid_transform_every = g_settings->getFloat("liquid_update");
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Server::~Server()
|
|
|
|
{
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"Server destructing"<<std::endl;
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Send shutdown message
|
|
|
|
SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
|
2012-09-08 20:44:26 +02:00
|
|
|
|
2012-11-30 18:41:13 +01:00
|
|
|
{
|
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Execute script shutdown hooks
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->on_shutdown();
|
2011-02-15 15:11:24 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Saving players"<<std::endl;
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env->saveLoadedPlayers();
|
2011-05-22 16:00:09 +02:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Saving environment metadata"<<std::endl;
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env->saveMeta();
|
2011-10-14 11:39:25 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Stop threads
|
2010-11-27 00:02:21 +01:00
|
|
|
stop();
|
2013-08-11 04:09:45 +02:00
|
|
|
delete m_thread;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-26 07:12:18 +01:00
|
|
|
// stop all emerge threads before deleting players that may have
|
|
|
|
// requested blocks to be emerged
|
|
|
|
m_emerge->stopThreads();
|
2013-04-07 22:27:27 +02:00
|
|
|
|
2012-03-23 19:23:03 +01:00
|
|
|
// Delete things in the reverse order of creation
|
2011-11-11 18:33:17 +01:00
|
|
|
delete m_env;
|
2014-01-26 07:12:18 +01:00
|
|
|
|
|
|
|
// N.B. the EmergeManager should be deleted after the Environment since Map
|
|
|
|
// depends on EmergeManager to write its current params to the map meta
|
|
|
|
delete m_emerge;
|
2012-07-26 21:06:45 +02:00
|
|
|
delete m_rollback;
|
2013-08-11 04:09:45 +02:00
|
|
|
delete m_banmanager;
|
2012-03-23 19:23:03 +01:00
|
|
|
delete m_event;
|
2012-01-12 06:10:39 +01:00
|
|
|
delete m_itemdef;
|
2011-11-16 13:08:31 +01:00
|
|
|
delete m_nodedef;
|
2011-11-29 16:15:18 +01:00
|
|
|
delete m_craftdef;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
// Deinitialize scripting
|
|
|
|
infostream<<"Server: Deinitializing scripting"<<std::endl;
|
2013-05-25 00:51:02 +02:00
|
|
|
delete m_script;
|
2012-07-24 19:57:17 +02:00
|
|
|
|
|
|
|
// Delete detached inventories
|
2014-05-30 22:04:07 +02:00
|
|
|
for (std::map<std::string, Inventory*>::iterator
|
|
|
|
i = m_detached_inventories.begin();
|
|
|
|
i != m_detached_inventories.end(); i++) {
|
|
|
|
delete i->second;
|
2012-07-24 19:57:17 +02:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2014-02-05 21:24:46 +01:00
|
|
|
void Server::start(Address bind_addr)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2014-02-05 21:24:46 +01:00
|
|
|
infostream<<"Starting server on "
|
|
|
|
<< bind_addr.serializeString() <<"..."<<std::endl;
|
2012-03-11 19:45:43 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Stop thread if already running
|
2013-12-03 23:32:03 +01:00
|
|
|
m_thread->Stop();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Initialize connection
|
2011-10-20 22:04:09 +02:00
|
|
|
m_con.SetTimeoutMs(30);
|
2014-02-05 21:24:46 +01:00
|
|
|
m_con.Serve(bind_addr);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
// Start thread
|
2013-08-11 04:09:45 +02:00
|
|
|
m_thread->Start();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
// ASCII art for the win!
|
|
|
|
actionstream
|
|
|
|
<<" .__ __ __ "<<std::endl
|
|
|
|
<<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
|
|
|
|
<<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
|
|
|
|
<<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
|
|
|
|
<<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
|
|
|
|
<<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
|
2012-03-11 14:49:14 +01:00
|
|
|
actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
|
2012-03-11 13:54:23 +01:00
|
|
|
actionstream<<"Server for gameid=\""<<m_gamespec.id
|
2014-02-05 21:24:46 +01:00
|
|
|
<<"\" listening on "<<bind_addr.serializeString()<<":"
|
|
|
|
<<bind_addr.getPort() << "."<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::stop()
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Stopping and waiting threads"<<std::endl;
|
2011-02-21 15:10:36 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Stop threads (set run=false first so both start stopping)
|
2013-12-03 23:32:03 +01:00
|
|
|
m_thread->Stop();
|
2013-02-14 04:43:15 +01:00
|
|
|
//m_emergethread.setRun(false);
|
2013-12-03 23:32:03 +01:00
|
|
|
m_thread->Wait();
|
2013-02-14 04:43:15 +01:00
|
|
|
//m_emergethread.stop();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Threads stopped"<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::step(float dtime)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
// Limit a bit
|
|
|
|
if(dtime > 2.0)
|
|
|
|
dtime = 2.0;
|
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_step_dtime_mutex);
|
|
|
|
m_step_dtime += dtime;
|
|
|
|
}
|
2012-03-11 19:45:43 +01:00
|
|
|
// Throw if fatal error occurred in thread
|
|
|
|
std::string async_err = m_async_fatal_error.get();
|
|
|
|
if(async_err != ""){
|
|
|
|
throw ServerError(async_err);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2014-01-06 20:05:28 +01:00
|
|
|
void Server::AsyncRunStep(bool initial_step)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-16 20:16:44 +02:00
|
|
|
g_profiler->add("Server::AsyncRunStep (num)", 1);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
float dtime;
|
|
|
|
{
|
|
|
|
JMutexAutoLock lock1(m_step_dtime_mutex);
|
|
|
|
dtime = m_step_dtime;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-05-30 23:15:43 +02:00
|
|
|
{
|
|
|
|
// Send blocks to clients
|
|
|
|
SendBlocks(dtime);
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-06 20:05:28 +01:00
|
|
|
if((dtime < 0.001) && (initial_step == false))
|
2010-12-13 20:32:35 +01:00
|
|
|
return;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-16 20:16:44 +02:00
|
|
|
g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
|
2011-05-31 10:59:51 +02:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Server steps "<<dtime<<std::endl;
|
|
|
|
//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-12-13 20:32:35 +01:00
|
|
|
{
|
|
|
|
JMutexAutoLock lock1(m_step_dtime_mutex);
|
2010-12-19 15:51:45 +01:00
|
|
|
m_step_dtime -= dtime;
|
2010-12-13 20:32:35 +01:00
|
|
|
}
|
2010-12-24 16:08:50 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Update uptime
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
m_uptime.set(m_uptime.get() + dtime);
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
handlePeerChanges();
|
2011-08-22 20:27:11 +02:00
|
|
|
|
2010-12-20 13:04:31 +01:00
|
|
|
/*
|
2012-03-16 15:34:30 +01:00
|
|
|
Update time of day and overall game time
|
2010-12-20 13:04:31 +01:00
|
|
|
*/
|
|
|
|
{
|
2011-05-22 16:00:09 +02:00
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
|
|
|
|
2012-03-16 15:34:30 +01:00
|
|
|
m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
|
2010-12-20 13:04:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Send to clients at constant intervals
|
|
|
|
*/
|
|
|
|
|
|
|
|
m_time_of_day_send_timer -= dtime;
|
|
|
|
if(m_time_of_day_send_timer < 0.0)
|
|
|
|
{
|
2011-10-12 12:53:38 +02:00
|
|
|
m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
|
2013-08-11 04:09:45 +02:00
|
|
|
u16 time = m_env->getTimeOfDay();
|
|
|
|
float time_speed = g_settings->getFloat("time_speed");
|
2014-01-31 00:24:00 +01:00
|
|
|
SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
|
2010-12-20 13:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
2010-12-13 20:32:35 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
2013-08-03 22:16:37 +02:00
|
|
|
// Figure out and report maximum lag to environment
|
|
|
|
float max_lag = m_env->getMaxLagEstimate();
|
|
|
|
max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
|
|
|
|
if(dtime > max_lag){
|
|
|
|
if(dtime > 0.1 && dtime > max_lag * 2.0)
|
|
|
|
infostream<<"Server: Maximum lag peaked to "<<dtime
|
|
|
|
<<" s"<<std::endl;
|
|
|
|
max_lag = dtime;
|
|
|
|
}
|
|
|
|
m_env->reportMaxLagEstimate(max_lag);
|
2011-06-26 23:27:17 +02:00
|
|
|
// Step environment
|
2011-10-16 20:16:44 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "SEnv step");
|
2011-10-16 21:39:35 +02:00
|
|
|
ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->step(dtime);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-21 13:13:28 +01:00
|
|
|
const float map_timer_and_unload_dtime = 2.92;
|
2011-06-26 23:27:17 +02:00
|
|
|
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
|
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
|
|
|
// Run Map's timers and unload unused data
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: map timer and unload");
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
|
2011-10-12 12:53:38 +02:00
|
|
|
g_settings->getFloat("server_unload_unused_data_timeout"));
|
2011-06-26 23:27:17 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
Do background stuff
|
|
|
|
*/
|
2011-11-26 13:03:56 +01:00
|
|
|
|
2011-11-26 13:30:57 +01:00
|
|
|
/*
|
2011-11-29 22:18:20 +01:00
|
|
|
Handle players
|
2011-11-26 13:30:57 +01:00
|
|
|
*/
|
2011-11-26 13:03:56 +01:00
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
|
|
|
std::list<u16> clientids = m_clients.getClientIDs();
|
2011-11-26 13:03:56 +01:00
|
|
|
|
2011-12-02 16:19:42 +01:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: handle players");
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clientids.begin();
|
|
|
|
i != clientids.end(); ++i)
|
2011-11-26 13:03:56 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
PlayerSAO *playersao = getPlayerSAO(*i);
|
2012-03-24 18:52:50 +01:00
|
|
|
if(playersao == NULL)
|
2011-11-26 13:03:56 +01:00
|
|
|
continue;
|
2011-11-29 22:18:20 +01:00
|
|
|
|
2011-12-01 22:33:48 +01:00
|
|
|
/*
|
2011-12-02 16:19:42 +01:00
|
|
|
Handle player HPs (die if hp=0)
|
2011-12-01 22:33:48 +01:00
|
|
|
*/
|
2013-01-06 15:32:23 +01:00
|
|
|
if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
|
|
|
|
{
|
|
|
|
if(playersao->getHP() == 0)
|
2014-01-31 00:24:00 +01:00
|
|
|
DiePlayer(*i);
|
2013-01-06 15:32:23 +01:00
|
|
|
else
|
2014-01-31 00:24:00 +01:00
|
|
|
SendPlayerHP(*i);
|
2013-01-06 15:32:23 +01:00
|
|
|
}
|
2011-12-01 22:33:48 +01:00
|
|
|
|
2013-07-19 19:50:33 +02:00
|
|
|
/*
|
|
|
|
Send player breath if changed
|
|
|
|
*/
|
2014-04-28 23:41:27 +02:00
|
|
|
if(playersao->m_breath_not_sent) {
|
2014-01-31 00:24:00 +01:00
|
|
|
SendPlayerBreath(*i);
|
2013-07-19 19:50:33 +02:00
|
|
|
}
|
|
|
|
|
2011-11-29 22:18:20 +01:00
|
|
|
/*
|
2013-01-06 15:32:23 +01:00
|
|
|
Send player inventories if necessary
|
2011-11-29 22:18:20 +01:00
|
|
|
*/
|
Update attachments at the ending of the addToScene function for parents. And with this... *drum roll* Client-side attachments are at last functional and stick visibly.
Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed)
Fix a bug in falling code where entities get stuck
Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed.
Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need
Create a separate function for detaching, and also update lua api documentation
When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct
Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too)
Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
2012-11-07 17:42:38 +01:00
|
|
|
if(playersao->m_moved){
|
2014-01-31 00:24:00 +01:00
|
|
|
SendMovePlayer(*i);
|
Update attachments at the ending of the addToScene function for parents. And with this... *drum roll* Client-side attachments are at last functional and stick visibly.
Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed)
Fix a bug in falling code where entities get stuck
Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed.
Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need
Create a separate function for detaching, and also update lua api documentation
When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct
Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too)
Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
2012-11-07 17:42:38 +01:00
|
|
|
playersao->m_moved = false;
|
2011-11-29 22:18:20 +01:00
|
|
|
}
|
2012-03-19 03:04:16 +01:00
|
|
|
if(playersao->m_inventory_not_sent){
|
2014-01-31 00:24:00 +01:00
|
|
|
UpdateCrafting(*i);
|
|
|
|
SendInventory(*i);
|
2011-11-29 22:18:20 +01:00
|
|
|
}
|
2011-11-26 13:03:56 +01:00
|
|
|
}
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-26 13:03:56 +01:00
|
|
|
/* Transform liquids */
|
2011-01-17 20:15:31 +01:00
|
|
|
m_liquid_transform_timer += dtime;
|
2013-02-24 15:39:07 +01:00
|
|
|
if(m_liquid_transform_timer >= m_liquid_transform_every)
|
2011-01-17 13:57:37 +01:00
|
|
|
{
|
2013-02-24 15:39:07 +01:00
|
|
|
m_liquid_transform_timer -= m_liquid_transform_every;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: liquid transform");
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->getMap().transformLiquids(modified_blocks);
|
2012-11-26 03:16:48 +01:00
|
|
|
#if 0
|
2011-01-17 20:15:31 +01:00
|
|
|
/*
|
|
|
|
Update lighting
|
|
|
|
*/
|
|
|
|
core::map<v3s16, MapBlock*> lighting_modified_blocks;
|
2011-11-11 18:33:17 +01:00
|
|
|
ServerMap &map = ((ServerMap&)m_env->getMap());
|
2011-01-17 20:15:31 +01:00
|
|
|
map.updateLighting(modified_blocks, lighting_modified_blocks);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
// Add blocks modified by lighting to modified_blocks
|
|
|
|
for(core::map<v3s16, MapBlock*>::Iterator
|
|
|
|
i = lighting_modified_blocks.getIterator();
|
|
|
|
i.atEnd() == false; i++)
|
|
|
|
{
|
|
|
|
MapBlock *block = i.getNode()->getValue();
|
|
|
|
modified_blocks.insert(block->getPos(), block);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
Set the modified blocks unsent for all the clients
|
|
|
|
*/
|
2014-01-31 00:24:00 +01:00
|
|
|
if(modified_blocks.size() > 0)
|
2011-01-17 20:15:31 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
SetBlocksNotSent(modified_blocks);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.step(dtime);
|
2013-02-21 23:00:44 +01:00
|
|
|
|
2014-01-06 23:50:45 +01:00
|
|
|
m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
|
2013-02-21 23:00:44 +01:00
|
|
|
#if USE_CURL
|
|
|
|
// send masterserver announce
|
|
|
|
{
|
|
|
|
float &counter = m_masterserver_timer;
|
2014-01-31 00:24:00 +01:00
|
|
|
if(!isSingleplayer() && (!counter || counter >= 300.0) &&
|
2014-06-20 06:59:39 +02:00
|
|
|
g_settings->getBool("server_announce"))
|
2013-02-21 23:00:44 +01:00
|
|
|
{
|
2014-06-20 06:59:39 +02:00
|
|
|
ServerList::sendAnnounce(counter ? "update" : "start",
|
|
|
|
m_clients.getPlayerNames(),
|
|
|
|
m_uptime.get(),
|
|
|
|
m_env->getGameTime(),
|
|
|
|
m_lag,
|
|
|
|
m_gamespec.id,
|
|
|
|
m_mods);
|
2013-02-21 23:00:44 +01:00
|
|
|
counter = 0.01;
|
|
|
|
}
|
|
|
|
counter += dtime;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-12-18 12:10:37 +01:00
|
|
|
/*
|
2011-02-20 23:45:14 +01:00
|
|
|
Check added and deleted active objects
|
2010-12-18 12:10:37 +01:00
|
|
|
*/
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
|
2010-12-18 12:10:37 +01:00
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Lock();
|
|
|
|
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
|
2011-10-16 20:16:44 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
// Radius inside which objects are active
|
2011-10-15 01:28:57 +02:00
|
|
|
s16 radius = g_settings->getS16("active_object_send_range_blocks");
|
|
|
|
radius *= MAP_BLOCKSIZE;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<u16, RemoteClient*>::iterator
|
2014-01-31 00:24:00 +01:00
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
RemoteClient *client = i->second;
|
2011-12-02 00:18:25 +01:00
|
|
|
|
|
|
|
// If definitions and textures have not been sent, don't
|
|
|
|
// send objects either
|
2014-06-28 08:02:38 +02:00
|
|
|
if (client->getState() < CS_DefinitionsSent)
|
2011-12-02 00:18:25 +01:00
|
|
|
continue;
|
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
Player *player = m_env->getPlayer(client->peer_id);
|
2011-02-23 01:49:57 +01:00
|
|
|
if(player==NULL)
|
2011-04-07 23:47:14 +02:00
|
|
|
{
|
2011-06-02 19:09:30 +02:00
|
|
|
// This can happen if the client timeouts somehow
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
|
2011-06-02 19:09:30 +02:00
|
|
|
<<client->peer_id
|
|
|
|
<<" has no associated player"<<std::endl;*/
|
2011-02-23 01:49:57 +01:00
|
|
|
continue;
|
2011-04-07 23:47:14 +02:00
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
v3s16 pos = floatToInt(player->getPosition(), BS);
|
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::set<u16> removed_objects;
|
|
|
|
std::set<u16> added_objects;
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->getRemovedActiveObjects(pos, radius,
|
2011-02-20 23:45:14 +01:00
|
|
|
client->m_known_objects, removed_objects);
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->getAddedActiveObjects(pos, radius,
|
2011-02-20 23:45:14 +01:00
|
|
|
client->m_known_objects, added_objects);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Ignore if nothing happened
|
|
|
|
if(removed_objects.size() == 0 && added_objects.size() == 0)
|
2011-04-07 23:47:14 +02:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"active objects: none changed"<<std::endl;
|
2011-02-20 23:45:14 +01:00
|
|
|
continue;
|
2011-04-07 23:47:14 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
std::string data_buffer;
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
char buf[4];
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Handle removed objects
|
|
|
|
writeU16((u8*)buf, removed_objects.size());
|
|
|
|
data_buffer.append(buf, 2);
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::set<u16>::iterator
|
|
|
|
i = removed_objects.begin();
|
|
|
|
i != removed_objects.end(); ++i)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
|
|
|
// Get object
|
2012-12-20 18:19:49 +01:00
|
|
|
u16 id = *i;
|
2011-11-11 18:33:17 +01:00
|
|
|
ServerActiveObject* obj = m_env->getActiveObject(id);
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Add to data buffer for sending
|
2012-12-20 18:19:49 +01:00
|
|
|
writeU16((u8*)buf, id);
|
2011-02-20 23:45:14 +01:00
|
|
|
data_buffer.append(buf, 2);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Remove from known objects
|
2012-12-20 18:19:49 +01:00
|
|
|
client->m_known_objects.erase(id);
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
if(obj && obj->m_known_by_count > 0)
|
|
|
|
obj->m_known_by_count--;
|
|
|
|
}
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Handle added objects
|
|
|
|
writeU16((u8*)buf, added_objects.size());
|
|
|
|
data_buffer.append(buf, 2);
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::set<u16>::iterator
|
|
|
|
i = added_objects.begin();
|
|
|
|
i != added_objects.end(); ++i)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
// Get object
|
2012-12-20 18:19:49 +01:00
|
|
|
u16 id = *i;
|
2011-11-11 18:33:17 +01:00
|
|
|
ServerActiveObject* obj = m_env->getActiveObject(id);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Get object type
|
|
|
|
u8 type = ACTIVEOBJECT_TYPE_INVALID;
|
|
|
|
if(obj == NULL)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"WARNING: "<<__FUNCTION_NAME
|
2011-02-20 23:45:14 +01:00
|
|
|
<<": NULL object"<<std::endl;
|
|
|
|
else
|
2012-03-29 15:10:11 +02:00
|
|
|
type = obj->getSendType();
|
2011-02-20 23:45:14 +01:00
|
|
|
|
|
|
|
// Add to data buffer for sending
|
|
|
|
writeU16((u8*)buf, id);
|
|
|
|
data_buffer.append(buf, 2);
|
|
|
|
writeU8((u8*)buf, type);
|
|
|
|
data_buffer.append(buf, 1);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-11 10:05:40 +02:00
|
|
|
if(obj)
|
|
|
|
data_buffer.append(serializeLongString(
|
2012-11-26 23:47:03 +01:00
|
|
|
obj->getClientInitializationData(client->net_proto_version)));
|
2011-04-11 10:05:40 +02:00
|
|
|
else
|
|
|
|
data_buffer.append(serializeLongString(""));
|
2011-02-21 15:10:36 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Add to known objects
|
2012-12-20 18:19:49 +01:00
|
|
|
client->m_known_objects.insert(id);
|
2011-02-20 23:45:14 +01:00
|
|
|
|
|
|
|
if(obj)
|
|
|
|
obj->m_known_by_count++;
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Send packet
|
|
|
|
SharedBuffer<u8> reply(2 + data_buffer.size());
|
|
|
|
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
|
|
|
|
memcpy((char*)&reply[2], data_buffer.c_str(),
|
|
|
|
data_buffer.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(client->peer_id, 0, reply, true);
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server: Sent object remove/add: "
|
2011-02-20 23:45:14 +01:00
|
|
|
<<removed_objects.size()<<" removed, "
|
|
|
|
<<added_objects.size()<<" added, "
|
|
|
|
<<"packet size is "<<reply.getSize()<<std::endl;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-04-10 03:15:10 +02:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
Collect a list of all the objects known by the clients
|
|
|
|
and report it back to the environment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
core::map<u16, bool> all_known_objects;
|
|
|
|
|
|
|
|
for(core::map<u16, RemoteClient*>::Iterator
|
|
|
|
i = m_clients.getIterator();
|
|
|
|
i.atEnd() == false; i++)
|
|
|
|
{
|
|
|
|
RemoteClient *client = i.getNode()->getValue();
|
|
|
|
// Go through all known objects of client
|
|
|
|
for(core::map<u16, bool>::Iterator
|
|
|
|
i = client->m_known_objects.getIterator();
|
|
|
|
i.atEnd()==false; i++)
|
|
|
|
{
|
|
|
|
u16 id = i.getNode()->getKey();
|
|
|
|
all_known_objects[id] = true;
|
|
|
|
}
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->setKnownActiveObjects(whatever);
|
2011-04-10 03:15:10 +02:00
|
|
|
#endif
|
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
2010-12-18 12:10:37 +01:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
/*
|
|
|
|
Send object messages
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
2011-12-02 16:19:42 +01:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: sending object messages");
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2011-02-20 23:45:14 +01:00
|
|
|
// Key = object id
|
|
|
|
// Value = data sent by object
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
|
2011-02-20 23:45:14 +01:00
|
|
|
|
|
|
|
// Get active object messages from environment
|
|
|
|
for(;;)
|
|
|
|
{
|
2011-11-11 18:33:17 +01:00
|
|
|
ActiveObjectMessage aom = m_env->getActiveObjectMessage();
|
2011-02-20 23:45:14 +01:00
|
|
|
if(aom.id == 0)
|
|
|
|
break;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<ActiveObjectMessage>* message_list = NULL;
|
|
|
|
std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
|
2011-02-20 23:45:14 +01:00
|
|
|
n = buffered_messages.find(aom.id);
|
2012-12-20 18:19:49 +01:00
|
|
|
if(n == buffered_messages.end())
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
message_list = new std::list<ActiveObjectMessage>;
|
|
|
|
buffered_messages[aom.id] = message_list;
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
else
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
message_list = n->second;
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
message_list->push_back(aom);
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Lock();
|
|
|
|
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
|
2011-02-20 23:45:14 +01:00
|
|
|
// Route data to every client
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<u16, RemoteClient*>::iterator
|
2014-01-31 00:24:00 +01:00
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
RemoteClient *client = i->second;
|
2011-02-20 23:45:14 +01:00
|
|
|
std::string reliable_data;
|
|
|
|
std::string unreliable_data;
|
|
|
|
// Go through all objects in message buffer
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
|
|
|
|
j = buffered_messages.begin();
|
|
|
|
j != buffered_messages.end(); ++j)
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
// If object is not known by client, skip it
|
2012-12-20 18:19:49 +01:00
|
|
|
u16 id = j->first;
|
|
|
|
if(client->m_known_objects.find(id) == client->m_known_objects.end())
|
2011-02-20 23:45:14 +01:00
|
|
|
continue;
|
|
|
|
// Get message list of object
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<ActiveObjectMessage>* list = j->second;
|
2011-02-20 23:45:14 +01:00
|
|
|
// Go through every message
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::list<ActiveObjectMessage>::iterator
|
|
|
|
k = list->begin(); k != list->end(); ++k)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
|
|
|
// Compose the full new data with header
|
|
|
|
ActiveObjectMessage aom = *k;
|
|
|
|
std::string new_data;
|
2011-02-21 15:10:36 +01:00
|
|
|
// Add object id
|
|
|
|
char buf[2];
|
|
|
|
writeU16((u8*)&buf[0], aom.id);
|
|
|
|
new_data.append(buf, 2);
|
2011-02-20 23:45:14 +01:00
|
|
|
// Add data
|
2011-02-21 15:10:36 +01:00
|
|
|
new_data += serializeString(aom.datastring);
|
2011-02-20 23:45:14 +01:00
|
|
|
// Add data to buffer
|
|
|
|
if(aom.reliable)
|
|
|
|
reliable_data += new_data;
|
|
|
|
else
|
|
|
|
unreliable_data += new_data;
|
|
|
|
}
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
|
|
|
/*
|
2011-02-20 23:45:14 +01:00
|
|
|
reliable_data and unreliable_data are now ready.
|
|
|
|
Send them.
|
2010-12-18 12:10:37 +01:00
|
|
|
*/
|
2011-02-20 23:45:14 +01:00
|
|
|
if(reliable_data.size() > 0)
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
SharedBuffer<u8> reply(2 + reliable_data.size());
|
|
|
|
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
|
|
|
|
memcpy((char*)&reply[2], reliable_data.c_str(),
|
|
|
|
reliable_data.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(client->peer_id, 0, reply, true);
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
if(unreliable_data.size() > 0)
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
SharedBuffer<u8> reply(2 + unreliable_data.size());
|
|
|
|
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
|
|
|
|
memcpy((char*)&reply[2], unreliable_data.c_str(),
|
|
|
|
unreliable_data.size());
|
|
|
|
// Send as unreliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(client->peer_id, 1, reply, false);
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2011-02-21 15:10:36 +01:00
|
|
|
|
|
|
|
/*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Size of object message data: "
|
2011-02-20 23:45:14 +01:00
|
|
|
<<"reliable: "<<reliable_data.size()
|
|
|
|
<<", unreliable: "<<unreliable_data.size()
|
|
|
|
<<std::endl;
|
2011-02-21 15:10:36 +01:00
|
|
|
}*/
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-02-20 23:45:14 +01:00
|
|
|
|
|
|
|
// Clear buffered_messages
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
|
|
|
|
i = buffered_messages.begin();
|
|
|
|
i != buffered_messages.end(); ++i)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
delete i->second;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
/*
|
|
|
|
Send queued-for-sending map edit events.
|
|
|
|
*/
|
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
// We will be accessing the environment
|
2012-03-28 11:51:47 +02:00
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
// Don't send too many at a time
|
2011-06-26 02:14:52 +02:00
|
|
|
//u32 count = 0;
|
|
|
|
|
|
|
|
// Single change sending is disabled if queue size is not small
|
|
|
|
bool disable_single_change_sending = false;
|
|
|
|
if(m_unsent_map_edit_queue.size() >= 4)
|
|
|
|
disable_single_change_sending = true;
|
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
int event_count = m_unsent_map_edit_queue.size();
|
2011-06-26 23:27:17 +02:00
|
|
|
|
|
|
|
// We'll log the amount of each
|
|
|
|
Profiler prof;
|
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
while(m_unsent_map_edit_queue.size() != 0)
|
|
|
|
{
|
|
|
|
MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
// Players far away from the change are stored here.
|
|
|
|
// Instead of sending the changes, MapBlocks are set not sent
|
|
|
|
// for them.
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<u16> far_players;
|
2011-02-23 01:49:57 +01:00
|
|
|
|
2013-11-23 15:35:49 +01:00
|
|
|
if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
|
2011-02-23 01:49:57 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Server: MEET_ADDNODE"<<std::endl;
|
2011-06-26 23:27:17 +02:00
|
|
|
prof.add("MEET_ADDNODE", 1);
|
2011-06-26 02:14:52 +02:00
|
|
|
if(disable_single_change_sending)
|
|
|
|
sendAddNode(event->p, event->n, event->already_known_by_peer,
|
2013-11-23 15:35:49 +01:00
|
|
|
&far_players, 5, event->type == MEET_ADDNODE);
|
2011-06-26 02:14:52 +02:00
|
|
|
else
|
|
|
|
sendAddNode(event->p, event->n, event->already_known_by_peer,
|
2013-11-23 15:35:49 +01:00
|
|
|
&far_players, 30, event->type == MEET_ADDNODE);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
else if(event->type == MEET_REMOVENODE)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Server: MEET_REMOVENODE"<<std::endl;
|
2011-06-26 23:27:17 +02:00
|
|
|
prof.add("MEET_REMOVENODE", 1);
|
2011-06-26 02:14:52 +02:00
|
|
|
if(disable_single_change_sending)
|
|
|
|
sendRemoveNode(event->p, event->already_known_by_peer,
|
|
|
|
&far_players, 5);
|
|
|
|
else
|
|
|
|
sendRemoveNode(event->p, event->already_known_by_peer,
|
|
|
|
&far_players, 30);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
2011-05-31 19:02:55 +02:00
|
|
|
else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
|
2011-06-26 23:27:17 +02:00
|
|
|
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
|
2011-05-31 19:02:55 +02:00
|
|
|
setBlockNotSent(event->p);
|
|
|
|
}
|
2011-02-23 01:49:57 +01:00
|
|
|
else if(event->type == MEET_OTHER)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: MEET_OTHER"<<std::endl;
|
2011-06-26 23:27:17 +02:00
|
|
|
prof.add("MEET_OTHER", 1);
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::set<v3s16>::iterator
|
|
|
|
i = event->modified_blocks.begin();
|
|
|
|
i != event->modified_blocks.end(); ++i)
|
2011-08-16 11:23:19 +02:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
setBlockNotSent(*i);
|
2011-08-16 11:23:19 +02:00
|
|
|
}
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-06-26 23:27:17 +02:00
|
|
|
prof.add("unknown", 1);
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"WARNING: Server: Unknown MapEditEvent "
|
2011-02-23 01:49:57 +01:00
|
|
|
<<((u32)event->type)<<std::endl;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
Set blocks not sent to far players
|
|
|
|
*/
|
2011-06-26 02:14:52 +02:00
|
|
|
if(far_players.size() > 0)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2011-07-01 20:04:40 +02:00
|
|
|
// Convert list format to that wanted by SetBlocksNotSent
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks2;
|
|
|
|
for(std::set<v3s16>::iterator
|
|
|
|
i = event->modified_blocks.begin();
|
|
|
|
i != event->modified_blocks.end(); ++i)
|
2011-06-26 02:14:52 +02:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
modified_blocks2[*i] =
|
|
|
|
m_env->getMap().getBlockNoCreateNoEx(*i);
|
2011-06-26 02:14:52 +02:00
|
|
|
}
|
2011-07-01 20:04:40 +02:00
|
|
|
// Set blocks not sent
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::list<u16>::iterator
|
2011-06-26 02:14:52 +02:00
|
|
|
i = far_players.begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
i != far_players.end(); ++i)
|
2011-06-26 02:14:52 +02:00
|
|
|
{
|
|
|
|
u16 peer_id = *i;
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
|
|
|
if(client==NULL)
|
|
|
|
continue;
|
|
|
|
client->SetBlocksNotSent(modified_blocks2);
|
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2011-02-23 01:49:57 +01:00
|
|
|
|
|
|
|
delete event;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2011-06-26 02:14:52 +02:00
|
|
|
/*// Don't send too many at a time
|
2011-06-25 03:25:14 +02:00
|
|
|
count++;
|
2011-06-25 15:32:09 +02:00
|
|
|
if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
|
2011-06-26 02:14:52 +02:00
|
|
|
break;*/
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
if(event_count >= 5){
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: MapEditEvents:"<<std::endl;
|
|
|
|
prof.print(infostream);
|
2012-03-11 03:15:45 +01:00
|
|
|
} else if(event_count != 0){
|
|
|
|
verbosestream<<"Server: MapEditEvents:"<<std::endl;
|
|
|
|
prof.print(verbosestream);
|
2011-06-26 23:27:17 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
|
2011-02-05 13:55:16 +01:00
|
|
|
/*
|
|
|
|
Trigger emergethread (it somehow gets to a non-triggered but
|
|
|
|
bysy state sometimes)
|
|
|
|
*/
|
2010-11-29 09:52:07 +01:00
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_emergethread_trigger_timer;
|
2010-11-29 09:52:07 +01:00
|
|
|
counter += dtime;
|
|
|
|
if(counter >= 2.0)
|
|
|
|
{
|
|
|
|
counter = 0.0;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-26 07:12:18 +01:00
|
|
|
m_emerge->startThreads();
|
2012-07-28 02:08:09 +02:00
|
|
|
|
|
|
|
// Update m_enable_rollback_recording here too
|
|
|
|
m_enable_rollback_recording =
|
|
|
|
g_settings->getBool("enable_rollback_recording");
|
2010-11-29 09:52:07 +01:00
|
|
|
}
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-05-29 20:11:16 +02:00
|
|
|
// Save map, players and auth stuff
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_savemap_timer;
|
2010-11-27 00:02:21 +01:00
|
|
|
counter += dtime;
|
2011-10-12 12:53:38 +02:00
|
|
|
if(counter >= g_settings->getFloat("server_map_save_interval"))
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
counter = 0.0;
|
2012-03-07 20:54:18 +01:00
|
|
|
JMutexAutoLock lock(m_env_mutex);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: saving stuff");
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Save ban file
|
|
|
|
if (m_banmanager->isModified()) {
|
2013-08-11 04:09:45 +02:00
|
|
|
m_banmanager->save();
|
2014-05-30 22:04:07 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-27 23:45:34 +01:00
|
|
|
// Save changed parts of map
|
|
|
|
m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
|
|
|
// Save players
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env->saveLoadedPlayers();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
// Save environment metadata
|
2014-05-30 22:04:07 +02:00
|
|
|
m_env->saveMeta();
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::Receive()
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2011-11-07 04:20:33 +01:00
|
|
|
SharedBuffer<u8> data;
|
2010-11-27 00:02:21 +01:00
|
|
|
u16 peer_id;
|
|
|
|
u32 datasize;
|
|
|
|
try{
|
2014-01-31 00:24:00 +01:00
|
|
|
datasize = m_con.Receive(peer_id,data);
|
2010-11-27 00:02:21 +01:00
|
|
|
ProcessData(*data, datasize, peer_id);
|
|
|
|
}
|
|
|
|
catch(con::InvalidIncomingDataException &e)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server::Receive(): "
|
2010-11-27 00:02:21 +01:00
|
|
|
"InvalidIncomingDataException: what()="
|
|
|
|
<<e.what()<<std::endl;
|
|
|
|
}
|
2014-06-22 00:05:41 +02:00
|
|
|
catch(SerializationError &e) {
|
|
|
|
infostream<<"Server::Receive(): "
|
|
|
|
"SerializationError: what()="
|
|
|
|
<<e.what()<<std::endl;
|
|
|
|
}
|
2014-02-13 20:17:42 +01:00
|
|
|
catch(ClientStateError &e)
|
|
|
|
{
|
|
|
|
errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
|
|
|
|
DenyAccess(peer_id, L"Your client sent something server didn't expect."
|
|
|
|
L"Try reconnecting or updating your client");
|
|
|
|
}
|
2014-05-30 22:04:07 +02:00
|
|
|
catch(con::PeerNotFoundException &e)
|
|
|
|
{
|
|
|
|
// Do nothing
|
|
|
|
}
|
2014-02-13 20:17:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
|
|
|
|
{
|
|
|
|
std::string playername = "";
|
|
|
|
PlayerSAO *playersao = NULL;
|
|
|
|
m_clients.Lock();
|
2014-06-28 08:02:38 +02:00
|
|
|
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
|
2014-02-13 20:17:42 +01:00
|
|
|
if (client != NULL) {
|
|
|
|
playername = client->getName();
|
|
|
|
playersao = emergePlayer(playername.c_str(), peer_id);
|
|
|
|
}
|
|
|
|
m_clients.Unlock();
|
|
|
|
|
|
|
|
RemotePlayer *player =
|
|
|
|
static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
|
|
|
|
|
|
|
|
// If failed, cancel
|
|
|
|
if((playersao == NULL) || (player == NULL))
|
|
|
|
{
|
|
|
|
if(player && player->peer_id != 0){
|
|
|
|
errorstream<<"Server: "<<playername<<": Failed to emerge player"
|
|
|
|
<<" (player allocated to an another client)"<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Another client is connected with this "
|
|
|
|
L"name. If your client closed unexpectedly, try again in "
|
|
|
|
L"a minute.");
|
|
|
|
} else {
|
|
|
|
errorstream<<"Server: "<<playername<<": Failed to emerge player"
|
|
|
|
<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Could not allocate player.");
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Send complete position information
|
|
|
|
*/
|
|
|
|
SendMovePlayer(peer_id);
|
|
|
|
|
|
|
|
// Send privileges
|
|
|
|
SendPlayerPrivileges(peer_id);
|
|
|
|
|
|
|
|
// Send inventory formspec
|
|
|
|
SendPlayerInventoryFormspec(peer_id);
|
|
|
|
|
|
|
|
// Send inventory
|
|
|
|
UpdateCrafting(peer_id);
|
|
|
|
SendInventory(peer_id);
|
|
|
|
|
|
|
|
// Send HP
|
|
|
|
if(g_settings->getBool("enable_damage"))
|
|
|
|
SendPlayerHP(peer_id);
|
|
|
|
|
|
|
|
// Send Breath
|
|
|
|
SendPlayerBreath(peer_id);
|
|
|
|
|
|
|
|
// Show death screen if necessary
|
|
|
|
if(player->hp == 0)
|
|
|
|
SendDeathscreen(peer_id, false, v3f(0,0,0));
|
|
|
|
|
|
|
|
// Note things in chat if not in simple singleplayer mode
|
|
|
|
if(!m_simple_singleplayer_mode)
|
|
|
|
{
|
|
|
|
// Send information about server to player in chat
|
|
|
|
SendChatMessage(peer_id, getStatusString());
|
|
|
|
|
|
|
|
// Send information about joining in chat
|
|
|
|
{
|
|
|
|
std::wstring name = L"unknown";
|
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
if(player != NULL)
|
|
|
|
name = narrow_to_wide(player->getName());
|
|
|
|
|
|
|
|
std::wstring message;
|
|
|
|
message += L"*** ";
|
|
|
|
message += name;
|
|
|
|
message += L" joined the game.";
|
|
|
|
SendChatMessage(PEER_ID_INEXISTENT,message);
|
|
|
|
}
|
|
|
|
}
|
2014-04-21 22:28:52 +02:00
|
|
|
Address addr = getPeerAddress(player->peer_id);
|
|
|
|
std::string ip_str = addr.serializeString();
|
|
|
|
actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
|
2014-02-13 20:17:42 +01:00
|
|
|
/*
|
|
|
|
Print out action
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
std::vector<std::string> names = m_clients.getPlayerNames();
|
|
|
|
|
|
|
|
actionstream<<player->getName() <<" joins game. List of players: ";
|
|
|
|
|
|
|
|
for (std::vector<std::string>::iterator i = names.begin();
|
|
|
|
i != names.end(); i++)
|
|
|
|
{
|
|
|
|
actionstream << *i << " ";
|
|
|
|
}
|
|
|
|
|
2014-05-20 18:09:32 +02:00
|
|
|
actionstream << player->getName() <<std::endl;
|
2014-02-13 20:17:42 +01:00
|
|
|
}
|
|
|
|
return playersao;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
// Environment is locked first.
|
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-07 20:54:18 +01:00
|
|
|
ScopeProfiler sp(g_profiler, "Server::ProcessData");
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2013-07-23 18:31:05 +02:00
|
|
|
std::string addr_s;
|
2010-11-27 00:02:21 +01:00
|
|
|
try{
|
2014-01-31 00:24:00 +01:00
|
|
|
Address address = getPeerAddress(peer_id);
|
2013-07-23 18:31:05 +02:00
|
|
|
addr_s = address.serializeString();
|
2011-10-20 22:04:09 +02:00
|
|
|
|
|
|
|
// drop player if is ip is banned
|
2013-08-11 04:09:45 +02:00
|
|
|
if(m_banmanager->isIpBanned(addr_s)){
|
2013-04-10 05:34:58 +02:00
|
|
|
std::string ban_name = m_banmanager->getBanName(addr_s);
|
2012-03-22 12:35:32 +01:00
|
|
|
infostream<<"Server: A banned client tried to connect from "
|
|
|
|
<<addr_s<<"; banned name was "
|
2013-04-10 05:34:58 +02:00
|
|
|
<<ban_name<<std::endl;
|
2012-03-22 12:35:32 +01:00
|
|
|
// This actually doesn't seem to transfer to the client
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Your ip is banned. Banned name was "
|
2013-04-10 05:34:58 +02:00
|
|
|
+narrow_to_wide(ban_name));
|
2011-10-20 22:04:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
catch(con::PeerNotFoundException &e)
|
|
|
|
{
|
2014-04-19 22:12:01 +02:00
|
|
|
/*
|
|
|
|
* no peer for this packet found
|
|
|
|
* most common reason is peer timeout, e.g. peer didn't
|
|
|
|
* respond for some time, your server was overloaded or
|
|
|
|
* things like that.
|
|
|
|
*/
|
|
|
|
infostream<<"Server::ProcessData(): Cancelling: peer "
|
2010-11-27 00:02:21 +01:00
|
|
|
<<peer_id<<" not found"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
|
|
|
|
if(datasize < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ToServerCommand command = (ToServerCommand)readU16(&data[0]);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
if(command == TOSERVER_INIT)
|
|
|
|
{
|
|
|
|
// [0] u16 TOSERVER_INIT
|
2013-08-01 22:51:36 +02:00
|
|
|
// [2] u8 SER_FMT_VER_HIGHEST_READ
|
2010-11-27 00:02:21 +01:00
|
|
|
// [3] u8[20] player_name
|
2011-05-20 21:28:03 +02:00
|
|
|
// [23] u8[28] password <--- can be sent without this, from old versions
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-05-20 21:28:03 +02:00
|
|
|
if(datasize < 2+1+PLAYERNAME_SIZE)
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
RemoteClient* client = getClient(peer_id, CS_Created);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2013-08-08 05:10:45 +02:00
|
|
|
// If net_proto_version is set, this client has already been handled
|
2014-06-28 08:02:38 +02:00
|
|
|
if(client->getState() > CS_Created)
|
2014-01-31 00:24:00 +01:00
|
|
|
{
|
2013-08-08 05:10:45 +02:00
|
|
|
verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
|
|
|
|
<<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
|
|
|
|
<<peer_id<<")"<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2013-08-04 07:17:07 +02:00
|
|
|
// Do not allow multiple players in simple singleplayer mode.
|
2014-01-31 00:24:00 +01:00
|
|
|
// This isn't a perfect way to do it, but will suffice for now
|
|
|
|
if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
|
2013-08-06 17:13:11 +02:00
|
|
|
infostream<<"Server: Not allowing another client ("<<addr_s
|
|
|
|
<<") to connect in simple singleplayer mode"<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Running in simple singleplayer mode.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// First byte after command is maximum supported
|
|
|
|
// serialization version
|
|
|
|
u8 client_max = data[2];
|
2013-08-01 22:51:36 +02:00
|
|
|
u8 our_max = SER_FMT_VER_HIGHEST_READ;
|
2010-11-27 00:02:21 +01:00
|
|
|
// Use the highest version supported by both
|
2012-12-20 18:19:49 +01:00
|
|
|
u8 deployed = std::min(client_max, our_max);
|
2010-11-27 00:02:21 +01:00
|
|
|
// If it's lower than the lowest supported, give up.
|
|
|
|
if(deployed < SER_FMT_VER_LOWEST)
|
|
|
|
deployed = SER_FMT_VER_INVALID;
|
|
|
|
|
|
|
|
if(deployed == SER_FMT_VER_INVALID)
|
|
|
|
{
|
2012-03-11 03:15:45 +01:00
|
|
|
actionstream<<"Server: A mismatched client tried to connect from "
|
|
|
|
<<addr_s<<std::endl;
|
2013-08-06 17:13:11 +02:00
|
|
|
infostream<<"Server: Cannot negotiate serialization version with "
|
|
|
|
<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, std::wstring(
|
2011-11-29 21:23:03 +01:00
|
|
|
L"Your client's version is not supported.\n"
|
|
|
|
L"Server version is ")
|
2013-09-25 04:29:07 +02:00
|
|
|
+ narrow_to_wide(minetest_version_simple) + L"."
|
2011-11-29 21:23:03 +01:00
|
|
|
);
|
2011-07-30 19:02:17 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
client->setPendingSerializationVersion(deployed);
|
|
|
|
|
2011-07-30 19:02:17 +02:00
|
|
|
/*
|
2011-07-31 01:27:26 +02:00
|
|
|
Read and check network protocol version
|
2011-07-30 19:02:17 +02:00
|
|
|
*/
|
2011-07-31 01:27:26 +02:00
|
|
|
|
2012-11-26 08:49:07 +01:00
|
|
|
u16 min_net_proto_version = 0;
|
2011-07-30 19:02:17 +02:00
|
|
|
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
|
2012-11-26 08:49:07 +01:00
|
|
|
min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
|
|
|
|
|
2012-11-26 22:58:27 +01:00
|
|
|
// Use same version as minimum and maximum if maximum version field
|
|
|
|
// doesn't exist (backwards compatibility)
|
2012-11-26 08:49:07 +01:00
|
|
|
u16 max_net_proto_version = min_net_proto_version;
|
|
|
|
if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
|
|
|
|
max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
|
|
|
|
|
2012-11-26 22:58:27 +01:00
|
|
|
// Start with client's maximum version
|
2012-11-26 08:49:07 +01:00
|
|
|
u16 net_proto_version = max_net_proto_version;
|
2012-11-26 22:58:27 +01:00
|
|
|
|
|
|
|
// Figure out a working version if it is possible at all
|
|
|
|
if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
|
|
|
|
min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
|
|
|
|
{
|
|
|
|
// If maximum is larger than our maximum, go with our maximum
|
|
|
|
if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
|
|
|
|
net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
|
|
|
|
// Else go with client's maximum
|
|
|
|
else
|
|
|
|
net_proto_version = max_net_proto_version;
|
|
|
|
}
|
2012-11-26 08:49:07 +01:00
|
|
|
|
2013-08-06 17:13:11 +02:00
|
|
|
verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
|
2012-11-26 08:49:07 +01:00
|
|
|
<<min_net_proto_version<<", max: "<<max_net_proto_version
|
|
|
|
<<", chosen: "<<net_proto_version<<std::endl;
|
2011-07-31 01:27:26 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
client->net_proto_version = net_proto_version;
|
2011-07-31 01:27:26 +02:00
|
|
|
|
2012-11-26 23:26:19 +01:00
|
|
|
if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
|
|
|
|
net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
|
2011-07-30 19:02:17 +02:00
|
|
|
{
|
2013-08-06 17:13:11 +02:00
|
|
|
actionstream<<"Server: A mismatched client tried to connect from "
|
|
|
|
<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, std::wstring(
|
2011-11-29 21:23:03 +01:00
|
|
|
L"Your client's version is not supported.\n"
|
|
|
|
L"Server version is ")
|
2013-09-25 04:29:07 +02:00
|
|
|
+ narrow_to_wide(minetest_version_simple) + L",\n"
|
2012-11-26 23:26:19 +01:00
|
|
|
+ L"server's PROTOCOL_VERSION is "
|
|
|
|
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
|
|
|
|
+ L"..."
|
|
|
|
+ narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
|
|
|
|
+ L", client's PROTOCOL_VERSION is "
|
|
|
|
+ narrow_to_wide(itos(min_net_proto_version))
|
|
|
|
+ L"..."
|
|
|
|
+ narrow_to_wide(itos(max_net_proto_version))
|
2011-11-29 21:23:03 +01:00
|
|
|
);
|
2011-07-30 19:10:16 +02:00
|
|
|
return;
|
2011-07-31 01:27:26 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-10-15 14:02:28 +02:00
|
|
|
if(g_settings->getBool("strict_protocol_version_checking"))
|
2011-09-22 19:47:47 +02:00
|
|
|
{
|
2012-11-26 23:26:19 +01:00
|
|
|
if(net_proto_version != LATEST_PROTOCOL_VERSION)
|
2011-10-15 14:02:28 +02:00
|
|
|
{
|
2012-11-26 23:26:19 +01:00
|
|
|
actionstream<<"Server: A mismatched (strict) client tried to "
|
|
|
|
<<"connect from "<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, std::wstring(
|
2011-11-29 21:23:03 +01:00
|
|
|
L"Your client's version is not supported.\n"
|
|
|
|
L"Server version is ")
|
2013-09-25 04:29:07 +02:00
|
|
|
+ narrow_to_wide(minetest_version_simple) + L",\n"
|
2012-11-26 23:26:19 +01:00
|
|
|
+ L"server's PROTOCOL_VERSION (strict) is "
|
|
|
|
+ narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
|
2011-12-02 00:24:54 +01:00
|
|
|
+ L", client's PROTOCOL_VERSION is "
|
2012-11-26 22:58:27 +01:00
|
|
|
+ narrow_to_wide(itos(min_net_proto_version))
|
|
|
|
+ L"..."
|
|
|
|
+ narrow_to_wide(itos(max_net_proto_version))
|
2011-11-29 21:23:03 +01:00
|
|
|
);
|
2011-10-15 14:02:28 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-09-22 19:47:47 +02:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Set up player
|
|
|
|
*/
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-01-15 02:28:19 +01:00
|
|
|
// Get player name
|
2011-05-20 21:28:03 +02:00
|
|
|
char playername[PLAYERNAME_SIZE];
|
|
|
|
for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
|
2011-01-15 02:28:19 +01:00
|
|
|
{
|
|
|
|
playername[i] = data[3+i];
|
|
|
|
}
|
2011-05-20 21:28:03 +02:00
|
|
|
playername[PLAYERNAME_SIZE-1] = 0;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-05-29 20:11:16 +02:00
|
|
|
if(playername[0]=='\0')
|
|
|
|
{
|
2012-03-10 15:47:37 +01:00
|
|
|
actionstream<<"Server: Player with an empty name "
|
2012-03-11 03:15:45 +01:00
|
|
|
<<"tried to connect from "<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Empty name");
|
2011-05-29 20:11:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
|
|
|
|
{
|
2012-03-10 15:47:37 +01:00
|
|
|
actionstream<<"Server: Player with an invalid name "
|
2012-03-11 03:15:45 +01:00
|
|
|
<<"tried to connect from "<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Name contains unallowed characters");
|
2011-05-29 20:11:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-12 00:53:30 +02:00
|
|
|
if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
|
|
|
|
{
|
2013-08-06 17:13:11 +02:00
|
|
|
actionstream<<"Server: Player with the name \"singleplayer\" "
|
2013-07-12 00:53:30 +02:00
|
|
|
<<"tried to connect from "<<addr_s<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Name is not allowed");
|
2013-07-12 00:53:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-12 07:51:35 +01:00
|
|
|
{
|
|
|
|
std::string reason;
|
|
|
|
if(m_script->on_prejoinplayer(playername, addr_s, reason))
|
|
|
|
{
|
|
|
|
actionstream<<"Server: Player with the name \""<<playername<<"\" "
|
|
|
|
<<"tried to connect from "<<addr_s<<" "
|
|
|
|
<<"but it was disallowed for the following reason: "
|
|
|
|
<<reason<<std::endl;
|
|
|
|
DenyAccess(peer_id, narrow_to_wide(reason.c_str()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"Server: New connection: \""<<playername<<"\" from "
|
2013-08-06 17:13:11 +02:00
|
|
|
<<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
|
2011-05-20 21:28:03 +02:00
|
|
|
// Get password
|
2012-06-07 01:11:28 +02:00
|
|
|
char given_password[PASSWORD_SIZE];
|
2011-07-31 14:32:45 +02:00
|
|
|
if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
|
2011-05-20 21:28:03 +02:00
|
|
|
{
|
|
|
|
// old version - assume blank password
|
2012-06-07 01:11:28 +02:00
|
|
|
given_password[0] = 0;
|
2011-05-20 21:28:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-10 15:47:37 +01:00
|
|
|
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
|
|
|
|
{
|
2012-06-07 01:11:28 +02:00
|
|
|
given_password[i] = data[23+i];
|
2012-03-10 15:47:37 +01:00
|
|
|
}
|
2012-06-07 01:11:28 +02:00
|
|
|
given_password[PASSWORD_SIZE-1] = 0;
|
2011-05-20 21:28:03 +02:00
|
|
|
}
|
2012-06-03 19:32:44 +02:00
|
|
|
|
2012-06-07 01:11:28 +02:00
|
|
|
if(!base64_is_valid(given_password)){
|
2013-08-04 07:17:07 +02:00
|
|
|
actionstream<<"Server: "<<playername
|
2012-06-07 01:11:28 +02:00
|
|
|
<<" supplied invalid password hash"<<std::endl;
|
2013-08-04 07:17:07 +02:00
|
|
|
DenyAccess(peer_id, L"Invalid password hash");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enforce user limit.
|
|
|
|
// Don't enforce for users that have some admin right
|
2014-06-28 08:02:38 +02:00
|
|
|
if(m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
|
2013-08-04 07:17:07 +02:00
|
|
|
!checkPriv(playername, "server") &&
|
|
|
|
!checkPriv(playername, "ban") &&
|
|
|
|
!checkPriv(playername, "privs") &&
|
|
|
|
!checkPriv(playername, "password") &&
|
|
|
|
playername != g_settings->get("name"))
|
|
|
|
{
|
|
|
|
actionstream<<"Server: "<<playername<<" tried to join, but there"
|
|
|
|
<<" are already max_users="
|
|
|
|
<<g_settings->getU16("max_users")<<" players."<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Too many users.");
|
2012-06-03 19:32:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-06-07 01:11:28 +02:00
|
|
|
std::string checkpwd; // Password hash to check against
|
2013-05-25 00:51:02 +02:00
|
|
|
bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-06-07 01:11:28 +02:00
|
|
|
// If no authentication info exists for user, create it
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!has_auth){
|
2012-06-07 01:11:28 +02:00
|
|
|
if(!isSingleplayer() &&
|
|
|
|
g_settings->getBool("disallow_empty_password") &&
|
|
|
|
std::string(given_password) == ""){
|
2013-08-04 07:17:07 +02:00
|
|
|
actionstream<<"Server: "<<playername
|
|
|
|
<<" supplied empty password"<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Empty passwords are "
|
2012-06-07 01:11:28 +02:00
|
|
|
L"disallowed. Set a password and try again.");
|
|
|
|
return;
|
|
|
|
}
|
2012-03-30 17:42:18 +02:00
|
|
|
std::wstring raw_default_password =
|
2011-11-20 20:16:15 +01:00
|
|
|
narrow_to_wide(g_settings->get("default_password"));
|
2012-06-07 01:11:28 +02:00
|
|
|
std::string initial_password =
|
2012-03-30 17:42:18 +02:00
|
|
|
translatePassword(playername, raw_default_password);
|
2011-11-20 20:16:15 +01:00
|
|
|
|
|
|
|
// If default_password is empty, allow any initial password
|
2012-03-30 17:42:18 +02:00
|
|
|
if (raw_default_password.length() == 0)
|
2012-06-07 01:11:28 +02:00
|
|
|
initial_password = given_password;
|
2011-11-20 20:16:15 +01:00
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->createAuth(playername, initial_password);
|
2012-03-30 17:42:18 +02:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
has_auth = m_script->getAuth(playername, &checkpwd, NULL);
|
2011-11-20 20:16:15 +01:00
|
|
|
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!has_auth){
|
2013-08-04 07:17:07 +02:00
|
|
|
actionstream<<"Server: "<<playername<<" cannot be authenticated"
|
|
|
|
<<" (auth handler does not work?)"<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Not allowed to login");
|
2012-03-30 17:42:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-11-20 20:16:15 +01:00
|
|
|
|
2012-06-07 01:11:28 +02:00
|
|
|
if(given_password != checkpwd){
|
2013-08-06 17:13:11 +02:00
|
|
|
actionstream<<"Server: "<<playername<<" supplied wrong password"
|
|
|
|
<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Wrong password");
|
2011-09-26 10:57:51 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-05-20 21:28:03 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
RemotePlayer *player =
|
|
|
|
static_cast<RemotePlayer*>(m_env->getPlayer(playername));
|
2011-05-20 21:28:03 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if(player && player->peer_id != 0){
|
|
|
|
errorstream<<"Server: "<<playername<<": Failed to emerge player"
|
|
|
|
<<" (player allocated to an another client)"<<std::endl;
|
|
|
|
DenyAccess(peer_id, L"Another client is connected with this "
|
|
|
|
L"name. If your client closed unexpectedly, try again in "
|
|
|
|
L"a minute.");
|
2011-01-17 23:26:09 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.setPlayerName(peer_id,playername);
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Answer with a TOCLIENT_INIT
|
|
|
|
*/
|
|
|
|
{
|
2012-11-26 21:31:21 +01:00
|
|
|
SharedBuffer<u8> reply(2+1+6+8+4);
|
2011-04-21 18:35:17 +02:00
|
|
|
writeU16(&reply[0], TOCLIENT_INIT);
|
|
|
|
writeU8(&reply[2], deployed);
|
2014-01-31 00:24:00 +01:00
|
|
|
//send dummy pos for legacy reasons only
|
|
|
|
writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
|
2011-11-11 18:33:17 +01:00
|
|
|
writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
|
2012-11-26 21:31:21 +01:00
|
|
|
writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, reply, true);
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_Init);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
if(command == TOSERVER_INIT2)
|
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server: Got TOSERVER_INIT2 from "
|
2011-10-20 22:04:09 +02:00
|
|
|
<<peer_id<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_GotInit2);
|
2014-01-31 00:24:00 +01:00
|
|
|
u16 protocol_version = m_clients.getProtocolVersion(peer_id);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
|
|
|
|
///// begin compatibility code
|
|
|
|
PlayerSAO* playersao = NULL;
|
|
|
|
if (protocol_version <= 22) {
|
|
|
|
playersao = StageTwoClientInit(peer_id);
|
|
|
|
|
|
|
|
if (playersao == NULL) {
|
|
|
|
errorstream
|
|
|
|
<< "TOSERVER_INIT2 stage 2 client init failed for peer "
|
|
|
|
<< peer_id << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
///// end compatibility code
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
Send some initialization data
|
|
|
|
*/
|
2011-11-15 10:02:47 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"Server: Sending content to "
|
|
|
|
<<getPlayerName(peer_id)<<std::endl;
|
|
|
|
|
2013-02-08 21:54:01 +01:00
|
|
|
// Send player movement settings
|
2014-01-31 00:24:00 +01:00
|
|
|
SendMovement(peer_id);
|
2013-02-08 21:54:01 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Send item definitions
|
2014-01-31 00:24:00 +01:00
|
|
|
SendItemDef(peer_id, m_itemdef, protocol_version);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-15 19:32:56 +01:00
|
|
|
// Send node definitions
|
2014-01-31 00:24:00 +01:00
|
|
|
SendNodeDef(peer_id, m_nodedef, protocol_version);
|
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_SetDefinitionsSent);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-31 15:23:26 +02:00
|
|
|
// Send media announcement
|
2012-03-25 10:50:29 +02:00
|
|
|
sendMediaAnnouncement(peer_id);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
// Send detached inventories
|
|
|
|
sendDetachedInventories(peer_id);
|
|
|
|
|
|
|
|
// Send time of day
|
|
|
|
u16 time = m_env->getTimeOfDay();
|
|
|
|
float time_speed = g_settings->getFloat("time_speed");
|
|
|
|
SendTimeOfDay(peer_id, time, time_speed);
|
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
///// begin compatibility code
|
|
|
|
if (protocol_version <= 22) {
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_SetClientReady);
|
2014-02-13 20:17:42 +01:00
|
|
|
m_script->on_joinplayer(playersao);
|
|
|
|
}
|
|
|
|
///// end compatibility code
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
// Warnings about protocol version can be issued here
|
|
|
|
if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
|
|
|
|
{
|
|
|
|
SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
|
|
|
|
L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
|
|
|
|
u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
|
2014-01-31 00:24:00 +01:00
|
|
|
|
|
|
|
if(peer_ser_ver == SER_FMT_VER_INVALID)
|
|
|
|
{
|
|
|
|
errorstream<<"Server::ProcessData(): Cancelling: Peer"
|
|
|
|
" serialization format invalid or not initialized."
|
|
|
|
" Skipping incoming command="<<command<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle commands relate to client startup */
|
|
|
|
if(command == TOSERVER_REQUEST_MEDIA) {
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
std::list<std::string> tosend;
|
|
|
|
u16 numfiles = readU16(is);
|
|
|
|
|
|
|
|
infostream<<"Sending "<<numfiles<<" files to "
|
|
|
|
<<getPlayerName(peer_id)<<std::endl;
|
|
|
|
verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
|
|
|
|
|
|
|
|
for(int i = 0; i < numfiles; i++) {
|
|
|
|
std::string name = deSerializeString(is);
|
|
|
|
tosend.push_back(name);
|
|
|
|
verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
|
|
|
|
<<name<<std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendRequestedMedia(peer_id, tosend);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_RECEIVED_MEDIA) {
|
2014-02-13 20:17:42 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_CLIENT_READY) {
|
|
|
|
// clients <= protocol version 22 did not send ready message,
|
|
|
|
// they're already initialized
|
2014-06-21 23:56:46 +02:00
|
|
|
if (peer_proto_ver <= 22) {
|
|
|
|
infostream << "Client sent message not expected by a "
|
|
|
|
<< "client using protocol version <= 22,"
|
|
|
|
<< "disconnecing peer_id: " << peer_id << std::endl;
|
|
|
|
m_con.DisconnectPeer(peer_id);
|
|
|
|
return;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
PlayerSAO* playersao = StageTwoClientInit(peer_id);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
if (playersao == NULL) {
|
|
|
|
errorstream
|
2014-07-16 20:01:00 +02:00
|
|
|
<< "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
|
2014-02-13 20:17:42 +01:00
|
|
|
<< peer_id << std::endl;
|
2014-07-16 20:01:00 +02:00
|
|
|
m_con.DisconnectPeer(peer_id);
|
2014-01-31 00:24:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-19 19:50:33 +02:00
|
|
|
|
2014-07-16 20:01:00 +02:00
|
|
|
if(datasize < 2+8) {
|
|
|
|
errorstream
|
|
|
|
<< "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
|
|
|
|
<< peer_id << std::endl;
|
|
|
|
m_con.DisconnectPeer(peer_id);
|
2014-02-13 20:17:42 +01:00
|
|
|
return;
|
2014-07-16 20:01:00 +02:00
|
|
|
}
|
2011-10-16 13:57:53 +02:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
m_clients.setClientVersion(
|
|
|
|
peer_id,
|
|
|
|
data[2], data[3], data[4],
|
|
|
|
std::string((char*) &data[8],(u16) data[6]));
|
2011-07-30 19:10:16 +02:00
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_SetClientReady);
|
2014-01-31 00:24:00 +01:00
|
|
|
m_script->on_joinplayer(playersao);
|
2014-02-13 20:17:42 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
else if(command == TOSERVER_GOTBLOCKS)
|
|
|
|
{
|
|
|
|
if(datasize < 2+1)
|
|
|
|
return;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
/*
|
|
|
|
[0] u16 command
|
|
|
|
[2] u8 count
|
|
|
|
[3] v3s16 pos_0
|
|
|
|
[3+6] v3s16 pos_1
|
|
|
|
...
|
|
|
|
*/
|
|
|
|
|
|
|
|
u16 count = data[2];
|
|
|
|
for(u16 i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
if((s16)datasize < 2+1+(i+1)*6)
|
|
|
|
throw con::InvalidIncomingDataException
|
|
|
|
("GOTBLOCKS length is too short");
|
|
|
|
v3s16 p = readV3S16(&data[2+1+i*6]);
|
|
|
|
/*infostream<<"Server: GOTBLOCKS ("
|
|
|
|
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
|
|
|
client->GotBlock(p);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
if (m_clients.getClientState(peer_id) < CS_Active)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
if (command == TOSERVER_PLAYERPOS) return;
|
|
|
|
|
|
|
|
errorstream<<"Got packet command: " << command << " for peer id "
|
|
|
|
<< peer_id << " but client isn't active yet. Dropping packet "
|
|
|
|
<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
2014-06-29 17:55:21 +02:00
|
|
|
if(player == NULL) {
|
2014-01-31 00:24:00 +01:00
|
|
|
errorstream<<"Server::ProcessData(): Cancelling: "
|
2010-11-27 00:02:21 +01:00
|
|
|
"No player for peer_id="<<peer_id
|
2014-06-29 17:55:21 +02:00
|
|
|
<< " disconnecting peer!" <<std::endl;
|
|
|
|
m_con.DisconnectPeer(peer_id);
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-03-19 03:04:16 +01:00
|
|
|
|
|
|
|
PlayerSAO *playersao = player->getPlayerSAO();
|
2014-06-29 17:55:21 +02:00
|
|
|
if(playersao == NULL) {
|
2014-01-31 00:24:00 +01:00
|
|
|
errorstream<<"Server::ProcessData(): Cancelling: "
|
2012-03-19 03:04:16 +01:00
|
|
|
"No player object for peer_id="<<peer_id
|
2014-06-29 17:55:21 +02:00
|
|
|
<< " disconnecting peer!" <<std::endl;
|
|
|
|
m_con.DisconnectPeer(peer_id);
|
2012-03-19 03:04:16 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
if(command == TOSERVER_PLAYERPOS)
|
|
|
|
{
|
2012-11-26 23:47:03 +01:00
|
|
|
if(datasize < 2+12+12+4+4)
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
u32 start = 0;
|
|
|
|
v3s32 ps = readV3S32(&data[start+2]);
|
|
|
|
v3s32 ss = readV3S32(&data[start+2+12]);
|
|
|
|
f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
|
|
|
|
f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
|
2012-11-26 23:47:03 +01:00
|
|
|
u32 keyPressed = 0;
|
|
|
|
if(datasize >= 2+12+12+4+4+4)
|
|
|
|
keyPressed = (u32)readU32(&data[2+12+12+4+4]);
|
2010-11-27 00:02:21 +01:00
|
|
|
v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
|
|
|
|
v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
|
|
|
|
pitch = wrapDegrees(pitch);
|
|
|
|
yaw = wrapDegrees(yaw);
|
2011-10-16 13:57:53 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
player->setPosition(position);
|
|
|
|
player->setSpeed(speed);
|
|
|
|
player->setPitch(pitch);
|
|
|
|
player->setYaw(yaw);
|
2012-11-22 20:01:31 +01:00
|
|
|
player->keyPressed=keyPressed;
|
|
|
|
player->control.up = (bool)(keyPressed&1);
|
|
|
|
player->control.down = (bool)(keyPressed&2);
|
|
|
|
player->control.left = (bool)(keyPressed&4);
|
|
|
|
player->control.right = (bool)(keyPressed&8);
|
|
|
|
player->control.jump = (bool)(keyPressed&16);
|
|
|
|
player->control.aux1 = (bool)(keyPressed&32);
|
|
|
|
player->control.sneak = (bool)(keyPressed&64);
|
|
|
|
player->control.LMB = (bool)(keyPressed&128);
|
|
|
|
player->control.RMB = (bool)(keyPressed&256);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
bool cheated = playersao->checkMovementCheat();
|
|
|
|
if(cheated){
|
|
|
|
// Call callbacks
|
|
|
|
m_script->on_cheat(playersao, "moved_too_fast");
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
|
|
|
|
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
|
|
|
|
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
|
|
|
|
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_DELETEDBLOCKS)
|
|
|
|
{
|
|
|
|
if(datasize < 2+1)
|
|
|
|
return;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
[0] u16 command
|
|
|
|
[2] u8 count
|
|
|
|
[3] v3s16 pos_0
|
|
|
|
[3+6] v3s16 pos_1
|
|
|
|
...
|
|
|
|
*/
|
|
|
|
|
|
|
|
u16 count = data[2];
|
|
|
|
for(u16 i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
if((s16)datasize < 2+1+(i+1)*6)
|
|
|
|
throw con::InvalidIncomingDataException
|
|
|
|
("DELETEDBLOCKS length is too short");
|
|
|
|
v3s16 p = readV3S16(&data[2+1+i*6]);
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"Server: DELETEDBLOCKS ("
|
2010-11-27 00:02:21 +01:00
|
|
|
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
|
|
|
client->SetBlockNotSent(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_CLICK_OBJECT)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
|
2011-10-15 01:28:57 +02:00
|
|
|
return;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2011-04-10 03:15:10 +02:00
|
|
|
else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
|
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_GROUND_ACTION)
|
|
|
|
{
|
|
|
|
infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
|
|
|
|
return;
|
2011-04-10 03:15:10 +02:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_RELEASE)
|
|
|
|
{
|
|
|
|
infostream<<"Server: RELEASE not supported anymore"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_SIGNTEXT)
|
|
|
|
{
|
|
|
|
infostream<<"Server: SIGNTEXT not supported anymore"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_SIGNNODETEXT)
|
|
|
|
{
|
2012-03-19 01:08:04 +01:00
|
|
|
infostream<<"Server: SIGNNODETEXT not supported anymore"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_INVENTORY_ACTION)
|
|
|
|
{
|
|
|
|
// Strip command and create a stream
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
|
2011-11-29 16:15:18 +01:00
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
// Create an action
|
|
|
|
InventoryAction *a = InventoryAction::deSerialize(is);
|
2011-11-30 18:49:34 +01:00
|
|
|
if(a == NULL)
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2011-11-30 18:49:34 +01:00
|
|
|
infostream<<"TOSERVER_INVENTORY_ACTION: "
|
|
|
|
<<"InventoryAction::deSerialize() returned NULL"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
// If something goes wrong, this player is to blame
|
|
|
|
RollbackScopeActor rollback_scope(m_rollback,
|
|
|
|
std::string("player:")+player->getName());
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
/*
|
|
|
|
Note: Always set inventory not sent, to repair cases
|
|
|
|
where the client made a bad prediction.
|
|
|
|
*/
|
|
|
|
|
2011-11-30 18:49:34 +01:00
|
|
|
/*
|
|
|
|
Handle restrictions and special cases of the move action
|
|
|
|
*/
|
2012-01-12 06:10:39 +01:00
|
|
|
if(a->getType() == IACTION_MOVE)
|
2011-11-30 18:49:34 +01:00
|
|
|
{
|
|
|
|
IMoveAction *ma = (IMoveAction*)a;
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
ma->from_inv.applyCurrentPlayer(player->getName());
|
|
|
|
ma->to_inv.applyCurrentPlayer(player->getName());
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
setInventoryModified(ma->from_inv);
|
|
|
|
setInventoryModified(ma->to_inv);
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
bool from_inv_is_current_player =
|
|
|
|
(ma->from_inv.type == InventoryLocation::PLAYER) &&
|
|
|
|
(ma->from_inv.name == player->getName());
|
|
|
|
|
|
|
|
bool to_inv_is_current_player =
|
|
|
|
(ma->to_inv.type == InventoryLocation::PLAYER) &&
|
|
|
|
(ma->to_inv.name == player->getName());
|
|
|
|
|
2011-11-30 18:49:34 +01:00
|
|
|
/*
|
2012-01-21 21:21:41 +01:00
|
|
|
Disable moving items out of craftpreview
|
2011-11-30 18:49:34 +01:00
|
|
|
*/
|
2012-01-21 21:21:41 +01:00
|
|
|
if(ma->from_list == "craftpreview")
|
2011-11-30 18:49:34 +01:00
|
|
|
{
|
|
|
|
infostream<<"Ignoring IMoveAction from "
|
2012-01-12 06:10:39 +01:00
|
|
|
<<(ma->from_inv.dump())<<":"<<ma->from_list
|
|
|
|
<<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
|
2012-01-21 21:21:41 +01:00
|
|
|
<<" because src is "<<ma->from_list<<std::endl;
|
2011-11-30 18:49:34 +01:00
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2011-07-30 17:51:15 +02:00
|
|
|
/*
|
2012-01-21 21:21:41 +01:00
|
|
|
Disable moving items into craftresult and craftpreview
|
2011-07-30 17:51:15 +02:00
|
|
|
*/
|
2012-01-21 21:21:41 +01:00
|
|
|
if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
|
2011-07-30 17:51:15 +02:00
|
|
|
{
|
2012-01-21 21:21:41 +01:00
|
|
|
infostream<<"Ignoring IMoveAction from "
|
|
|
|
<<(ma->from_inv.dump())<<":"<<ma->from_list
|
|
|
|
<<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
|
|
|
|
<<" because dst is "<<ma->to_list<<std::endl;
|
2011-11-30 18:49:34 +01:00
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disallow moving items in elsewhere than player's inventory
|
2012-01-12 06:10:39 +01:00
|
|
|
// if not allowed to interact
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!checkPriv(player->getName(), "interact") &&
|
|
|
|
(!from_inv_is_current_player ||
|
|
|
|
!to_inv_is_current_player))
|
2011-11-30 18:49:34 +01:00
|
|
|
{
|
|
|
|
infostream<<"Cannot move outside of player's inventory: "
|
2012-01-12 06:10:39 +01:00
|
|
|
<<"No interact privilege"<<std::endl;
|
2011-11-30 18:49:34 +01:00
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
Handle restrictions and special cases of the drop action
|
|
|
|
*/
|
|
|
|
else if(a->getType() == IACTION_DROP)
|
|
|
|
{
|
|
|
|
IDropAction *da = (IDropAction*)a;
|
2012-01-12 06:10:39 +01:00
|
|
|
|
|
|
|
da->from_inv.applyCurrentPlayer(player->getName());
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
setInventoryModified(da->from_inv);
|
|
|
|
|
2013-06-16 17:01:21 +02:00
|
|
|
/*
|
|
|
|
Disable dropping items out of craftpreview
|
|
|
|
*/
|
|
|
|
if(da->from_list == "craftpreview")
|
|
|
|
{
|
|
|
|
infostream<<"Ignoring IDropAction from "
|
|
|
|
<<(da->from_inv.dump())<<":"<<da->from_list
|
|
|
|
<<" because src is "<<da->from_list<<std::endl;
|
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Disallow dropping items if not allowed to interact
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!checkPriv(player->getName(), "interact"))
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2011-11-30 18:49:34 +01:00
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
2011-07-30 17:51:15 +02:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
/*
|
|
|
|
Handle restrictions and special cases of the craft action
|
|
|
|
*/
|
|
|
|
else if(a->getType() == IACTION_CRAFT)
|
|
|
|
{
|
|
|
|
ICraftAction *ca = (ICraftAction*)a;
|
|
|
|
|
|
|
|
ca->craft_inv.applyCurrentPlayer(player->getName());
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
setInventoryModified(ca->craft_inv);
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
//bool craft_inv_is_current_player =
|
|
|
|
// (ca->craft_inv.type == InventoryLocation::PLAYER) &&
|
|
|
|
// (ca->craft_inv.name == player->getName());
|
|
|
|
|
|
|
|
// Disallow crafting if not allowed to interact
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!checkPriv(player->getName(), "interact"))
|
2012-01-21 21:21:41 +01:00
|
|
|
{
|
|
|
|
infostream<<"Cannot craft: "
|
|
|
|
<<"No interact privilege"<<std::endl;
|
|
|
|
delete a;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-30 18:49:34 +01:00
|
|
|
// Do the action
|
2012-03-19 03:04:16 +01:00
|
|
|
a->apply(this, playersao, this);
|
2011-11-30 18:49:34 +01:00
|
|
|
// Eat the action
|
|
|
|
delete a;
|
2011-04-10 03:15:10 +02:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(command == TOSERVER_CHAT_MESSAGE)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-11-26 13:03:56 +01:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
u16 command
|
|
|
|
u16 length
|
|
|
|
wstring message
|
2011-11-26 13:03:56 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
u8 buf[6];
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
// Read stuff
|
|
|
|
is.read((char*)buf, 2);
|
|
|
|
u16 len = readU16(buf);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
std::wstring message;
|
|
|
|
for(u16 i=0; i<len; i++)
|
|
|
|
{
|
|
|
|
is.read((char*)buf, 2);
|
|
|
|
message += (wchar_t)readU16(buf);
|
2011-11-26 13:03:56 +01:00
|
|
|
}
|
|
|
|
|
2012-07-27 01:37:04 +02:00
|
|
|
// If something goes wrong, this player is to blame
|
|
|
|
RollbackScopeActor rollback_scope(m_rollback,
|
|
|
|
std::string("player:")+player->getName());
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
// Get player name of this client
|
|
|
|
std::wstring name = narrow_to_wide(player->getName());
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
// Run script hook
|
2013-05-25 00:51:02 +02:00
|
|
|
bool ate = m_script->on_chat_message(player->getName(),
|
2011-11-29 16:15:18 +01:00
|
|
|
wide_to_narrow(message));
|
|
|
|
// If script ate the message, don't proceed
|
|
|
|
if(ate)
|
|
|
|
return;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
// Line to send to players
|
|
|
|
std::wstring line;
|
|
|
|
// Whether to send to the player that sent the line
|
2014-01-31 00:24:00 +01:00
|
|
|
bool send_to_sender_only = false;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-07-22 15:42:43 +02:00
|
|
|
// Commands are implemented in Lua, so only catch invalid
|
|
|
|
// commands that were not "eaten" and send an error back
|
2011-11-29 16:15:18 +01:00
|
|
|
if(message[0] == L'/')
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2012-07-22 15:42:43 +02:00
|
|
|
message = message.substr(1);
|
2014-01-31 00:24:00 +01:00
|
|
|
send_to_sender_only = true;
|
2012-07-22 15:42:43 +02:00
|
|
|
if(message.length() == 0)
|
|
|
|
line += L"-!- Empty command";
|
2011-11-29 16:15:18 +01:00
|
|
|
else
|
2012-07-22 15:42:43 +02:00
|
|
|
line += L"-!- Invalid command: " + str_split(message, L' ')[0];
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-30 17:42:18 +02:00
|
|
|
if(checkPriv(player->getName(), "shout")){
|
2011-11-29 16:15:18 +01:00
|
|
|
line += L"<";
|
|
|
|
line += name;
|
|
|
|
line += L"> ";
|
|
|
|
line += message;
|
2012-03-30 17:42:18 +02:00
|
|
|
} else {
|
2012-06-24 23:18:16 +02:00
|
|
|
line += L"-!- You don't have permission to shout.";
|
2014-01-31 00:24:00 +01:00
|
|
|
send_to_sender_only = true;
|
2011-11-21 10:15:15 +01:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
if(line != L"")
|
|
|
|
{
|
2011-11-21 10:15:15 +01:00
|
|
|
/*
|
2014-01-31 00:24:00 +01:00
|
|
|
Send the message to sender
|
2011-11-21 10:15:15 +01:00
|
|
|
*/
|
2014-01-31 00:24:00 +01:00
|
|
|
if (send_to_sender_only)
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
SendChatMessage(peer_id, line);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
Send the message to others
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{
|
|
|
|
actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
|
2011-11-21 10:15:15 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
|
|
|
{
|
|
|
|
if (*i != peer_id)
|
|
|
|
SendChatMessage(*i, line);
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(command == TOSERVER_DAMAGE)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
u8 damage = readU8(is);
|
2010-12-11 17:11:03 +01:00
|
|
|
|
2013-01-06 15:32:23 +01:00
|
|
|
if(g_settings->getBool("enable_damage"))
|
|
|
|
{
|
|
|
|
actionstream<<player->getName()<<" damaged by "
|
|
|
|
<<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
|
|
|
|
<<std::endl;
|
2012-01-24 00:00:26 +01:00
|
|
|
|
2013-01-06 15:32:23 +01:00
|
|
|
playersao->setHP(playersao->getHP() - damage);
|
2012-01-24 00:00:26 +01:00
|
|
|
|
2013-01-06 15:32:23 +01:00
|
|
|
if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
|
|
|
|
DiePlayer(peer_id);
|
2012-01-24 00:00:26 +01:00
|
|
|
|
2013-01-06 15:32:23 +01:00
|
|
|
if(playersao->m_hp_not_sent)
|
|
|
|
SendPlayerHP(peer_id);
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2013-07-19 19:50:33 +02:00
|
|
|
else if(command == TOSERVER_BREATH)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
u16 breath = readU16(is);
|
|
|
|
playersao->setBreath(breath);
|
2014-04-28 23:41:27 +02:00
|
|
|
m_script->player_event(playersao,"breath_changed");
|
2013-07-19 19:50:33 +02:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(command == TOSERVER_PASSWORD)
|
|
|
|
{
|
2010-12-18 12:10:37 +01:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
[0] u16 TOSERVER_PASSWORD
|
|
|
|
[2] u8[28] old password
|
|
|
|
[30] u8[28] new password
|
2010-12-18 12:10:37 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
if(datasize != 2+PASSWORD_SIZE*2)
|
|
|
|
return;
|
|
|
|
/*char password[PASSWORD_SIZE];
|
|
|
|
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
|
|
|
|
password[i] = data[2+i];
|
|
|
|
password[PASSWORD_SIZE-1] = 0;*/
|
|
|
|
std::string oldpwd;
|
|
|
|
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
|
2010-12-18 12:10:37 +01:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
char c = data[2+i];
|
|
|
|
if(c == 0)
|
|
|
|
break;
|
|
|
|
oldpwd += c;
|
|
|
|
}
|
|
|
|
std::string newpwd;
|
|
|
|
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
|
|
|
|
{
|
|
|
|
char c = data[2+PASSWORD_SIZE+i];
|
|
|
|
if(c == 0)
|
|
|
|
break;
|
|
|
|
newpwd += c;
|
|
|
|
}
|
|
|
|
|
2012-06-03 19:32:44 +02:00
|
|
|
if(!base64_is_valid(newpwd)){
|
|
|
|
infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
|
|
|
|
// Wrong old password supplied!!
|
|
|
|
SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
infostream<<"Server: Client requests a password change from "
|
|
|
|
<<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
|
|
|
|
|
|
|
|
std::string playername = player->getName();
|
|
|
|
|
2012-03-30 17:42:18 +02:00
|
|
|
std::string checkpwd;
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->getAuth(playername, &checkpwd, NULL);
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
if(oldpwd != checkpwd)
|
|
|
|
{
|
|
|
|
infostream<<"Server: invalid old password"<<std::endl;
|
|
|
|
// Wrong old password supplied!!
|
|
|
|
SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
|
|
|
|
return;
|
2010-12-18 12:10:37 +01:00
|
|
|
}
|
2010-11-29 09:52:07 +01:00
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
bool success = m_script->setPassword(playername, newpwd);
|
2012-03-30 17:42:18 +02:00
|
|
|
if(success){
|
|
|
|
actionstream<<player->getName()<<" changes password"<<std::endl;
|
2012-06-24 23:18:16 +02:00
|
|
|
SendChatMessage(peer_id, L"Password change successful.");
|
2012-03-30 17:42:18 +02:00
|
|
|
} else {
|
|
|
|
actionstream<<player->getName()<<" tries to change password but "
|
|
|
|
<<"it fails"<<std::endl;
|
2012-06-24 23:18:16 +02:00
|
|
|
SendChatMessage(peer_id, L"Password change failed or inavailable.");
|
2012-03-30 17:42:18 +02:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_PLAYERITEM)
|
|
|
|
{
|
|
|
|
if (datasize < 2+2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
u16 item = readU16(&data[2]);
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->setWieldIndex(item);
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_RESPAWN)
|
|
|
|
{
|
2013-01-06 15:32:23 +01:00
|
|
|
if(player->hp != 0 || !g_settings->getBool("enable_damage"))
|
2011-11-29 16:15:18 +01:00
|
|
|
return;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
RespawnPlayer(peer_id);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
actionstream<<player->getName()<<" respawns at "
|
|
|
|
<<PP(player->getPosition()/BS)<<std::endl;
|
2011-12-02 16:30:22 +01:00
|
|
|
|
|
|
|
// ActiveObject is added to environment in AsyncRunStep after
|
|
|
|
// the previous addition has been succesfully removed
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(command == TOSERVER_INTERACT)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
[0] u16 command
|
|
|
|
[2] u8 action
|
|
|
|
[3] u16 item
|
|
|
|
[5] u32 length of the next item
|
|
|
|
[9] serialized PointedThing
|
|
|
|
actions:
|
|
|
|
0: start digging (from undersurface) or use
|
|
|
|
1: stop digging (all parameters ignored)
|
|
|
|
2: digging completed
|
|
|
|
3: place block or item (to abovesurface)
|
|
|
|
4: use item
|
2010-12-23 21:35:53 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
u8 action = readU8(is);
|
|
|
|
u16 item_i = readU16(is);
|
|
|
|
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
|
|
|
|
PointedThing pointed;
|
|
|
|
pointed.deSerialize(tmp_is);
|
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
|
|
|
|
<<item_i<<", pointed="<<pointed.dump()<<std::endl;
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-01-24 00:00:26 +01:00
|
|
|
if(player->hp == 0)
|
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
|
2012-01-24 00:00:26 +01:00
|
|
|
<<" tried to interact, but is dead!"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
v3f player_pos = playersao->getLastGoodPosition();
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
// Update wielded item
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->setWieldIndex(item_i);
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
// Get pointed to node (undefined if not POINTEDTYPE_NODE)
|
|
|
|
v3s16 p_under = pointed.node_undersurface;
|
|
|
|
v3s16 p_above = pointed.node_abovesurface;
|
|
|
|
|
|
|
|
// Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
|
|
|
|
ServerActiveObject *pointed_object = NULL;
|
|
|
|
if(pointed.type == POINTEDTHING_OBJECT)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
pointed_object = m_env->getActiveObject(pointed.object_id);
|
|
|
|
if(pointed_object == NULL)
|
|
|
|
{
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"TOSERVER_INTERACT: "
|
2011-11-29 16:15:18 +01:00
|
|
|
"pointed object is NULL"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2010-12-23 21:35:53 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2010-12-23 21:35:53 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
v3f pointed_pos_under = player_pos;
|
|
|
|
v3f pointed_pos_above = player_pos;
|
|
|
|
if(pointed.type == POINTEDTHING_NODE)
|
|
|
|
{
|
|
|
|
pointed_pos_under = intToFloat(p_under, BS);
|
|
|
|
pointed_pos_above = intToFloat(p_above, BS);
|
|
|
|
}
|
|
|
|
else if(pointed.type == POINTEDTHING_OBJECT)
|
|
|
|
{
|
|
|
|
pointed_pos_under = pointed_object->getBasePosition();
|
|
|
|
pointed_pos_above = pointed_pos_under;
|
|
|
|
}
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
/*
|
|
|
|
Check that target is reasonably close
|
|
|
|
(only when digging or placing things)
|
|
|
|
*/
|
|
|
|
if(action == 0 || action == 2 || action == 3)
|
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
float d = player_pos.getDistanceFrom(pointed_pos_under);
|
2012-03-04 20:08:03 +01:00
|
|
|
float max_d = BS * 14; // Just some large enough value
|
2011-11-29 16:15:18 +01:00
|
|
|
if(d > max_d){
|
|
|
|
actionstream<<"Player "<<player->getName()
|
|
|
|
<<" tried to access "<<pointed.dump()
|
|
|
|
<<" from too far: "
|
|
|
|
<<"d="<<d<<", max_d="<<max_d
|
|
|
|
<<". ignoring."<<std::endl;
|
|
|
|
// Re-send block to revert change on client-side
|
2011-04-05 17:23:30 +02:00
|
|
|
RemoteClient *client = getClient(peer_id);
|
2012-01-12 06:10:39 +01:00
|
|
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
2011-04-05 17:23:30 +02:00
|
|
|
client->SetBlockNotSent(blockpos);
|
2013-08-03 23:45:49 +02:00
|
|
|
// Call callbacks
|
|
|
|
m_script->on_cheat(playersao, "interacted_too_far");
|
2011-11-29 16:15:18 +01:00
|
|
|
// Do nothing else
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Make sure the player is allowed to do it
|
|
|
|
*/
|
2012-03-30 17:42:18 +02:00
|
|
|
if(!checkPriv(player->getName(), "interact"))
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2012-04-09 20:13:22 +02:00
|
|
|
actionstream<<player->getName()<<" attempted to interact with "
|
|
|
|
<<pointed.dump()<<" without 'interact' privilege"
|
|
|
|
<<std::endl;
|
|
|
|
// Re-send block to revert change on client-side
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
2012-06-10 11:46:48 +02:00
|
|
|
// Digging completed -> under
|
|
|
|
if(action == 2){
|
|
|
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
|
|
|
client->SetBlockNotSent(blockpos);
|
|
|
|
}
|
|
|
|
// Placement -> above
|
|
|
|
if(action == 3){
|
|
|
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
|
|
|
|
client->SetBlockNotSent(blockpos);
|
|
|
|
}
|
2012-01-21 00:11:44 +01:00
|
|
|
return;
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
/*
|
|
|
|
If something goes wrong, this player is to blame
|
|
|
|
*/
|
|
|
|
RollbackScopeActor rollback_scope(m_rollback,
|
|
|
|
std::string("player:")+player->getName());
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
/*
|
|
|
|
0: start digging or punch object
|
|
|
|
*/
|
|
|
|
if(action == 0)
|
|
|
|
{
|
|
|
|
if(pointed.type == POINTEDTHING_NODE)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-04-10 03:15:10 +02:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
NOTE: This can be used in the future to check if
|
|
|
|
somebody is cheating, by checking the timing.
|
2011-04-10 03:15:10 +02:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
MapNode n(CONTENT_IGNORE);
|
|
|
|
try
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
n = m_env->getMap().getNode(p_under);
|
2011-08-22 21:55:17 +02:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
catch(InvalidPositionException &e)
|
2011-08-22 21:55:17 +02:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
infostream<<"Server: Not punching: Node not found."
|
|
|
|
<<" Adding block to emerge queue."
|
|
|
|
<<std::endl;
|
2013-02-14 04:43:15 +01:00
|
|
|
m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2012-01-21 00:11:44 +01:00
|
|
|
if(n.getContent() != CONTENT_IGNORE)
|
2014-01-24 01:21:01 +01:00
|
|
|
m_script->node_on_punch(p_under, n, playersao, pointed);
|
2012-07-21 13:38:49 +02:00
|
|
|
// Cheat prevention
|
|
|
|
playersao->noCheatDigStart(p_under);
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
else if(pointed.type == POINTEDTHING_OBJECT)
|
|
|
|
{
|
|
|
|
// Skip if object has been removed
|
|
|
|
if(pointed_object->m_removed)
|
|
|
|
return;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
actionstream<<player->getName()<<" punches object "
|
2012-03-11 03:15:45 +01:00
|
|
|
<<pointed.object_id<<": "
|
|
|
|
<<pointed_object->getDescription()<<std::endl;
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
ItemStack punchitem = playersao->getWieldedItem();
|
2012-03-04 20:08:03 +01:00
|
|
|
ToolCapabilities toolcap =
|
|
|
|
punchitem.getToolCapabilities(m_itemdef);
|
|
|
|
v3f dir = (pointed_object->getBasePosition() -
|
2012-03-19 03:04:16 +01:00
|
|
|
(player->getPosition() + player->getEyeOffset())
|
2012-03-04 20:08:03 +01:00
|
|
|
).normalize();
|
2012-03-19 03:04:16 +01:00
|
|
|
float time_from_last_punch =
|
|
|
|
playersao->resetTimeFromLastPunch();
|
|
|
|
pointed_object->punch(dir, &toolcap, playersao,
|
|
|
|
time_from_last_punch);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
} // action == 0
|
2010-12-23 21:35:53 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
1: stop digging
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(action == 1)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
} // action == 1
|
|
|
|
|
2011-04-04 04:12:33 +02:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
2: Digging completed
|
2011-04-04 04:12:33 +02:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(action == 2)
|
2011-04-04 04:12:33 +02:00
|
|
|
{
|
2012-07-21 13:38:49 +02:00
|
|
|
// Only digging of nodes
|
2012-01-21 00:11:44 +01:00
|
|
|
if(pointed.type == POINTEDTHING_NODE)
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2012-01-21 00:11:44 +01:00
|
|
|
MapNode n(CONTENT_IGNORE);
|
|
|
|
try
|
2011-11-27 04:01:38 +01:00
|
|
|
{
|
2012-01-21 00:11:44 +01:00
|
|
|
n = m_env->getMap().getNode(p_under);
|
2011-09-22 11:11:48 +02:00
|
|
|
}
|
2012-01-21 00:11:44 +01:00
|
|
|
catch(InvalidPositionException &e)
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2012-01-21 00:11:44 +01:00
|
|
|
infostream<<"Server: Not finishing digging: Node not found."
|
|
|
|
<<" Adding block to emerge queue."
|
|
|
|
<<std::endl;
|
2013-02-14 04:43:15 +01:00
|
|
|
m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2012-07-21 13:38:49 +02:00
|
|
|
|
|
|
|
/* Cheat prevention */
|
|
|
|
bool is_valid_dig = true;
|
|
|
|
if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
|
|
|
|
{
|
|
|
|
v3s16 nocheat_p = playersao->getNoCheatDigPos();
|
|
|
|
float nocheat_t = playersao->getNoCheatDigTime();
|
|
|
|
playersao->noCheatDigEnd();
|
|
|
|
// If player didn't start digging this, ignore dig
|
|
|
|
if(nocheat_p != p_under){
|
|
|
|
infostream<<"Server: NoCheat: "<<player->getName()
|
|
|
|
<<" started digging "
|
|
|
|
<<PP(nocheat_p)<<" and completed digging "
|
|
|
|
<<PP(p_under)<<"; not digging."<<std::endl;
|
|
|
|
is_valid_dig = false;
|
2013-08-03 23:45:49 +02:00
|
|
|
// Call callbacks
|
|
|
|
m_script->on_cheat(playersao, "finished_unknown_dig");
|
2012-07-21 13:38:49 +02:00
|
|
|
}
|
|
|
|
// Get player's wielded item
|
|
|
|
ItemStack playeritem;
|
|
|
|
InventoryList *mlist = playersao->getInventory()->getList("main");
|
|
|
|
if(mlist != NULL)
|
|
|
|
playeritem = mlist->getItem(playersao->getWieldIndex());
|
|
|
|
ToolCapabilities playeritem_toolcap =
|
|
|
|
playeritem.getToolCapabilities(m_itemdef);
|
|
|
|
// Get diggability and expected digging time
|
|
|
|
DigParams params = getDigParams(m_nodedef->get(n).groups,
|
|
|
|
&playeritem_toolcap);
|
|
|
|
// If can't dig, try hand
|
|
|
|
if(!params.diggable){
|
|
|
|
const ItemDefinition &hand = m_itemdef->get("");
|
|
|
|
const ToolCapabilities *tp = hand.tool_capabilities;
|
|
|
|
if(tp)
|
|
|
|
params = getDigParams(m_nodedef->get(n).groups, tp);
|
|
|
|
}
|
|
|
|
// If can't dig, ignore dig
|
|
|
|
if(!params.diggable){
|
|
|
|
infostream<<"Server: NoCheat: "<<player->getName()
|
|
|
|
<<" completed digging "<<PP(p_under)
|
|
|
|
<<", which is not diggable with tool. not digging."
|
|
|
|
<<std::endl;
|
|
|
|
is_valid_dig = false;
|
2013-08-03 23:45:49 +02:00
|
|
|
// Call callbacks
|
|
|
|
m_script->on_cheat(playersao, "dug_unbreakable");
|
2012-07-21 13:38:49 +02:00
|
|
|
}
|
2013-08-03 22:16:37 +02:00
|
|
|
// Check digging time
|
|
|
|
// If already invalidated, we don't have to
|
|
|
|
if(!is_valid_dig){
|
|
|
|
// Well not our problem then
|
|
|
|
}
|
|
|
|
// Clean and long dig
|
|
|
|
else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
|
|
|
|
// All is good, but grab time from pool; don't care if
|
|
|
|
// it's actually available
|
|
|
|
playersao->getDigPool().grab(params.time);
|
|
|
|
}
|
|
|
|
// Short or laggy dig
|
|
|
|
// Try getting the time from pool
|
|
|
|
else if(playersao->getDigPool().grab(params.time)){
|
|
|
|
// All is good
|
|
|
|
}
|
|
|
|
// Dig not possible
|
|
|
|
else{
|
2012-07-21 13:38:49 +02:00
|
|
|
infostream<<"Server: NoCheat: "<<player->getName()
|
2013-08-03 22:16:37 +02:00
|
|
|
<<" completed digging "<<PP(p_under)
|
|
|
|
<<"too fast; not digging."<<std::endl;
|
2012-07-21 13:38:49 +02:00
|
|
|
is_valid_dig = false;
|
2013-08-03 23:45:49 +02:00
|
|
|
// Call callbacks
|
|
|
|
m_script->on_cheat(playersao, "dug_too_fast");
|
2012-07-21 13:38:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Actually dig node */
|
|
|
|
|
|
|
|
if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->node_on_dig(p_under, n, playersao);
|
2012-04-09 20:13:22 +02:00
|
|
|
|
2012-07-21 13:38:49 +02:00
|
|
|
// Send unusual result (that is, node not being removed)
|
|
|
|
if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
|
2012-04-09 20:13:22 +02:00
|
|
|
{
|
|
|
|
// Re-send block to revert change on client-side
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
|
|
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
|
|
|
client->SetBlockNotSent(blockpos);
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
|
|
|
} // action == 2
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
3: place block or right-click object
|
2010-12-23 21:35:53 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(action == 3)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
ItemStack item = playersao->getWieldedItem();
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-01-21 00:11:44 +01:00
|
|
|
// Reset build time counter
|
|
|
|
if(pointed.type == POINTEDTHING_NODE &&
|
|
|
|
item.getDefinition(m_itemdef).type == ITEM_NODE)
|
|
|
|
getClient(peer_id)->m_time_from_building = 0.0;
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
if(pointed.type == POINTEDTHING_OBJECT)
|
|
|
|
{
|
|
|
|
// Right click object
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Skip if object has been removed
|
|
|
|
if(pointed_object->m_removed)
|
|
|
|
return;
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
actionstream<<player->getName()<<" right-clicks object "
|
2012-03-11 03:15:45 +01:00
|
|
|
<<pointed.object_id<<": "
|
|
|
|
<<pointed_object->getDescription()<<std::endl;
|
2011-11-29 16:15:18 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Do stuff
|
2012-03-19 03:04:16 +01:00
|
|
|
pointed_object->rightClick(playersao);
|
2012-01-12 06:10:39 +01:00
|
|
|
}
|
2013-05-25 00:51:02 +02:00
|
|
|
else if(m_script->item_OnPlace(
|
2012-03-19 03:04:16 +01:00
|
|
|
item, playersao, pointed))
|
2012-01-12 06:10:39 +01:00
|
|
|
{
|
|
|
|
// Placement was handled in lua
|
2011-05-23 21:40:25 +02:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Apply returned ItemStack
|
2012-07-25 13:07:45 +02:00
|
|
|
playersao->setWieldedItem(item);
|
2012-01-12 06:10:39 +01:00
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2013-04-27 03:28:27 +02:00
|
|
|
// If item has node placement prediction, always send the
|
|
|
|
// blocks to make sure the client knows what exactly happened
|
2012-06-10 11:46:48 +02:00
|
|
|
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
|
|
|
|
RemoteClient *client = getClient(peer_id);
|
|
|
|
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
|
|
|
|
client->SetBlockNotSent(blockpos);
|
2013-04-27 03:28:27 +02:00
|
|
|
v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
|
|
|
if(blockpos2 != blockpos){
|
|
|
|
client->SetBlockNotSent(blockpos2);
|
|
|
|
}
|
2012-06-10 11:46:48 +02:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
} // action == 3
|
|
|
|
|
2011-05-22 22:09:12 +02:00
|
|
|
/*
|
2011-11-29 16:15:18 +01:00
|
|
|
4: use
|
2011-05-22 22:09:12 +02:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(action == 4)
|
2011-05-29 20:11:16 +02:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
ItemStack item = playersao->getWieldedItem();
|
2012-01-12 06:10:39 +01:00
|
|
|
|
|
|
|
actionstream<<player->getName()<<" uses "<<item.name
|
2011-11-29 16:15:18 +01:00
|
|
|
<<", pointing at "<<pointed.dump()<<std::endl;
|
2011-05-29 20:11:16 +02:00
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
if(m_script->item_OnUse(
|
2012-03-19 03:04:16 +01:00
|
|
|
item, playersao, pointed))
|
2011-11-29 16:15:18 +01:00
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
// Apply returned ItemStack
|
2012-07-25 13:07:45 +02:00
|
|
|
playersao->setWieldedItem(item);
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2011-07-31 14:32:45 +02:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
} // action == 4
|
2012-11-22 20:01:31 +01:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Catch invalid actions
|
|
|
|
*/
|
|
|
|
else
|
2011-05-29 20:11:16 +02:00
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
infostream<<"WARNING: Server: Invalid action "
|
|
|
|
<<action<<std::endl;
|
2011-05-22 22:09:12 +02:00
|
|
|
}
|
2011-10-15 13:46:59 +02:00
|
|
|
}
|
2012-03-24 18:01:26 +01:00
|
|
|
else if(command == TOSERVER_REMOVED_SOUNDS)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
int num = readU16(is);
|
|
|
|
for(int k=0; k<num; k++){
|
|
|
|
s32 id = readS32(is);
|
|
|
|
std::map<s32, ServerPlayingSound>::iterator i =
|
|
|
|
m_playing_sounds.find(id);
|
|
|
|
if(i == m_playing_sounds.end())
|
|
|
|
continue;
|
|
|
|
ServerPlayingSound &psound = i->second;
|
|
|
|
psound.clients.erase(peer_id);
|
|
|
|
if(psound.clients.size() == 0)
|
|
|
|
m_playing_sounds.erase(i++);
|
|
|
|
}
|
|
|
|
}
|
2012-06-01 19:51:15 +02:00
|
|
|
else if(command == TOSERVER_NODEMETA_FIELDS)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-06-01 19:51:15 +02:00
|
|
|
v3s16 p = readV3S16(is);
|
|
|
|
std::string formname = deSerializeString(is);
|
|
|
|
int num = readU16(is);
|
|
|
|
std::map<std::string, std::string> fields;
|
|
|
|
for(int k=0; k<num; k++){
|
|
|
|
std::string fieldname = deSerializeString(is);
|
|
|
|
std::string fieldvalue = deSerializeLongString(is);
|
|
|
|
fields[fieldname] = fieldvalue;
|
|
|
|
}
|
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
// If something goes wrong, this player is to blame
|
|
|
|
RollbackScopeActor rollback_scope(m_rollback,
|
|
|
|
std::string("player:")+player->getName());
|
|
|
|
|
|
|
|
// Check the target node for rollback data; leave others unnoticed
|
|
|
|
RollbackNode rn_old(&m_env->getMap(), p, this);
|
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->node_on_receive_fields(p, formname, fields,playersao);
|
2012-07-26 21:06:45 +02:00
|
|
|
|
|
|
|
// Report rollback data
|
|
|
|
RollbackNode rn_new(&m_env->getMap(), p, this);
|
|
|
|
if(rollback() && rn_new != rn_old){
|
|
|
|
RollbackAction action;
|
|
|
|
action.setSetNode(p, rn_old, rn_new);
|
|
|
|
rollback()->reportAction(action);
|
|
|
|
}
|
2012-06-01 19:51:15 +02:00
|
|
|
}
|
2012-07-22 16:10:58 +02:00
|
|
|
else if(command == TOSERVER_INVENTORY_FIELDS)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-07-22 16:10:58 +02:00
|
|
|
std::string formname = deSerializeString(is);
|
|
|
|
int num = readU16(is);
|
|
|
|
std::map<std::string, std::string> fields;
|
|
|
|
for(int k=0; k<num; k++){
|
|
|
|
std::string fieldname = deSerializeString(is);
|
|
|
|
std::string fieldvalue = deSerializeLongString(is);
|
|
|
|
fields[fieldname] = fieldvalue;
|
|
|
|
}
|
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->on_playerReceiveFields(playersao, formname, fields);
|
2012-07-22 16:10:58 +02:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
else
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server::ProcessData(): Ignoring "
|
2010-11-27 00:02:21 +01:00
|
|
|
"unknown command "<<command<<std::endl;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
} //try
|
|
|
|
catch(SendFailedException &e)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
errorstream<<"Server::ProcessData(): SendFailedException: "
|
2010-11-27 00:02:21 +01:00
|
|
|
<<"what="<<e.what()
|
|
|
|
<<std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
void Server::setTimeOfDay(u32 time)
|
|
|
|
{
|
|
|
|
m_env->setTimeOfDay(time);
|
|
|
|
m_time_of_day_send_timer = 0;
|
|
|
|
}
|
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
void Server::onMapEditEvent(MapEditEvent *event)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Server::onMapEditEvent()"<<std::endl;
|
2011-02-23 01:49:57 +01:00
|
|
|
if(m_ignore_map_edit_events)
|
|
|
|
return;
|
2012-03-29 00:22:08 +02:00
|
|
|
if(m_ignore_map_edit_events_area.contains(event->getArea()))
|
|
|
|
return;
|
2011-02-23 01:49:57 +01:00
|
|
|
MapEditEvent *e = event->clone();
|
|
|
|
m_unsent_map_edit_queue.push_back(e);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2011-12-06 14:21:56 +01:00
|
|
|
Inventory* Server::getInventory(const InventoryLocation &loc)
|
|
|
|
{
|
|
|
|
switch(loc.type){
|
|
|
|
case InventoryLocation::UNDEFINED:
|
|
|
|
{}
|
|
|
|
break;
|
2012-01-12 06:10:39 +01:00
|
|
|
case InventoryLocation::CURRENT_PLAYER:
|
|
|
|
{}
|
|
|
|
break;
|
2011-12-06 14:21:56 +01:00
|
|
|
case InventoryLocation::PLAYER:
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(loc.name.c_str());
|
|
|
|
if(!player)
|
|
|
|
return NULL;
|
2012-03-19 03:04:16 +01:00
|
|
|
PlayerSAO *playersao = player->getPlayerSAO();
|
|
|
|
if(!playersao)
|
|
|
|
return NULL;
|
|
|
|
return playersao->getInventory();
|
2011-12-06 14:21:56 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case InventoryLocation::NODEMETA:
|
|
|
|
{
|
|
|
|
NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
|
|
|
|
if(!meta)
|
|
|
|
return NULL;
|
|
|
|
return meta->getInventory();
|
|
|
|
}
|
|
|
|
break;
|
2012-07-24 19:57:17 +02:00
|
|
|
case InventoryLocation::DETACHED:
|
|
|
|
{
|
|
|
|
if(m_detached_inventories.count(loc.name) == 0)
|
|
|
|
return NULL;
|
|
|
|
return m_detached_inventories[loc.name];
|
|
|
|
}
|
|
|
|
break;
|
2011-12-06 14:21:56 +01:00
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
void Server::setInventoryModified(const InventoryLocation &loc)
|
|
|
|
{
|
|
|
|
switch(loc.type){
|
|
|
|
case InventoryLocation::UNDEFINED:
|
|
|
|
{}
|
|
|
|
break;
|
|
|
|
case InventoryLocation::PLAYER:
|
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
Player *player = m_env->getPlayer(loc.name.c_str());
|
|
|
|
if(!player)
|
|
|
|
return;
|
|
|
|
PlayerSAO *playersao = player->getPlayerSAO();
|
|
|
|
if(!playersao)
|
2011-12-06 14:21:56 +01:00
|
|
|
return;
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->m_inventory_not_sent = true;
|
|
|
|
playersao->m_wielded_item_not_sent = true;
|
2011-12-06 14:21:56 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case InventoryLocation::NODEMETA:
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(loc.p);
|
|
|
|
|
|
|
|
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
|
|
|
|
if(block)
|
|
|
|
block->raiseModified(MOD_STATE_WRITE_NEEDED);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-12-06 14:21:56 +01:00
|
|
|
setBlockNotSent(blockpos);
|
|
|
|
}
|
|
|
|
break;
|
2012-07-24 19:57:17 +02:00
|
|
|
case InventoryLocation::DETACHED:
|
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
|
2012-07-24 19:57:17 +02:00
|
|
|
}
|
|
|
|
break;
|
2011-12-06 14:21:56 +01:00
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
2011-04-04 14:13:19 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
|
|
|
|
{
|
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
m_clients.Lock();
|
|
|
|
// Set the modified blocks unsent for all the clients
|
|
|
|
for (std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i) {
|
|
|
|
RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
|
|
|
|
if (client != NULL)
|
|
|
|
client->SetBlocksNotSent(block);
|
|
|
|
}
|
|
|
|
m_clients.Unlock();
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Server::peerAdded(con::Peer *peer)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server::peerAdded(): peer->id="
|
2010-11-27 00:02:21 +01:00
|
|
|
<<peer->id<<std::endl;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
con::PeerChange c;
|
|
|
|
c.type = con::PEER_ADDED;
|
2010-12-24 16:08:50 +01:00
|
|
|
c.peer_id = peer->id;
|
|
|
|
c.timeout = false;
|
|
|
|
m_peer_change_queue.push_back(c);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::deletingPeer(con::Peer *peer, bool timeout)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server::deletingPeer(): peer->id="
|
2010-11-27 00:02:21 +01:00
|
|
|
<<peer->id<<", timeout="<<timeout<<std::endl;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer->id, CSE_Disconnect);
|
2014-01-31 00:24:00 +01:00
|
|
|
con::PeerChange c;
|
|
|
|
c.type = con::PEER_REMOVED;
|
2010-12-24 16:08:50 +01:00
|
|
|
c.peer_id = peer->id;
|
|
|
|
c.timeout = timeout;
|
|
|
|
m_peer_change_queue.push_back(c);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
|
|
|
|
{
|
|
|
|
*retval = m_con.getPeerStat(peer_id,type);
|
|
|
|
if (*retval == -1) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Server::getClientInfo(
|
|
|
|
u16 peer_id,
|
|
|
|
ClientState* state,
|
|
|
|
u32* uptime,
|
|
|
|
u8* ser_vers,
|
|
|
|
u16* prot_vers,
|
|
|
|
u8* major,
|
|
|
|
u8* minor,
|
|
|
|
u8* patch,
|
|
|
|
std::string* vers_string
|
|
|
|
)
|
|
|
|
{
|
|
|
|
*state = m_clients.getClientState(peer_id);
|
|
|
|
m_clients.Lock();
|
2014-06-28 08:02:38 +02:00
|
|
|
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
|
2014-02-13 20:17:42 +01:00
|
|
|
|
2014-05-11 00:14:57 +02:00
|
|
|
if (client == NULL) {
|
|
|
|
m_clients.Unlock();
|
2014-02-13 20:17:42 +01:00
|
|
|
return false;
|
2014-06-28 08:02:38 +02:00
|
|
|
}
|
2014-02-13 20:17:42 +01:00
|
|
|
|
|
|
|
*uptime = client->uptime();
|
|
|
|
*ser_vers = client->serialization_version;
|
|
|
|
*prot_vers = client->net_proto_version;
|
|
|
|
|
|
|
|
*major = client->getMajor();
|
|
|
|
*minor = client->getMinor();
|
|
|
|
*patch = client->getPatch();
|
|
|
|
*vers_string = client->getPatch();
|
|
|
|
|
|
|
|
m_clients.Unlock();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::handlePeerChanges()
|
|
|
|
{
|
|
|
|
while(m_peer_change_queue.size() > 0)
|
|
|
|
{
|
|
|
|
con::PeerChange c = m_peer_change_queue.pop_front();
|
|
|
|
|
|
|
|
verbosestream<<"Server: Handling peer change: "
|
|
|
|
<<"id="<<c.peer_id<<", timeout="<<c.timeout
|
|
|
|
<<std::endl;
|
|
|
|
|
|
|
|
switch(c.type)
|
|
|
|
{
|
|
|
|
case con::PEER_ADDED:
|
|
|
|
m_clients.CreateClient(c.peer_id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case con::PEER_REMOVED:
|
|
|
|
DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert("Invalid peer change event received!" == 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendMovement(u16 peer_id)
|
2013-02-08 21:54:01 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_MOVEMENT);
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_speed_walk"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_speed_fast"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_speed_climb"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_speed_jump"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
|
|
|
|
writeF1000(os, g_settings->getFloat("movement_gravity"));
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2013-02-08 21:54:01 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendHP(u16 peer_id, u8 hp)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_HP);
|
|
|
|
writeU8(os, hp);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendBreath(u16 peer_id, u16 breath)
|
2013-07-19 19:50:33 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_BREATH);
|
|
|
|
writeU16(os, breath);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2013-07-19 19:50:33 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
|
2011-05-20 21:28:03 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_ACCESS_DENIED);
|
2011-05-29 20:11:16 +02:00
|
|
|
os<<serializeWideString(reason);
|
2011-05-20 21:28:03 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-05-20 21:28:03 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
|
|
|
|
v3f camera_point_target)
|
2011-10-15 13:46:59 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_DEATHSCREEN);
|
|
|
|
writeU8(os, set_camera_point_target);
|
|
|
|
writeV3F1000(os, camera_point_target);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-10-15 13:46:59 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendItemDef(u16 peer_id,
|
2013-03-28 21:40:44 +01:00
|
|
|
IItemDefManager *itemdef, u16 protocol_version)
|
2011-11-15 00:00:16 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u32 length of the next item
|
2012-01-12 06:10:39 +01:00
|
|
|
zlib-compressed serialized ItemDefManager
|
2011-11-15 00:00:16 +01:00
|
|
|
*/
|
2012-01-12 06:10:39 +01:00
|
|
|
writeU16(os, TOCLIENT_ITEMDEF);
|
2011-11-15 00:00:16 +01:00
|
|
|
std::ostringstream tmp_os(std::ios::binary);
|
2013-03-28 21:40:44 +01:00
|
|
|
itemdef->serialize(tmp_os, protocol_version);
|
2012-01-12 06:10:39 +01:00
|
|
|
std::ostringstream tmp_os2(std::ios::binary);
|
|
|
|
compressZlib(tmp_os.str(), tmp_os2);
|
|
|
|
os<<serializeLongString(tmp_os2.str());
|
2011-11-15 00:00:16 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server: Sending item definitions to id("<<peer_id
|
|
|
|
<<"): size="<<s.size()<<std::endl;
|
2011-11-15 19:32:56 +01:00
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-11-15 19:32:56 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
void Server::SendNodeDef(u16 peer_id,
|
2012-11-26 22:59:03 +01:00
|
|
|
INodeDefManager *nodedef, u16 protocol_version)
|
2011-11-15 19:32:56 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u32 length of the next item
|
2012-01-12 06:10:39 +01:00
|
|
|
zlib-compressed serialized NodeDefManager
|
2011-11-15 19:32:56 +01:00
|
|
|
*/
|
|
|
|
writeU16(os, TOCLIENT_NODEDEF);
|
|
|
|
std::ostringstream tmp_os(std::ios::binary);
|
2012-11-26 22:59:03 +01:00
|
|
|
nodedef->serialize(tmp_os, protocol_version);
|
2012-01-12 06:10:39 +01:00
|
|
|
std::ostringstream tmp_os2(std::ios::binary);
|
|
|
|
compressZlib(tmp_os.str(), tmp_os2);
|
|
|
|
os<<serializeLongString(tmp_os2.str());
|
2011-11-15 19:32:56 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server: Sending node definitions to id("<<peer_id
|
|
|
|
<<"): size="<<s.size()<<std::endl;
|
2011-11-15 00:00:16 +01:00
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-11-15 00:00:16 +01:00
|
|
|
}
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Non-static send methods
|
|
|
|
*/
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Server::SendInventory(u16 peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
|
|
|
assert(playersao);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->m_inventory_not_sent = false;
|
2011-11-29 22:18:20 +01:00
|
|
|
|
2010-12-22 15:30:23 +01:00
|
|
|
/*
|
2011-04-21 18:35:17 +02:00
|
|
|
Serialize it
|
2010-12-22 15:30:23 +01:00
|
|
|
*/
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
std::ostringstream os;
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->getInventory()->serialize(os);
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
std::string s = os.str();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
SharedBuffer<u8> data(s.size()+2);
|
|
|
|
writeU16(&data[0], TOCLIENT_INVENTORY);
|
|
|
|
memcpy(&data[2], s.c_str(), s.size());
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
u8 buf[12];
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOCLIENT_CHAT_MESSAGE);
|
|
|
|
os.write((char*)buf, 2);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Write length
|
|
|
|
writeU16(buf, message.size());
|
|
|
|
os.write((char*)buf, 2);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Write string
|
|
|
|
for(u32 i=0; i<message.size(); i++)
|
2010-12-22 15:30:23 +01:00
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
u16 w = message[i];
|
|
|
|
writeU16(buf, w);
|
|
|
|
os.write((char*)buf, 2);
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
2014-01-31 00:24:00 +01:00
|
|
|
|
|
|
|
if (peer_id != PEER_ID_INEXISTENT)
|
|
|
|
{
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_clients.sendToAll(0,data,true);
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
2013-04-14 00:20:22 +02:00
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
|
|
|
|
const std::string &formname)
|
2013-01-02 20:45:04 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
u8 buf[12];
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
|
2013-01-02 20:45:04 +01:00
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
|
|
|
|
os.write((char*)buf, 2);
|
2014-06-25 19:04:47 +02:00
|
|
|
os<<serializeLongString(FORMSPEC_VERSION_STRING + formspec);
|
2013-01-03 18:59:28 +01:00
|
|
|
os<<serializeString(formname);
|
2013-01-02 20:45:04 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2013-01-02 20:45:04 +01:00
|
|
|
}
|
2011-04-11 20:28:04 +02:00
|
|
|
|
2013-01-23 18:32:02 +01:00
|
|
|
// Spawns a particle on peer with peer_id
|
2013-04-14 00:20:22 +02:00
|
|
|
void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
|
|
|
|
float expirationtime, float size, bool collisiondetection,
|
2013-04-22 20:35:10 +02:00
|
|
|
bool vertical, std::string texture)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_SPAWN_PARTICLE);
|
|
|
|
writeV3F1000(os, pos);
|
|
|
|
writeV3F1000(os, velocity);
|
|
|
|
writeV3F1000(os, acceleration);
|
|
|
|
writeF1000(os, expirationtime);
|
|
|
|
writeF1000(os, size);
|
|
|
|
writeU8(os, collisiondetection);
|
|
|
|
os<<serializeLongString(texture);
|
2013-04-22 20:35:10 +02:00
|
|
|
writeU8(os, vertical);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (peer_id != PEER_ID_INEXISTENT)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_clients.sendToAll(0,data,true);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adds a ParticleSpawner on peer with peer_id
|
|
|
|
void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
|
|
|
|
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
|
2013-04-22 20:35:10 +02:00
|
|
|
float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
|
|
|
|
|
|
|
|
writeU16(os, amount);
|
|
|
|
writeF1000(os, spawntime);
|
|
|
|
writeV3F1000(os, minpos);
|
|
|
|
writeV3F1000(os, maxpos);
|
|
|
|
writeV3F1000(os, minvel);
|
|
|
|
writeV3F1000(os, maxvel);
|
|
|
|
writeV3F1000(os, minacc);
|
|
|
|
writeV3F1000(os, maxacc);
|
|
|
|
writeF1000(os, minexptime);
|
|
|
|
writeF1000(os, maxexptime);
|
|
|
|
writeF1000(os, minsize);
|
|
|
|
writeF1000(os, maxsize);
|
|
|
|
writeU8(os, collisiondetection);
|
|
|
|
os<<serializeLongString(texture);
|
|
|
|
writeU32(os, id);
|
2013-04-22 20:35:10 +02:00
|
|
|
writeU8(os, vertical);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (peer_id != PEER_ID_INEXISTENT)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_clients.sendToAll(0,data,true);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
|
|
|
|
|
|
|
|
writeU16(os, id);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (peer_id != PEER_ID_INEXISTENT) {
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_clients.sendToAll(0,data,true);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
|
2013-04-11 20:23:38 +02:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
2013-04-14 00:20:22 +02:00
|
|
|
writeU16(os, TOCLIENT_HUDADD);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeU32(os, id);
|
2013-04-14 00:20:22 +02:00
|
|
|
writeU8(os, (u8)form->type);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeV2F1000(os, form->pos);
|
2013-04-14 00:20:22 +02:00
|
|
|
os << serializeString(form->name);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeV2F1000(os, form->scale);
|
2013-04-14 00:20:22 +02:00
|
|
|
os << serializeString(form->text);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeU32(os, form->number);
|
|
|
|
writeU32(os, form->item);
|
|
|
|
writeU32(os, form->dir);
|
2013-04-22 11:53:55 +02:00
|
|
|
writeV2F1000(os, form->align);
|
2013-04-23 01:47:59 +02:00
|
|
|
writeV2F1000(os, form->offset);
|
2014-01-26 21:31:59 +01:00
|
|
|
writeV3F1000(os, form->world_pos);
|
2014-04-28 23:41:27 +02:00
|
|
|
writeV2S32(os,form->size);
|
2013-04-11 20:23:38 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 1, data, true);
|
2013-04-11 20:23:38 +02:00
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
void Server::SendHUDRemove(u16 peer_id, u32 id)
|
2013-04-11 20:23:38 +02:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
2013-04-14 00:20:22 +02:00
|
|
|
writeU16(os, TOCLIENT_HUDRM);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeU32(os, id);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-06 20:05:28 +01:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 1, data, true);
|
2013-04-11 20:23:38 +02:00
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
|
2013-04-11 20:23:38 +02:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
2013-04-14 00:20:22 +02:00
|
|
|
writeU16(os, TOCLIENT_HUDCHANGE);
|
2013-04-11 20:23:38 +02:00
|
|
|
writeU32(os, id);
|
2013-04-14 00:20:22 +02:00
|
|
|
writeU8(os, (u8)stat);
|
|
|
|
switch (stat) {
|
|
|
|
case HUD_STAT_POS:
|
|
|
|
case HUD_STAT_SCALE:
|
2013-04-22 11:53:55 +02:00
|
|
|
case HUD_STAT_ALIGN:
|
2013-04-23 01:47:59 +02:00
|
|
|
case HUD_STAT_OFFSET:
|
2013-04-14 00:20:22 +02:00
|
|
|
writeV2F1000(os, *(v2f *)value);
|
|
|
|
break;
|
|
|
|
case HUD_STAT_NAME:
|
|
|
|
case HUD_STAT_TEXT:
|
|
|
|
os << serializeString(*(std::string *)value);
|
|
|
|
break;
|
2014-01-26 21:31:59 +01:00
|
|
|
case HUD_STAT_WORLD_POS:
|
|
|
|
writeV3F1000(os, *(v3f *)value);
|
|
|
|
break;
|
2014-04-28 23:41:27 +02:00
|
|
|
case HUD_STAT_SIZE:
|
|
|
|
writeV2S32(os,*(v2s32 *)value);
|
|
|
|
break;
|
2013-04-14 00:20:22 +02:00
|
|
|
case HUD_STAT_NUMBER:
|
|
|
|
case HUD_STAT_ITEM:
|
|
|
|
case HUD_STAT_DIR:
|
|
|
|
default:
|
|
|
|
writeU32(os, *(u32 *)value);
|
|
|
|
break;
|
|
|
|
}
|
2013-04-11 20:23:38 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
2013-04-14 00:20:22 +02:00
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
2013-04-11 20:23:38 +02:00
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2013-04-11 20:23:38 +02:00
|
|
|
}
|
|
|
|
|
2013-04-26 01:27:22 +02:00
|
|
|
void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
|
2013-04-24 12:52:46 +02:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
2013-04-26 01:27:22 +02:00
|
|
|
writeU16(os, TOCLIENT_HUD_SET_FLAGS);
|
2014-05-11 00:35:31 +02:00
|
|
|
|
|
|
|
//////////////////////////// compatibility code to be removed //////////////
|
|
|
|
flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
2013-04-26 01:27:22 +02:00
|
|
|
writeU32(os, flags);
|
|
|
|
writeU32(os, mask);
|
2013-04-24 12:52:46 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
2013-04-26 01:27:22 +02:00
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
2013-04-24 12:52:46 +02:00
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2013-04-24 12:52:46 +02:00
|
|
|
}
|
|
|
|
|
2013-05-04 02:08:52 +02:00
|
|
|
void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
|
|
|
writeU16(os, TOCLIENT_HUD_SET_PARAM);
|
|
|
|
writeU16(os, param);
|
|
|
|
os<<serializeString(value);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
2010-12-25 00:54:39 +01:00
|
|
|
|
2013-05-02 22:52:50 +02:00
|
|
|
void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
|
|
|
|
const std::string &type, const std::vector<std::string> ¶ms)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
|
|
|
writeU16(os, TOCLIENT_SET_SKY);
|
|
|
|
writeARGB8(os, bgcolor);
|
|
|
|
os<<serializeString(type);
|
|
|
|
writeU16(os, params.size());
|
|
|
|
for(size_t i=0; i<params.size(); i++)
|
|
|
|
os<<serializeString(params[i]);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
|
2013-10-18 10:53:19 +02:00
|
|
|
void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
|
|
|
|
float ratio)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
// Write command
|
|
|
|
writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
|
|
|
|
writeU8(os, do_override);
|
|
|
|
writeU16(os, ratio*65535);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
// Make packet
|
|
|
|
SharedBuffer<u8> data(2+2+4);
|
|
|
|
writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
|
|
|
|
writeU16(&data[2], time);
|
|
|
|
writeF1000(&data[4], time_speed);
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (peer_id == PEER_ID_INEXISTENT) {
|
|
|
|
m_clients.sendToAll(0,data,true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
2013-08-11 04:09:45 +02:00
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
void Server::SendPlayerHP(u16 peer_id)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
|
|
|
assert(playersao);
|
|
|
|
playersao->m_hp_not_sent = false;
|
2014-01-31 00:24:00 +01:00
|
|
|
SendHP(peer_id, playersao->getHP());
|
2014-04-28 23:41:27 +02:00
|
|
|
m_script->player_event(playersao,"health_changed");
|
2013-08-13 23:06:39 +02:00
|
|
|
|
|
|
|
// Send to other clients
|
|
|
|
std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
|
|
|
|
ActiveObjectMessage aom(playersao->getId(), true, str);
|
|
|
|
playersao->m_messages_out.push_back(aom);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2013-07-19 19:50:33 +02:00
|
|
|
void Server::SendPlayerBreath(u16 peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
|
|
|
assert(playersao);
|
|
|
|
playersao->m_breath_not_sent = false;
|
2014-04-28 23:41:27 +02:00
|
|
|
m_script->player_event(playersao,"breath_changed");
|
2014-01-31 00:24:00 +01:00
|
|
|
SendBreath(peer_id, playersao->getBreath());
|
2013-07-19 19:50:33 +02:00
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
void Server::SendMovePlayer(u16 peer_id)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-03-19 03:04:16 +01:00
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
assert(player);
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
std::ostringstream os(std::ios_base::binary);
|
2011-04-21 18:35:17 +02:00
|
|
|
writeU16(os, TOCLIENT_MOVE_PLAYER);
|
|
|
|
writeV3F1000(os, player->getPosition());
|
|
|
|
writeF1000(os, player->getPitch());
|
|
|
|
writeF1000(os, player->getYaw());
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
v3f pos = player->getPosition();
|
|
|
|
f32 pitch = player->getPitch();
|
|
|
|
f32 yaw = player->getYaw();
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
|
2011-04-21 18:35:17 +02:00
|
|
|
<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
|
|
|
|
<<" pitch="<<pitch
|
|
|
|
<<" yaw="<<yaw
|
|
|
|
<<std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-04-12 13:50:22 +02:00
|
|
|
void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
|
2014-01-08 13:47:53 +01:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS);
|
2014-04-12 13:50:22 +02:00
|
|
|
writeV2S32(os, animation_frames[0]);
|
|
|
|
writeV2S32(os, animation_frames[1]);
|
|
|
|
writeV2S32(os, animation_frames[2]);
|
|
|
|
writeV2S32(os, animation_frames[3]);
|
2014-01-08 13:47:53 +01:00
|
|
|
writeF1000(os, animation_speed);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
|
2014-04-11 15:32:46 +02:00
|
|
|
void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOCLIENT_EYE_OFFSET);
|
|
|
|
writeV3F1000(os, first);
|
|
|
|
writeV3F1000(os, third);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
2012-03-31 15:23:26 +02:00
|
|
|
void Server::SendPlayerPrivileges(u16 peer_id)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
assert(player);
|
2012-07-19 13:09:16 +02:00
|
|
|
if(player->peer_id == PEER_ID_INEXISTENT)
|
|
|
|
return;
|
|
|
|
|
2012-03-31 15:23:26 +02:00
|
|
|
std::set<std::string> privs;
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->getAuth(player->getName(), NULL, &privs);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-31 15:23:26 +02:00
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_PRIVILEGES);
|
|
|
|
writeU16(os, privs.size());
|
|
|
|
for(std::set<std::string>::const_iterator i = privs.begin();
|
|
|
|
i != privs.end(); i++){
|
|
|
|
os<<serializeString(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2012-03-31 15:23:26 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 13:09:16 +02:00
|
|
|
void Server::SendPlayerInventoryFormspec(u16 peer_id)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
assert(player);
|
|
|
|
if(player->peer_id == PEER_ID_INEXISTENT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
|
2014-06-25 19:04:47 +02:00
|
|
|
os<<serializeLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
|
2012-07-19 13:09:16 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2012-07-19 13:09:16 +02:00
|
|
|
}
|
|
|
|
|
2012-03-24 18:01:26 +01:00
|
|
|
s32 Server::playSound(const SimpleSoundSpec &spec,
|
|
|
|
const ServerSoundParams ¶ms)
|
|
|
|
{
|
|
|
|
// Find out initial position of sound
|
|
|
|
bool pos_exists = false;
|
|
|
|
v3f pos = params.getPos(m_env, &pos_exists);
|
|
|
|
// If position is not found while it should be, cancel sound
|
|
|
|
if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
|
|
|
|
return -1;
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2012-03-24 18:01:26 +01:00
|
|
|
// Filter destination clients
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> dst_clients;
|
2012-03-24 18:01:26 +01:00
|
|
|
if(params.to_player != "")
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(params.to_player.c_str());
|
|
|
|
if(!player){
|
|
|
|
infostream<<"Server::playSound: Player \""<<params.to_player
|
|
|
|
<<"\" not found"<<std::endl;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(player->peer_id == PEER_ID_INEXISTENT){
|
|
|
|
infostream<<"Server::playSound: Player \""<<params.to_player
|
|
|
|
<<"\" not connected"<<std::endl;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
dst_clients.push_back(player->peer_id);
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin(); i != clients.end(); ++i)
|
2012-03-24 18:01:26 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
Player *player = m_env->getPlayer(*i);
|
2012-03-24 18:01:26 +01:00
|
|
|
if(!player)
|
|
|
|
continue;
|
|
|
|
if(pos_exists){
|
|
|
|
if(player->getPosition().getDistanceFrom(pos) >
|
|
|
|
params.max_hear_distance)
|
|
|
|
continue;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
dst_clients.push_back(*i);
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(dst_clients.size() == 0)
|
|
|
|
return -1;
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2012-03-24 18:01:26 +01:00
|
|
|
// Create the sound
|
|
|
|
s32 id = m_next_sound_id++;
|
|
|
|
// The sound will exist as a reference in m_playing_sounds
|
|
|
|
m_playing_sounds[id] = ServerPlayingSound();
|
|
|
|
ServerPlayingSound &psound = m_playing_sounds[id];
|
|
|
|
psound.params = params;
|
2014-01-31 00:24:00 +01:00
|
|
|
for(std::list<u16>::iterator i = dst_clients.begin();
|
2012-03-24 18:01:26 +01:00
|
|
|
i != dst_clients.end(); i++)
|
2014-01-31 00:24:00 +01:00
|
|
|
psound.clients.insert(*i);
|
2012-03-24 18:01:26 +01:00
|
|
|
// Create packet
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_PLAY_SOUND);
|
|
|
|
writeS32(os, id);
|
|
|
|
os<<serializeString(spec.name);
|
|
|
|
writeF1000(os, spec.gain * params.gain);
|
|
|
|
writeU8(os, params.type);
|
|
|
|
writeV3F1000(os, pos);
|
|
|
|
writeU16(os, params.object);
|
|
|
|
writeU8(os, params.loop);
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send
|
2014-01-31 00:24:00 +01:00
|
|
|
for(std::list<u16>::iterator i = dst_clients.begin();
|
2012-03-24 18:01:26 +01:00
|
|
|
i != dst_clients.end(); i++){
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(*i, 0, data, true);
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
void Server::stopSound(s32 handle)
|
|
|
|
{
|
|
|
|
// Get sound reference
|
|
|
|
std::map<s32, ServerPlayingSound>::iterator i =
|
|
|
|
m_playing_sounds.find(handle);
|
|
|
|
if(i == m_playing_sounds.end())
|
|
|
|
return;
|
|
|
|
ServerPlayingSound &psound = i->second;
|
|
|
|
// Create packet
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_STOP_SOUND);
|
|
|
|
writeS32(os, handle);
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send
|
|
|
|
for(std::set<u16>::iterator i = psound.clients.begin();
|
|
|
|
i != psound.clients.end(); i++){
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(*i, 0, data, true);
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
// Remove sound reference
|
|
|
|
m_playing_sounds.erase(i);
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<u16> *far_players, float far_d_nodes)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
float maxd = far_d_nodes*BS;
|
|
|
|
v3f p_f = intToFloat(p, BS);
|
|
|
|
|
|
|
|
// Create packet
|
|
|
|
u32 replysize = 8;
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOCLIENT_REMOVENODE);
|
|
|
|
writeS16(&reply[2], p.X);
|
|
|
|
writeS16(&reply[4], p.Y);
|
|
|
|
writeS16(&reply[6], p.Z);
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
if(far_players)
|
|
|
|
{
|
|
|
|
// Get player
|
2014-01-31 00:24:00 +01:00
|
|
|
Player *player = m_env->getPlayer(*i);
|
2011-04-21 18:35:17 +02:00
|
|
|
if(player)
|
2010-12-25 00:54:39 +01:00
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
// If player is far away, only set modified blocks not sent
|
|
|
|
v3f player_pos = player->getPosition();
|
|
|
|
if(player_pos.getDistanceFrom(p_f) > maxd)
|
2010-12-25 15:04:51 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
far_players->push_back(*i);
|
2011-04-21 18:35:17 +02:00
|
|
|
continue;
|
2010-12-25 15:04:51 +01:00
|
|
|
}
|
2010-12-25 00:54:39 +01:00
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
2010-12-25 00:54:39 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(*i, 0, reply, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
|
2013-11-23 15:35:49 +01:00
|
|
|
std::list<u16> *far_players, float far_d_nodes,
|
|
|
|
bool remove_metadata)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
float maxd = far_d_nodes*BS;
|
|
|
|
v3f p_f = intToFloat(p, BS);
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
if(far_players)
|
|
|
|
{
|
|
|
|
// Get player
|
2014-01-31 00:24:00 +01:00
|
|
|
Player *player = m_env->getPlayer(*i);
|
2011-04-21 18:35:17 +02:00
|
|
|
if(player)
|
2010-12-22 15:30:23 +01:00
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
// If player is far away, only set modified blocks not sent
|
|
|
|
v3f player_pos = player->getPosition();
|
|
|
|
if(player_pos.getDistanceFrom(p_f) > maxd)
|
2010-12-25 15:04:51 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
far_players->push_back(*i);
|
2011-04-21 18:35:17 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
SharedBuffer<u8> reply(0);
|
|
|
|
m_clients.Lock();
|
|
|
|
RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
|
|
|
|
if (client != 0)
|
|
|
|
{
|
|
|
|
// Create packet
|
|
|
|
u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
|
|
|
|
reply = SharedBuffer<u8>(replysize);
|
|
|
|
writeU16(&reply[0], TOCLIENT_ADDNODE);
|
|
|
|
writeS16(&reply[2], p.X);
|
|
|
|
writeS16(&reply[4], p.Y);
|
|
|
|
writeS16(&reply[6], p.Z);
|
|
|
|
n.serialize(&reply[8], client->serialization_version);
|
|
|
|
u32 index = 8 + MapNode::serializedLength(client->serialization_version);
|
|
|
|
writeU8(&reply[index], remove_metadata ? 0 : 1);
|
|
|
|
|
|
|
|
if (!remove_metadata) {
|
|
|
|
if (client->net_proto_version <= 21) {
|
|
|
|
// Old clients always clear metadata; fix it
|
|
|
|
// by sending the full block again.
|
|
|
|
client->SetBlockNotSent(p);
|
|
|
|
}
|
2013-11-23 15:35:49 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
if (reply.getSize() > 0)
|
|
|
|
m_clients.send(*i, 0, reply, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-31 19:02:55 +02:00
|
|
|
void Server::setBlockNotSent(v3s16 p)
|
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
m_clients.Lock();
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2011-05-31 19:02:55 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
|
2011-05-31 19:02:55 +02:00
|
|
|
client->SetBlockNotSent(p);
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-05-31 19:02:55 +02:00
|
|
|
}
|
|
|
|
|
2013-08-01 22:51:36 +02:00
|
|
|
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2011-04-25 23:23:38 +02:00
|
|
|
|
|
|
|
v3s16 p = block->getPos();
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-25 23:23:38 +02:00
|
|
|
#if 0
|
|
|
|
// Analyze it a bit
|
|
|
|
bool completely_air = true;
|
|
|
|
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
|
|
|
|
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
|
|
|
|
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
|
|
|
|
{
|
|
|
|
if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
|
|
|
|
{
|
|
|
|
completely_air = false;
|
|
|
|
x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print result
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
|
2011-04-25 23:23:38 +02:00
|
|
|
if(completely_air)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"[completely air] ";
|
|
|
|
infostream<<std::endl;
|
2011-04-25 23:23:38 +02:00
|
|
|
#endif
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Create a packet with the block in the right format
|
|
|
|
*/
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
std::ostringstream os(std::ios_base::binary);
|
2012-01-21 00:11:44 +01:00
|
|
|
block->serialize(os, ver, false);
|
2013-08-01 22:51:36 +02:00
|
|
|
block->serializeNetworkSpecific(os, net_proto_version);
|
2011-04-21 18:35:17 +02:00
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
|
|
|
|
|
|
|
|
u32 replysize = 8 + blockdata.getSize();
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOCLIENT_BLOCKDATA);
|
|
|
|
writeS16(&reply[2], p.X);
|
|
|
|
writeS16(&reply[4], p.Y);
|
|
|
|
writeS16(&reply[6], p.Z);
|
|
|
|
memcpy(&reply[8], *blockdata, blockdata.getSize());
|
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
|
2011-04-21 18:35:17 +02:00
|
|
|
<<": \tpacket size: "<<replysize<<std::endl;*/
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Send packet
|
|
|
|
*/
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 2, reply, true);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::SendBlocks(float dtime)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
JMutexAutoLock envlock(m_env_mutex);
|
2014-01-31 00:24:00 +01:00
|
|
|
//TODO check if one big lock could be faster then multiple small ones
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2012-03-07 20:54:18 +01:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::vector<PrioritySortedBlockTransfer> queue;
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
s32 total_sending = 0;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
|
|
|
|
m_clients.Lock();
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2011-05-30 23:15:43 +02:00
|
|
|
{
|
2014-06-28 08:02:38 +02:00
|
|
|
RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
|
2011-05-30 23:15:43 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (client == NULL)
|
|
|
|
return;
|
2011-12-02 00:18:25 +01:00
|
|
|
|
2011-05-30 23:15:43 +02:00
|
|
|
total_sending += client->SendingCount();
|
2014-01-31 00:24:00 +01:00
|
|
|
client->GetNextBlocks(m_env,m_emerge, dtime, queue);
|
2011-05-30 23:15:43 +02:00
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sort.
|
|
|
|
// Lowest priority number comes first.
|
|
|
|
// Lowest is most important.
|
2012-12-20 18:19:49 +01:00
|
|
|
std::sort(queue.begin(), queue.end());
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Lock();
|
2011-04-21 18:35:17 +02:00
|
|
|
for(u32 i=0; i<queue.size(); i++)
|
|
|
|
{
|
|
|
|
//TODO: Calculate limit dynamically
|
2011-10-12 12:53:38 +02:00
|
|
|
if(total_sending >= g_settings->getS32
|
2011-04-21 18:35:17 +02:00
|
|
|
("max_simultaneous_block_sends_server_total"))
|
|
|
|
break;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
PrioritySortedBlockTransfer q = queue[i];
|
|
|
|
|
|
|
|
MapBlock *block = NULL;
|
|
|
|
try
|
|
|
|
{
|
2011-11-11 18:33:17 +01:00
|
|
|
block = m_env->getMap().getBlockNoCreate(q.pos);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-28 08:02:38 +02:00
|
|
|
RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2013-08-04 07:17:07 +02:00
|
|
|
if(!client)
|
|
|
|
continue;
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2013-08-01 22:51:36 +02:00
|
|
|
SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
client->SentBlock(q.pos);
|
|
|
|
total_sending++;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.Unlock();
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
void Server::fillMediaCache()
|
2012-03-11 03:15:45 +01:00
|
|
|
{
|
2012-01-02 12:31:50 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
2012-03-25 11:10:58 +02:00
|
|
|
infostream<<"Server: Calculating media file checksums"<<std::endl;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-25 11:10:58 +02:00
|
|
|
// Collect all media file paths
|
|
|
|
std::list<std::string> paths;
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::vector<ModSpec>::iterator i = m_mods.begin();
|
2012-03-11 03:15:45 +01:00
|
|
|
i != m_mods.end(); i++){
|
|
|
|
const ModSpec &mod = *i;
|
2012-03-25 11:10:58 +02:00
|
|
|
paths.push_back(mod.path + DIR_DELIM + "textures");
|
|
|
|
paths.push_back(mod.path + DIR_DELIM + "sounds");
|
|
|
|
paths.push_back(mod.path + DIR_DELIM + "media");
|
2012-10-23 23:11:24 +02:00
|
|
|
paths.push_back(mod.path + DIR_DELIM + "models");
|
2012-03-25 11:10:58 +02:00
|
|
|
}
|
2013-08-02 15:18:48 +02:00
|
|
|
paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-25 11:10:58 +02:00
|
|
|
// Collect media file information from paths into cache
|
|
|
|
for(std::list<std::string>::iterator i = paths.begin();
|
|
|
|
i != paths.end(); i++)
|
|
|
|
{
|
|
|
|
std::string mediapath = *i;
|
|
|
|
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
|
2012-03-11 03:15:45 +01:00
|
|
|
for(u32 j=0; j<dirlist.size(); j++){
|
|
|
|
if(dirlist[j].dir) // Ignode dirs
|
|
|
|
continue;
|
2012-03-25 11:10:58 +02:00
|
|
|
std::string filename = dirlist[j].name;
|
2012-03-30 21:49:44 +02:00
|
|
|
// If name contains illegal characters, ignore the file
|
2012-03-25 11:10:58 +02:00
|
|
|
if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
|
2012-03-30 21:49:44 +02:00
|
|
|
infostream<<"Server: ignoring illegal file name: \""
|
2012-03-25 11:10:58 +02:00
|
|
|
<<filename<<"\""<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
continue;
|
|
|
|
}
|
2012-03-30 21:49:44 +02:00
|
|
|
// If name is not in a supported format, ignore it
|
|
|
|
const char *supported_ext[] = {
|
|
|
|
".png", ".jpg", ".bmp", ".tga",
|
|
|
|
".pcx", ".ppm", ".psd", ".wal", ".rgb",
|
|
|
|
".ogg",
|
2012-10-24 21:10:05 +02:00
|
|
|
".x", ".b3d", ".md2", ".obj",
|
2012-03-30 21:49:44 +02:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
if(removeStringEnd(filename, supported_ext) == ""){
|
|
|
|
infostream<<"Server: ignoring unsupported file extension: \""
|
|
|
|
<<filename<<"\""<<std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Ok, attempt to load the file and add to cache
|
2012-03-25 11:10:58 +02:00
|
|
|
std::string filepath = mediapath + DIR_DELIM + filename;
|
2012-03-11 03:15:45 +01:00
|
|
|
// Read data
|
2012-03-25 11:10:58 +02:00
|
|
|
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
|
2012-03-11 03:15:45 +01:00
|
|
|
if(fis.good() == false){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::fillMediaCache(): Could not open \""
|
2012-03-25 11:10:58 +02:00
|
|
|
<<filename<<"\" for reading"<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::ostringstream tmp_os(std::ios_base::binary);
|
|
|
|
bool bad = false;
|
|
|
|
for(;;){
|
|
|
|
char buf[1024];
|
|
|
|
fis.read(buf, 1024);
|
|
|
|
std::streamsize len = fis.gcount();
|
|
|
|
tmp_os.write(buf, len);
|
|
|
|
if(fis.eof())
|
|
|
|
break;
|
|
|
|
if(!fis.good()){
|
|
|
|
bad = true;
|
|
|
|
break;
|
2012-01-03 23:37:46 +01:00
|
|
|
}
|
2012-03-11 03:15:45 +01:00
|
|
|
}
|
|
|
|
if(bad){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::fillMediaCache(): Failed to read \""
|
2012-03-25 11:10:58 +02:00
|
|
|
<<filename<<"\""<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(tmp_os.str().length() == 0){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::fillMediaCache(): Empty file \""
|
2012-03-25 11:10:58 +02:00
|
|
|
<<filepath<<"\""<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
continue;
|
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
SHA1 sha1;
|
|
|
|
sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
unsigned char *digest = sha1.getDigest();
|
2012-03-25 13:47:51 +02:00
|
|
|
std::string sha1_base64 = base64_encode(digest, 20);
|
|
|
|
std::string sha1_hex = hex_encode((char*)digest, 20);
|
2012-03-11 03:15:45 +01:00
|
|
|
free(digest);
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
// Put in list
|
2012-03-25 13:47:51 +02:00
|
|
|
this->m_media[filename] = MediaInfo(filepath, sha1_base64);
|
|
|
|
verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
|
2012-03-11 03:15:45 +01:00
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
struct SendableMediaAnnouncement
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
std::string sha1_digest;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
SendableMediaAnnouncement(const std::string &name_="",
|
|
|
|
const std::string &sha1_digest_=""):
|
2012-03-25 10:50:29 +02:00
|
|
|
name(name_),
|
|
|
|
sha1_digest(sha1_digest_)
|
|
|
|
{}
|
|
|
|
};
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
void Server::sendMediaAnnouncement(u16 peer_id)
|
|
|
|
{
|
2012-01-02 12:31:50 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
|
2012-03-11 03:15:45 +01:00
|
|
|
<<std::endl;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<SendableMediaAnnouncement> file_announcements;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
|
|
|
|
i != m_media.end(); i++){
|
2012-01-02 12:31:50 +01:00
|
|
|
// Put in list
|
2012-03-25 10:50:29 +02:00
|
|
|
file_announcements.push_back(
|
|
|
|
SendableMediaAnnouncement(i->first, i->second.sha1_digest));
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
// Make packet
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
2012-01-02 12:31:50 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
u16 command
|
2012-03-25 10:50:29 +02:00
|
|
|
u32 number of files
|
2012-01-02 12:31:50 +01:00
|
|
|
for each texture {
|
|
|
|
u16 length of name
|
|
|
|
string name
|
2012-03-25 10:50:29 +02:00
|
|
|
u16 length of sha1_digest
|
2012-01-02 12:31:50 +01:00
|
|
|
string sha1_digest
|
|
|
|
}
|
|
|
|
*/
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
|
|
|
|
writeU16(os, file_announcements.size());
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::list<SendableMediaAnnouncement>::iterator
|
2012-03-25 10:50:29 +02:00
|
|
|
j = file_announcements.begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
j != file_announcements.end(); ++j){
|
2012-01-02 12:31:50 +01:00
|
|
|
os<<serializeString(j->name);
|
|
|
|
os<<serializeString(j->sha1_digest);
|
|
|
|
}
|
2012-12-14 12:30:17 +01:00
|
|
|
os<<serializeString(g_settings->get("remote_media"));
|
2012-01-02 12:31:50 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 0, data, true);
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
struct SendableMedia
|
2011-11-15 10:02:47 +01:00
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
std::string path;
|
|
|
|
std::string data;
|
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
SendableMedia(const std::string &name_="", const std::string &path_="",
|
|
|
|
const std::string &data_=""):
|
2011-11-15 10:02:47 +01:00
|
|
|
name(name_),
|
|
|
|
path(path_),
|
|
|
|
data(data_)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
void Server::sendRequestedMedia(u16 peer_id,
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
const std::list<std::string> &tosend)
|
2012-03-25 10:50:29 +02:00
|
|
|
{
|
2011-11-15 10:02:47 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
verbosestream<<"Server::sendRequestedMedia(): "
|
|
|
|
<<"Sending files to client"<<std::endl;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
/* Read files */
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2011-11-15 22:58:56 +01:00
|
|
|
// Put 5kB in one bunch (this is not accurate)
|
|
|
|
u32 bytes_per_bunch = 5000;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::vector< std::list<SendableMedia> > file_bunches;
|
|
|
|
file_bunches.push_back(std::list<SendableMedia>());
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
u32 file_size_bunch_total = 0;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
for(std::list<std::string>::const_iterator i = tosend.begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
i != tosend.end(); ++i)
|
2012-03-25 10:50:29 +02:00
|
|
|
{
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
const std::string &name = *i;
|
|
|
|
|
|
|
|
if(m_media.find(name) == m_media.end()){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::sendRequestedMedia(): Client asked for "
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
<<"unknown file \""<<(name)<<"\""<<std::endl;
|
2012-01-03 23:37:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
|
|
|
|
//TODO get path + name
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
std::string tpath = m_media[name].path;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
|
|
|
// Read data
|
|
|
|
std::ifstream fis(tpath.c_str(), std::ios_base::binary);
|
|
|
|
if(fis.good() == false){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::sendRequestedMedia(): Could not open \""
|
2012-01-02 12:31:50 +01:00
|
|
|
<<tpath<<"\" for reading"<<std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::ostringstream tmp_os(std::ios_base::binary);
|
|
|
|
bool bad = false;
|
|
|
|
for(;;){
|
|
|
|
char buf[1024];
|
|
|
|
fis.read(buf, 1024);
|
|
|
|
std::streamsize len = fis.gcount();
|
|
|
|
tmp_os.write(buf, len);
|
2012-03-25 10:50:29 +02:00
|
|
|
file_size_bunch_total += len;
|
2012-01-02 12:31:50 +01:00
|
|
|
if(fis.eof())
|
|
|
|
break;
|
|
|
|
if(!fis.good()){
|
|
|
|
bad = true;
|
|
|
|
break;
|
2011-11-15 22:58:56 +01:00
|
|
|
}
|
2011-11-15 10:02:47 +01:00
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
if(bad){
|
2012-03-25 10:50:29 +02:00
|
|
|
errorstream<<"Server::sendRequestedMedia(): Failed to read \""
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
<<name<<"\""<<std::endl;
|
2012-01-02 12:31:50 +01:00
|
|
|
continue;
|
|
|
|
}
|
2012-03-25 10:50:29 +02:00
|
|
|
/*infostream<<"Server::sendRequestedMedia(): Loaded \""
|
2012-01-02 12:31:50 +01:00
|
|
|
<<tname<<"\""<<std::endl;*/
|
|
|
|
// Put in list
|
2012-03-25 10:50:29 +02:00
|
|
|
file_bunches[file_bunches.size()-1].push_back(
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
SendableMedia(name, tpath, tmp_os.str()));
|
2012-01-02 12:31:50 +01:00
|
|
|
|
|
|
|
// Start next bunch if got enough data
|
2012-03-25 10:50:29 +02:00
|
|
|
if(file_size_bunch_total >= bytes_per_bunch){
|
2012-12-20 18:19:49 +01:00
|
|
|
file_bunches.push_back(std::list<SendableMedia>());
|
2012-03-25 10:50:29 +02:00
|
|
|
file_size_bunch_total = 0;
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
|
|
|
|
2011-11-15 10:02:47 +01:00
|
|
|
}
|
|
|
|
|
2011-11-15 22:58:56 +01:00
|
|
|
/* Create and send packets */
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
u32 num_bunches = file_bunches.size();
|
|
|
|
for(u32 i=0; i<num_bunches; i++)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 total number of texture bunches
|
|
|
|
u16 index of this bunch
|
|
|
|
u32 number of files in this bunch
|
|
|
|
for each file {
|
|
|
|
u16 length of name
|
|
|
|
string name
|
|
|
|
u32 length of data
|
|
|
|
data
|
2011-11-15 22:58:56 +01:00
|
|
|
}
|
2012-03-25 10:50:29 +02:00
|
|
|
*/
|
2011-11-15 10:02:47 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
writeU16(os, TOCLIENT_MEDIA);
|
|
|
|
writeU16(os, num_bunches);
|
|
|
|
writeU16(os, i);
|
|
|
|
writeU32(os, file_bunches[i].size());
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::list<SendableMedia>::iterator
|
2012-03-25 10:50:29 +02:00
|
|
|
j = file_bunches[i].begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
j != file_bunches[i].end(); ++j){
|
2012-03-25 10:50:29 +02:00
|
|
|
os<<serializeString(j->name);
|
|
|
|
os<<serializeLongString(j->data);
|
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
verbosestream<<"Server::sendRequestedMedia(): bunch "
|
|
|
|
<<i<<"/"<<num_bunches
|
|
|
|
<<" files="<<file_bunches[i].size()
|
|
|
|
<<" size=" <<s.size()<<std::endl;
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-31 00:24:00 +01:00
|
|
|
m_clients.send(peer_id, 2, data, true);
|
2012-03-25 10:50:29 +02:00
|
|
|
}
|
2011-11-15 10:02:47 +01:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:57:17 +02:00
|
|
|
void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
|
|
|
|
{
|
|
|
|
if(m_detached_inventories.count(name) == 0){
|
|
|
|
errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Inventory *inv = m_detached_inventories[name];
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOCLIENT_DETACHED_INVENTORY);
|
|
|
|
os<<serializeString(name);
|
|
|
|
inv->serialize(os);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
if (peer_id != PEER_ID_INEXISTENT)
|
|
|
|
{
|
|
|
|
// Send as reliable
|
|
|
|
m_clients.send(peer_id, 0, data, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_clients.sendToAll(0,data,true);
|
2012-07-24 19:57:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::sendDetachedInventories(u16 peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
for(std::map<std::string, Inventory*>::iterator
|
|
|
|
i = m_detached_inventories.begin();
|
|
|
|
i != m_detached_inventories.end(); i++){
|
|
|
|
const std::string &name = i->first;
|
|
|
|
//Inventory *inv = i->second;
|
|
|
|
sendDetachedInventory(name, peer_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Something random
|
|
|
|
*/
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
void Server::DiePlayer(u16 peer_id)
|
2011-10-15 13:46:59 +02:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
|
|
|
assert(playersao);
|
2011-12-01 22:33:48 +01:00
|
|
|
|
2012-01-24 00:00:26 +01:00
|
|
|
infostream<<"Server::DiePlayer(): Player "
|
2012-03-19 03:04:16 +01:00
|
|
|
<<playersao->getPlayer()->getName()
|
|
|
|
<<" dies"<<std::endl;
|
|
|
|
|
|
|
|
playersao->setHP(0);
|
|
|
|
|
2012-01-05 23:12:33 +01:00
|
|
|
// Trigger scripted stuff
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->on_dieplayer(playersao);
|
2011-12-02 16:19:42 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
SendPlayerHP(peer_id);
|
2014-01-31 00:24:00 +01:00
|
|
|
SendDeathscreen(peer_id, false, v3f(0,0,0));
|
2011-10-15 13:46:59 +02:00
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
void Server::RespawnPlayer(u16 peer_id)
|
2011-10-15 13:46:59 +02:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
PlayerSAO *playersao = getPlayerSAO(peer_id);
|
|
|
|
assert(playersao);
|
|
|
|
|
|
|
|
infostream<<"Server::RespawnPlayer(): Player "
|
|
|
|
<<playersao->getPlayer()->getName()
|
|
|
|
<<" respawns"<<std::endl;
|
|
|
|
|
|
|
|
playersao->setHP(PLAYER_MAX_HP);
|
|
|
|
|
2013-05-25 00:51:02 +02:00
|
|
|
bool repositioned = m_script->on_respawnplayer(playersao);
|
2011-11-26 02:20:19 +01:00
|
|
|
if(!repositioned){
|
|
|
|
v3f pos = findSpawnPos(m_env->getServerMap());
|
2012-03-19 03:04:16 +01:00
|
|
|
playersao->setPos(pos);
|
2011-11-26 02:20:19 +01:00
|
|
|
}
|
2011-10-15 13:46:59 +02:00
|
|
|
}
|
|
|
|
|
2013-08-04 07:17:07 +02:00
|
|
|
void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
SendAccessDenied(peer_id, reason);
|
2014-06-28 08:02:38 +02:00
|
|
|
m_clients.event(peer_id, CSE_SetDenied);
|
2014-01-31 00:24:00 +01:00
|
|
|
m_con.DisconnectPeer(peer_id);
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2014-01-31 00:24:00 +01:00
|
|
|
std::wstring message;
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
/*
|
|
|
|
Clear references to playing sounds
|
|
|
|
*/
|
|
|
|
for(std::map<s32, ServerPlayingSound>::iterator
|
|
|
|
i = m_playing_sounds.begin();
|
|
|
|
i != m_playing_sounds.end();)
|
|
|
|
{
|
|
|
|
ServerPlayingSound &psound = i->second;
|
|
|
|
psound.clients.erase(peer_id);
|
|
|
|
if(psound.clients.size() == 0)
|
|
|
|
m_playing_sounds.erase(i++);
|
|
|
|
else
|
|
|
|
i++;
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
2013-08-04 07:17:07 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
// Collect information about leaving in chat
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
if(player != NULL && reason != CDR_DENY)
|
|
|
|
{
|
|
|
|
std::wstring name = narrow_to_wide(player->getName());
|
|
|
|
message += L"*** ";
|
|
|
|
message += name;
|
|
|
|
message += L" left the game.";
|
|
|
|
if(reason == CDR_TIMEOUT)
|
|
|
|
message += L" (timed out)";
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
/* Run scripts and remove from environment */
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
if(player != NULL)
|
|
|
|
{
|
|
|
|
PlayerSAO *playersao = player->getPlayerSAO();
|
|
|
|
assert(playersao);
|
2013-08-04 07:17:07 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
m_script->on_leaveplayer(playersao);
|
2013-08-04 07:17:07 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
playersao->disconnected();
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
/*
|
|
|
|
Print out action
|
|
|
|
*/
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
if(player != NULL && reason != CDR_DENY)
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
|
|
|
{
|
|
|
|
// Get player
|
|
|
|
Player *player = m_env->getPlayer(*i);
|
|
|
|
if(!player)
|
|
|
|
continue;
|
|
|
|
// Get name of player
|
|
|
|
os<<player->getName()<<" ";
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
actionstream<<player->getName()<<" "
|
|
|
|
<<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
|
|
|
|
<<" List of players: "<<os.str()<<std::endl;
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
2014-02-12 19:47:27 +01:00
|
|
|
{
|
|
|
|
JMutexAutoLock env_lock(m_env_mutex);
|
|
|
|
m_clients.DeleteClient(peer_id);
|
|
|
|
}
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send leave chat message to all remaining clients
|
|
|
|
if(message.length() != 0)
|
2014-01-31 00:24:00 +01:00
|
|
|
SendChatMessage(PEER_ID_INEXISTENT,message);
|
2013-08-04 07:17:07 +02:00
|
|
|
}
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
void Server::UpdateCrafting(u16 peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-11 18:33:17 +01:00
|
|
|
Player* player = m_env->getPlayer(peer_id);
|
2011-04-21 18:35:17 +02:00
|
|
|
assert(player);
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// Get a preview for crafting
|
|
|
|
ItemStack preview;
|
2013-10-26 11:25:28 +02:00
|
|
|
InventoryLocation loc;
|
|
|
|
loc.setPlayer(player->getName());
|
2012-07-25 13:07:45 +02:00
|
|
|
getCraftingResult(&player->inventory, preview, false, this);
|
2013-10-26 11:25:28 +02:00
|
|
|
m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
// Put the new preview in
|
|
|
|
InventoryList *plist = player->inventory.getList("craftpreview");
|
|
|
|
assert(plist);
|
|
|
|
assert(plist->getSize() >= 1);
|
|
|
|
plist->changeItem(0, preview);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
RemoteClient *client = getClientNoEx(peer_id,state_min);
|
2013-08-04 07:17:07 +02:00
|
|
|
if(!client)
|
|
|
|
throw ClientNotFoundException("Client not found");
|
2014-01-31 00:24:00 +01:00
|
|
|
|
2013-08-04 07:17:07 +02:00
|
|
|
return client;
|
|
|
|
}
|
2014-01-31 00:24:00 +01:00
|
|
|
RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
|
2013-08-04 07:17:07 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
return m_clients.getClientNoEx(peer_id, state_min);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
std::string Server::getPlayerName(u16 peer_id)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
if(player == NULL)
|
|
|
|
return "[id="+itos(peer_id)+"]";
|
|
|
|
return player->getName();
|
|
|
|
}
|
|
|
|
|
|
|
|
PlayerSAO* Server::getPlayerSAO(u16 peer_id)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(peer_id);
|
|
|
|
if(player == NULL)
|
|
|
|
return NULL;
|
|
|
|
return player->getPlayerSAO();
|
|
|
|
}
|
|
|
|
|
2011-02-15 15:11:24 +01:00
|
|
|
std::wstring Server::getStatusString()
|
|
|
|
{
|
|
|
|
std::wostringstream os(std::ios_base::binary);
|
|
|
|
os<<L"# Server: ";
|
2011-04-24 23:24:40 +02:00
|
|
|
// Version
|
2013-09-25 04:29:07 +02:00
|
|
|
os<<L"version="<<narrow_to_wide(minetest_version_simple);
|
2011-02-15 15:11:24 +01:00
|
|
|
// Uptime
|
2011-04-24 23:24:40 +02:00
|
|
|
os<<L", uptime="<<m_uptime.get();
|
2013-08-03 22:16:37 +02:00
|
|
|
// Max lag estimate
|
|
|
|
os<<L", max_lag="<<m_env->getMaxLagEstimate();
|
2011-02-15 15:11:24 +01:00
|
|
|
// Information about clients
|
2014-01-31 00:24:00 +01:00
|
|
|
bool first = true;
|
2011-02-15 15:11:24 +01:00
|
|
|
os<<L", clients={";
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
for(std::list<u16>::iterator i = clients.begin();
|
|
|
|
i != clients.end(); ++i)
|
2011-02-15 15:11:24 +01:00
|
|
|
{
|
|
|
|
// Get player
|
2014-01-31 00:24:00 +01:00
|
|
|
Player *player = m_env->getPlayer(*i);
|
2011-02-15 15:11:24 +01:00
|
|
|
// Get name of player
|
|
|
|
std::wstring name = L"unknown";
|
|
|
|
if(player != NULL)
|
|
|
|
name = narrow_to_wide(player->getName());
|
|
|
|
// Add name to information string
|
2012-07-28 21:49:23 +02:00
|
|
|
if(!first)
|
|
|
|
os<<L",";
|
|
|
|
else
|
|
|
|
first = false;
|
|
|
|
os<<name;
|
2011-02-15 15:11:24 +01:00
|
|
|
}
|
|
|
|
os<<L"}";
|
2011-11-11 18:33:17 +01:00
|
|
|
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
|
2011-07-30 18:49:42 +02:00
|
|
|
os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
|
2011-10-12 12:53:38 +02:00
|
|
|
if(g_settings->get("motd") != "")
|
|
|
|
os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
|
2011-02-15 15:11:24 +01:00
|
|
|
return os.str();
|
|
|
|
}
|
|
|
|
|
2012-03-30 17:42:18 +02:00
|
|
|
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
|
2012-03-15 14:27:16 +01:00
|
|
|
{
|
2012-03-30 17:42:18 +02:00
|
|
|
std::set<std::string> privs;
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->getAuth(name, NULL, &privs);
|
2012-03-30 17:42:18 +02:00
|
|
|
return privs;
|
2012-03-15 14:27:16 +01:00
|
|
|
}
|
|
|
|
|
2012-03-30 17:42:18 +02:00
|
|
|
bool Server::checkPriv(const std::string &name, const std::string &priv)
|
2011-11-20 20:16:15 +01:00
|
|
|
{
|
2012-03-30 17:42:18 +02:00
|
|
|
std::set<std::string> privs = getPlayerEffectivePrivs(name);
|
|
|
|
return (privs.count(priv) != 0);
|
2011-11-20 20:16:15 +01:00
|
|
|
}
|
|
|
|
|
2012-03-31 15:23:26 +02:00
|
|
|
void Server::reportPrivsModified(const std::string &name)
|
|
|
|
{
|
|
|
|
if(name == ""){
|
2014-01-31 00:24:00 +01:00
|
|
|
std::list<u16> clients = m_clients.getClientIDs();
|
|
|
|
for(std::list<u16>::iterator
|
|
|
|
i = clients.begin();
|
|
|
|
i != clients.end(); ++i){
|
|
|
|
Player *player = m_env->getPlayer(*i);
|
2012-03-31 16:08:39 +02:00
|
|
|
reportPrivsModified(player->getName());
|
2012-03-31 15:23:26 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Player *player = m_env->getPlayer(name.c_str());
|
|
|
|
if(!player)
|
|
|
|
return;
|
|
|
|
SendPlayerPrivileges(player->peer_id);
|
2012-04-07 17:46:10 +02:00
|
|
|
PlayerSAO *sao = player->getPlayerSAO();
|
|
|
|
if(!sao)
|
|
|
|
return;
|
|
|
|
sao->updatePrivileges(
|
2012-04-01 12:19:50 +02:00
|
|
|
getPlayerEffectivePrivs(name),
|
|
|
|
isSingleplayer());
|
2012-03-31 15:23:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-19 13:09:16 +02:00
|
|
|
void Server::reportInventoryFormspecModified(const std::string &name)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(name.c_str());
|
|
|
|
if(!player)
|
|
|
|
return;
|
|
|
|
SendPlayerInventoryFormspec(player->peer_id);
|
|
|
|
}
|
|
|
|
|
2013-08-11 04:09:45 +02:00
|
|
|
void Server::setIpBanned(const std::string &ip, const std::string &name)
|
2011-10-12 12:53:38 +02:00
|
|
|
{
|
2013-08-11 04:09:45 +02:00
|
|
|
m_banmanager->add(ip, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::unsetIpBanned(const std::string &ip_or_name)
|
|
|
|
{
|
|
|
|
m_banmanager->remove(ip_or_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Server::getBanDescription(const std::string &ip_or_name)
|
|
|
|
{
|
|
|
|
return m_banmanager->getBanDescription(ip_or_name);
|
2011-10-12 12:53:38 +02:00
|
|
|
}
|
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
void Server::notifyPlayer(const char *name, const std::wstring &msg)
|
2011-10-16 19:03:43 +02:00
|
|
|
{
|
2011-11-11 18:33:17 +01:00
|
|
|
Player *player = m_env->getPlayer(name);
|
2011-10-16 19:03:43 +02:00
|
|
|
if(!player)
|
|
|
|
return;
|
2014-02-02 01:55:24 +01:00
|
|
|
|
|
|
|
if (player->peer_id == PEER_ID_INEXISTENT)
|
|
|
|
return;
|
|
|
|
|
2014-02-25 02:41:40 +01:00
|
|
|
SendChatMessage(player->peer_id, msg);
|
2011-10-16 19:03:43 +02:00
|
|
|
}
|
|
|
|
|
2013-01-03 18:59:28 +01:00
|
|
|
bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
|
2013-01-02 20:45:04 +01:00
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(playername);
|
|
|
|
|
|
|
|
if(!player)
|
|
|
|
{
|
|
|
|
infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-01-03 18:59:28 +01:00
|
|
|
SendShowFormspecMessage(player->peer_id, formspec, formname);
|
2013-01-02 20:45:04 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
u32 Server::hudAdd(Player *player, HudElement *form) {
|
|
|
|
if (!player)
|
|
|
|
return -1;
|
|
|
|
|
2014-05-25 14:34:32 +02:00
|
|
|
u32 id = player->addHud(form);
|
|
|
|
|
2013-04-11 20:23:38 +02:00
|
|
|
SendHUDAdd(player->peer_id, id, form);
|
2014-05-25 14:34:32 +02:00
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
return id;
|
2013-04-11 20:23:38 +02:00
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
bool Server::hudRemove(Player *player, u32 id) {
|
2014-05-25 14:34:32 +02:00
|
|
|
if (!player)
|
2013-04-11 20:23:38 +02:00
|
|
|
return false;
|
|
|
|
|
2014-05-25 14:34:32 +02:00
|
|
|
HudElement* todel = player->removeHud(id);
|
|
|
|
|
|
|
|
if (!todel)
|
|
|
|
return false;
|
2013-04-14 00:20:22 +02:00
|
|
|
|
2014-05-25 14:34:32 +02:00
|
|
|
delete todel;
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
SendHUDRemove(player->peer_id, id);
|
2013-04-11 20:23:38 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
|
|
|
|
if (!player)
|
2013-04-11 20:23:38 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
SendHUDChange(player->peer_id, id, stat, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-04-26 01:27:22 +02:00
|
|
|
bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
|
2013-04-24 12:52:46 +02:00
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
2013-04-26 01:27:22 +02:00
|
|
|
SendHUDSetFlags(player->peer_id, flags, mask);
|
2014-05-11 00:35:31 +02:00
|
|
|
player->hud_flags = flags;
|
2014-08-21 17:27:52 +02:00
|
|
|
|
|
|
|
PlayerSAO* playersao = player->getPlayerSAO();
|
|
|
|
|
|
|
|
if (playersao == NULL)
|
|
|
|
return false;
|
2014-04-28 23:41:27 +02:00
|
|
|
|
2014-08-21 17:27:52 +02:00
|
|
|
m_script->player_event(playersao, "hud_changed");
|
2013-04-24 12:52:46 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-04 02:08:52 +02:00
|
|
|
bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios::binary);
|
|
|
|
writeS32(os, hotbar_itemcount);
|
|
|
|
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-03 19:51:40 +02:00
|
|
|
void Server::hudSetHotbarImage(Player *player, std::string name) {
|
|
|
|
if (!player)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
|
|
|
|
if (!player)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
|
|
|
|
}
|
|
|
|
|
2014-04-12 13:50:22 +02:00
|
|
|
bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed)
|
2014-01-08 13:47:53 +01:00
|
|
|
{
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-04-11 15:32:46 +02:00
|
|
|
bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
|
|
|
|
{
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SendEyeOffset(player->peer_id, first, third);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-02 22:52:50 +02:00
|
|
|
bool Server::setSky(Player *player, const video::SColor &bgcolor,
|
|
|
|
const std::string &type, const std::vector<std::string> ¶ms)
|
|
|
|
{
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SendSetSky(player->peer_id, bgcolor, type, params);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-18 10:53:19 +02:00
|
|
|
bool Server::overrideDayNightRatio(Player *player, bool do_override,
|
|
|
|
float ratio)
|
|
|
|
{
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
void Server::notifyPlayers(const std::wstring &msg)
|
2011-10-17 23:01:50 +02:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
SendChatMessage(PEER_ID_INEXISTENT,msg);
|
2011-10-17 23:01:50 +02:00
|
|
|
}
|
|
|
|
|
2013-01-23 18:32:02 +01:00
|
|
|
void Server::spawnParticle(const char *playername, v3f pos,
|
|
|
|
v3f velocity, v3f acceleration,
|
|
|
|
float expirationtime, float size, bool
|
2013-04-22 20:35:10 +02:00
|
|
|
collisiondetection, bool vertical, std::string texture)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(playername);
|
|
|
|
if(!player)
|
|
|
|
return;
|
|
|
|
SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
|
2013-04-22 20:35:10 +02:00
|
|
|
expirationtime, size, collisiondetection, vertical, texture);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
|
|
|
|
float expirationtime, float size,
|
2013-04-22 20:35:10 +02:00
|
|
|
bool collisiondetection, bool vertical, std::string texture)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
2014-01-31 00:24:00 +01:00
|
|
|
SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
|
2013-04-22 20:35:10 +02:00
|
|
|
expirationtime, size, collisiondetection, vertical, texture);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 Server::addParticleSpawner(const char *playername,
|
|
|
|
u16 amount, float spawntime,
|
|
|
|
v3f minpos, v3f maxpos,
|
|
|
|
v3f minvel, v3f maxvel,
|
|
|
|
v3f minacc, v3f maxacc,
|
|
|
|
float minexptime, float maxexptime,
|
|
|
|
float minsize, float maxsize,
|
2013-04-22 20:35:10 +02:00
|
|
|
bool collisiondetection, bool vertical, std::string texture)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(playername);
|
|
|
|
if(!player)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
u32 id = 0;
|
|
|
|
for(;;) // look for unused particlespawner id
|
|
|
|
{
|
|
|
|
id++;
|
|
|
|
if (std::find(m_particlespawner_ids.begin(),
|
|
|
|
m_particlespawner_ids.end(), id)
|
|
|
|
== m_particlespawner_ids.end())
|
|
|
|
{
|
|
|
|
m_particlespawner_ids.push_back(id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SendAddParticleSpawner(player->peer_id, amount, spawntime,
|
|
|
|
minpos, maxpos, minvel, maxvel, minacc, maxacc,
|
|
|
|
minexptime, maxexptime, minsize, maxsize,
|
2013-04-22 20:35:10 +02:00
|
|
|
collisiondetection, vertical, texture, id);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
|
|
|
|
v3f minpos, v3f maxpos,
|
|
|
|
v3f minvel, v3f maxvel,
|
|
|
|
v3f minacc, v3f maxacc,
|
|
|
|
float minexptime, float maxexptime,
|
|
|
|
float minsize, float maxsize,
|
2013-04-22 20:35:10 +02:00
|
|
|
bool collisiondetection, bool vertical, std::string texture)
|
2013-01-23 18:32:02 +01:00
|
|
|
{
|
|
|
|
u32 id = 0;
|
|
|
|
for(;;) // look for unused particlespawner id
|
|
|
|
{
|
|
|
|
id++;
|
|
|
|
if (std::find(m_particlespawner_ids.begin(),
|
|
|
|
m_particlespawner_ids.end(), id)
|
|
|
|
== m_particlespawner_ids.end())
|
|
|
|
{
|
|
|
|
m_particlespawner_ids.push_back(id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 00:24:00 +01:00
|
|
|
SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
|
2013-01-23 18:32:02 +01:00
|
|
|
minpos, maxpos, minvel, maxvel, minacc, maxacc,
|
|
|
|
minexptime, maxexptime, minsize, maxsize,
|
2013-04-22 20:35:10 +02:00
|
|
|
collisiondetection, vertical, texture, id);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::deleteParticleSpawner(const char *playername, u32 id)
|
|
|
|
{
|
|
|
|
Player *player = m_env->getPlayer(playername);
|
|
|
|
if(!player)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_particlespawner_ids.erase(
|
|
|
|
std::remove(m_particlespawner_ids.begin(),
|
|
|
|
m_particlespawner_ids.end(), id),
|
|
|
|
m_particlespawner_ids.end());
|
|
|
|
SendDeleteParticleSpawner(player->peer_id, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::deleteParticleSpawnerAll(u32 id)
|
|
|
|
{
|
|
|
|
m_particlespawner_ids.erase(
|
|
|
|
std::remove(m_particlespawner_ids.begin(),
|
|
|
|
m_particlespawner_ids.end(), id),
|
|
|
|
m_particlespawner_ids.end());
|
2014-01-31 00:24:00 +01:00
|
|
|
SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:57:17 +02:00
|
|
|
Inventory* Server::createDetachedInventory(const std::string &name)
|
|
|
|
{
|
|
|
|
if(m_detached_inventories.count(name) > 0){
|
|
|
|
infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
|
|
|
|
delete m_detached_inventories[name];
|
|
|
|
} else {
|
|
|
|
infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
|
|
|
|
}
|
|
|
|
Inventory *inv = new Inventory(m_itemdef);
|
|
|
|
assert(inv);
|
|
|
|
m_detached_inventories[name] = inv;
|
2014-01-31 00:24:00 +01:00
|
|
|
//TODO find a better way to do this
|
|
|
|
sendDetachedInventory(name,PEER_ID_INEXISTENT);
|
2012-07-24 19:57:17 +02:00
|
|
|
return inv;
|
|
|
|
}
|
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
class BoolScopeSet
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BoolScopeSet(bool *dst, bool val):
|
|
|
|
m_dst(dst)
|
|
|
|
{
|
|
|
|
m_orig_state = *m_dst;
|
|
|
|
*m_dst = val;
|
|
|
|
}
|
|
|
|
~BoolScopeSet()
|
|
|
|
{
|
|
|
|
*m_dst = m_orig_state;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
bool *m_dst;
|
|
|
|
bool m_orig_state;
|
|
|
|
};
|
|
|
|
|
|
|
|
// actions: time-reversed list
|
|
|
|
// Return value: success/failure
|
|
|
|
bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
|
|
|
|
std::list<std::string> *log)
|
|
|
|
{
|
|
|
|
infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
|
|
|
|
ServerMap *map = (ServerMap*)(&m_env->getMap());
|
|
|
|
// Disable rollback report sink while reverting
|
|
|
|
BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
// Fail if no actions to handle
|
|
|
|
if(actions.empty()){
|
|
|
|
log->push_back("Nothing to do.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int num_tried = 0;
|
|
|
|
int num_failed = 0;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
for(std::list<RollbackAction>::const_iterator
|
|
|
|
i = actions.begin();
|
|
|
|
i != actions.end(); i++)
|
|
|
|
{
|
|
|
|
const RollbackAction &action = *i;
|
|
|
|
num_tried++;
|
|
|
|
bool success = action.applyRevert(map, this, this);
|
|
|
|
if(!success){
|
|
|
|
num_failed++;
|
|
|
|
std::ostringstream os;
|
|
|
|
os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
|
|
|
|
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
|
|
|
|
if(log)
|
|
|
|
log->push_back(os.str());
|
|
|
|
}else{
|
|
|
|
std::ostringstream os;
|
2012-12-17 16:46:54 +01:00
|
|
|
os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
|
2012-07-26 21:06:45 +02:00
|
|
|
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
|
|
|
|
if(log)
|
|
|
|
log->push_back(os.str());
|
|
|
|
}
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-07-26 21:06:45 +02:00
|
|
|
infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
|
|
|
|
<<" failed"<<std::endl;
|
|
|
|
|
|
|
|
// Call it done if less than half failed
|
|
|
|
return num_failed <= num_tried/2;
|
|
|
|
}
|
|
|
|
|
2011-11-14 20:41:30 +01:00
|
|
|
// IGameDef interface
|
|
|
|
// Under envlock
|
2012-01-12 06:10:39 +01:00
|
|
|
IItemDefManager* Server::getItemDefManager()
|
2011-11-14 20:41:30 +01:00
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
return m_itemdef;
|
2011-11-14 20:41:30 +01:00
|
|
|
}
|
|
|
|
INodeDefManager* Server::getNodeDefManager()
|
|
|
|
{
|
2011-11-16 13:08:31 +01:00
|
|
|
return m_nodedef;
|
2011-11-14 20:41:30 +01:00
|
|
|
}
|
2011-11-17 01:28:46 +01:00
|
|
|
ICraftDefManager* Server::getCraftDefManager()
|
|
|
|
{
|
|
|
|
return m_craftdef;
|
|
|
|
}
|
2011-11-14 20:41:30 +01:00
|
|
|
ITextureSource* Server::getTextureSource()
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
IShaderSource* Server::getShaderSource()
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-11-16 12:03:28 +01:00
|
|
|
u16 Server::allocateUnknownNodeId(const std::string &name)
|
|
|
|
{
|
2011-11-16 13:08:31 +01:00
|
|
|
return m_nodedef->allocateDummy(name);
|
2011-11-16 12:03:28 +01:00
|
|
|
}
|
2012-03-23 11:05:17 +01:00
|
|
|
ISoundManager* Server::getSoundManager()
|
|
|
|
{
|
|
|
|
return &dummySoundManager;
|
|
|
|
}
|
2012-03-23 19:23:03 +01:00
|
|
|
MtEventManager* Server::getEventManager()
|
|
|
|
{
|
|
|
|
return m_event;
|
|
|
|
}
|
2012-07-26 21:06:45 +02:00
|
|
|
IRollbackReportSink* Server::getRollbackReportSink()
|
|
|
|
{
|
2012-07-28 02:08:09 +02:00
|
|
|
if(!m_enable_rollback_recording)
|
|
|
|
return NULL;
|
2012-07-26 21:06:45 +02:00
|
|
|
if(!m_rollback_sink_enabled)
|
|
|
|
return NULL;
|
|
|
|
return m_rollback;
|
|
|
|
}
|
2011-11-14 20:41:30 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
IWritableItemDefManager* Server::getWritableItemDefManager()
|
2011-11-15 01:03:28 +01:00
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
return m_itemdef;
|
2011-11-15 01:03:28 +01:00
|
|
|
}
|
|
|
|
IWritableNodeDefManager* Server::getWritableNodeDefManager()
|
|
|
|
{
|
2011-11-16 13:08:31 +01:00
|
|
|
return m_nodedef;
|
2011-11-15 01:03:28 +01:00
|
|
|
}
|
2011-11-17 01:28:46 +01:00
|
|
|
IWritableCraftDefManager* Server::getWritableCraftDefManager()
|
|
|
|
{
|
|
|
|
return m_craftdef;
|
|
|
|
}
|
2011-11-15 01:03:28 +01:00
|
|
|
|
2011-12-11 15:49:40 +01:00
|
|
|
const ModSpec* Server::getModSpec(const std::string &modname)
|
|
|
|
{
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::vector<ModSpec>::iterator i = m_mods.begin();
|
2011-12-11 15:49:40 +01:00
|
|
|
i != m_mods.end(); i++){
|
|
|
|
const ModSpec &mod = *i;
|
|
|
|
if(mod.name == modname)
|
|
|
|
return &mod;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-12-20 18:19:49 +01:00
|
|
|
void Server::getModNames(std::list<std::string> &modlist)
|
2012-07-22 03:29:37 +02:00
|
|
|
{
|
2012-12-08 18:10:54 +01:00
|
|
|
for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
|
2012-07-22 03:29:37 +02:00
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
modlist.push_back(i->name);
|
2012-07-22 03:29:37 +02:00
|
|
|
}
|
|
|
|
}
|
2012-04-01 09:08:52 +02:00
|
|
|
std::string Server::getBuiltinLuaPath()
|
|
|
|
{
|
|
|
|
return porting::path_share + DIR_DELIM + "builtin";
|
|
|
|
}
|
2011-12-11 15:49:40 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
v3f findSpawnPos(ServerMap &map)
|
|
|
|
{
|
2011-04-26 14:38:42 +02:00
|
|
|
//return v3f(50,50,50)*BS;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2011-11-13 02:17:42 +01:00
|
|
|
v3s16 nodepos;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
#if 0
|
|
|
|
nodepos = v2s16(0,0);
|
|
|
|
groundheight = 20;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 1
|
2014-02-04 04:42:10 +01:00
|
|
|
s16 water_level = map.getWaterLevel();
|
2012-10-29 04:41:48 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
// Try to find a good place a few times
|
|
|
|
for(s32 i=0; i<1000; i++)
|
|
|
|
{
|
|
|
|
s32 range = 1 + i;
|
|
|
|
// We're going to try to throw the player to this position
|
2013-04-21 21:39:34 +02:00
|
|
|
v2s16 nodepos2d = v2s16(
|
|
|
|
-range + (myrand() % (range * 2)),
|
|
|
|
-range + (myrand() % (range * 2)));
|
|
|
|
|
|
|
|
// Get ground height at point
|
2014-01-07 03:17:19 +01:00
|
|
|
s16 groundheight = map.findGroundLevel(nodepos2d);
|
2013-04-21 21:39:34 +02:00
|
|
|
if (groundheight <= water_level) // Don't go underwater
|
2011-04-21 18:35:17 +02:00
|
|
|
continue;
|
2014-01-07 03:17:19 +01:00
|
|
|
if (groundheight > water_level + 6) // Don't go to high places
|
2011-04-21 18:35:17 +02:00
|
|
|
continue;
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2013-04-21 21:39:34 +02:00
|
|
|
nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
|
2011-11-13 02:17:42 +01:00
|
|
|
bool is_good = false;
|
|
|
|
s32 air_count = 0;
|
2013-04-21 21:39:34 +02:00
|
|
|
for (s32 i = 0; i < 10; i++) {
|
2011-11-13 02:17:42 +01:00
|
|
|
v3s16 blockpos = getNodeBlockPos(nodepos);
|
|
|
|
map.emergeBlock(blockpos, true);
|
2013-04-21 21:39:34 +02:00
|
|
|
content_t c = map.getNodeNoEx(nodepos).getContent();
|
|
|
|
if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
|
2011-11-13 02:17:42 +01:00
|
|
|
air_count++;
|
2013-04-21 21:39:34 +02:00
|
|
|
if (air_count >= 2){
|
2011-11-13 02:17:42 +01:00
|
|
|
is_good = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nodepos.Y++;
|
|
|
|
}
|
|
|
|
if(is_good){
|
|
|
|
// Found a good place
|
|
|
|
//infostream<<"Searched through "<<i<<" places."<<std::endl;
|
|
|
|
break;
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
#endif
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-11-13 02:17:42 +01:00
|
|
|
return intToFloat(nodepos, BS);
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
|
2011-01-15 02:28:19 +01:00
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
RemotePlayer *player = NULL;
|
|
|
|
bool newplayer = false;
|
|
|
|
|
2011-01-15 02:28:19 +01:00
|
|
|
/*
|
|
|
|
Try to get an existing player
|
|
|
|
*/
|
2012-03-19 03:04:16 +01:00
|
|
|
player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
|
2012-01-24 00:00:26 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
// If player is already connected, cancel
|
|
|
|
if(player != NULL && player->peer_id != 0)
|
|
|
|
{
|
|
|
|
infostream<<"emergePlayer(): Player already connected"<<std::endl;
|
|
|
|
return NULL;
|
2011-01-15 02:28:19 +01:00
|
|
|
}
|
2011-01-17 23:26:09 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
If player with the wanted peer_id already exists, cancel.
|
|
|
|
*/
|
2011-11-11 18:33:17 +01:00
|
|
|
if(m_env->getPlayer(peer_id) != NULL)
|
2011-01-17 23:26:09 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"emergePlayer(): Player with wrong name but same"
|
2011-01-17 23:26:09 +01:00
|
|
|
" peer_id already exists"<<std::endl;
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-03-19 03:04:16 +01:00
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Load player if it isn't already loaded
|
|
|
|
if (!player) {
|
|
|
|
player = static_cast<RemotePlayer*>(m_env->loadPlayer(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create player if it doesn't exist
|
|
|
|
if (!player) {
|
2012-03-19 03:04:16 +01:00
|
|
|
newplayer = true;
|
|
|
|
player = new RemotePlayer(this);
|
|
|
|
player->updateName(name);
|
2011-11-26 02:20:19 +01:00
|
|
|
/* Set player position */
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Server: Finding spawn place for player \""
|
2011-11-12 14:41:29 +01:00
|
|
|
<<name<<"\""<<std::endl;
|
2011-11-11 18:33:17 +01:00
|
|
|
v3f pos = findSpawnPos(m_env->getServerMap());
|
2012-03-19 03:04:16 +01:00
|
|
|
player->setPosition(pos);
|
2011-01-15 02:28:19 +01:00
|
|
|
|
2011-11-26 02:20:19 +01:00
|
|
|
/* Add player to environment */
|
2011-11-11 18:33:17 +01:00
|
|
|
m_env->addPlayer(player);
|
2012-03-19 03:04:16 +01:00
|
|
|
}
|
2011-01-15 02:28:19 +01:00
|
|
|
|
2014-05-30 22:04:07 +02:00
|
|
|
// Create a new player active object
|
2012-03-31 16:08:39 +02:00
|
|
|
PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
|
2012-04-01 12:19:50 +02:00
|
|
|
getPlayerEffectivePrivs(player->getName()),
|
|
|
|
isSingleplayer());
|
2011-11-26 02:20:19 +01:00
|
|
|
|
2013-05-10 03:10:33 +02:00
|
|
|
/* Clean up old HUD elements from previous sessions */
|
2014-05-25 14:34:32 +02:00
|
|
|
player->clearHud();
|
2013-05-10 03:10:33 +02:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
/* Add object to environment */
|
|
|
|
m_env->addActiveObject(playersao);
|
2011-01-15 02:28:19 +01:00
|
|
|
|
2012-03-19 03:04:16 +01:00
|
|
|
/* Run scripts */
|
2014-05-30 22:04:07 +02:00
|
|
|
if (newplayer) {
|
2013-05-25 00:51:02 +02:00
|
|
|
m_script->on_newplayer(playersao);
|
2014-05-30 22:04:07 +02:00
|
|
|
}
|
2012-03-19 03:04:16 +01:00
|
|
|
|
|
|
|
return playersao;
|
2011-01-15 02:28:19 +01:00
|
|
|
}
|
|
|
|
|
2011-02-15 15:11:24 +01:00
|
|
|
void dedicated_server_loop(Server &server, bool &kill)
|
2011-01-23 16:29:15 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2012-03-11 03:15:45 +01:00
|
|
|
verbosestream<<"dedicated_server_loop()"<<std::endl;
|
2011-05-30 23:15:43 +02:00
|
|
|
|
|
|
|
IntervalLimiter m_profiler_interval;
|
2011-01-23 16:29:15 +01:00
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
2012-03-07 20:44:53 +01:00
|
|
|
float steplen = g_settings->getFloat("dedicated_server_step");
|
2011-01-23 16:29:15 +01:00
|
|
|
// This is kind of a hack but can be done like this
|
|
|
|
// because server.step() is very light
|
2011-05-30 23:15:43 +02:00
|
|
|
{
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "dedicated server sleep");
|
2012-03-07 20:44:53 +01:00
|
|
|
sleep_ms((int)(steplen*1000.0));
|
2011-05-30 23:15:43 +02:00
|
|
|
}
|
2012-03-07 20:44:53 +01:00
|
|
|
server.step(steplen);
|
2011-01-23 16:29:15 +01:00
|
|
|
|
2011-02-15 15:11:24 +01:00
|
|
|
if(server.getShutdownRequested() || kill)
|
|
|
|
{
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"Dedicated server quitting"<<std::endl;
|
2013-02-21 23:00:44 +01:00
|
|
|
#if USE_CURL
|
|
|
|
if(g_settings->getBool("server_announce") == true)
|
|
|
|
ServerList::sendAnnounce("delete");
|
|
|
|
#endif
|
2011-02-15 15:11:24 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-05-30 23:15:43 +02:00
|
|
|
/*
|
|
|
|
Profiler
|
|
|
|
*/
|
|
|
|
float profiler_print_interval =
|
2011-10-12 12:53:38 +02:00
|
|
|
g_settings->getFloat("profiler_print_interval");
|
2011-05-30 23:15:43 +02:00
|
|
|
if(profiler_print_interval != 0)
|
|
|
|
{
|
2012-03-07 20:44:53 +01:00
|
|
|
if(m_profiler_interval.step(steplen, profiler_print_interval))
|
2011-05-30 23:15:43 +02:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Profiler:"<<std::endl;
|
|
|
|
g_profiler->print(infostream);
|
2011-10-12 12:53:38 +02:00
|
|
|
g_profiler->clear();
|
2011-05-30 23:15:43 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-23 16:29:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
|