/*
Minetest
Copyright (C) 2010-2013 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 "environment.h"
#include "filesys.h"
#include "porting.h"
#include "collision.h"
#include "content_mapnode.h"
#include "mapblock.h"
#include "serverobject.h"
#include "content_sao.h"
#include "settings.h"
#include "log.h"
#include "profiler.h"
#include "scripting_game.h"
#include "nodedef.h"
#include "nodemetadata.h"
#include "main.h" // For g_settings, g_profiler
#include "gamedef.h"
#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<<")"
Environment::Environment():
m_time_of_day(9000),
m_time_of_day_f(9000./24000),
m_time_of_day_speed(0),
m_time_counter(0)
{
}
Environment::~Environment()
{
// Deallocate players
for(std::list<Player*>::iterator i = m_players.begin();
i != m_players.end(); ++i)
{
delete (*i);
}
}
void Environment::addPlayer(Player *player)
{
DSTACK(__FUNCTION_NAME);
/*
Check that peer_ids are unique.
Also check that names are unique.
Exception: there can be multiple players with peer_id=0
*/
// If peer id is non-zero, it has to be unique.
if(player->peer_id != 0)
assert(getPlayer(player->peer_id) == NULL);
// Name has to be unique.
assert(getPlayer(player->getName()) == NULL);
// Add.
m_players.push_back(player);
}
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)
{
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;
}
}
Player * Environment::getPlayer(u16 peer_id)
{
for(std::list<Player*>::iterator i = m_players.begin();
i != m_players.end(); ++i)
{
Player *player = *i;
if(player->peer_id == peer_id)
return player;
}
return NULL;
}
Player * Environment::getPlayer(const char *name)
{
for(std::list<Player*>::iterator i = m_players.begin();
i != m_players.end(); ++i)
{
Player *player = *i;
if(strcmp(player->getName(), name) == 0)
return player;
}
return NULL;
}
Player * Environment::getRandomConnectedPlayer()
{
std::list<Player*> connected_players = getPlayers(true);
u32 chosen_one = myrand() % connected_players.size();
u32 j = 0;
for(std::list<Player*>::iterator
i = connected_players.begin();
i != connected_players.end(); ++i)
{
if(j == chosen_one)
{
Player *player = *i;
return player;
}
j++;
}
return NULL;
}
Player * Environment::getNearestConnectedPlayer(v3f pos)
{
std::list<Player*> connected_players = getPlayers(true);
f32 nearest_d = 0;
Player *nearest_player = NULL;
for(std::list<Player*>::iterator
i = connected_players.begin();
i != connected_players.end(); ++i)
{
Player *player = *i;
f32 d = player->getPosition().getDistanceFrom(pos);
if(d < nearest_d || nearest_player == NULL)
{
nearest_d = d;
nearest_player = player;
}
}
return nearest_player;
}
std::list<Player*> Environment::getPlayers()
{
return m_players;
}
std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
{
std::list<Player*> newlist;
for(std::list<Player*>::iterator
i = m_players.begin();
i != m_players.end(); ++i)
{
Player *player = *i;
if(ignore_disconnected)
{
// Ignore disconnected players
if(player->peer_id == 0)
continue;
}
newlist.push_back(player);
}
return newlist;
}
u32 Environment::getDayNightRatio()
{
bool smooth = g_settings->getBool("enable_shaders");
return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
}
void Environment::stepTimeOfDay(float dtime)
{
m_time_counter += dtime;
f32 speed = m_time_of_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(m_time_of_day + units >= 24000)
sync_f = true;
m_time_of_day = (m_time_of_day + units) % 24000;
if(sync_f)
m_time_of_day_f = (float)m_time_of_day / 24000.0;
}
if(!sync_f){
m_time_of_day_f += m_time_of_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)
m_time_of_day_f += 1.0;
}
}
/*
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);
}
/*
ActiveBlockList
*/
void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
{
v3s16 p;
for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
{
// Set in list
list.insert(p);
}
}
void ActiveBlockList::update(std::list<v3s16> &active_positions,
s16 radius,
std::set<v3s16> &blocks_removed,
std::set<v3s16> &blocks_added)
{
/*
Create the new list
*/
std::set<v3s16> newlist;
for(std::list<v3s16>::iterator i = active_positions.begin();
i != active_positions.end(); ++i)
{
fillRadiusBlock(*i, radius, newlist);
}
/*
Find out which blocks on the old list are not on the new list
*/
// Go through old list
for(std::set<v3s16>::iterator i = m_list.begin();
i != m_list.end(); ++i)
{
v3s16 p = *i;
// If not on new list, it's been removed
if(newlist.find(p) == newlist.end())
blocks_removed.insert(p);
}
/*
Find out which blocks on the new list are not on the old list
*/
// Go through new list
for(std::set<v3s16>::iterator i = newlist.begin();
i != newlist.end(); ++i)
{
v3s16 p = *i;
// If not on old list, it's been added
if(m_list.find(p) == m_list.end())
blocks_added.insert(p);
}
/*
Update m_list
*/
m_list.clear();
for(std::set<v3s16>::iterator i = newlist.begin();
i != newlist.end(); ++i)
{
v3s16 p = *i;
m_list.insert(p);
}
}
/*
ServerEnvironment
*/
ServerEnvironment::ServerEnvironment(ServerMap *map,
GameScripting *scriptIface,
IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
m_map(map),
m_script(scriptIface),
m_gamedef(gamedef),
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_recommended_send_interval(0.1),
m_max_lag_estimate(0.1)
{
m_use_weather = g_settings->getBool("weather");
}
ServerEnvironment::~ServerEnvironment()
{
// Clear active block list.
// This makes the next one delete all active objects.
m_active_blocks.clear();
// Convert all objects to static and delete the active objects
deactivateFarObjects(true);
// Drop/delete map
m_map->drop();
// Delete ActiveBlockModifiers
for(std::list<ABMWithState>::iterator
i = m_abms.begin(); i != m_abms.end(); ++i){
delete i->abm;
}
}
Map & ServerEnvironment::getMap()
{
return *m_map;
}
ServerMap & ServerEnvironment::getServerMap()
{
return *m_map;
}
bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
{
float distance = pos1.getDistanceFrom(pos2);
//calculate normalized direction vector
v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
(pos2.Y - pos1.Y)/distance,
(pos2.Z - pos1.Z)/distance);
//find out if there's a node on path between pos1 and pos2
for (float i = 1; i < distance; i += stepsize) {
v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
normalized_vector.Y * i,
normalized_vector.Z * i) +pos1,BS);
MapNode n = getMap().getNodeNoEx(pos);
if(n.param0 != CONTENT_AIR) {
return false;
}
}
return true;
}
void ServerEnvironment::serializePlayers(const std::string &savedir)
{
std::string players_path = savedir + "/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::ostringstream ss(std::ios_base::binary);
player->serialize(ss);
if(!fs::safeWriteToFile(path, ss.str()))
{
infostream<<"Failed to write "<<path<<std::endl;
continue;
}
saved_players.insert(player);
} else {
saved_players.insert(player);
}
}
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
|