51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <set>
-#include <list>
-#include <map>
#include "environment.h"
#include "filesys.h"
#include "porting.h"
#include "mapblock.h"
#include "serverobject.h"
#include "content_sao.h"
-#include "mapgen.h"
#include "settings.h"
#include "log.h"
#include "profiler.h"
-#include "cpp_api/scriptapi.h"
+#include "scripting_game.h"
#include "nodedef.h"
#include "nodemetadata.h"
#include "main.h" // For g_settings, g_profiler
#ifndef SERVER
#include "clientmap.h"
#include "localplayer.h"
+#include "mapblock_mesh.h"
#include "event.h"
#endif
#include "daynightratio.h"
#include "map.h"
+#include "emerge.h"
#include "util/serialize.h"
+#include "jthread/jmutexautolock.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
m_time_of_day(9000),
m_time_of_day_f(9000./24000),
m_time_of_day_speed(0),
- m_time_counter(0)
+ m_time_counter(0),
+ m_enable_day_night_ratio_override(false),
+ m_day_night_ratio_override(0.0f)
{
+ m_cache_enable_shaders = g_settings->getBool("enable_shaders");
}
Environment::~Environment()
void Environment::removePlayer(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
-re_search:
+
for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
+ i != m_players.end();)
{
Player *player = *i;
- if(player->peer_id != peer_id)
- continue;
-
- delete player;
- m_players.erase(i);
- // See if there is an another one
- // (shouldn't be, but just to be sure)
- goto re_search;
+ if(player->peer_id == peer_id) {
+ delete player;
+ i = m_players.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+void Environment::removePlayer(const char *name)
+{
+ for (std::list<Player*>::iterator it = m_players.begin();
+ it != m_players.end(); ++it) {
+ if (strcmp((*it)->getName(), name) == 0) {
+ delete *it;
+ m_players.erase(it);
+ return;
+ }
}
}
return newlist;
}
-void Environment::printPlayers(std::ostream &o)
+u32 Environment::getDayNightRatio()
{
- o<<"Players in environment:"<<std::endl;
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); i++)
- {
- Player *player = *i;
- o<<"Player peer_id="<<player->peer_id<<std::endl;
- }
+ if(m_enable_day_night_ratio_override)
+ return m_day_night_ratio_override;
+ return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
}
-u32 Environment::getDayNightRatio()
+void Environment::setTimeOfDaySpeed(float speed)
+{
+ JMutexAutoLock(this->m_lock);
+ m_time_of_day_speed = speed;
+}
+
+float Environment::getTimeOfDaySpeed()
{
- bool smooth = g_settings->getBool("enable_shaders");
- return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
+ JMutexAutoLock(this->m_lock);
+ float retval = m_time_of_day_speed;
+ return retval;
}
void Environment::stepTimeOfDay(float dtime)
{
+ float day_speed = 0;
+ {
+ JMutexAutoLock(this->m_lock);
+ day_speed = m_time_of_day_speed;
+ }
+
m_time_counter += dtime;
- f32 speed = m_time_of_day_speed * 24000./(24.*3600);
+ f32 speed = day_speed * 24000./(24.*3600);
u32 units = (u32)(m_time_counter*speed);
- m_time_counter -= (f32)units / speed;
bool sync_f = false;
if(units > 0){
// Sync at overflow
if(sync_f)
m_time_of_day_f = (float)m_time_of_day / 24000.0;
}
+ if (speed > 0) {
+ m_time_counter -= (f32)units / speed;
+ }
if(!sync_f){
- m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
+ m_time_of_day_f += day_speed/24/3600*dtime;
if(m_time_of_day_f > 1.0)
m_time_of_day_f -= 1.0;
if(m_time_of_day_f < 0.0)
/*
Create the new list
*/
- std::set<v3s16> newlist;
+ std::set<v3s16> newlist = m_forceloaded_list;
for(std::list<v3s16>::iterator i = active_positions.begin();
i != active_positions.end(); ++i)
{
ServerEnvironment
*/
-ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
- IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
+ServerEnvironment::ServerEnvironment(ServerMap *map,
+ GameScripting *scriptIface, IGameDef *gamedef,
+ const std::string &path_world) :
m_map(map),
m_script(scriptIface),
m_gamedef(gamedef),
- m_emerger(emerger),
- m_random_spawn_timer(3),
+ m_path_world(path_world),
m_send_recommended_timer(0),
m_active_block_interval_overload_skip(0),
m_game_time(0),
return *m_map;
}
-bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
+bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
{
float distance = pos1.getDistanceFrom(pos2);
MapNode n = getMap().getNodeNoEx(pos);
if(n.param0 != CONTENT_AIR) {
+ if (p) {
+ *p = pos;
+ }
return false;
}
}
return true;
}
-void ServerEnvironment::serializePlayers(const std::string &savedir)
+void ServerEnvironment::saveLoadedPlayers()
{
- std::string players_path = savedir + "/players";
+ std::string players_path = m_path_world + DIR_DELIM "players";
fs::CreateDir(players_path);
- std::set<Player*> saved_players;
-
- std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
- for(u32 i=0; i<player_files.size(); i++)
- {
- if(player_files[i].dir || player_files[i].name[0] == '.')
- continue;
-
- // Full path to this file
- std::string path = players_path + "/" + player_files[i].name;
-
- //infostream<<"Checking player file "<<path<<std::endl;
-
- // Load player to see what is its name
- RemotePlayer testplayer(m_gamedef);
- {
- // Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if(is.good() == false)
- {
- infostream<<"Failed to read "<<path<<std::endl;
- continue;
- }
- testplayer.deSerialize(is, player_files[i].name);
- }
-
- //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
-
- // Search for the player
- std::string playername = testplayer.getName();
- Player *player = getPlayer(playername.c_str());
- if(player == NULL)
- {
- infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
- continue;
- }
-
- //infostream<<"Found matching player, overwriting."<<std::endl;
-
- // OK, found. Save player there.
- if(player->checkModified())
- {
- // Open file and serialize
- std::ofstream os(path.c_str(), std::ios_base::binary);
- if(os.good() == false)
- {
- infostream<<"Failed to overwrite "<<path<<std::endl;
- continue;
- }
- player->serialize(os);
- saved_players.insert(player);
- } else {
- saved_players.insert(player);
+ for (std::list<Player*>::iterator it = m_players.begin();
+ it != m_players.end();
+ ++it) {
+ RemotePlayer *player = static_cast<RemotePlayer*>(*it);
+ if (player->checkModified()) {
+ player->save(players_path);
}
}
+}
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
- Player *player = *i;
- if(saved_players.find(player) != saved_players.end())
- {
- /*infostream<<"Player "<<player->getName()
- <<" was already saved."<<std::endl;*/
- continue;
- }
- std::string playername = player->getName();
- // Don't save unnamed player
- if(playername == "")
- {
- //infostream<<"Not saving unnamed player."<<std::endl;
- continue;
- }
- /*
- Find a sane filename
- */
- if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
- playername = "player";
- std::string path = players_path + "/" + playername;
- bool found = false;
- for(u32 i=0; i<1000; i++)
- {
- if(fs::PathExists(path) == false)
- {
- found = true;
- break;
- }
- path = players_path + "/" + playername + itos(i);
- }
- if(found == false)
- {
- infostream<<"Didn't find free file for player"<<std::endl;
- continue;
- }
+void ServerEnvironment::savePlayer(const std::string &playername)
+{
+ std::string players_path = m_path_world + DIR_DELIM "players";
+ fs::CreateDir(players_path);
- {
- /*infostream<<"Saving player "<<player->getName()<<" to "
- <<path<<std::endl;*/
- // Open file and serialize
- std::ofstream os(path.c_str(), std::ios_base::binary);
- if(os.good() == false)
- {
- infostream<<"Failed to overwrite "<<path<<std::endl;
- continue;
- }
- player->serialize(os);
- saved_players.insert(player);
- }
+ RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+ if (player) {
+ player->save(players_path);
}
-
- //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
}
-void ServerEnvironment::deSerializePlayers(const std::string &savedir)
+Player *ServerEnvironment::loadPlayer(const std::string &playername)
{
- std::string players_path = savedir + "/players";
-
- std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
- for(u32 i=0; i<player_files.size(); i++)
- {
- if(player_files[i].dir)
- continue;
-
- // Full path to this file
- std::string path = players_path + "/" + player_files[i].name;
+ std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
- //infostream<<"Checking player file "<<path<<std::endl;
-
- // Load player to see what is its name
- RemotePlayer testplayer(m_gamedef);
- {
- // Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if(is.good() == false)
- {
- infostream<<"Failed to read "<<path<<std::endl;
- continue;
- }
- testplayer.deSerialize(is, player_files[i].name);
- }
-
- if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
- {
- infostream<<"Not loading player with invalid name: "
- <<testplayer.getName()<<std::endl;
- }
-
- /*infostream<<"Loaded test player with name "<<testplayer.getName()
- <<std::endl;*/
-
- // Search for the player
- std::string playername = testplayer.getName();
- Player *player = getPlayer(playername.c_str());
- bool newplayer = false;
- if(player == NULL)
- {
- //infostream<<"Is a new player"<<std::endl;
- player = new RemotePlayer(m_gamedef);
- newplayer = true;
- }
+ RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+ bool newplayer = false;
+ bool found = false;
+ if (!player) {
+ player = new RemotePlayer(m_gamedef, playername.c_str());
+ newplayer = true;
+ }
- // Load player
- {
- verbosestream<<"Reading player "<<testplayer.getName()<<" from "
- <<path<<std::endl;
- // Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if(is.good() == false)
- {
- infostream<<"Failed to read "<<path<<std::endl;
- continue;
- }
- player->deSerialize(is, player_files[i].name);
+ RemotePlayer testplayer(m_gamedef, "");
+ std::string path = players_path + playername;
+ for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+ // Open file and deserialize
+ std::ifstream is(path.c_str(), std::ios_base::binary);
+ if (!is.good()) {
+ return NULL;
}
-
- if(newplayer)
- {
- addPlayer(player);
+ testplayer.deSerialize(is, path);
+ is.close();
+ if (testplayer.getName() == playername) {
+ *player = testplayer;
+ found = true;
+ break;
}
+ path = players_path + playername + itos(i);
+ }
+ if (!found) {
+ infostream << "Player file for player " << playername
+ << " not found" << std::endl;
+ return NULL;
+ }
+ if (newplayer) {
+ addPlayer(player);
}
+ player->setModified(false);
+ return player;
}
-void ServerEnvironment::saveMeta(const std::string &savedir)
+void ServerEnvironment::saveMeta()
{
- std::string path = savedir + "/env_meta.txt";
+ std::string path = m_path_world + DIR_DELIM "env_meta.txt";
// Open file and serialize
- std::ofstream os(path.c_str(), std::ios_base::binary);
- if(os.good() == false)
- {
- infostream<<"ServerEnvironment::saveMeta(): Failed to open "
- <<path<<std::endl;
- throw SerializationError("Couldn't save env meta");
- }
+ std::ostringstream ss(std::ios_base::binary);
Settings args;
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
- args.writeLines(os);
- os<<"EnvArgsEnd\n";
+ args.writeLines(ss);
+ ss<<"EnvArgsEnd\n";
+
+ if(!fs::safeWriteToFile(path, ss.str()))
+ {
+ infostream<<"ServerEnvironment::saveMeta(): Failed to write "
+ <<path<<std::endl;
+ throw SerializationError("Couldn't save env meta");
+ }
}
-void ServerEnvironment::loadMeta(const std::string &savedir)
+void ServerEnvironment::loadMeta()
{
- std::string path = savedir + "/env_meta.txt";
+ std::string path = m_path_world + DIR_DELIM "env_meta.txt";
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
- if(is.good() == false)
- {
- infostream<<"ServerEnvironment::loadMeta(): Failed to open "
- <<path<<std::endl;
+ if (!is.good()) {
+ infostream << "ServerEnvironment::loadMeta(): Failed to open "
+ << path << std::endl;
throw SerializationError("Couldn't load env meta");
}
Settings args;
-
- for(;;)
- {
- if(is.eof())
- throw SerializationError
- ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
- std::string line;
- std::getline(is, line);
- std::string trimmedline = trim(line);
- if(trimmedline == "EnvArgsEnd")
- break;
- args.parseConfigLine(line);
+
+ if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+ throw SerializationError("ServerEnvironment::loadMeta(): "
+ "EnvArgsEnd not found!");
}
-
- try{
+
+ try {
m_game_time = args.getU64("game_time");
- }catch(SettingNotFoundException &e){
+ } catch (SettingNotFoundException &e) {
// Getting this is crucial, otherwise timestamps are useless
throw SerializationError("Couldn't load env meta game_time");
}
- try{
+ try {
m_time_of_day = args.getU64("time_of_day");
- }catch(SettingNotFoundException &e){
+ } catch (SettingNotFoundException &e) {
// This is not as important
m_time_of_day = 9000;
}
}
}
}
+ // Find out how many objects the given block and its neighbours contain.
+ // Returns the number of objects in the block, and also in 'wider' the
+ // number of objects in the block and all its neighbours. The latter
+ // may an estimate if any neighbours are unloaded.
+ u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
+ {
+ wider = 0;
+ u32 wider_unknown_count = 0;
+ for(s16 x=-1; x<=1; x++)
+ for(s16 y=-1; y<=1; y++)
+ for(s16 z=-1; z<=1; z++)
+ {
+ MapBlock *block2 = map->getBlockNoCreateNoEx(
+ block->getPos() + v3s16(x,y,z));
+ if(block2==NULL){
+ wider_unknown_count++;
+ continue;
+ }
+ wider += block2->m_static_objects.m_active.size()
+ + block2->m_static_objects.m_stored.size();
+ }
+ // Extrapolate
+ u32 active_object_count = block->m_static_objects.m_active.size();
+ u32 wider_known_count = 3*3*3 - wider_unknown_count;
+ wider += wider_unknown_count * wider / wider_known_count;
+ return active_object_count;
+
+ }
void apply(MapBlock *block)
{
if(m_aabms.empty())
ServerMap *map = &m_env->getServerMap();
+ u32 active_object_count_wider;
+ u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
+ m_env->m_added_objects = 0;
+
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
}
neighbor_found:
- // Find out how many objects the block contains
- u32 active_object_count = block->m_static_objects.m_active.size();
- // Find out how many objects this and all the neighbors contain
- u32 active_object_count_wider = 0;
- u32 wider_unknown_count = 0;
- for(s16 x=-1; x<=1; x++)
- for(s16 y=-1; y<=1; y++)
- for(s16 z=-1; z<=1; z++)
- {
- MapBlock *block2 = map->getBlockNoCreateNoEx(
- block->getPos() + v3s16(x,y,z));
- if(block2==NULL){
- wider_unknown_count = 0;
- continue;
- }
- active_object_count_wider +=
- block2->m_static_objects.m_active.size()
- + block2->m_static_objects.m_stored.size();
- }
- // Extrapolate
- u32 wider_known_count = 3*3*3 - wider_unknown_count;
- active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
-
// Call all the trigger variations
i->abm->trigger(m_env, p, n);
i->abm->trigger(m_env, p, n,
active_object_count, active_object_count_wider);
+
+ // Count surrounding objects again if the abms added any
+ if(m_env->m_added_objects > 0) {
+ active_object_count = countObjects(block, map, active_object_count_wider);
+ m_env->m_added_objects = 0;
+ }
}
}
}
void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
{
+ // Reset usage timer immediately, otherwise a block that becomes active
+ // again at around the same time as it would normally be unloaded will
+ // get unloaded incorrectly. (I think this still leaves a small possibility
+ // of a race condition between this and server::AsyncRunStep, which only
+ // some kind of synchronisation will fix, but it at least reduces the window
+ // of opportunity for it to break from seconds to nanoseconds)
+ block->resetUsageTimer();
+
// Get time difference
u32 dtime_s = 0;
u32 stamp = block->getTimestamp();
{
INodeDefManager *ndef = m_gamedef->ndef();
MapNode n_old = m_map->getNodeNoEx(p);
+
// Call destructor
- if(ndef->get(n_old).has_on_destruct)
+ if (ndef->get(n_old).has_on_destruct)
m_script->node_on_destruct(p, n_old);
+
// Replace node
- bool succeeded = m_map->addNodeWithEvent(p, n);
- if(!succeeded)
+ if (!m_map->addNodeWithEvent(p, n))
return false;
+
+ // Update active VoxelManipulator if a mapgen thread
+ m_map->updateVManip(p);
+
// Call post-destructor
- if(ndef->get(n_old).has_after_destruct)
+ if (ndef->get(n_old).has_after_destruct)
m_script->node_after_destruct(p, n_old);
+
// Call constructor
- if(ndef->get(n).has_on_construct)
+ if (ndef->get(n).has_on_construct)
m_script->node_on_construct(p, n);
+
return true;
}
{
INodeDefManager *ndef = m_gamedef->ndef();
MapNode n_old = m_map->getNodeNoEx(p);
+
// Call destructor
- if(ndef->get(n_old).has_on_destruct)
+ if (ndef->get(n_old).has_on_destruct)
m_script->node_on_destruct(p, n_old);
+
// Replace with air
// This is slightly optimized compared to addNodeWithEvent(air)
- bool succeeded = m_map->removeNodeWithEvent(p);
- if(!succeeded)
+ if (!m_map->removeNodeWithEvent(p))
return false;
+
+ // Update active VoxelManipulator if a mapgen thread
+ m_map->updateVManip(p);
+
// Call post-destructor
- if(ndef->get(n_old).has_after_destruct)
+ if (ndef->get(n_old).has_after_destruct)
m_script->node_after_destruct(p, n_old);
+
// Air doesn't require constructor
return true;
}
+bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
+{
+ if (!m_map->addNodeWithEvent(p, n, false))
+ return false;
+
+ // Update active VoxelManipulator if a mapgen thread
+ m_map->updateVManip(p);
+
+ return true;
+}
+
std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
{
std::set<u16> objects;
{
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Removing all active objects"<<std::endl;
- std::list<u16> objects_to_remove;
+ std::vector<u16> objects_to_remove;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
ServerActiveObject* obj = i->second;
if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
continue;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
+
// Remove references from m_active_objects
- for(std::list<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
// Get list of loaded blocks
- std::list<v3s16> loaded_blocks;
+ std::vector<v3s16> loaded_blocks;
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Listing all loaded blocks"<<std::endl;
m_map->listAllLoadedBlocks(loaded_blocks);
<<loaded_blocks.size()<<std::endl;
// Get list of loadable blocks
- std::list<v3s16> loadable_blocks;
+ std::vector<v3s16> loadable_blocks;
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Listing all loadable blocks"<<std::endl;
m_map->listAllLoadableBlocks(loadable_blocks);
<<", now clearing"<<std::endl;
// Grab a reference on each loaded block to avoid unloading it
- for(std::list<v3s16>::iterator i = loaded_blocks.begin();
- i != loaded_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
assert(block);
u32 num_blocks_checked = 0;
u32 num_blocks_cleared = 0;
u32 num_objs_cleared = 0;
- for(std::list<v3s16>::iterator i = loadable_blocks.begin();
- i != loadable_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
+ i != loadable_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->emergeBlock(p, false);
if(!block){
}
num_blocks_checked++;
- if(num_blocks_checked % report_interval == 0){
+ if(report_interval != 0 &&
+ num_blocks_checked % report_interval == 0){
float percent = 100.0 * (float)num_blocks_checked /
loadable_blocks.size();
infostream<<"ServerEnvironment::clearAllObjects(): "
m_map->unloadUnreferencedBlocks();
// Drop references that were added above
- for(std::list<v3s16>::iterator i = loaded_blocks.begin();
- i != loaded_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
assert(block);
continue;
// Move
- player->move(dtime, *m_map, 100*BS);
+ player->move(dtime, this, 100*BS);
}
}
{
v3s16 p = *i;
- MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+ MapBlock *block = m_map->getBlockOrEmerge(p);
if(block==NULL){
- // Block needs to be fetched first
- m_emerger->queueBlockEmerge(p, false);
m_active_blocks.m_list.erase(p);
continue;
}
i != m_active_objects.end(); ++i)
{
ServerActiveObject* obj = i->second;
- // Remove non-peaceful mobs on peaceful mode
- if(g_settings->getBool("only_peaceful_mobs")){
- if(!obj->isPeaceful())
- obj->m_removed = true;
- }
// Don't step if is to be removed or stored statically
if(obj->m_removed || obj->m_pending_deactivation)
continue;
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{
assert(object);
+ m_added_objects++;
u16 id = addActiveObjectRaw(object, true, 0);
return id;
}
{
assert(obj);
- v3f objectpos = obj->getBasePosition();
+ v3f objectpos = obj->getBasePosition();
// The block in which the object resides in
v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
inside a radius around a position
*/
void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
+ s16 player_radius,
std::set<u16> ¤t_objects,
std::set<u16> &added_objects)
{
v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
+ f32 player_radius_f = player_radius * BS;
+
+ if (player_radius_f < 0)
+ player_radius_f = 0;
+
/*
Go through the object list,
- discard m_removed objects,
ServerActiveObject *object = i->second;
if(object == NULL)
continue;
- // Discard if removed
- if(object->m_removed)
+ // Discard if removed or deactivating
+ if(object->m_removed || object->m_pending_deactivation)
continue;
- if(object->unlimitedTransferDistance() == false){
+
+ f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+ if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
// Discard if too far
- f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
- if(distance_f > radius_f)
+ if (distance_f > player_radius_f && player_radius_f != 0)
continue;
- }
+ } else if (distance_f > radius_f)
+ continue;
+
// Discard if already on current_objects
std::set<u16>::iterator n;
n = current_objects.find(id);
inside a radius around a position
*/
void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
+ s16 player_radius,
std::set<u16> ¤t_objects,
std::set<u16> &removed_objects)
{
v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
+ f32 player_radius_f = player_radius * BS;
+
+ if (player_radius_f < 0)
+ player_radius_f = 0;
+
/*
Go through current_objects; object is removed if:
- object is not found in m_active_objects (this is actually an
continue;
}
- if(object->m_removed)
+ if(object->m_removed || object->m_pending_deactivation)
{
removed_objects.insert(id);
continue;
}
- // If transfer distance is unlimited, don't remove
- if(object->unlimitedTransferDistance())
- continue;
-
f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
-
- if(distance_f >= radius_f)
- {
- removed_objects.insert(id);
+ if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ if (distance_f <= player_radius_f || player_radius_f == 0)
+ continue;
+ } else if (distance_f <= radius_f)
continue;
- }
-
- // Not removed
+
+ // Object is no longer visible
+ removed_objects.insert(id);
}
}
if(m_active_object_messages.empty())
return ActiveObjectMessage(0);
- return m_active_object_messages.pop_front();
+ ActiveObjectMessage message = m_active_object_messages.front();
+ m_active_object_messages.pop_front();
+ return message;
}
/*
StaticObject s_obj(object->getType(), objectpos, staticdata);
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
- MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
- if(block)
- {
+ MapBlock *block = m_map->emergeBlock(blockpos);
+ if(block){
block->m_static_objects.m_active[object->getId()] = s_obj;
object->m_static_exists = true;
object->m_static_block = blockpos;
if(set_changed)
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
"addActiveObjectRaw");
- }
- else{
+ } else {
v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
- <<"could not find block for storing id="<<object->getId()
+ <<"could not emerge block for storing id="<<object->getId()
<<" statically (pos="<<PP(p)<<")"<<std::endl;
}
}
*/
void ServerEnvironment::removeRemovedObjects()
{
- std::list<u16> objects_to_remove;
+ std::vector<u16> objects_to_remove;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
u16 id = i->first;
ServerActiveObject* obj = i->second;
// This shouldn't happen but check it
if (block) {
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "removeRemovedObjects");
+ "removeRemovedObjects/remove");
obj->m_static_exists = false;
} else {
- infostream << "failed to emerge block from which "
- "an object to be removed was loaded from. id="<<id<<std::endl;
+ infostream<<"Failed to emerge block from which an object to "
+ <<"be removed was loaded from. id="<<id<<std::endl;
}
}
- // If m_known_by_count > 0, don't actually remove.
+ // If m_known_by_count > 0, don't actually remove. On some future
+ // invocation this will be 0, which is when removal will continue.
if(obj->m_known_by_count > 0)
continue;
-
+
+ /*
+ Move static data from active to stored if not marked as removed
+ */
+ if(obj->m_static_exists && !obj->m_removed){
+ MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+ if (block) {
+ std::map<u16, StaticObject>::iterator i =
+ block->m_static_objects.m_active.find(id);
+ if(i != block->m_static_objects.m_active.end()){
+ block->m_static_objects.m_stored.push_back(i->second);
+ block->m_static_objects.m_active.erase(id);
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "removeRemovedObjects/deactivate");
+ }
+ } else {
+ infostream<<"Failed to emerge block from which an object to "
+ <<"be deactivated was loaded from. id="<<id<<std::endl;
+ }
+ }
+
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
// Delete
if(obj->environmentDeletes())
delete obj;
+
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
// Remove references from m_active_objects
- for(std::list<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
}
*/
void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
{
- if(block==NULL)
+ if(block == NULL)
return;
+
// Ignore if no stored objects (to not set changed flag)
- if(block->m_static_objects.m_stored.size() == 0)
+ if(block->m_static_objects.m_stored.empty())
return;
verbosestream<<"ServerEnvironment::activateObjects(): "
<<"activating objects of block "<<PP(block->getPos())
<<" ("<<block->m_static_objects.m_stored.size()
<<" objects)"<<std::endl;
- bool large_amount = (block->m_static_objects.m_stored.size() > 49);
+ bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
if(large_amount){
errorstream<<"suspiciously large amount of objects detected: "
<<block->m_static_objects.m_stored.size()<<" in "
"large amount of objects");
return;
}
- // A list for objects that couldn't be converted to active for some
- // reason. They will be stored back.
+
+ // Activate stored objects
std::list<StaticObject> new_stored;
- // Loop through stored static objects
for(std::list<StaticObject>::iterator
i = block->m_static_objects.m_stored.begin();
- i != block->m_static_objects.m_stored.end(); ++i)
- {
- /*infostream<<"Server: Creating an active object from "
- <<"static data"<<std::endl;*/
+ i != block->m_static_objects.m_stored.end(); ++i) {
StaticObject &s_obj = *i;
+
// Create an active object from the data
ServerActiveObject *obj = ServerActiveObject::create
- (s_obj.type, this, 0, s_obj.pos, s_obj.data);
+ ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
// If couldn't create object, store static data back.
- if(obj==NULL)
- {
+ if(obj == NULL) {
errorstream<<"ServerEnvironment::activateObjects(): "
<<"failed to create active object from static object "
<<"in block "<<PP(s_obj.pos/BS)
StaticObject &s_obj = *i;
block->m_static_objects.m_stored.push_back(s_obj);
}
+
+ // Turn the active counterparts of activated objects not pending for
+ // deactivation
+ for(std::map<u16, StaticObject>::iterator
+ i = block->m_static_objects.m_active.begin();
+ i != block->m_static_objects.m_active.end(); ++i)
+ {
+ u16 id = i->first;
+ ServerActiveObject *object = getActiveObject(id);
+ assert(object);
+ object->m_pending_deactivation = false;
+ }
+
/*
Note: Block hasn't really been modified here.
The objects have just been activated and moved from the stored
If force_delete is set, active object is deleted nevertheless. It
shall only be set so in the destructor of the environment.
- If block wasn't generated (not in memory or on disk),
+ If block wasn't generated (not in memory or on disk),
*/
void ServerEnvironment::deactivateFarObjects(bool force_delete)
{
continue;
u16 id = i->first;
- v3f objectpos = obj->getBasePosition();
+ v3f objectpos = obj->getBasePosition();
// The block in which the object resides in
v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
+ // If object's static data is stored in a deactivated block and object
+ // is actually located in an active block, re-save to the block in
+ // which the object is actually located in.
+ if(!force_delete &&
+ obj->m_static_exists &&
+ !m_active_blocks.contains(obj->m_static_block) &&
+ m_active_blocks.contains(blockpos_o))
+ {
+ v3s16 old_static_block = obj->m_static_block;
+
+ // Save to block where object is located
+ MapBlock *block = m_map->emergeBlock(blockpos_o, false);
+ if(!block){
+ errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+ <<"Could not save object id="<<id
+ <<" to it's current block "<<PP(blockpos_o)
+ <<std::endl;
+ continue;
+ }
+ std::string staticdata_new = obj->getStaticData();
+ StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
+ block->m_static_objects.insert(id, s_obj);
+ obj->m_static_block = blockpos_o;
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "deactivateFarObjects: Static data moved in");
+
+ // Delete from block where object was located
+ block = m_map->emergeBlock(old_static_block, false);
+ if(!block){
+ errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+ <<"Could not delete object id="<<id
+ <<" from it's previous block "<<PP(old_static_block)
+ <<std::endl;
+ continue;
+ }
+ block->m_static_objects.remove(id);
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "deactivateFarObjects: Static data moved out");
+ continue;
+ }
+
// If block is active, don't remove
if(!force_delete && m_active_blocks.contains(blockpos_o))
continue;
block = m_map->emergeBlock(blockpos);
} catch(InvalidPositionException &e){
// Handled via NULL pointer
+ // NOTE: emergeBlock's failure is usually determined by it
+ // actually returning NULL
}
if(block)
{
- if(block->m_static_objects.m_stored.size() >= 49){
+ if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
<<" statically but block "<<PP(blockpos)
<<" already contains "
<<block->m_static_objects.m_stored.size()
- <<" (over 49) objects."
+ <<" objects."
<<" Forcing delete."<<std::endl;
force_delete = true;
} else {
- // If static counterpart already exists, remove it first.
- // This shouldn't happen, but happens rarely for some
- // unknown reason. Unsuccessful attempts have been made to
- // find said reason.
+ // If static counterpart already exists in target block,
+ // remove it first.
+ // This shouldn't happen because the object is removed from
+ // the previous block before this according to
+ // obj->m_static_block, but happens rarely for some unknown
+ // reason. Unsuccessful attempts have been made to find
+ // said reason.
if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
infostream<<"ServerEnv: WARNING: Performing hack #83274"
<<std::endl;
block->m_static_objects.remove(id);
}
- //store static data
- block->m_static_objects.insert(0, s_obj);
+ // Store static data
+ u16 store_id = pending_delete ? id : 0;
+ block->m_static_objects.insert(store_id, s_obj);
// Only mark block as modified if data changed considerably
if(shall_be_written)
m_gamedef(gamedef),
m_irr(irr)
{
+ char zero = 0;
+ memset(m_attachements, zero, sizeof(m_attachements));
}
ClientEnvironment::~ClientEnvironment()
v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
MapNode n = m_map->getNodeNoEx(p);
ContentFeatures c = m_gamedef->ndef()->get(n);
- if(c.isLiquid() && c.drowning && lplayer->hp > 0){
+ u8 drowning_damage = c.drowning;
+ if(drowning_damage > 0 && lplayer->hp > 0){
u16 breath = lplayer->getBreath();
if(breath > 10){
breath = 11;
updateLocalPlayerBreath(breath);
}
- if(lplayer->getBreath() == 0){
- damageLocalPlayer(1, true);
+ if(lplayer->getBreath() == 0 && drowning_damage > 0){
+ damageLocalPlayer(drowning_damage, true);
}
}
if(m_breathing_interval.step(dtime, 0.5))
if (!lplayer->hp){
lplayer->setBreath(11);
}
- else if(!c.isLiquid() || !c.drowning){
+ else if(c.drowning == 0){
u16 breath = lplayer->getBreath();
if(breath <= 10){
breath += 1;
if(player->isLocal() == false)
{
// Move
- player->move(dtime, *m_map, 100*BS);
+ player->move(dtime, this, 100*BS);
}
-
- // Update lighting on all players on client
- float light = 1.0;
- try{
- // Get node at head
- v3s16 p = player->getLightPosition();
- MapNode n = m_map->getNode(p);
- light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
- }
- catch(InvalidPositionException &e){
- light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
- }
- player->light = light;
}
-
+
+ // Update lighting on local player (used for wield item)
+ u32 day_night_ratio = getDayNightRatio();
+ {
+ // Get node at head
+
+ // On InvalidPositionException, use this as default
+ // (day: LIGHT_SUN, night: 0)
+ MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
+
+ v3s16 p = lplayer->getLightPosition();
+ node_at_lplayer = m_map->getNodeNoEx(p);
+
+ u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
+ u8 day = light & 0xff;
+ u8 night = (light >> 8) & 0xff;
+ finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
+ }
+
/*
Step active objects and update lighting of them
*/
{
// Update lighting
u8 light = 0;
- try{
- // Get node at head
- v3s16 p = obj->getLightPosition();
- MapNode n = m_map->getNode(p);
- light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
- }
- catch(InvalidPositionException &e){
- light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
- }
+ bool pos_ok;
+
+ // Get node at head
+ v3s16 p = obj->getLightPosition();
+ MapNode n = m_map->getNodeNoEx(p, &pos_ok);
+ if (pos_ok)
+ light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
+ else
+ light = blend_light(day_night_ratio, LIGHT_SUN, 0);
+
obj->updateLight(light);
}
}
object->addToScene(m_smgr, m_texturesource, m_irr);
{ // Update lighting immediately
u8 light = 0;
- try{
- // Get node at head
- v3s16 p = object->getLightPosition();
- MapNode n = m_map->getNode(p);
+ bool pos_ok;
+
+ // Get node at head
+ v3s16 p = object->getLightPosition();
+ MapNode n = m_map->getNodeNoEx(p, &pos_ok);
+ if (pos_ok)
light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
- }
- catch(InvalidPositionException &e){
+ else
light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
- }
+
object->updateLight(light);
}
return object->getId();
const std::string &init_data)
{
ClientActiveObject* obj =
- ClientActiveObject::create(type, m_gamedef, this);
+ ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
if(obj == NULL)
{
infostream<<"ClientEnvironment::addActiveObject(): "
assert(lplayer);
if(handle_hp){
+ if (lplayer->hp == 0) // Don't damage a dead player
+ return;
if(lplayer->hp > damage)
lplayer->hp -= damage;
else
ClientEnvEvent ClientEnvironment::getClientEvent()
{
+ ClientEnvEvent event;
if(m_client_event_queue.empty())
- {
- ClientEnvEvent event;
event.type = CEE_NONE;
- return event;
+ else {
+ event = m_client_event_queue.front();
+ m_client_event_queue.pop_front();
}
- return m_client_event_queue.pop_front();
+ return event;
}
#endif // #ifndef SERVER