Preliminary "active block" stuff + set up test code to grow grass.

This commit is contained in:
Perttu Ahola 2011-05-22 17:00:09 +03:00
parent af7d50e910
commit 0af5311538
14 changed files with 848 additions and 383 deletions

@ -1193,31 +1193,23 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
if(datasize < 4) if(datasize < 4)
return; return;
u16 time = readU16(&data[2]); u16 time_of_day = readU16(&data[2]);
time = time % 24000; time_of_day = time_of_day % 24000;
m_time_of_day = time; //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
//dstream<<"Client: time="<<time<<std::endl;
/* /*
Day/night
time_of_day: time_of_day:
0 = midnight 0 = midnight
12000 = midday 12000 = midday
*/ */
{ {
u32 dr = time_to_daynight_ratio(m_time_of_day); m_env.setTimeOfDay(time_of_day);
dstream<<"Client: time_of_day="<<m_time_of_day u32 dr = m_env.getDayNightRatio();
dstream<<"Client: time_of_day="<<time_of_day
<<", dr="<<dr <<", dr="<<dr
<<std::endl; <<std::endl;
if(dr != m_env.getDayNightRatio())
{
dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
m_env.setDayNightRatio(dr);
m_env.expireMeshes(true);
}
} }
} }

@ -23,9 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h" #include "collision.h"
Environment::Environment() Environment::Environment():
m_time_of_day(9000)
{ {
m_daynight_ratio = 0.5;
} }
Environment::~Environment() Environment::~Environment()
@ -174,14 +174,84 @@ void Environment::printPlayers(std::ostream &o)
} }
} }
void Environment::setDayNightRatio(u32 r) /*void Environment::setDayNightRatio(u32 r)
{ {
m_daynight_ratio = r; getDayNightRatio() = r;
} }*/
u32 Environment::getDayNightRatio() u32 Environment::getDayNightRatio()
{ {
return m_daynight_ratio; //return getDayNightRatio();
return time_to_daynight_ratio(m_time_of_day);
}
/*
ActiveBlockList
*/
void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &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[p] = true;
}
}
void ActiveBlockList::update(core::list<v3s16> &active_positions,
s16 radius,
core::map<v3s16, bool> &blocks_removed,
core::map<v3s16, bool> &blocks_added)
{
/*
Create the new list
*/
core::map<v3s16, bool> newlist;
for(core::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(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
// If not on new list, it's been removed
if(newlist.find(p) == NULL)
blocks_removed.insert(p, true);
}
/*
Find out which blocks on the new list are not on the old list
*/
// Go through new list
for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
// If not on old list, it's been added
if(m_list.find(p) == NULL)
blocks_added.insert(p, true);
}
/*
Update m_list
*/
m_list.clear();
for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
m_list.insert(p, true);
}
} }
/* /*
@ -192,16 +262,20 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
m_map(map), m_map(map),
m_server(server), m_server(server),
m_random_spawn_timer(3), m_random_spawn_timer(3),
m_send_recommended_timer(0) m_send_recommended_timer(0),
m_game_time(0),
m_game_time_fraction_counter(0)
{ {
} }
ServerEnvironment::~ServerEnvironment() ServerEnvironment::~ServerEnvironment()
{ {
/* // Clear active block list.
Convert all objects to static (thus delete the active objects) // This makes the next one delete all active objects.
*/ m_active_blocks.clear();
deactivateFarObjects(-1);
// Convert all objects to static and delete the active objects
deactivateFarObjects(true);
// Drop/delete map // Drop/delete map
m_map->drop(); m_map->drop();
@ -384,7 +458,71 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
} }
} }
void ServerEnvironment::saveMeta(const std::string &savedir)
{
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)
{
dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
<<path<<std::endl;
throw SerializationError("Couldn't save env meta");
}
Settings args;
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
args.writeLines(os);
os<<"EnvArgsEnd\n";
}
void ServerEnvironment::loadMeta(const std::string &savedir)
{
std::string path = savedir + "/env_meta.txt";
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
if(is.good() == false)
{
dstream<<"WARNING: 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);
}
try{
m_game_time = args.getU64("game_time");
}catch(SettingNotFoundException &e){
// Getting this is crucial, otherwise timestamps are useless
throw SerializationError("Couldn't load env meta game_time");
}
try{
m_time_of_day = args.getU64("time_of_day");
}catch(SettingNotFoundException &e){
// This is not as important
m_time_of_day = 9000;
}
}
#if 0 #if 0
// This is probably very useless
void spawnRandomObjects(MapBlock *block) void spawnRandomObjects(MapBlock *block)
{ {
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
@ -440,9 +578,21 @@ void ServerEnvironment::step(float dtime)
//TimeTaker timer("ServerEnv step"); //TimeTaker timer("ServerEnv step");
// Get some settings // Get some settings
//bool free_move = g_settings.getBool("free_move");
bool footprints = g_settings.getBool("footprints"); bool footprints = g_settings.getBool("footprints");
/*
Increment game time
*/
{
m_game_time_fraction_counter += dtime;
u32 inc_i = (u32)m_game_time_fraction_counter;
m_game_time += inc_i;
m_game_time_fraction_counter -= (float)inc_i;
}
/*
Let map update it's timers
*/
{ {
//TimeTaker timer("Server m_map->timerUpdate()"); //TimeTaker timer("Server m_map->timerUpdate()");
m_map->timerUpdate(dtime); m_map->timerUpdate(dtime);
@ -486,12 +636,188 @@ void ServerEnvironment::step(float dtime)
} }
} }
/*
Manage active block list
*/
if(m_active_blocks_management_interval.step(dtime, 2.0))
{
/*
Get player block positions
*/
core::list<v3s16> players_blockpos;
for(core::list<Player*>::Iterator
i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
// Ignore disconnected players
if(player->peer_id == 0)
continue;
v3s16 blockpos = getNodeBlockPos(
floatToInt(player->getPosition(), BS));
players_blockpos.push_back(blockpos);
}
/*
Update list of active blocks, collecting changes
*/
const s16 active_block_range = 5;
core::map<v3s16, bool> blocks_removed;
core::map<v3s16, bool> blocks_added;
m_active_blocks.update(players_blockpos, active_block_range,
blocks_removed, blocks_added);
/*
Handle removed blocks
*/
// Convert active objects that are no more in active blocks to static
deactivateFarObjects(false);
for(core::map<v3s16, bool>::Iterator
i = blocks_removed.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
/*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") became inactive"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
continue;
// Set current time as timestamp
block->setTimestamp(m_game_time);
}
/*
Handle added blocks
*/
for(core::map<v3s16, bool>::Iterator
i = blocks_added.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
/*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") became active"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
continue;
// Get time difference
u32 dtime_s = 0;
u32 stamp = block->getTimestamp();
if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
dtime_s = m_game_time - block->getTimestamp();
// Set current time as timestamp
block->setTimestamp(m_game_time);
//dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
// Activate stored objects
activateObjects(block);
// TODO: Do something
// Here's a quick demonstration
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0);
// Test something:
// Convert all mud under proper day lighting to grass
if(n.d == CONTENT_MUD)
{
if(1)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(content_features(n_top.d).walkable == false &&
n_top.getLight(LIGHTBANK_DAY) >= 13)
{
n.d = CONTENT_GRASS;
m_map->addNodeWithEvent(p, n);
}
}
}
}
}
}
/*
Mess around in active blocks
*/
if(m_active_blocks_test_interval.step(dtime, 5.0))
{
for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
/*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") being handled"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
continue;
// Set current time as timestamp
block->setTimestamp(m_game_time);
/*
Do stuff!
Note that map modifications should be done using the event-
making map methods so that the server gets information
about them.
Reading can be done quickly directly from the block.
Everything should bind to inside this single content
searching loop to keep things fast.
*/
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0);
// Test something:
// Convert mud under proper lighting to grass
if(n.d == CONTENT_MUD)
{
if(myrand()%4 == 0)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(content_features(n_top.d).walkable == false &&
n_top.getLightBlend(getDayNightRatio()) >= 13)
{
n.d = CONTENT_GRASS;
m_map->addNodeWithEvent(p, n);
}
}
}
}
}
}
/* /*
Step active objects Step active objects
*/ */
{ {
//TimeTaker timer("Step active objects"); //TimeTaker timer("Step active objects");
// This helps the objects to send data at the same time
bool send_recommended = false; bool send_recommended = false;
m_send_recommended_timer += dtime; m_send_recommended_timer += dtime;
if(m_send_recommended_timer > 0.15) if(m_send_recommended_timer > 0.15)
@ -505,33 +831,23 @@ void ServerEnvironment::step(float dtime)
i.atEnd()==false; i++) i.atEnd()==false; i++)
{ {
ServerActiveObject* obj = i.getNode()->getValue(); ServerActiveObject* obj = i.getNode()->getValue();
// Don't step if is to be removed or stored statically
if(obj->m_removed || obj->m_pending_deactivation)
continue;
// Step object, putting messages directly to the queue // Step object, putting messages directly to the queue
obj->step(dtime, m_active_object_messages, send_recommended); obj->step(dtime, m_active_object_messages, send_recommended);
} }
} }
/*
Manage active objects
*/
if(m_object_management_interval.step(dtime, 0.5)) if(m_object_management_interval.step(dtime, 0.5))
{ {
//TimeTaker timer("ServerEnv object management");
const s16 to_active_range_blocks = 3;
const s16 to_static_range_blocks = 4;
//const f32 to_static_max_f = (to_active_max_blocks+2)*MAP_BLOCKSIZE*BS;
/* /*
Remove objects that satisfy (m_removed && m_known_by_count==0) Remove objects that satisfy (m_removed && m_known_by_count==0)
*/ */
removeRemovedObjects(); removeRemovedObjects();
/*
Convert stored objects from blocks near the players to active.
*/
activateNearObjects(to_active_range_blocks);
/*
Convert objects that are far away from all the players to static.
*/
deactivateFarObjects(to_static_range_blocks);
} }
if(g_settings.getBool("enable_experimental")) if(g_settings.getBool("enable_experimental"))
@ -791,11 +1107,18 @@ void ServerEnvironment::removeRemovedObjects()
objects_to_remove.push_back(id); objects_to_remove.push_back(id);
continue; continue;
} }
// If not m_removed, don't remove.
if(obj->m_removed == false) /*
We will delete objects that are marked as removed or thatare
waiting for deletion after deactivation
*/
if(obj->m_removed == false && obj->m_pending_deactivation == false)
continue; continue;
// Delete static data from block
if(obj->m_static_exists) /*
Delete static data from block if is marked as removed
*/
if(obj->m_static_exists && obj->m_removed)
{ {
MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
if(block) if(block)
@ -804,9 +1127,11 @@ void ServerEnvironment::removeRemovedObjects()
block->setChangedFlag(); block->setChangedFlag();
} }
} }
// If m_known_by_count > 0, don't actually remove. // If m_known_by_count > 0, don't actually remove.
if(obj->m_known_by_count > 0) if(obj->m_known_by_count > 0)
continue; continue;
// Delete // Delete
delete obj; delete obj;
// Id to be removed from m_active_objects // Id to be removed from m_active_objects
@ -823,35 +1148,15 @@ void ServerEnvironment::removeRemovedObjects()
/* /*
Convert stored objects from blocks near the players to active. Convert stored objects from blocks near the players to active.
*/ */
void ServerEnvironment::activateNearObjects(s16 range_blocks) void ServerEnvironment::activateObjects(MapBlock *block)
{ {
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
// Ignore disconnected players
if(player->peer_id == 0)
continue;
v3f playerpos = player->getPosition();
v3s16 blockpos0 = getNodeBlockPos(floatToInt(playerpos, BS));
v3s16 bpmin = blockpos0 - v3s16(1,1,1)*range_blocks;
v3s16 bpmax = blockpos0 + v3s16(1,1,1)*range_blocks;
// Loop through all nearby blocks
for(s16 x=bpmin.X; x<=bpmax.X; x++)
for(s16 y=bpmin.Y; y<=bpmax.Y; y++)
for(s16 z=bpmin.Z; z<=bpmax.Z; z++)
{
v3s16 blockpos(x,y,z);
MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
if(block==NULL) if(block==NULL)
continue; return;
// Ignore if no stored objects (to not set changed flag) // 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.size() == 0)
continue; return;
// This will contain the leftovers of the stored list // A list for objects that couldn't be converted to static for some
// reason. They will be stored back.
core::list<StaticObject> new_stored; core::list<StaticObject> new_stored;
// Loop through stored static objects // Loop through stored static objects
for(core::list<StaticObject>::Iterator for(core::list<StaticObject>::Iterator
@ -864,10 +1169,9 @@ void ServerEnvironment::activateNearObjects(s16 range_blocks)
// Create an active object from the data // Create an active object from the data
ServerActiveObject *obj = ServerActiveObject::create ServerActiveObject *obj = ServerActiveObject::create
(s_obj.type, this, 0, s_obj.pos, s_obj.data); (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)
{ {
// This is necessary to preserve stuff during bugs
// and errors
new_stored.push_back(s_obj); new_stored.push_back(s_obj);
continue; continue;
} }
@ -877,7 +1181,7 @@ void ServerEnvironment::activateNearObjects(s16 range_blocks)
} }
// Clear stored list // Clear stored list
block->m_static_objects.m_stored.clear(); block->m_static_objects.m_stored.clear();
// Add leftover stuff to stored list // Add leftover failed stuff to stored list
for(core::list<StaticObject>::Iterator for(core::list<StaticObject>::Iterator
i = new_stored.begin(); i = new_stored.begin();
i != new_stored.end(); i++) i != new_stored.end(); i++)
@ -885,20 +1189,21 @@ void ServerEnvironment::activateNearObjects(s16 range_blocks)
StaticObject &s_obj = *i; StaticObject &s_obj = *i;
block->m_static_objects.m_stored.push_back(s_obj); block->m_static_objects.m_stored.push_back(s_obj);
} }
// Block has been modified
block->setChangedFlag(); block->setChangedFlag();
}
}
} }
/* /*
Convert objects that are far away from all the players to static. Convert objects that are not in active blocks to static.
If range_blocks == -1, convert everything to static even if known If m_known_by_count != 0, active object is not deleted, but static
by a player. data is still updated.
If force_delete is set, active object is deleted nevertheless. It
shall only be set so in the destructor of the environment.
*/ */
void ServerEnvironment::deactivateFarObjects(s16 range_blocks) void ServerEnvironment::deactivateFarObjects(bool force_delete)
{ {
bool force_everything = (range_blocks == -1);
core::list<u16> objects_to_remove; core::list<u16> objects_to_remove;
for(core::map<u16, ServerActiveObject*>::Iterator for(core::map<u16, ServerActiveObject*>::Iterator
i = m_active_objects.getIterator(); i = m_active_objects.getIterator();
@ -917,46 +1222,15 @@ void ServerEnvironment::deactivateFarObjects(s16 range_blocks)
continue; continue;
} }
if(force_everything == false) // The block in which the object resides in
{
// If known by some client, don't convert to static.
if(obj->m_known_by_count > 0)
continue;
// If close to some player, don't convert to static.
bool close_to_player = false;
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
// Ignore disconnected players
if(player->peer_id == 0)
continue;
v3f playerpos = player->getPosition();
v3s16 blockpos_p = getNodeBlockPos(floatToInt(playerpos, BS));
v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS)); v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
if(blockpos_p.X >= blockpos_o.X - range_blocks // If block is active, don't remove
&& blockpos_p.Y >= blockpos_o.Y - range_blocks if(m_active_blocks.contains(blockpos_o))
&& blockpos_p.Z >= blockpos_o.Z - range_blocks
&& blockpos_p.X <= blockpos_o.X + range_blocks
&& blockpos_p.Y <= blockpos_o.Y + range_blocks
&& blockpos_p.Z <= blockpos_o.Z + range_blocks)
{
close_to_player = true;
break;
}
}
if(close_to_player)
continue; continue;
}
/* /*
Update the static data and remove the active object. Update the static data
*/ */
// Delete old static object // Delete old static object
@ -971,7 +1245,7 @@ void ServerEnvironment::deactivateFarObjects(s16 range_blocks)
oldblock = block; oldblock = block;
} }
} }
// Add new static object // Create new static object
std::string staticdata = obj->getStaticData(); std::string staticdata = obj->getStaticData();
StaticObject s_obj(obj->getType(), objectpos, staticdata); StaticObject s_obj(obj->getType(), objectpos, staticdata);
// Add to the block where the object is located in // Add to the block where the object is located in
@ -998,6 +1272,19 @@ void ServerEnvironment::deactivateFarObjects(s16 range_blocks)
obj->m_static_exists = false; obj->m_static_exists = false;
continue; continue;
} }
/*
Delete active object if not known by some client,
else set pending deactivation
*/
// If known by some client, don't delete.
if(obj->m_known_by_count > 0 && force_delete == false)
{
obj->m_pending_deactivation = true;
continue;
}
/*dstream<<"INFO: Server: Stored static data. Deleting object." /*dstream<<"INFO: Server: Stored static data. Deleting object."
<<std::endl;*/ <<std::endl;*/
// Delete active object // Delete active object
@ -1005,6 +1292,7 @@ void ServerEnvironment::deactivateFarObjects(s16 range_blocks)
// Id to be removed from m_active_objects // Id to be removed from m_active_objects
objects_to_remove.push_back(id); objects_to_remove.push_back(id);
} }
// Remove references from m_active_objects // Remove references from m_active_objects
for(core::list<u16>::Iterator i = objects_to_remove.begin(); for(core::list<u16>::Iterator i = objects_to_remove.begin();
i != objects_to_remove.end(); i++) i != objects_to_remove.end(); i++)
@ -1230,7 +1518,7 @@ void ClientEnvironment::step(float dtime)
// Get node at head // Get node at head
v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS); v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
MapNode n = m_map->getNode(p); MapNode n = m_map->getNode(p);
light = n.getLightBlend(m_daynight_ratio); light = n.getLightBlend(getDayNightRatio());
} }
catch(InvalidPositionException &e) {} catch(InvalidPositionException &e) {}
player->updateLight(light); player->updateLight(light);
@ -1254,7 +1542,7 @@ void ClientEnvironment::step(float dtime)
{ {
v3s16 p_blocks = getNodeBlockPos(bottompos); v3s16 p_blocks = getNodeBlockPos(bottompos);
MapBlock *b = m_map->getBlockNoCreate(p_blocks); MapBlock *b = m_map->getBlockNoCreate(p_blocks);
//b->updateMesh(m_daynight_ratio); //b->updateMesh(getDayNightRatio());
b->setMeshExpired(true); b->setMeshExpired(true);
} }
} }
@ -1283,7 +1571,7 @@ void ClientEnvironment::step(float dtime)
// Get node at head // Get node at head
v3s16 p = obj->getLightPosition(); v3s16 p = obj->getLightPosition();
MapNode n = m_map->getNode(p); MapNode n = m_map->getNode(p);
light = n.getLightBlend(m_daynight_ratio); light = n.getLightBlend(getDayNightRatio());
} }
catch(InvalidPositionException &e) {} catch(InvalidPositionException &e) {}
obj->updateLight(light); obj->updateLight(light);
@ -1292,7 +1580,7 @@ void ClientEnvironment::step(float dtime)
void ClientEnvironment::updateMeshes(v3s16 blockpos) void ClientEnvironment::updateMeshes(v3s16 blockpos)
{ {
m_map->updateMeshes(blockpos, m_daynight_ratio); m_map->updateMeshes(blockpos, getDayNightRatio());
} }
void ClientEnvironment::expireMeshes(bool only_daynight_diffed) void ClientEnvironment::expireMeshes(bool only_daynight_diffed)

