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 "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 "event.h"
#endif
#include "daynightratio.h"
#include "map.h"
+#include "emerge.h"
#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
return newlist;
}
-void Environment::printPlayers(std::ostream &o)
-{
- 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;
- }
-}
-
u32 Environment::getDayNightRatio()
{
- bool smooth = (g_settings->getS32("enable_shaders") != 0);
+ bool smooth = g_settings->getBool("enable_shaders");
return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
}
ServerEnvironment
*/
-ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
+ServerEnvironment::ServerEnvironment(ServerMap *map,
+ GameScripting *scriptIface,
IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
m_map(map),
- m_lua(L),
+ m_script(scriptIface),
m_gamedef(gamedef),
m_emerger(emerger),
m_random_spawn_timer(3),
m_active_block_interval_overload_skip(0),
m_game_time(0),
m_game_time_fraction_counter(0),
- m_recommended_send_interval(0.1)
+ m_recommended_send_interval(0.1),
+ m_max_lag_estimate(0.1)
{
+ m_use_weather = g_settings->getBool("weather");
}
ServerEnvironment::~ServerEnvironment()
std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
for(u32 i=0; i<player_files.size(); i++)
{
- if(player_files[i].dir)
+ if(player_files[i].dir || player_files[i].name[0] == '.')
continue;
// Full path to this file
infostream<<"Failed to read "<<path<<std::endl;
continue;
}
- testplayer.deSerialize(is);
+ testplayer.deSerialize(is, player_files[i].name);
}
//infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
//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)
+ std::ostringstream ss(std::ios_base::binary);
+ player->serialize(ss);
+ if(!fs::safeWriteToFile(path, ss.str()))
{
- infostream<<"Failed to overwrite "<<path<<std::endl;
+ infostream<<"Failed to write "<<path<<std::endl;
continue;
}
- player->serialize(os);
+ saved_players.insert(player);
+ } else {
saved_players.insert(player);
}
}
/*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)
+ std::ostringstream ss(std::ios_base::binary);
+ player->serialize(ss);
+ if(!fs::safeWriteToFile(path, ss.str()))
{
- infostream<<"Failed to overwrite "<<path<<std::endl;
+ infostream<<"Failed to write "<<path<<std::endl;
continue;
}
- player->serialize(os);
saved_players.insert(player);
}
}
infostream<<"Failed to read "<<path<<std::endl;
continue;
}
- testplayer.deSerialize(is);
+ testplayer.deSerialize(is, player_files[i].name);
}
if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
infostream<<"Failed to read "<<path<<std::endl;
continue;
}
- player->deSerialize(is);
+ player->deSerialize(is, player_files[i].name);
}
if(newplayer)
std::string path = savedir + "/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)
// Activate stored objects
activateObjects(block, dtime_s);
+
+ // Calculate weather conditions
+ if (m_use_weather) {
+ m_map->updateBlockHeat(this, block->getPos() * MAP_BLOCKSIZE, block);
+ m_map->updateBlockHumidity(this, block->getPos() * MAP_BLOCKSIZE, block);
+ } else {
+ block->heat = HEAT_UNDEFINED;
+ block->humidity = HUMIDITY_UNDEFINED;
+ block->weather_update_time = 0;
+ }
// Run node timers
std::map<v3s16, NodeTimer> elapsed_timers =
i != elapsed_timers.end(); i++){
n = block->getNodeNoEx(i->first);
v3s16 p = i->first + block->getPosRelative();
- if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
+ if(m_script->node_on_timer(p,n,i->second.elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
}
}
MapNode n_old = m_map->getNodeNoEx(p);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
- scriptapi_node_on_destruct(m_lua, p, n_old);
+ m_script->node_on_destruct(p, n_old);
// Replace node
bool succeeded = m_map->addNodeWithEvent(p, n);
if(!succeeded)
return false;
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
- scriptapi_node_after_destruct(m_lua, p, n_old);
+ m_script->node_after_destruct(p, n_old);
// Call constructor
if(ndef->get(n).has_on_construct)
- scriptapi_node_on_construct(m_lua, p, n);
+ m_script->node_on_construct(p, n);
return true;
}
MapNode n_old = m_map->getNodeNoEx(p);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
- scriptapi_node_on_destruct(m_lua, p, n_old);
+ 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);
return false;
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
- scriptapi_node_after_destruct(m_lua, p, n_old);
+ m_script->node_after_destruct(p, n_old);
// Air doesn't require constructor
return true;
}
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- scriptapi_rm_object_reference(m_lua, obj);
+ m_script->removeObjectReference(obj);
// Delete active object
if(obj->environmentDeletes())
m_active_objects.erase(*i);
}
+ // Get list of loaded blocks
+ std::list<v3s16> loaded_blocks;
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Listing all loaded blocks"<<std::endl;
+ m_map->listAllLoadedBlocks(loaded_blocks);
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Done listing all loaded blocks: "
+ <<loaded_blocks.size()<<std::endl;
+
+ // Get list of loadable blocks
std::list<v3s16> loadable_blocks;
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Listing all loadable blocks"<<std::endl;
<<"Done listing all loadable blocks: "
<<loadable_blocks.size()
<<", 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)
+ {
+ v3s16 p = *i;
+ MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+ assert(block);
+ block->refGrab();
+ }
+
+ // Remove objects in all loadable blocks
+ u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
+ unload_interval = MYMAX(unload_interval, 1);
u32 report_interval = loadable_blocks.size() / 10;
u32 num_blocks_checked = 0;
u32 num_blocks_cleared = 0;
<<" in "<<num_blocks_cleared<<" blocks ("
<<percent<<"%)"<<std::endl;
}
+ if(num_blocks_checked % unload_interval == 0){
+ m_map->unloadUnreferencedBlocks();
+ }
+ }
+ m_map->unloadUnreferencedBlocks();
+
+ // Drop references that were added above
+ for(std::list<v3s16>::iterator i = loaded_blocks.begin();
+ i != loaded_blocks.end(); ++i)
+ {
+ v3s16 p = *i;
+ MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+ assert(block);
+ block->refDrop();
}
+
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Finished: Cleared "<<num_objs_cleared<<" objects"
<<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL){
// Block needs to be fetched first
- m_emerger->queueBlockEmerge(p, false);
+ m_emerger->enqueueBlockEmerge(
+ PEER_ID_INEXISTENT, p, false);
m_active_blocks.m_list.erase(p);
continue;
}
i != elapsed_timers.end(); i++){
n = block->getNodeNoEx(i->first);
p = i->first + block->getPosRelative();
- if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
+ if(m_script->node_on_timer(p,n,i->second.elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
}
}
/*
Step script environment (run global on_step())
*/
- scriptapi_environment_step(m_lua, dtime);
+ m_script->environment_Step(dtime);
/*
Step active objects
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;
}
/*
<<std::endl;
// Register reference in scripting api (must be done before post-init)
- scriptapi_add_object_reference(m_lua, object);
+ m_script->addObjectReference(object);
// Post-initialize object
object->addedToEnvironment(dtime_s);
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- scriptapi_rm_object_reference(m_lua, obj);
+ m_script->removeObjectReference(obj);
// Delete
if(obj->environmentDeletes())
<<"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 "
// 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;
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 {
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- scriptapi_rm_object_reference(m_lua, obj);
+ m_script->removeObjectReference(obj);
// Delete active object
if(obj->environmentDeletes())
{
f32 damage_f = (speed - tolerance)/BS * post_factor;
u16 damage = (u16)(damage_f+0.5);
- if(damage != 0)
+ if(damage != 0){
damageLocalPlayer(damage, true);
+ MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
+ m_gamedef->event()->put(e);
+ }
}
}
damageLocalPlayer(damage_per_second, true);
}
}
-
+
+ /*
+ Drowning
+ */
+ if(m_drowning_interval.step(dtime, 2.0))
+ {
+ v3f pf = lplayer->getPosition();
+
+ // head
+ v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
+ MapNode n = m_map->getNodeNoEx(p);
+ ContentFeatures c = m_gamedef->ndef()->get(n);
+ u8 drowning_damage = c.drowning;
+ if(drowning_damage > 0 && lplayer->hp > 0){
+ u16 breath = lplayer->getBreath();
+ if(breath > 10){
+ breath = 11;
+ }
+ if(breath > 0){
+ breath -= 1;
+ }
+ lplayer->setBreath(breath);
+ updateLocalPlayerBreath(breath);
+ }
+
+ if(lplayer->getBreath() == 0 && drowning_damage > 0){
+ damageLocalPlayer(drowning_damage, true);
+ }
+ }
+ if(m_breathing_interval.step(dtime, 0.5))
+ {
+ v3f pf = lplayer->getPosition();
+
+ // head
+ 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 (!lplayer->hp){
+ lplayer->setBreath(11);
+ }
+ else if(c.drowning == 0){
+ u16 breath = lplayer->getBreath();
+ if(breath <= 10){
+ breath += 1;
+ lplayer->setBreath(breath);
+ updateLocalPlayerBreath(breath);
+ }
+ }
+ }
+
/*
Stuff that can be done in an arbitarily large dtime
*/
Step active objects and update lighting of them
*/
+ g_profiler->avg("CEnv: num of objects", m_active_objects.size());
bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
for(std::map<u16, ClientActiveObject*>::iterator
i = m_active_objects.begin();
/*
Step and handle simple objects
*/
+ g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
for(std::list<ClientSimpleObject*>::iterator
i = m_simple_objects.begin(); i != m_simple_objects.end();)
{
m_client_event_queue.push_back(event);
}
+void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
+{
+ ClientEnvEvent event;
+ event.type = CEE_PLAYER_BREATH;
+ event.player_breath.amount = breath;
+ m_client_event_queue.push_back(event);
+}
+
/*
Client likes to call these
*/
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