/*
Minetest
Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "serverenvironment.h"
#include "content_sao.h"
#include "settings.h"
#include "log.h"
#include "nodedef.h"
#include "nodemetadata.h"
#include "gamedef.h"
#include "map.h"
#include "profiler.h"
#include "raycast.h"
#include "remoteplayer.h"
#include "scripting_server.h"
#include "server.h"
#include "voxelalgorithms.h"
#include "util/serialize.h"
#include "util/basic_macros.h"
#include "util/pointedthing.h"
#include "threading/mutex_auto_lock.h"
#include "filesys.h"
#include "gameparams.h"
#include "database-dummy.h"
#include "database-files.h"
#include "database-sqlite3.h"
#if USE_POSTGRESQL
#include "database-postgresql.h"
#endif
#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
// A number that is much smaller than the timeout for particle spawners should/could ever be
#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
/*
ABMWithState
*/
ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
abm(abm_),
timer(0)
{
// Initialize timer to random value to spread processing
float itv = abm->getTriggerInterval();
itv = MYMAX(0.001, itv); // No less than 1ms
int minval = MYMAX(-0.51*itv, -60); // Clamp to
int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
timer = myrand_range(minval, maxval);
}
/*
LBMManager
*/
void LBMContentMapping::deleteContents()
{
for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
it != lbm_list.end(); ++it) {
delete *it;
}
}
void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
{
// Add the lbm_def to the LBMContentMapping.
// Unknown names get added to the global NameIdMapping.
INodeDefManager *nodedef = gamedef->ndef();
lbm_list.push_back(lbm_def);
for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
it != lbm_def->trigger_contents.end(); ++it) {
std::set<content_t> c_ids;
bool found = nodedef->getIds(*it, c_ids);
if (!found) {
content_t c_id = gamedef->allocateUnknownNodeId(*it);
if (c_id == CONTENT_IGNORE) {
// Seems it can't be allocated.
warningstream << "Could not internalize node name \"" << *it
<< "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
continue;
}
c_ids.insert(c_id);
}
for (std::set<content_t>::const_iterator iit =
c_ids.begin(); iit != c_ids.end(); ++iit) {
content_t c_id = *iit;
map[c_id].push_back(lbm_def);
}
}
}
const std::vector<LoadingBlockModifierDef *> *
LBMContentMapping::lookup(content_t c) const
{
container_map::const_iterator it = map.find(c);
if (it == map.end())
return NULL;
// This first dereferences the iterator, returning
// a std::vector<LoadingBlockModifierDef *>
// reference, then we convert it to a pointer.
return &(it->second);
}
LBMManager::~LBMManager()
{
for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
delete it->second;
}
for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
it != m_lbm_lookup.end(); ++it) {
(it->second).deleteContents();
}
}
void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
{
// Precondition, in query mode the map isn't used anymore
FATAL_ERROR_IF(m_query_mode == true,
"attempted to modify LBMManager in query mode");
if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
throw ModError("Error adding LBM \"" + lbm_def->name +
"\": Does not follow naming conventions: "
"Only characters [a-z0-9_:] are allowed.");
}
m_lbm_defs[lbm_def->name] = lbm_def;
}
void LBMManager::loadIntroductionTimes(const std::string ×,
IGameDef *gamedef, u32 now)
{
m_query_mode = true;
// name -> time map.
// Storing it in a map first instead of
// handling the stuff directly in the loop
// removes all duplicate entries.
// TODO make this std::unordered_map
std::map<std::string, u32> introduction_times;
/*
The introduction times string consists of name~time entries,
with each entry terminated by a semicolon. The time is decimal.
*/
size_t idx = 0;
size_t idx_new;
while ((idx_new = times.find(";", idx)) != std::string::npos) {
std::string entry = times.substr(idx, idx_new - idx);
std::vector<std::string> components = str_split(entry, '~');
if (components.size() != 2)
throw SerializationError("Introduction times entry \""
+ entry + "\" requires exactly one '~'!");
const std::string &name = components[0];
u32 time = from_string<u32>(components[1]);
introduction_times[name] = time;
idx = idx_new + 1;
}
// Put stuff from introduction_times into m_lbm_lookup
for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
it != introduction_times.end(); ++it) {
const std::string &name = it->first;
u32 time = it->second;
std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
m_lbm_defs.find(name);
if (def_it == m_lbm_defs.end()) {
// This seems to be an LBM entry for
// an LBM we haven't loaded. Discard it.
continue;
}
LoadingBlockModifierDef *lbm_def = def_it->second;
if (lbm_def->run_at_every_load) {
// This seems to be an LBM entry for
// an LBM that runs at every load.
// Don't add it just yet.
continue;
}
m_lbm_lookup[time].addLBM(lbm_def, gamedef);
// Erase the entry so that we know later
// what elements didn't get put into m_lbm_lookup
m_lbm_defs.erase(name);
}
// Now also add the elements from m_lbm_defs to m_lbm_lookup
// that weren't added in the previous step.
// They are introduced first time to this world,
// or are run at every load (introducement time hardcoded to U32_MAX).
LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
if (it->second->run_at_every_load) {
lbms_running_always.addLBM(it->second, gamedef);
} else {
lbms_we_introduce_now.addLBM(it->second, gamedef);
}
}
// Clear the list, so that we don't delete remaining elements
// twice in the destructor
m_lbm_defs.clear();
}
std::string LBMManager::createIntroductionTimesString()
{
// Precondition, we must be in query mode
FATAL_ERROR_IF(m_query_mode == false,
"attempted to query on non fully set up LBMManager");
std::ostringstream oss;
for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
it != m_lbm_lookup.end(); ++it) {
u32 time = it->first;
std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
iit != lbm_list.end(); ++iit) {
// Don't add if the LBM runs at every load,
// then introducement time is hardcoded
// and doesn't need to be stored
|