@ -64,14 +64,52 @@ public:
core::list<Player*> getPlayers(bool ignore_disconnected); core::list<Player*> getPlayers(bool ignore_disconnected);
void printPlayers(std::ostream &o); void printPlayers(std::ostream &o);
void setDayNightRatio(u32 r); //void setDayNightRatio(u32 r);
u32 getDayNightRatio(); u32 getDayNightRatio();
// 0-23999
virtual void setTimeOfDay(u32 time)
{
m_time_of_day = time;
}
u32 getTimeOfDay()
{
return m_time_of_day;
}
protected: protected:
// peer_ids in here should be unique, except that there may be many 0s // peer_ids in here should be unique, except that there may be many 0s
core::list<Player*> m_players; core::list<Player*> m_players;
// Brightness // Brightness
u32 m_daynight_ratio; //u32 m_daynight_ratio;
// Time of day in milli-hours (0-23999); determines day and night
u32 m_time_of_day;
};
/*
List of active blocks, used by ServerEnvironment
*/
class ActiveBlockList
{
public:
void update(core::list<v3s16> &active_positions,
s16 radius,
core::map<v3s16, bool> &blocks_removed,
core::map<v3s16, bool> &blocks_added);
bool contains(v3s16 p){
return (m_list.find(p) != NULL);
}
void clear(){
m_list.clear();
}
core::map<v3s16, bool> m_list;
private:
}; };
/* /*
@ -107,9 +145,18 @@ public:
void step(f32 dtime); void step(f32 dtime);
/*
Save players
*/
void serializePlayers(const std::string &savedir); void serializePlayers(const std::string &savedir);
void deSerializePlayers(const std::string &savedir); void deSerializePlayers(const std::string &savedir);
/*
Save and load time of day and game timer
*/
void saveMeta(const std::string &savedir);
void loadMeta(const std::string &savedir);
/* /*
ActiveObjects ActiveObjects
*/ */
@ -153,25 +200,48 @@ private:
Remove all objects that satisfy (m_removed && m_known_by_count==0) Remove all objects that satisfy (m_removed && m_known_by_count==0)
*/ */
void removeRemovedObjects(); void removeRemovedObjects();
/*
Convert stored objects from blocks near the players to active.
*/
void activateNearObjects(s16 range_blocks);
/*
Convert objects that are far away from all the players to static.
If range_blocks == -1, convert everything to static even if known /*
by a player. Convert stored objects from block to active
*/ */
void deactivateFarObjects(s16 range_blocks); void activateObjects(MapBlock *block);
/*
Convert objects that are not in active blocks to static.
If m_known_by_count != 0, active object is not deleted, but static
data is still updated.
If force_delete is set, active object is deleted nevertheless. It
shall only be set so in the destructor of the environment.
*/
void deactivateFarObjects(bool force_delete);
/*
Member variables
*/
// The map
ServerMap *m_map; ServerMap *m_map;
// Pointer to server (which is handling this environment)
Server *m_server; Server *m_server;
// Active object list
core::map<u16, ServerActiveObject*> m_active_objects; core::map<u16, ServerActiveObject*> m_active_objects;
// Outgoing network message buffer for active objects
Queue<ActiveObjectMessage> m_active_object_messages; Queue<ActiveObjectMessage> m_active_object_messages;
float m_random_spawn_timer; // Some timers
float m_random_spawn_timer; // used for experimental code
float m_send_recommended_timer; float m_send_recommended_timer;
IntervalLimiter m_object_management_interval; IntervalLimiter m_object_management_interval;
// List of active blocks
ActiveBlockList m_active_blocks;
IntervalLimiter m_active_blocks_management_interval;
IntervalLimiter m_active_blocks_test_interval;
// Time from the beginning of the game in seconds.
// Incremented in step().
u32 m_game_time;
// A helper variable for incrementing the latter
float m_game_time_fraction_counter;
}; };
#ifndef SERVER #ifndef SERVER
@ -228,6 +298,20 @@ public:
void updateMeshes(v3s16 blockpos); void updateMeshes(v3s16 blockpos);
void expireMeshes(bool only_daynight_diffed); void expireMeshes(bool only_daynight_diffed);
void setTimeOfDay(u32 time)
{
u32 old_dr = getDayNightRatio();
Environment::setTimeOfDay(time);
if(getDayNightRatio() != old_dr)
{
dout_client<<DTIME<<"ClientEnvironment: DayNightRatio changed"
<<" -> expiring meshes"<<std::endl;
expireMeshes(true);
}
}
/* /*
ActiveObjects ActiveObjects
*/ */

