Copyright (C) 2010-2011 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 General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+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 General Public License for more details.
+GNU Lesser General Public License for more details.
-You should have received a copy of the GNU General Public License along
+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 "localplayer.h"
#endif
#include "daynightratio.h"
+#include "map.h"
+#include "util/serialize.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
u32 Environment::getDayNightRatio()
{
- return time_to_daynight_ratio(m_time_of_day);
+ bool smooth = (g_settings->getS32("enable_shaders") != 0);
+ return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
}
void Environment::stepTimeOfDay(float dtime)
m_emerger(emerger),
m_random_spawn_timer(3),
m_send_recommended_timer(0),
+ m_active_block_interval_overload_skip(0),
m_game_time(0),
- m_game_time_fraction_counter(0)
+ m_game_time_fraction_counter(0),
+ m_recommended_send_interval(0.1)
{
}
}
}
+Map & ServerEnvironment::getMap()
+{
+ return *m_map;
+}
+
+ServerMap & ServerEnvironment::getServerMap()
+{
+ return *m_map;
+}
+
+
void ServerEnvironment::serializePlayers(const std::string &savedir)
{
std::string players_path = savedir + "/players";
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)
+ 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,
<<dtime_s<<" seconds old."<<std::endl;*/
// Activate stored objects
- activateObjects(block);
-
- // Run node metadata
- bool changed = block->m_node_metadata->step((float)dtime_s);
- if(changed)
- {
- MapEditEvent event;
- event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
- event.p = block->getPos();
- m_map->dispatchEvent(&event);
-
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "node metadata modified in activateBlock");
+ activateObjects(block, dtime_s);
+
+ // Run node timers
+ std::map<v3s16, NodeTimer> elapsed_timers =
+ block->m_node_timers.step((float)dtime_s);
+ if(!elapsed_timers.empty()){
+ MapNode n;
+ for(std::map<v3s16, NodeTimer>::iterator
+ i = elapsed_timers.begin();
+ i != elapsed_timers.end(); i++){
+ n = block->getNodeNoEx(i->first);
+ if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
+ block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
+ }
}
/* Handle ActiveBlockModifiers */
m_abms.push_back(ABMWithState(abm));
}
+bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
+{
+ INodeDefManager *ndef = m_gamedef->ndef();
+ 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);
+ // 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);
+ // Call constructor
+ if(ndef->get(n).has_on_construct)
+ scriptapi_node_on_construct(m_lua, p, n);
+ return true;
+}
+
+bool ServerEnvironment::removeNode(v3s16 p)
+{
+ INodeDefManager *ndef = m_gamedef->ndef();
+ 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);
+ // Replace with air
+ // This is slightly optimized compared to addNodeWithEvent(air)
+ bool succeeded = m_map->removeNodeWithEvent(p);
+ if(!succeeded)
+ return false;
+ // Call post-destructor
+ if(ndef->get(n_old).has_after_destruct)
+ scriptapi_node_after_destruct(m_lua, p, n_old);
+ // Air doesn't require constructor
+ return true;
+}
+
std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
{
std::set<u16> objects;
/* Step time of day */
stepTimeOfDay(dtime);
+ // Update this one
+ // NOTE: This is kind of funny on a singleplayer game, but doesn't
+ // really matter that much.
+ m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
+
/*
Increment game time
*/
block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
"Timestamp older than 60s (step)");
- // Run node metadata
- bool changed = block->m_node_metadata->step(dtime);
- if(changed)
- {
- MapEditEvent event;
- event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
- event.p = p;
- m_map->dispatchEvent(&event);
-
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "node metadata modified in step");
+ // Run node timers
+ std::map<v3s16, NodeTimer> elapsed_timers =
+ block->m_node_timers.step((float)dtime);
+ if(!elapsed_timers.empty()){
+ MapNode n;
+ for(std::map<v3s16, NodeTimer>::iterator
+ i = elapsed_timers.begin();
+ 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))
+ block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
+ }
}
}
}
const float abm_interval = 1.0;
if(m_active_block_modifier_interval.step(dtime, abm_interval))
- {
+ do{ // breakable
+ if(m_active_block_interval_overload_skip > 0){
+ ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
+ m_active_block_interval_overload_skip--;
+ break;
+ }
ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
TimeTaker timer("modify in active blocks");
infostream<<"WARNING: active block modifiers took "
<<time_ms<<"ms (longer than "
<<max_time_ms<<"ms)"<<std::endl;
+ m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
}
- }
+ }while(0);
/*
Step script environment (run global on_step())
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{
assert(object);
- u16 id = addActiveObjectRaw(object, true);
+ u16 id = addActiveObjectRaw(object, true, 0);
return id;
}
*/
u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
- bool set_changed)
+ bool set_changed, u32 dtime_s)
{
assert(object);
if(object->getId() == 0){
// Register reference in scripting api (must be done before post-init)
scriptapi_add_object_reference(m_lua, object);
// Post-initialize object
- object->addedToEnvironment();
+ object->addedToEnvironment(dtime_s);
// Add static data to block
if(object->isStaticAllowed())
"addActiveObjectRaw");
}
else{
+ v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"could not find block for storing id="<<object->getId()
- <<" statically"<<std::endl;
+ <<" statically (pos="<<PP(p)<<")"<<std::endl;
}
}
/*
Convert stored objects from blocks near the players to active.
*/
-void ServerEnvironment::activateObjects(MapBlock *block)
+void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
{
if(block==NULL)
return;
"large amount of objects");
return;
}
- // A list for objects that couldn't be converted to static for some
+ // A list for objects that couldn't be converted to active for some
// reason. They will be stored back.
core::list<StaticObject> new_stored;
// Loop through stored static objects
<<"activated static object pos="<<PP(s_obj.pos/BS)
<<" type="<<(int)s_obj.type<<std::endl;
// This will also add the object to the active static list
- addActiveObjectRaw(obj, false);
+ addActiveObjectRaw(obj, false, dtime_s);
}
// Clear stored list
block->m_static_objects.m_stored.clear();
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
// Get or generate the block
- MapBlock *block = m_map->emergeBlock(blockpos);
+ MapBlock *block = NULL;
+ try{
+ block = m_map->emergeBlock(blockpos);
+ } catch(InvalidPositionException &e){
+ // Handled via NULL pointer
+ }
if(block)
{
force_delete = true;
} else {
u16 new_id = pending_delete ? id : 0;
+ // 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(new_id && block->m_static_objects.m_active.find(new_id)){
+ infostream<<"ServerEnv: WARNING: Performing hack #83274"
+ <<std::endl;
+ block->m_static_objects.remove(new_id);
+ }
block->m_static_objects.insert(new_id, s_obj);
// Only mark block as modified if data changed considerably
}
else{
if(!force_delete){
+ v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnv: Could not find or generate "
<<"a block for storing id="<<obj->getId()
- <<" statically"<<std::endl;
+ <<" statically (pos="<<PP(p)<<")"<<std::endl;
continue;
}
}
i != player_collisions.end(); i++)
{
CollisionInfo &info = *i;
- if(info.t == COLLISION_FALL)
+ v3f speed_diff = info.new_speed - info.old_speed;;
+ // Handle only fall damage
+ // (because otherwise walking against something in fast_move kills you)
+ if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
+ continue;
+ // Get rid of other components
+ speed_diff.X = 0;
+ speed_diff.Z = 0;
+ f32 pre_factor = 1; // 1 hp per node/s
+ f32 tolerance = BS*14; // 5 without damage
+ f32 post_factor = 1; // 1 hp per node/s
+ if(info.type == COLLISION_NODE)
{
- //f32 tolerance = BS*10; // 2 without damage
- //f32 tolerance = BS*12; // 3 without damage
- f32 tolerance = BS*14; // 5 without damage
- f32 factor = 1;
- if(info.speed > tolerance)
- {
- f32 damage_f = (info.speed - tolerance)/BS*factor;
- u16 damage = (u16)(damage_f+0.5);
- if(damage != 0)
- damageLocalPlayer(damage, true);
- }
+ const ContentFeatures &f = m_gamedef->ndef()->
+ get(m_map->getNodeNoEx(info.node_p));
+ // Determine fall damage multiplier
+ int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
+ pre_factor = 1.0 + (float)addp/100.0;
+ }
+ float speed = pre_factor * speed_diff.getLength();
+ if(speed > tolerance)
+ {
+ f32 damage_f = (speed - tolerance)/BS * post_factor;
+ u16 damage = (u16)(damage_f+0.5);
+ if(damage != 0)
+ damageLocalPlayer(damage, true);
}
}
}
// Update lighting on all players on client
- u8 light = LIGHT_MAX;
+ float light = 1.0;
try{
// Get node at head
v3s16 p = player->getLightPosition();
MapNode n = m_map->getNode(p);
- light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
+ light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
}
catch(InvalidPositionException &e){
- light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
+ light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
}
player->light = light;
}
Step active objects and update lighting of them
*/
+ bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
for(core::map<u16, ClientActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
// Step object
obj->step(dtime, this);
- if(m_active_object_light_update_interval.step(dtime, 0.21))
+ if(update_lighting)
{
// Update lighting
u8 light = 0;
{
errorstream<<"ClientEnvironment::addActiveObject():"
<<" id="<<id<<" type="<<type
- <<": SerializationError in initialize(),"
- <<" init_data="<<serializeJsonString(init_data)
+ <<": SerializationError in initialize(): "
+ <<e.what()
+ <<": init_data="<<serializeJsonString(init_data)
<<std::endl;
}
<<"id="<<id<<" not found"<<std::endl;
return;
}
- obj->removeFromScene();
+ obj->removeFromScene(true);
delete obj;
m_active_objects.remove(id);
}