#if USE_POSTGRESQL
#include "database/database-postgresql.h"
#endif
+#if USE_LEVELDB
+#include "database/database-leveldb.h"
+#endif
#include "server/luaentity_sao.h"
#include "server/player_sao.h"
void ServerEnvironment::saveMeta()
{
+ if (!m_meta_loaded)
+ return;
+
std::string path = m_path_world + DIR_DELIM "env_meta.txt";
// Open file and serialize
std::ostringstream ss(std::ios_base::binary);
- Settings args;
+ Settings args("EnvArgsEnd");
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
m_lbm_mgr.createIntroductionTimesString());
args.setU64("day_count", m_day_count);
args.writeLines(ss);
- ss<<"EnvArgsEnd\n";
if(!fs::safeWriteToFile(path, ss.str()))
{
void ServerEnvironment::loadMeta()
{
+ SANITY_CHECK(!m_meta_loaded);
+ m_meta_loaded = true;
+
// If file doesn't exist, load default environment metadata
if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
infostream << "ServerEnvironment: Loading default environment metadata"
throw SerializationError("Couldn't load env meta");
}
- Settings args;
+ Settings args("EnvArgsEnd");
- if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+ if (!args.parseConfigLines(is)) {
throw SerializationError("ServerEnvironment::loadMeta(): "
"EnvArgsEnd not found!");
}
return true;
}
+u8 ServerEnvironment::findSunlight(v3s16 pos) const
+{
+ // Directions for neighbouring nodes with specified order
+ static const v3s16 dirs[] = {
+ v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
+ v3s16(0, -1, 0), v3s16(0, 1, 0)
+ };
+
+ const NodeDefManager *ndef = m_server->ndef();
+
+ // found_light remembers the highest known sunlight value at pos
+ u8 found_light = 0;
+
+ struct stack_entry {
+ v3s16 pos;
+ s16 dist;
+ };
+ std::stack<stack_entry> stack;
+ stack.push({pos, 0});
+
+ std::unordered_map<s64, s8> dists;
+ dists[MapDatabase::getBlockAsInteger(pos)] = 0;
+
+ while (!stack.empty()) {
+ struct stack_entry e = stack.top();
+ stack.pop();
+
+ v3s16 currentPos = e.pos;
+ s8 dist = e.dist + 1;
+
+ for (const v3s16& off : dirs) {
+ v3s16 neighborPos = currentPos + off;
+ s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
+
+ // Do not walk neighborPos multiple times unless the distance to the start
+ // position is shorter
+ auto it = dists.find(neighborHash);
+ if (it != dists.end() && dist >= it->second)
+ continue;
+
+ // Position to walk
+ bool is_position_ok;
+ MapNode node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok) {
+ // This happens very rarely because the map at currentPos is loaded
+ m_map->emergeBlock(neighborPos, false);
+ node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok)
+ continue; // not generated
+ }
+
+ const ContentFeatures &def = ndef->get(node);
+ if (!def.sunlight_propagates) {
+ // Do not test propagation here again
+ dists[neighborHash] = -1;
+ continue;
+ }
+
+ // Sunlight could have come from here
+ dists[neighborHash] = dist;
+ u8 daylight = node.param1 & 0x0f;
+
+ // In the special case where sunlight shines from above and thus
+ // does not decrease with upwards distance, daylight is always
+ // bigger than nightlight, which never reaches 15
+ int possible_finlight = daylight - dist;
+ if (possible_finlight <= found_light) {
+ // Light from here cannot make a brighter light at currentPos than
+ // found_light
+ continue;
+ }
+
+ u8 nightlight = node.param1 >> 4;
+ if (daylight > nightlight) {
+ // Found a valid daylight
+ found_light = possible_finlight;
+ } else {
+ // Sunlight may be darker, so walk the neighbours
+ stack.push({neighborPos, dist});
+ }
+ }
+ }
+ return found_light;
+}
+
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
{
infostream << "ServerEnvironment::clearObjects(): "
// If known by some client, don't delete immediately
if (obj->m_known_by_count > 0) {
- obj->m_pending_removal = true;
+ obj->markForRemoval();
return false;
}
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete active object
if (obj->environmentDeletes())
}
}
- if (m_database_check_interval.step(dtime, 10.0f)) {
- m_auth_database->pingDatabase();
- m_player_database->pingDatabase();
- m_map->pingDatabase();
- }
/*
Manage active block list
*/
std::shuffle(output.begin(), output.end(), m_rgen);
int i = 0;
- // The time budget for ABMs is 20%.
- u32 max_time_ms = m_cache_abm_interval * 1000 / 5;
+ // determine the time budget for ABMs
+ u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
for (const v3s16 &p : output) {
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if (!block)
}
}
-ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
+bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
{
if(m_active_object_messages.empty())
- return ActiveObjectMessage(0);
+ return false;
- ActiveObjectMessage message = m_active_object_messages.front();
+ *dest = std::move(m_active_object_messages.front());
m_active_object_messages.pop();
- return message;
+ return true;
}
void ServerEnvironment::getSelectedActiveObjects(
const core::line3d<f32> &shootline_on_map,
std::vector<PointedThing> &objects)
{
- std::vector<u16> objectIds;
- getObjectsInsideRadius(objectIds, shootline_on_map.start,
- shootline_on_map.getLength() + 10.0f);
+ std::vector<ServerActiveObject *> objs;
+ getObjectsInsideRadius(objs, shootline_on_map.start,
+ shootline_on_map.getLength() + 10.0f, nullptr);
const v3f line_vector = shootline_on_map.getVector();
- for (u16 objectId : objectIds) {
- ServerActiveObject* obj = getActiveObject(objectId);
-
+ for (auto obj : objs) {
+ if (obj->isGone())
+ continue;
aabb3f selection_box;
if (!obj->getSelectionBox(&selection_box))
continue;
if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
¤t_intersection, ¤t_normal)) {
objects.emplace_back(
- (s16) objectId, current_intersection, current_normal,
+ (s16) obj->getId(), current_intersection, current_normal,
(current_intersection - shootline_on_map.start).getLengthSQ());
}
}
}
// Register reference in scripting api (must be done before post-init)
- m_script->addObjectReference(object);
+ m_script->addObjectReference(dynamic_cast<ActiveObject *>(object));
// Post-initialize object
object->addedToEnvironment(dtime_s);
/*
Delete static data from block if removed
*/
- if (obj->m_pending_removal)
+ if (obj->isPendingRemoval())
deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
// If still known by clients, don't actually remove. On some future
/*
Move static data from active to stored if deactivated
*/
- if (!obj->m_pending_removal && obj->m_static_exists) {
+ if (!obj->isPendingRemoval() && obj->m_static_exists) {
MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
if (block) {
const auto i = block->m_static_objects.m_active.find(id);
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete
if (obj->environmentDeletes())
// force_delete might be overriden per object
bool force_delete = _force_delete;
- // Do not deactivate if static data creation not allowed
- if (!force_delete && !obj->isStaticAllowed())
+ // Do not deactivate if disallowed
+ if (!force_delete && !obj->shouldUnload())
return false;
// removeRemovedObjects() is responsible for these
if (!force_delete && obj->m_static_exists &&
!m_active_blocks.contains(obj->m_static_block) &&
m_active_blocks.contains(blockpos_o)) {
+
// Delete from block where object was located
deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
}
// If block is still active, don't remove
- if (!force_delete && m_active_blocks.contains(blockpos_o))
+ bool still_active = obj->isStaticAllowed() ?
+ m_active_blocks.contains(blockpos_o) :
+ getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
+ if (!force_delete && still_active)
return false;
verbosestream << "ServerEnvironment::deactivateFarObjects(): "
force_delete = true;
}
+ // Regardless of what happens to the object at this point, deactivate it first.
+ // This ensures that LuaEntity on_deactivate is always called.
+ obj->markForDeactivation();
+
/*
If known by some client, set pending deactivation.
Otherwise delete it immediately.
<< "object id=" << id << " is known by clients"
<< "; not deleting yet" << std::endl;
- obj->m_pending_deactivation = true;
return false;
}
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete active object
if (obj->environmentDeletes())
if (name == "dummy")
return new Database_Dummy();
+
#if USE_POSTGRESQL
if (name == "postgresql") {
std::string connect_string;
return new PlayerDatabasePostgreSQL(connect_string);
}
#endif
+
+#if USE_LEVELDB
+ if (name == "leveldb")
+ return new PlayerDatabaseLevelDB(savedir);
+#endif
+
if (name == "files")
return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
if (!world_mt.exists("player_backend")) {
errorstream << "Please specify your current backend in world.mt:"
<< std::endl
- << " player_backend = {files|sqlite3|postgresql}"
+ << " player_backend = {files|sqlite3|leveldb|postgresql}"
<< std::endl;
return false;
}
if (name == "sqlite3")
return new AuthDatabaseSQLite3(savedir);
+#if USE_POSTGRESQL
+ if (name == "postgresql") {
+ std::string connect_string;
+ conf.getNoEx("pgsql_auth_connection", connect_string);
+ return new AuthDatabasePostgreSQL(connect_string);
+ }
+#endif
+
if (name == "files")
return new AuthDatabaseFiles(savedir);
+#if USE_LEVELDB
+ if (name == "leveldb")
+ return new AuthDatabaseLevelDB(savedir);
+#endif
+
throw BaseException(std::string("Database backend ") + name + " not supported.");
}