@ -27,10 +27,8 @@ NOTE: Global locale is now set at initialization
NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
hardware buffer (it is not freed automatically) hardware buffer (it is not freed automatically)
Random suggeestions (AKA very old suggestions that haven't been done): Old, wild and random suggestions that probably won't be done:
---------------------------------------------------------------------- -------------------------------------------------------------
SUGG: Fix address to be ipv6 compatible
SUGG: If player is on ground, mainly fetch ground-level blocks SUGG: If player is on ground, mainly fetch ground-level blocks
@ -83,6 +81,10 @@ SUGG: Background music based on cellular automata?
SUGG: Simple light color information to air SUGG: Simple light color information to air
SUGG: Server-side objects could be moved based on nodes to enable very
lightweight operation and simple AI
- Not practical; client would still need to show smooth movement.
Gaming ideas: Gaming ideas:
------------- -------------
@ -137,6 +139,8 @@ Build system / running:
Networking and serialization: Networking and serialization:
----------------------------- -----------------------------
SUGG: Fix address to be ipv6 compatible
User Interface: User Interface:
--------------- ---------------
@ -207,18 +211,47 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
FIXME: The new optimized map sending doesn't sometimes send enough blocks FIXME: The new optimized map sending doesn't sometimes send enough blocks
from big caves and such from big caves and such
TODO: A list of "active blocks" in which stuff happens.
+ Add a never-resetted game timer to the server
+ Add a timestamp value to blocks
+ The simple rule: All blocks near some player are "active"
- Do stuff in real time in active blocks
+ Handle objects
TODO: Make proper hooks in here
- Grow grass, delete leaves without a tree
- Spawn some mobs based on some rules
- Transform cobble to mossy cobble near water
- Run a custom script
- ...And all kinds of other dynamic stuff
+ Keep track of when a block becomes active and becomes inactive
+ When a block goes inactive:
+ Store objects statically to block
+ Store timer value as the timestamp
+ When a block goes active:
+ Create active objects out of static objects
TODO: Make proper hooks in here
- Simulate the results of what would have happened if it would have
been active for all the time
- Grow a lot of grass and so on
+ Initially it is fine to send information about every active object
to every player. Eventually it should be modified to only send info
about the nearest ones.
+ This was left to be done by the old system and it sends only the
nearest ones.
Objects: Objects:
-------- --------
TODO: Get rid of MapBlockObjects and use ActiveObjects TODO: Get rid of MapBlockObjects and use only ActiveObjects
- Skipping the MapBlockObject data is nasty - there is no "total
length" stored; have to make a SkipMBOs function which contains
enough of the current code to skip them properly.
SUGG: MovingObject::move and Player::move are basically the same. SUGG: MovingObject::move and Player::move are basically the same.
combine them. combine them.
- NOTE: Player::move is more up-to-date. - NOTE: Player::move is more up-to-date.
- NOTE: There is a simple move implementation now in collision.{h,cpp} - NOTE: There is a simple move implementation now in collision.{h,cpp}
- NOTE: MovingObject will be deleted (MapBlockObject)
SUGG: Server-side objects could be moved based on nodes to enable very
lightweight operation and simple AI
Map: Map:
---- ----

@ -607,13 +607,13 @@ s16 Map::propagateSunlight(v3s16 start,
} }
else else
{ {
// Turn mud into grass /*// Turn mud into grass
if(n.d == CONTENT_MUD) if(n.d == CONTENT_MUD)
{ {
n.d = CONTENT_GRASS; n.d = CONTENT_GRASS;
block->setNode(relpos, n); block->setNode(relpos, n);
modified_blocks.insert(blockpos, block); modified_blocks.insert(blockpos, block);
} }*/
// Sunlight goes no further // Sunlight goes no further
break; break;
@ -838,9 +838,6 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
} }
/* /*
This is called after changing a node from transparent to opaque.
The lighting value of the node should be left as-is after changing
other values. This sets the lighting value to 0.
*/ */
void Map::addNodeAndUpdate(v3s16 p, MapNode n, void Map::addNodeAndUpdate(v3s16 p, MapNode n,
core::map<v3s16, MapBlock*> &modified_blocks) core::map<v3s16, MapBlock*> &modified_blocks)
@ -878,11 +875,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
{ {
} }
#if 1
/* /*
If the new node doesn't propagate sunlight and there is If the new node is solid and there is grass below, change it to mud
grass below, change it to mud
*/ */
if(content_features(n.d).sunlight_propagates == false) if(content_features(n.d).walkable == true)
{ {
try{ try{
MapNode bottomnode = getNode(bottompos); MapNode bottomnode = getNode(bottompos);
@ -898,6 +895,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
{ {
} }
} }
#endif
/* /*
If the new node is mud and it is under sunlight, change it If the new node is mud and it is under sunlight, change it
@ -5452,25 +5450,13 @@ void ServerMap::saveBlock(MapBlock *block)
*/ */
o.write((char*)&version, 1); o.write((char*)&version, 1);
// Write basic data
block->serialize(o, version); block->serialize(o, version);
/* // Write extra data stored on disk
Versions up from 9 have block objects. block->serializeDiskExtra(o, version);
*/
if(version >= 9)
{
block->serializeObjects(o, version);
}
/* // We just wrote it to the disk so clear modified flag
Versions up from 15 have static objects.
*/
if(version >= 15)
{
block->m_static_objects.serialize(o);
}
// We just wrote it to the disk
block->resetChangedFlag(); block->resetChangedFlag();
} }
@ -5515,33 +5501,20 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
created_new = true; created_new = true;
} }
// deserialize block data // Read basic data
block->deSerialize(is, version); block->deSerialize(is, version);
/* // Read extra data stored on disk
Versions up from 9 have block objects. block->deSerializeDiskExtra(is, version);
*/
if(version >= 9)
{
block->updateObjects(is, version, NULL, 0);
}
/*
Versions up from 15 have static objects.
*/
if(version >= 15)
{
block->m_static_objects.deSerialize(is);
}
// If it's a new block, insert it to the map
if(created_new) if(created_new)
sector->insertBlock(block); sector->insertBlock(block);
/* /*
Convert old formats to new and save Save blocks loaded in old format in new format
*/ */
// Save old format blocks in new format
if(version < SER_FMT_VER_HIGHEST || save_after_load) if(version < SER_FMT_VER_HIGHEST || save_after_load)
{ {
saveBlock(block); saveBlock(block);

@ -1549,7 +1549,8 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
m_lighting_expired(true), m_lighting_expired(true),
m_day_night_differs(false), m_day_night_differs(false),
//m_not_fully_generated(false), //m_not_fully_generated(false),
m_objects(this) m_objects(this),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
{ {
data = NULL; data = NULL;
if(dummy == false) if(dummy == false)
@ -1720,17 +1721,17 @@ void MapBlock::replaceMesh(scene::SMesh *mesh_new)
Propagates sunlight down through the block. Propagates sunlight down through the block.
Doesn't modify nodes that are not affected by sunlight. Doesn't modify nodes that are not affected by sunlight.
Returns false if sunlight at bottom block is invalid Returns false if sunlight at bottom block is invalid.
Returns true if sunlight at bottom block is valid.
Returns true if bottom block doesn't exist. Returns true if bottom block doesn't exist.
If there is a block above, continues from it. If there is a block above, continues from it.
If there is no block above, assumes there is sunlight, unless If there is no block above, assumes there is sunlight, unless
is_underground is set or highest node is water. is_underground is set or highest node is water.
At the moment, all sunlighted nodes are added to light_sources. All sunlighted nodes are added to light_sources.
- SUGG: This could be optimized
Turns sunglighted mud into grass. If grow_grass==true, turns sunglighted mud into grass.
if remove_light==true, sets non-sunlighted nodes black. if remove_light==true, sets non-sunlighted nodes black.
@ -1948,45 +1949,6 @@ void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
*/ */
m_objects.step(dtime, server, daynight_ratio); m_objects.step(dtime, server, daynight_ratio);
#if 0
/*
Spawn some objects at random.
Use dayNightDiffed() to approximate being near ground level
*/
if(m_spawn_timer < -999)
{
m_spawn_timer = 60;
}
if(dayNightDiffed() == true && getObjectCount() == 0)
{
m_spawn_timer -= dtime;
if(m_spawn_timer <= 0.0)
{
m_spawn_timer += myrand() % 300;
v2s16 p2d(
(myrand()%(MAP_BLOCKSIZE-1))+0,
(myrand()%(MAP_BLOCKSIZE-1))+0
);
s16 y = getGroundLevel(p2d);
if(y >= 0)
{
v3s16 p(p2d.X, y+1, p2d.Y);
if(getNode(p).d == CONTENT_AIR
&& getNode(p).getLightBlend(daynight_ratio) <= 11)
{
RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
addObject(obj);
}
}
}
}
#endif
setChangedFlag(); setChangedFlag();
} }
@ -2359,6 +2321,8 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
/* /*
Translate nodes as specified in the translate_to fields of Translate nodes as specified in the translate_to fields of
node features node features
NOTE: This isn't really used. Should it be removed?
*/ */
for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++) for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
{ {
@ -2374,5 +2338,55 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
} }
} }
void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
{
// Versions up from 9 have block objects.
if(version >= 9)
{
serializeObjects(os, version);
}
// Versions up from 15 have static objects.
if(version >= 15)
{
m_static_objects.serialize(os);
}
// Timestamp
if(version >= 17)
{
writeU32(os, getTimestamp());
}
}
void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
{
/*
Versions up from 9 have block objects.
*/
if(version >= 9)
{
updateObjects(is, version, NULL, 0);
}
/*
Versions up from 15 have static objects.
*/
if(version >= 15)
{
m_static_objects.deSerialize(is);
}
// Timestamp
if(version >= 17)
{
setTimestamp(readU32(is));
}
else
{
setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
}
}
//END //END

@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodemetadata.h" #include "nodemetadata.h"
#include "staticobject.h" #include "staticobject.h"
#define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
// Named by looking towards z+ // Named by looking towards z+
enum{ enum{
@ -244,24 +245,28 @@ public:
reallocate(); reallocate();
} }
bool getChangedFlag() /*
This is called internally or externally after the block is
modified, so that the block is saved and possibly not deleted from
memory.
*/
void setChangedFlag()
{ {
return changed; changed = true;
} }
void resetChangedFlag() void resetChangedFlag()
{ {
changed = false; changed = false;
} }
void setChangedFlag() bool getChangedFlag()
{ {
changed = true; return changed;
} }
bool getIsUnderground() bool getIsUnderground()
{ {
return is_underground; return is_underground;
} }
void setIsUnderground(bool a_is_underground) void setIsUnderground(bool a_is_underground)
{ {
is_underground = a_is_underground; is_underground = a_is_underground;
@ -359,6 +364,15 @@ public:
return getNode(p.X, p.Y, p.Z); return getNode(p.X, p.Y, p.Z);
} }
MapNode getNodeNoEx(v3s16 p)
{
try{
return getNode(p.X, p.Y, p.Z);
}catch(InvalidPositionException &e){
return MapNode(CONTENT_IGNORE);
}
}
void setNode(s16 x, s16 y, s16 z, MapNode & n) void setNode(s16 x, s16 y, s16 z, MapNode & n)
{ {
if(data == NULL) if(data == NULL)
@ -444,47 +458,19 @@ public:
face_dir); face_dir);
} }
#ifndef SERVER #ifndef SERVER // Only on client
// light = 0...255
/*static void makeFastFace(TileSpec tile, u8 light, v3f p,
v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest);*/
/*TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap &temp_mods);*/
/*u8 getNodeContent(v3s16 p, MapNode mn,
NodeModMap &temp_mods);*/
/*
Generates the FastFaces of a node row. This has a
ridiculous amount of parameters because that way they
can be precalculated by the caller.
translate_dir: unit vector with only one of x, y or z
face_dir: unit vector with only one of x, y or z
*/
/*void updateFastFaceRow(
u32 daynight_ratio,
v3f posRelative_f,
v3s16 startpos,
u16 length,
v3s16 translate_dir,
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
core::array<FastFace> &dest,
NodeModMap &temp_mods);*/
#if 1
/* /*
Thread-safely updates the whole mesh of the mapblock. Thread-safely updates the whole mesh of the mapblock.
NOTE: Prefer generating the mesh separately and then using
replaceMesh().
*/ */
#if 1
void updateMesh(u32 daynight_ratio); void updateMesh(u32 daynight_ratio);
#endif #endif
// Replace the mesh with a new one
void replaceMesh(scene::SMesh *mesh_new); void replaceMesh(scene::SMesh *mesh_new);
#endif
#endif // !SERVER
// See comments in mapblock.cpp // See comments in mapblock.cpp
bool propagateSunlight(core::map<v3s16, bool> & light_sources, bool propagateSunlight(core::map<v3s16, bool> & light_sources,
@ -498,6 +484,7 @@ public:
/* /*
MapBlockObject stuff MapBlockObject stuff
DEPRECATED
*/ */
void serializeObjects(std::ostream &os, u8 version) void serializeObjects(std::ostream &os, u8 version)
@ -545,13 +532,6 @@ public:
*/ */
void stepObjects(float dtime, bool server, u32 daynight_ratio); void stepObjects(float dtime, bool server, u32 daynight_ratio);
/*void wrapObject(MapBlockObject *object)
{
m_objects.wrapObject(object);
setChangedFlag();
}*/
// origin is relative to block // origin is relative to block
void getObjects(v3f origin, f32 max_d, void getObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest) core::array<DistanceSortedObject> &dest)
@ -564,7 +544,7 @@ public:
return m_objects.getCount(); return m_objects.getCount();
} }
#ifndef SERVER #ifndef SERVER // Only on client
/* /*
Methods for setting temporary modifications to nodes for Methods for setting temporary modifications to nodes for
drawing drawing
@ -639,14 +619,30 @@ public:
*/ */
s16 getGroundLevel(v2s16 p2d); s16 getGroundLevel(v2s16 p2d);
/*
Timestamp (see m_timestamp)
NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
*/
void setTimestamp(u32 time)
{
m_timestamp = time;
setChangedFlag();
}
u32 getTimestamp()
{
return m_timestamp;
}
/* /*
Serialization Serialization
*/ */
// Doesn't write version by itself // These don't write or read version by itself
void serialize(std::ostream &os, u8 version); void serialize(std::ostream &os, u8 version);
void deSerialize(std::istream &is, u8 version); void deSerialize(std::istream &is, u8 version);
// Used after the basic ones when writing on disk (serverside)
void serializeDiskExtra(std::ostream &os, u8 version);
void deSerializeDiskExtra(std::istream &is, u8 version);
private: private:
/* /*
@ -676,7 +672,7 @@ public:
Public member variables Public member variables
*/ */
#ifndef SERVER #ifndef SERVER // Only on client
scene::SMesh *mesh; scene::SMesh *mesh;
JMutex mesh_mutex; JMutex mesh_mutex;
#endif #endif
@ -729,12 +725,9 @@ private:
// Whether day and night lighting differs // Whether day and night lighting differs
bool m_day_night_differs; bool m_day_night_differs;
// TODO: Remove this // DEPRECATED
MapBlockObjectList m_objects; MapBlockObjectList m_objects;
// Object spawning stuff
//float m_spawn_timer;
#ifndef SERVER // Only on client #ifndef SERVER // Only on client
/* /*
Set to true if the mesh has been ordered to be updated Set to true if the mesh has been ordered to be updated
@ -748,6 +741,12 @@ private:
NodeModMap m_temp_mods; NodeModMap m_temp_mods;
JMutex m_temp_mods_mutex; JMutex m_temp_mods_mutex;
#endif #endif
/*
When block is removed from active blocks, this is set to gametime.
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
*/
u32 m_timestamp;
}; };
inline bool blockpos_over_limit(v3s16 p) inline bool blockpos_over_limit(v3s16 p)

@ -148,6 +148,8 @@ struct ContentFeatures
bool light_propagates; bool light_propagates;
bool sunlight_propagates; bool sunlight_propagates;
u8 solidness; // Used when choosing which face is drawn u8 solidness; // Used when choosing which face is drawn
// This is used for collision detection.
// Also for general solidness queries.
bool walkable; bool walkable;
bool pointable; bool pointable;
bool diggable; bool diggable;

@ -26,53 +26,55 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "utility.h" #include "utility.h"
/* /*
Map format serialization version
--------------------------------
For map data (blocks, nodes, sectors).
NOTE: The goal is to increment this so that saved maps will be NOTE: The goal is to increment this so that saved maps will be
loadable by any version. Other compatibility is not loadable by any version. Other compatibility is not
maintained. maintained.
Serialization format versions (for raw map data (blocks, nodes, sectors)):
== Unsupported ==
0: original networked test with 1-byte nodes 0: original networked test with 1-byte nodes
1: update with 2-byte nodes 1: update with 2-byte nodes
== Supported ==
2: lighting is transmitted in param 2: lighting is transmitted in param
3: optional fetching of far blocks 3: optional fetching of far blocks
4: block compression 4: block compression
5: sector objects NOTE: block compression was left accidentally out 5: sector objects NOTE: block compression was left accidentally out
6: failed attempt at switching block compression on again 6: failed attempt at switching block compression on again
7: block compression switched on again 7: block compression switched on again
8: (dev) server-initiated block transfers and all kinds of stuff 8: server-initiated block transfers and all kinds of stuff
9: (dev) block objects 9: block objects
10: (dev) water pressure 10: water pressure
11: (dev) zlib'd blocks, block flags 11: zlib'd blocks, block flags
12: (dev) UnlimitedHeightmap now uses interpolated areas 12: UnlimitedHeightmap now uses interpolated areas
13: (dev) Mapgen v2 13: Mapgen v2
14: (dev) NodeMetadata 14: NodeMetadata
15: (dev) StaticObjects 15: StaticObjects
16: (dev) larger maximum size of node metadata, and compression 16: larger maximum size of node metadata, and compression
17: MapBlocks contain timestamp
*/ */
// This represents an uninitialized or invalid format // This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255 #define SER_FMT_VER_INVALID 255
// Highest supported serialization version // Highest supported serialization version
#define SER_FMT_VER_HIGHEST 16 #define SER_FMT_VER_HIGHEST 17
// Lowest supported serialization version // Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0 #define SER_FMT_VER_LOWEST 0
#define ser_ver_supported(v) (v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST) #define ser_ver_supported(v) (v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST)
/*
Misc. serialization functions
*/
void compressZlib(SharedBuffer<u8> data, std::ostream &os); void compressZlib(SharedBuffer<u8> data, std::ostream &os);
void compressZlib(const std::string &data, std::ostream &os); void compressZlib(const std::string &data, std::ostream &os);
void decompressZlib(std::istream &is, std::ostream &os); void decompressZlib(std::istream &is, std::ostream &os);
// These choose between zlib and a self-made one according to version
void compress(SharedBuffer<u8> data, std::ostream &os, u8 version); void compress(SharedBuffer<u8> data, std::ostream &os, u8 version);
//void compress(const std::string &data, std::ostream &os, u8 version); //void compress(const std::string &data, std::ostream &os, u8 version);
void decompress(std::istream &is, std::ostream &os, u8 version); void decompress(std::istream &is, std::ostream &os, u8 version);
/*class Serializable
{
public:
void serialize(std::ostream &os, u8 version) = 0;
void deSerialize(std::istream &istr);
};*/
#endif #endif

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mineral.h" #include "mineral.h"
#include "config.h" #include "config.h"
#include "servercommand.h" #include "servercommand.h"
#include "filesys.h"
#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
@ -798,7 +799,7 @@ void RemoteClient::SendObjectData(
*/ */
if(stepped_blocks.find(p) == NULL) if(stepped_blocks.find(p) == NULL)
{ {
block->stepObjects(dtime, true, server->getDayNightRatio()); block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
stepped_blocks.insert(p, true); stepped_blocks.insert(p, true);
block->setChangedFlag(); block->setChangedFlag();
} }
@ -968,7 +969,6 @@ Server::Server(
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this), m_thread(this),
m_emergethread(this), m_emergethread(this),
m_time_of_day(9000),
m_time_counter(0), m_time_counter(0),
m_time_of_day_send_timer(0), m_time_of_day_send_timer(0),
m_uptime(0), m_uptime(0),
@ -988,9 +988,18 @@ Server::Server(
m_step_dtime_mutex.Init(); m_step_dtime_mutex.Init();
m_step_dtime = 0.0; m_step_dtime = 0.0;
// Register us to receive map edit events
m_env.getMap().addEventReceiver(this); m_env.getMap().addEventReceiver(this);
// If file exists, load environment metadata
if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
{
dstream<<"Server: Loading environment metadata"<<std::endl;
m_env.loadMeta(m_mapsavedir);
}
// Load players // Load players
dstream<<"Server: Loading players"<<std::endl;
m_env.deSerializePlayers(m_mapsavedir); m_env.deSerializePlayers(m_mapsavedir);
} }
@ -1033,6 +1042,12 @@ Server::~Server()
dstream<<"Server: Saving players"<<std::endl; dstream<<"Server: Saving players"<<std::endl;
m_env.serializePlayers(m_mapsavedir); m_env.serializePlayers(m_mapsavedir);
/*
Save environment metadata
*/
dstream<<"Server: Saving environment metadata"<<std::endl;
m_env.saveMeta(m_mapsavedir);
/* /*
Stop threads Stop threads
*/ */
@ -1136,14 +1151,17 @@ void Server::AsyncRunStep()
} }
/* /*
Update m_time_of_day Update m_time_of_day and overall game time
*/ */
{ {
JMutexAutoLock envlock(m_env_mutex);
m_time_counter += dtime; m_time_counter += dtime;
f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600); f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
u32 units = (u32)(m_time_counter*speed); u32 units = (u32)(m_time_counter*speed);
m_time_counter -= (f32)units / speed; m_time_counter -= (f32)units / speed;
m_time_of_day.set((m_time_of_day.get() + units) % 24000);
m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
//dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl; //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
@ -1167,7 +1185,7 @@ void Server::AsyncRunStep()
//Player *player = m_env.getPlayer(client->peer_id); //Player *player = m_env.getPlayer(client->peer_id);
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY( SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
m_time_of_day.get()); m_env.getTimeOfDay());
// Send as reliable // Send as reliable
m_con.Send(client->peer_id, 0, data, true); m_con.Send(client->peer_id, 0, data, true);
} }
@ -1654,6 +1672,9 @@ void Server::AsyncRunStep()
// Save players // Save players
m_env.serializePlayers(m_mapsavedir); m_env.serializePlayers(m_mapsavedir);
// Save environment metadata
m_env.saveMeta(m_mapsavedir);
} }
} }
} }
@ -1900,7 +1921,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Send time of day // Send time of day
{ {
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY( SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
m_time_of_day.get()); m_env.getTimeOfDay());
m_con.Send(peer->id, 0, data, true); m_con.Send(peer->id, 0, data, true);
} }

@ -382,20 +382,21 @@ public:
core::list<PlayerInfo> getPlayerInfo(); core::list<PlayerInfo> getPlayerInfo();
u32 getDayNightRatio() /*u32 getDayNightRatio()
{ {
return time_to_daynight_ratio(m_time_of_day.get()); return time_to_daynight_ratio(m_time_of_day.get());
} }*/
// Environment must be locked when called
void setTimeOfDay(u32 time) void setTimeOfDay(u32 time)
{ {
m_time_of_day.set(time); m_env.setTimeOfDay(time);
m_time_of_day_send_timer = 0; m_time_of_day_send_timer = 0;
} }
bool getShutdownRequested() bool getShutdownRequested()
{ {
return m_shutdown_requested.get(); return m_shutdown_requested;
} }
/* /*
@ -416,7 +417,7 @@ public:
void requestShutdown(void) void requestShutdown(void)
{ {
m_shutdown_requested.set(true); m_shutdown_requested = true;
} }
@ -426,7 +427,8 @@ public:
private: private:
// Virtual methods from con::PeerHandler. // con::PeerHandler implementation.
// These queue stuff to be processed by handlePeerChanges().
// As of now, these create and remove clients and players. // As of now, these create and remove clients and players.
void peerAdded(con::Peer *peer); void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout); void deletingPeer(con::Peer *peer, bool timeout);
@ -484,21 +486,16 @@ private:
*/ */
Player *emergePlayer(const char *name, const char *password, u16 peer_id); Player *emergePlayer(const char *name, const char *password, u16 peer_id);
/*
Update water pressure.
This also adds suitable nodes to active_nodes.
environment has to be locked when calling.
*/
/*void UpdateBlockWaterPressure(MapBlock *block,
core::map<v3s16, MapBlock*> &modified_blocks);*/
// Locks environment and connection by its own // Locks environment and connection by its own
struct PeerChange; struct PeerChange;
void handlePeerChange(PeerChange &c); void handlePeerChange(PeerChange &c);
void handlePeerChanges(); void handlePeerChanges();
//float m_flowwater_timer; /*
Variables
*/
// Some timers
float m_liquid_transform_timer; float m_liquid_transform_timer;
float m_print_info_timer; float m_print_info_timer;
float m_objectdata_timer; float m_objectdata_timer;
@ -507,50 +504,81 @@ private:
// NOTE: If connection and environment are both to be locked, // NOTE: If connection and environment are both to be locked,
// environment shall be locked first. // environment shall be locked first.
JMutex m_env_mutex;
// Environment
ServerEnvironment m_env; ServerEnvironment m_env;
JMutex m_env_mutex;
JMutex m_con_mutex; // Connection
con::Connection m_con; con::Connection m_con;
core::map<u16, RemoteClient*> m_clients; // Behind the con mutex JMutex m_con_mutex;
// Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients;
/*
Threads
*/
// A buffer for time steps
// step() increments and AsyncRunStep() run by m_thread reads it.
float m_step_dtime; float m_step_dtime;
JMutex m_step_dtime_mutex; JMutex m_step_dtime_mutex;
// The server mainly operates in this thread
ServerThread m_thread; ServerThread m_thread;
// This thread fetches and generates map
EmergeThread m_emergethread; EmergeThread m_emergethread;
// Queue of block coordinates to be processed by the emerge thread
BlockEmergeQueue m_emerge_queue; BlockEmergeQueue m_emerge_queue;
// Nodes that are destinations of flowing liquid at the moment /*
//core::map<v3s16, u8> m_flow_active_nodes; Time related stuff
*/
// 0-23999 // 0-23999
MutexedVariable<u32> m_time_of_day; //MutexedVariable<u32> m_time_of_day;
// Used to buffer dtime for adding to m_time_of_day // Used to buffer dtime for adding to m_time_of_day
float m_time_counter; float m_time_counter;
// Timer for sending time of day over network
float m_time_of_day_send_timer; float m_time_of_day_send_timer;
// Uptime of server in seconds
MutexedVariable<double> m_uptime; MutexedVariable<double> m_uptime;
/*
Peer change queue.
Queues stuff from peerAdded() and deletingPeer() to
handlePeerChanges()
*/
enum PeerChangeType enum PeerChangeType
{ {
PEER_ADDED, PEER_ADDED,
PEER_REMOVED PEER_REMOVED
}; };
struct PeerChange struct PeerChange
{ {
PeerChangeType type; PeerChangeType type;
u16 peer_id; u16 peer_id;
bool timeout; bool timeout;
}; };
Queue<PeerChange> m_peer_change_queue; Queue<PeerChange> m_peer_change_queue;
/*
Random stuff
*/
// Map directory
std::string m_mapsavedir; std::string m_mapsavedir;
MutexedVariable<bool> m_shutdown_requested; bool m_shutdown_requested;
/*
Map edit event queue. Automatically receives all map edits.
The constructor of this class registers us to receive them through
onMapEditEvent
NOTE: Should these be moved to actually be members of
ServerEnvironment?
*/
/* /*
Queue of map edits from the environment for sending to the clients Queue of map edits from the environment for sending to the clients

@ -29,6 +29,7 @@ ServerActiveObject::ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos):
ActiveObject(id), ActiveObject(id),
m_known_by_count(0), m_known_by_count(0),
m_removed(false), m_removed(false),
m_pending_deactivation(false),
m_static_exists(false), m_static_exists(false),
m_static_block(1337,1337,1337), m_static_block(1337,1337,1337),
m_env(env), m_env(env),

@ -106,8 +106,13 @@ public:
*/ */
virtual u16 punch(const std::string &toolname){return 0;} virtual u16 punch(const std::string &toolname){return 0;}
// Number of players which know about this object /*
Number of players which know about this object. Object won't be
deleted until this is 0 to keep the id preserved for the right
object.
*/
u16 m_known_by_count; u16 m_known_by_count;
/* /*
- Whether this object is to be removed when nobody knows about - Whether this object is to be removed when nobody knows about
it anymore. it anymore.
@ -119,6 +124,16 @@ public:
*/ */
bool m_removed; bool m_removed;
/*
This is set to true when a block should be removed from the active
object list but couldn't be removed because the id has to be
reserved for some client.
The environment checks this periodically. If this is true and also
m_known_by_count is true,
*/
bool m_pending_deactivation;
/* /*
Whether the object's static data has been stored to a block Whether the object's static data has been stored to a block
*/ */

@ -221,6 +221,19 @@ inline u16 readU16(std::istream &is)
return readU16((u8*)buf); return readU16((u8*)buf);
} }
inline void writeU32(std::ostream &os, u16 p)
{
char buf[4];
writeU16((u8*)buf, p);
os.write(buf, 4);
}
inline u16 readU32(std::istream &is)
{
char buf[4];
is.read(buf, 4);
return readU32((u8*)buf);
}
inline void writeF1000(std::ostream &os, f32 p) inline void writeF1000(std::ostream &os, f32 p)
{ {
char buf[2]; char buf[2];