Temporary commit; lots of test code and stuff

This commit is contained in:
Perttu Ahola 2011-02-21 00:45:14 +02:00
parent f5ff378dd0
commit c57637b4c3
25 changed files with 1917 additions and 763 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 846 B

@ -50,6 +50,7 @@ configure_file(
) )
set(common_SRCS set(common_SRCS
serverobject.cpp
noise.cpp noise.cpp
mineral.cpp mineral.cpp
porting.cpp porting.cpp
@ -75,8 +76,10 @@ set(common_SRCS
test.cpp test.cpp
) )
# Client sources
set(minetest_SRCS set(minetest_SRCS
${common_SRCS} ${common_SRCS}
clientobject.cpp
guiMainMenu.cpp guiMainMenu.cpp
guiMessageMenu.cpp guiMessageMenu.cpp
guiTextInputMenu.cpp guiTextInputMenu.cpp
@ -88,6 +91,7 @@ set(minetest_SRCS
main.cpp main.cpp
) )
# Server sources
set(minetestserver_SRCS set(minetestserver_SRCS
${common_SRCS} ${common_SRCS}
servermain.cpp servermain.cpp

71
src/activeobject.h Normal file

@ -0,0 +1,71 @@
/*
Minetest-c55
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
(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.
You should have received a copy of the GNU 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.
*/
#ifndef ACTIVEOBJECT_HEADER
#define ACTIVEOBJECT_HEADER
#include "common_irrlicht.h"
#include <string>
struct ActiveObjectMessage
{
ActiveObjectMessage(u16 id_, bool reliable_=true, std::string data_=""):
id(id_),
reliable(reliable_),
datastring(data_)
{}
u16 id;
bool reliable;
std::string datastring;
};
#define ACTIVEOBJECT_TYPE_INVALID 0
#define ACTIVEOBJECT_TYPE_TEST 1
#define ACTIVEOBJECT_TYPE_LUA 2
/*
Parent class for ServerActiveObject and ClientActiveObject
*/
class ActiveObject
{
public:
ActiveObject(u16 id):
m_id(id)
{
}
u16 getId()
{
return m_id;
}
void setId(u16 id)
{
m_id = id;
}
virtual u8 getType() const = 0;
protected:
u16 m_id; // 0 is invalid, "no id"
};
#endif

@ -56,10 +56,12 @@ Client::Client(
const char *playername, const char *playername,
MapDrawControl &control): MapDrawControl &control):
m_thread(this), m_thread(this),
m_env(new ClientMap(this, control, m_env(
new ClientMap(this, control,
device->getSceneManager()->getRootSceneNode(), device->getSceneManager()->getRootSceneNode(),
device->getSceneManager(), 666), device->getSceneManager(), 666),
dout_client), device->getSceneManager()
),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_device(device), m_device(device),
camera_position(0,0,0), camera_position(0,0,0),
@ -82,20 +84,25 @@ Client::Client(
m_step_dtime_mutex.Init(); m_step_dtime_mutex.Init();
m_thread.Start(); m_thread.Start();
/*
Add local player
*/
{ {
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
//m_env.getMap().StartUpdater();
Player *player = new LocalPlayer(); Player *player = new LocalPlayer();
player->updateName(playername); player->updateName(playername);
/*f32 y = BS*2 + BS*20;
player->setPosition(v3f(0, y, 0));*/
//player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
m_env.addPlayer(player); m_env.addPlayer(player);
} }
// Add some active objects for testing
/*{
ClientActiveObject *obj = new TestCAO(0, v3f(0, 10*BS, 0));
m_env.addActiveObject(obj);
}*/
} }
Client::~Client() Client::~Client()
@ -493,7 +500,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
v3s16 playerpos_s16(0, BS*2+BS*20, 0); v3s16 playerpos_s16(0, BS*2+BS*20, 0);
if(datasize >= 2+1+6) if(datasize >= 2+1+6)
playerpos_s16 = readV3S16(&data[2+1]); playerpos_s16 = readV3S16(&data[2+1]);
v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0); v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
{ //envlock { //envlock
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
@ -1037,6 +1044,99 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
m_chat_queue.push_back(message); m_chat_queue.push_back(message);
} }
else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
{
/*
u16 command
u16 count of removed objects
for all removed objects {
u16 id
}
u16 count of added objects
for all added objects {
u16 id
u8 type
}
*/
char buf[6];
// Get all data except the command number
std::string datastring((char*)&data[2], datasize-2);
// Throw them in an istringstream
std::istringstream is(datastring, std::ios_base::binary);
// Read stuff
// Read removed objects
is.read(buf, 2);
u16 removed_count = readU16((u8*)buf);
for(u16 i=0; i<removed_count; i++)
{
is.read(buf, 2);
u16 id = readU16((u8*)buf);
// Remove it
{
JMutexAutoLock envlock(m_env_mutex);
m_env.removeActiveObject(id);
}
}
// Read added objects
is.read(buf, 2);
u16 added_count = readU16((u8*)buf);
for(u16 i=0; i<added_count; i++)
{
is.read(buf, 2);
u16 id = readU16((u8*)buf);
is.read(buf, 1);
u8 type = readU8((u8*)buf);
// Add it
{
JMutexAutoLock envlock(m_env_mutex);
m_env.addActiveObject(id, type);
}
}
}
else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
{
/*
u16 command
for all objects
{
u16 id
u16 message length
string message
}
*/
char buf[6];
// Get all data except the command number
std::string datastring((char*)&data[2], datasize-2);
// Throw them in an istringstream
std::istringstream is(datastring, std::ios_base::binary);
while(is.eof() == false)
{
// Read stuff
is.read(buf, 2);
u16 id = readU16((u8*)buf);
if(is.eof())
break;
is.read(buf, 2);
u16 message_size = readU16((u8*)buf);
std::string message;
message.reserve(message_size);
for(u16 i=0; i<message_size; i++)
{
is.read(buf, 1);
message.append(buf, 1);
}
// Pass on to the environment
{
JMutexAutoLock envlock(m_env_mutex);
m_env.processActiveObjectMessage(id, message);
}
}
}
// Default to queueing it (for slow commands) // Default to queueing it (for slow commands)
else else
{ {
@ -1197,7 +1297,7 @@ bool Client::AsyncProcessPacket()
main thread, from which is will want to retrieve textures. main thread, from which is will want to retrieve textures.
*/ */
m_env.getMap().updateMeshes(block->getPos(), getDayNightRatio()); m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
} }
else else
{ {
@ -1464,7 +1564,7 @@ void Client::removeNode(v3s16 p)
i.atEnd() == false; i++) i.atEnd() == false; i++)
{ {
v3s16 p = i.getNode()->getKey(); v3s16 p = i.getNode()->getKey();
m_env.getMap().updateMeshes(p, m_env.getDayNightRatio()); m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
} }
} }
@ -1486,7 +1586,7 @@ void Client::addNode(v3s16 p, MapNode n)
i.atEnd() == false; i++) i.atEnd() == false; i++)
{ {
v3s16 p = i.getNode()->getKey(); v3s16 p = i.getNode()->getKey();
m_env.getMap().updateMeshes(p, m_env.getDayNightRatio()); m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
} }
} }
@ -1503,36 +1603,6 @@ MapNode Client::getNode(v3s16 p)
return m_env.getMap().getNode(p); return m_env.getMap().getNode(p);
} }
/*void Client::getNode(v3s16 p, MapNode n)
{
JMutexAutoLock envlock(m_env_mutex);
m_env.getMap().setNode(p, n);
}*/
/*f32 Client::getGroundHeight(v2s16 p)
{
JMutexAutoLock envlock(m_env_mutex);
return m_env.getMap().getGroundHeight(p);
}*/
/*bool Client::isNodeUnderground(v3s16 p)
{
JMutexAutoLock envlock(m_env_mutex);
return m_env.getMap().isNodeUnderground(p);
}*/
/*Player * Client::getLocalPlayer()
{
JMutexAutoLock envlock(m_env_mutex);
return m_env.getLocalPlayer();
}*/
/*core::list<Player*> Client::getPlayers()
{
JMutexAutoLock envlock(m_env_mutex);
return m_env.getPlayers();
}*/
v3f Client::getPlayerPosition() v3f Client::getPlayerPosition()
{ {
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
@ -1597,7 +1667,7 @@ MapBlockObject * Client::getSelectedObject(
// Calculate from_pos relative to block // Calculate from_pos relative to block
v3s16 block_pos_i_on_map = block->getPosRelative(); v3s16 block_pos_i_on_map = block->getPosRelative();
v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map); v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map; v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
block->getObjects(from_pos_f_on_block, max_d, objects); block->getObjects(from_pos_f_on_block, max_d, objects);
@ -1617,7 +1687,7 @@ MapBlockObject * Client::getSelectedObject(
// Calculate shootline relative to block // Calculate shootline relative to block
v3s16 block_pos_i_on_map = block->getPosRelative(); v3s16 block_pos_i_on_map = block->getPosRelative();
v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map); v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
core::line3d<f32> shootline_on_block( core::line3d<f32> shootline_on_block(
shootline_on_map.start - block_pos_f_on_map, shootline_on_map.start - block_pos_f_on_map,
shootline_on_map.end - block_pos_f_on_map shootline_on_map.end - block_pos_f_on_map
@ -1672,7 +1742,7 @@ u32 Client::getDayNightRatio()
v3f playerpos = player->getPosition(); v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed(); v3f playerspeed = player->getSpeed();
v3s16 center_nodepos = floatToInt(playerpos); v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos); v3s16 center = getNodeBlockPos(center_nodepos);
u32 counter = 0; u32 counter = 0;

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "jmutex.h" #include "jmutex.h"
#include <ostream> #include <ostream>
#include "clientobject.h"
class ClientNotReadyException : public BaseException class ClientNotReadyException : public BaseException
{ {
@ -165,11 +166,6 @@ public:
// Returns InvalidPositionException if not found // Returns InvalidPositionException if not found
MapNode getNode(v3s16 p); MapNode getNode(v3s16 p);
// Returns InvalidPositionException if not found
//void setNode(v3s16 p, MapNode n);
// Returns InvalidPositionException if not found
//f32 getGroundHeight(v2s16 p);
v3f getPlayerPosition(); v3f getPlayerPosition();
@ -192,7 +188,6 @@ public:
// Prints a line or two of info // Prints a line or two of info
void printDebugInfo(std::ostream &os); void printDebugInfo(std::ostream &os);
//s32 getDayNightIndex();
u32 getDayNightRatio(); u32 getDayNightRatio();
//void updateSomeExpiredMeshes(); //void updateSomeExpiredMeshes();
@ -230,27 +225,6 @@ public:
} }
} }
#if 0
void setTempMod(v3s16 p, NodeMod mod)
{
JMutexAutoLock envlock(m_env_mutex);
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
bool changed = false;
v3s16 blockpos = ((ClientMap&)m_env.getMap()).setTempMod(p, mod, &changed);
if(changed)
m_env.getMap().updateMeshes(blockpos, m_env.getDayNightRatio());
}
void clearTempMod(v3s16 p)
{
JMutexAutoLock envlock(m_env_mutex);
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
bool changed = false;
v3s16 blockpos = ((ClientMap&)m_env.getMap()).clearTempMod(p, &changed);
if(changed)
m_env.getMap().updateMeshes(blockpos, m_env.getDayNightRatio());
}
#endif
float getAvgRtt() float getAvgRtt()
{ {
JMutexAutoLock lock(m_con_mutex); JMutexAutoLock lock(m_con_mutex);
@ -302,15 +276,12 @@ 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.
Environment m_env; ClientEnvironment m_env;
JMutex m_env_mutex; JMutex m_env_mutex;
con::Connection m_con; con::Connection m_con;
JMutex m_con_mutex; JMutex m_con_mutex;
/*core::map<v3s16, float> m_fetchblock_history;
JMutex m_fetchblock_mutex;*/
core::list<IncomingPacket> m_incoming_queue; core::list<IncomingPacket> m_incoming_queue;
JMutex m_incoming_queue_mutex; JMutex m_incoming_queue_mutex;

169
src/clientobject.cpp Normal file

@ -0,0 +1,169 @@
/*
Minetest-c55
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
(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.
You should have received a copy of the GNU 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 "clientobject.h"
#include "debug.h"
#include "porting.h"
#include "constants.h"
#include "utility.h"
ClientActiveObject::ClientActiveObject(u16 id):
ActiveObject(id)
{
}
ClientActiveObject::~ClientActiveObject()
{
removeFromScene();
}
ClientActiveObject* ClientActiveObject::create(u8 type)
{
if(type == ACTIVEOBJECT_TYPE_INVALID)
{
dstream<<"ClientActiveObject::create(): passed "
<<"ACTIVEOBJECT_TYPE_INVALID"<<std::endl;
return NULL;
}
else if(type == ACTIVEOBJECT_TYPE_TEST)
{
dstream<<"ClientActiveObject::create(): passed "
<<"ACTIVEOBJECT_TYPE_TEST"<<std::endl;
return new TestCAO(0);
}
else if(type == ACTIVEOBJECT_TYPE_LUA)
{
dstream<<"ClientActiveObject::create(): passed "
<<"ACTIVEOBJECT_TYPE_LUA"<<std::endl;
return NULL;
}
else
{
dstream<<"ClientActiveObject::create(): passed "
<<"unknown type="<<type<<std::endl;
return NULL;
}
}
/*
TestCAO
*/
TestCAO::TestCAO(u16 id):
ClientActiveObject(id),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
}
TestCAO::~TestCAO()
{
}
void TestCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
buf->getMaterial().setTexture
(0, driver->getTexture(porting::getDataPath("rat.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
updateNodePos();
}
void TestCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void TestCAO::updateLight(u8 light_at_pos)
{
}
v3s16 TestCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void TestCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
//m_node->setRotation(v3f(0, 45, 0));
}
void TestCAO::step(float dtime)
{
if(m_node)
{
v3f rot = m_node->getRotation();
//dstream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
rot.Y += dtime * 180;
m_node->setRotation(rot);
}
}
void TestCAO::processMessage(const std::string &data)
{
//dstream<<"TestCAO: Got data: "<<data<<std::endl;
std::istringstream is(data, std::ios::binary);
u16 cmd;
is>>cmd;
if(cmd == 0)
{
v3f newpos;
is>>newpos.X;
is>>newpos.Y;
is>>newpos.Z;
m_position = newpos;
updateNodePos();
}
}

88
src/clientobject.h Normal file

@ -0,0 +1,88 @@
/*
Minetest-c55
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
(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.
You should have received a copy of the GNU 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.
*/
#ifndef CLIENTOBJECT_HEADER
#define CLIENTOBJECT_HEADER
#include "common_irrlicht.h"
#include "activeobject.h"
/*
Some planning
-------------
* Client receives a network packet with information of added objects
in it
* Client supplies the information to its ClientEnvironment
* The environment adds the specified objects to itself
*/
class ClientActiveObject : public ActiveObject
{
public:
ClientActiveObject(u16 id);
virtual ~ClientActiveObject();
virtual void addToScene(scene::ISceneManager *smgr){}
virtual void removeFromScene(){}
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
// Step object in time
virtual void step(float dtime){}
// Process a message sent by the server side object
virtual void processMessage(const std::string &data){}
static ClientActiveObject* create(u8 type);
protected:
};
class TestCAO : public ClientActiveObject
{
public:
TestCAO(u16 id);
virtual ~TestCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_TEST;
}
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime);
void processMessage(const std::string &data);
private:
scene::IMeshSceneNode *m_node;
v3f m_position;
};
#endif

@ -106,6 +106,31 @@ enum ToClientCommand
wstring message wstring message
*/ */
TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD = 0x31,
/*
u16 command
u16 count of removed objects
for all removed objects {
u16 id
}
u16 count of added objects
for all added objects {
u16 id
u8 type
}
*/
TOCLIENT_ACTIVE_OBJECT_MESSAGES = 0x32,
/*
u16 command
for all objects
{
u16 id
u16 message length
string message
}
*/
}; };
enum ToServerCommand enum ToServerCommand

@ -20,11 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h" #include "environment.h"
#include "filesys.h" #include "filesys.h"
Environment::Environment(Map *map, std::ostream &dout): Environment::Environment()
m_dout(dout)
{ {
m_map = map; m_daynight_ratio = 0.5;
m_daynight_ratio = 0.2;
} }
Environment::~Environment() Environment::~Environment()
@ -35,183 +33,16 @@ Environment::~Environment()
{ {
delete (*i); delete (*i);
} }
// The map is removed by the SceneManager
m_map->drop();
//delete m_map;
}
void Environment::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
/*
Run Map's timers
*/
//TimeTaker maptimerupdatetimer("m_map->timerUpdate()", g_device);
// 0ms
m_map->timerUpdate(dtime);
//maptimerupdatetimer.stop();
/*
Get the highest speed some player is going
*/
//TimeTaker playerspeed("playerspeed", g_device);
// 0ms
f32 maximum_player_speed = 0.001; // just some small value
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
f32 speed = (*i)->getSpeed().getLength();
if(speed > maximum_player_speed)
maximum_player_speed = speed;
}
//playerspeed.stop();
/*
Maximum position increment
*/
//f32 position_max_increment = 0.05*BS;
f32 position_max_increment = 0.1*BS;
// Maximum time increment (for collision detection etc)
// time = distance / speed
f32 dtime_max_increment = position_max_increment / maximum_player_speed;
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
//TimeTaker playerupdate("playerupdate", g_device);
/*
Stuff that has a maximum time increment
*/
// Don't allow overly huge dtime
if(dtime > 0.5)
dtime = 0.5;
u32 loopcount = 0;
do
{
loopcount++;
f32 dtime_part;
if(dtime > dtime_max_increment)
dtime_part = dtime_max_increment;
else
dtime_part = dtime;
dtime -= dtime_part;
/*
Handle players
*/
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
// Apply physics to local player
bool free_move = g_settings.getBool("free_move");
if(player->isLocal() && free_move == false)
{
// Apply gravity to local player
v3f speed = player->getSpeed();
if(player->swimming_up == false)
speed.Y -= 9.81 * BS * dtime_part * 2;
/*
Apply water resistance
*/
if(player->in_water_stable || player->in_water)
{
f32 max_down = 2.0*BS;
if(speed.Y < -max_down) speed.Y = -max_down;
f32 max = 2.5*BS;
if(speed.getLength() > max)
{
speed = speed / speed.getLength() * max;
}
}
player->setSpeed(speed);
}
/*
Move the player.
For local player, this also calculates collision detection.
*/
player->move(dtime_part, *m_map, position_max_increment);
/*
Update lighting on remote players on client
*/
u8 light = LIGHT_MAX;
try{
// Get node at feet
v3s16 p = floatToInt(playerpos + v3f(0,BS/4,0));
MapNode n = m_map->getNode(p);
light = n.getLightBlend(m_daynight_ratio);
}
catch(InvalidPositionException &e) {}
player->updateLight(light);
/*
Add footsteps to grass
*/
if(g_settings.getBool("footprints"))
{
// Get node that is at BS/4 under player
v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0));
try{
MapNode n = m_map->getNode(bottompos);
if(n.d == CONTENT_GRASS)
{
n.d = CONTENT_GRASS_FOOTSTEPS;
m_map->setNode(bottompos, n);
#ifndef SERVER
// Update mesh on client
if(m_map->mapType() == MAPTYPE_CLIENT)
{
v3s16 p_blocks = getNodeBlockPos(bottompos);
MapBlock *b = m_map->getBlockNoCreate(p_blocks);
b->updateMesh(m_daynight_ratio);
}
#endif
}
}
catch(InvalidPositionException &e)
{
}
}
}
}
while(dtime > 0.001);
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
}
Map & Environment::getMap()
{
return *m_map;
} }
void Environment::addPlayer(Player *player) void Environment::addPlayer(Player *player)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
/* /*
Check that only one local player exists and peer_ids are unique. Check that peer_ids are unique.
Also check that names are unique. Also check that names are unique.
Exception: there can be multiple players with peer_id=0 Exception: there can be multiple players with peer_id=0
*/ */
#ifndef SERVER
/*
It is a failure if player is local and there already is a local
player
*/
assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
#endif
// If peer id is non-zero, it has to be unique. // If peer id is non-zero, it has to be unique.
if(player->peer_id != 0) if(player->peer_id != 0)
assert(getPlayer(player->peer_id) == NULL); assert(getPlayer(player->peer_id) == NULL);
@ -240,20 +71,6 @@ re_search:
} }
} }
#ifndef SERVER
LocalPlayer * Environment::getLocalPlayer()
{
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
if(player->isLocal())
return (LocalPlayer*)player;
}
return NULL;
}
#endif
Player * Environment::getPlayer(u16 peer_id) Player * Environment::getPlayer(u16 peer_id)
{ {
for(core::list<Player*>::Iterator i = m_players.begin(); for(core::list<Player*>::Iterator i = m_players.begin();
@ -315,7 +132,38 @@ void Environment::printPlayers(std::ostream &o)
} }
} }
void Environment::serializePlayers(const std::string &savedir) void Environment::setDayNightRatio(u32 r)
{
m_daynight_ratio = r;
}
u32 Environment::getDayNightRatio()
{
return m_daynight_ratio;
}
/*
ServerEnvironment
*/
ServerEnvironment::ServerEnvironment(ServerMap *map):
m_map(map),
m_random_spawn_timer(0)
{
/*
TEST CODE
*/
TestSAO *obj = new TestSAO(0, v3f(0, BS*5, 0));
addActiveObject(obj);
}
ServerEnvironment::~ServerEnvironment()
{
// Drop/delete map
m_map->drop();
}
void ServerEnvironment::serializePlayers(const std::string &savedir)
{ {
std::string players_path = savedir + "/players"; std::string players_path = savedir + "/players";
fs::CreateDir(players_path); fs::CreateDir(players_path);
@ -430,7 +278,7 @@ void Environment::serializePlayers(const std::string &savedir)
//dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl; //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
} }
void Environment::deSerializePlayers(const std::string &savedir) void ServerEnvironment::deSerializePlayers(const std::string &savedir)
{ {
std::string players_path = savedir + "/players"; std::string players_path = savedir + "/players";
@ -492,25 +340,630 @@ void Environment::deSerializePlayers(const std::string &savedir)
} }
} }
void ServerEnvironment::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
// Get some settings
//bool free_move = g_settings.getBool("free_move");
bool footprints = g_settings.getBool("footprints");
{
//TimeTaker timer("Server m_map->timerUpdate()", g_device);
m_map->timerUpdate(dtime);
}
/*
Handle players
*/
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
// Move
player->move(dtime, *m_map, 100*BS);
/*
Add footsteps to grass
*/
if(footprints)
{
// Get node that is at BS/4 under player
v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
try{
MapNode n = m_map->getNode(bottompos);
if(n.d == CONTENT_GRASS)
{
n.d = CONTENT_GRASS_FOOTSTEPS;
m_map->setNode(bottompos, n);
}
}
catch(InvalidPositionException &e)
{
}
}
}
/*
Step active objects
*/
for(core::map<u16, ServerActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
ServerActiveObject* obj = i.getNode()->getValue();
// Step object, putting messages directly to the queue
obj->step(dtime, m_active_object_messages);
}
/*
Remove (m_removed && m_known_by_count==0) objects
*/
{
core::list<u16> objects_to_remove;
for(core::map<u16, ServerActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
u16 id = i.getNode()->getKey();
ServerActiveObject* obj = i.getNode()->getValue();
// This shouldn't happen but check it
if(obj == NULL)
{
dstream<<"WARNING: NULL object found in ServerEnvironment"
<<" while finding removed objects. id="<<id<<std::endl;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
continue;
}
else
{
// If not m_removed, don't remove.
if(obj->m_removed == false)
continue;
// Delete
delete obj;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
}
// Remove references from m_active_objects
for(core::list<u16>::Iterator i = objects_to_remove.begin();
i != objects_to_remove.end(); i++)
{
m_active_objects.remove(*i);
}
}
/*
TEST CODE
*/
m_random_spawn_timer -= dtime;
if(m_random_spawn_timer < 0)
{
m_random_spawn_timer += 0.1;
TestSAO *obj = new TestSAO(0,
v3f(myrand_range(-2*BS,2*BS), BS*5, myrand_range(-2*BS,2*BS)));
addActiveObject(obj);
}
}
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
{
core::map<u16, ServerActiveObject*>::Node *n;
n = m_active_objects.find(id);
if(n == NULL)
return NULL;
return n->getValue();
}
bool isFreeServerActiveObjectId(u16 id,
core::map<u16, ServerActiveObject*> &objects)
{
if(id == 0)
return false;
for(core::map<u16, ServerActiveObject*>::Iterator
i = objects.getIterator();
i.atEnd()==false; i++)
{
if(i.getNode()->getKey() == id)
return false;
}
return true;
}
u16 getFreeServerActiveObjectId(
core::map<u16, ServerActiveObject*> &objects)
{
u16 new_id = 1;
for(;;)
{
if(isFreeServerActiveObjectId(new_id, objects))
return new_id;
if(new_id == 65535)
return 0;
new_id++;
}
}
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{
assert(object);
if(object->getId() == 0)
{
u16 new_id = getFreeServerActiveObjectId(m_active_objects);
if(new_id == 0)
{
dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
<<"no free ids available"<<std::endl;
delete object;
return 0;
}
object->setId(new_id);
}
if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
{
dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
<<"id is not free ("<<object->getId()<<")"<<std::endl;
delete object;
return 0;
}
dstream<<"INGO: ServerEnvironment::addActiveObject(): "
<<"added (id="<<object->getId()<<")"<<std::endl;
m_active_objects.insert(object->getId(), object);
return object->getId();
}
/*
Finds out what new objects have been added to
inside a radius around a position
*/
void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
core::map<u16, bool> &current_objects,
core::map<u16, bool> &added_objects)
{
v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
/*
Go through the object list,
- discard m_removed objects,
- discard objects that are too far away,
- discard objects that are found in current_objects.
- add remaining objects to added_objects
*/
for(core::map<u16, ServerActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
u16 id = i.getNode()->getKey();
// Get object
ServerActiveObject *object = i.getNode()->getValue();
if(object == NULL)
continue;
// Discard if removed
if(object->m_removed)
continue;
// Discard if too far
f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
if(distance_f > radius_f)
continue;
// Discard if already on current_objects
core::map<u16, bool>::Node *n;
n = current_objects.find(id);
if(n != NULL)
continue;
// Add to added_objects
added_objects.insert(id, false);
}
}
/*
Finds out what objects have been removed from
inside a radius around a position
*/
void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
core::map<u16, bool> &current_objects,
core::map<u16, bool> &removed_objects)
{
v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
/*
Go through current_objects; object is removed if:
- object is not found in m_active_objects (this is actually an
error condition; objects should be set m_removed=true and removed
only after all clients have been informed about removal), or
- object has m_removed=true, or
- object is too far away
*/
for(core::map<u16, bool>::Iterator
i = current_objects.getIterator();
i.atEnd()==false; i++)
{
u16 id = i.getNode()->getKey();
ServerActiveObject *object = getActiveObject(id);
if(object == NULL)
{
dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
<<" object in current_objects is NULL"<<std::endl;
}
else if(object->m_removed == false)
{
f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
/*dstream<<"removed == false"
<<"distance_f = "<<distance_f
<<", radius_f = "<<radius_f<<std::endl;*/
if(distance_f < radius_f)
{
// Not removed
continue;
}
}
removed_objects.insert(id, false);
}
}
ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
{
if(m_active_object_messages.size() == 0)
return ActiveObjectMessage(0);
return m_active_object_messages.pop_front();
}
#ifndef SERVER #ifndef SERVER
void Environment::updateMeshes(v3s16 blockpos)
/*
ClientEnvironment
*/
ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
m_map(map),
m_smgr(smgr)
{
assert(m_map);
assert(m_smgr);
}
ClientEnvironment::~ClientEnvironment()
{
// delete active objects
for(core::map<u16, ClientActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
delete i.getNode()->getValue();
}
// Drop/delete map
m_map->drop();
}
void ClientEnvironment::addPlayer(Player *player)
{
DSTACK(__FUNCTION_NAME);
/*
It is a failure if player is local and there already is a local
player
*/
assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
Environment::addPlayer(player);
}
LocalPlayer * ClientEnvironment::getLocalPlayer()
{
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
if(player->isLocal())
return (LocalPlayer*)player;
}
return NULL;
}
void ClientEnvironment::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
// Get some settings
bool free_move = g_settings.getBool("free_move");
bool footprints = g_settings.getBool("footprints");
{
//TimeTaker timer("Client m_map->timerUpdate()", g_device);
m_map->timerUpdate(dtime);
}
/*
Get the speed the player is going
*/
f32 player_speed = 0.001; // just some small value
LocalPlayer *lplayer = getLocalPlayer();
if(lplayer)
player_speed = lplayer->getSpeed().getLength();
/*
Maximum position increment
*/
//f32 position_max_increment = 0.05*BS;
f32 position_max_increment = 0.1*BS;
// Maximum time increment (for collision detection etc)
// time = distance / speed
f32 dtime_max_increment = position_max_increment / player_speed;
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
// Don't allow overly huge dtime
if(dtime > 0.5)
dtime = 0.5;
f32 dtime_downcount = dtime;
/*
Stuff that has a maximum time increment
*/
u32 loopcount = 0;
do
{
loopcount++;
f32 dtime_part;
if(dtime_downcount > dtime_max_increment)
dtime_part = dtime_max_increment;
else
dtime_part = dtime;
dtime_downcount -= dtime_part;
/*
Handle local player
*/
{
Player *player = getLocalPlayer();
v3f playerpos = player->getPosition();
// Apply physics
if(free_move == false)
{
// Gravity
v3f speed = player->getSpeed();
if(player->swimming_up == false)
speed.Y -= 9.81 * BS * dtime_part * 2;
// Water resistance
if(player->in_water_stable || player->in_water)
{
f32 max_down = 2.0*BS;
if(speed.Y < -max_down) speed.Y = -max_down;
f32 max = 2.5*BS;
if(speed.getLength() > max)
{
speed = speed / speed.getLength() * max;
}
}
player->setSpeed(speed);
}
/*
Move the player.
This also does collision detection.
*/
player->move(dtime_part, *m_map, position_max_increment);
}
}
while(dtime_downcount > 0.001);
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
/*
Stuff that can be done in an arbitarily large dtime
*/
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
/*
Handle non-local players
*/
if(player->isLocal() == false)
{
// Move
player->move(dtime, *m_map, 100*BS);
// Update lighting on remote players on client
u8 light = LIGHT_MAX;
try{
// Get node at head
v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
MapNode n = m_map->getNode(p);
light = n.getLightBlend(m_daynight_ratio);
}
catch(InvalidPositionException &e) {}
player->updateLight(light);
}
/*
Add footsteps to grass
*/
if(footprints)
{
// Get node that is at BS/4 under player
v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
try{
MapNode n = m_map->getNode(bottompos);
if(n.d == CONTENT_GRASS)
{
n.d = CONTENT_GRASS_FOOTSTEPS;
m_map->setNode(bottompos, n);
// Update mesh on client
if(m_map->mapType() == MAPTYPE_CLIENT)
{
v3s16 p_blocks = getNodeBlockPos(bottompos);
MapBlock *b = m_map->getBlockNoCreate(p_blocks);
b->updateMesh(m_daynight_ratio);
}
}
}
catch(InvalidPositionException &e)
{
}
}
}
/*
Step active objects
*/
for(core::map<u16, ClientActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
ClientActiveObject* obj = i.getNode()->getValue();
// Step object
obj->step(dtime);
}
}
void ClientEnvironment::updateMeshes(v3s16 blockpos)
{ {
m_map->updateMeshes(blockpos, m_daynight_ratio); m_map->updateMeshes(blockpos, m_daynight_ratio);
} }
void Environment::expireMeshes(bool only_daynight_diffed) void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
{ {
m_map->expireMeshes(only_daynight_diffed); m_map->expireMeshes(only_daynight_diffed);
} }
#endif
void Environment::setDayNightRatio(u32 r) ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
{ {
m_daynight_ratio = r; core::map<u16, ClientActiveObject*>::Node *n;
n = m_active_objects.find(id);
if(n == NULL)
return NULL;
return n->getValue();
} }
u32 Environment::getDayNightRatio() bool isFreeClientActiveObjectId(u16 id,
core::map<u16, ClientActiveObject*> &objects)
{ {
return m_daynight_ratio; if(id == 0)
return false;
for(core::map<u16, ClientActiveObject*>::Iterator
i = objects.getIterator();
i.atEnd()==false; i++)
{
if(i.getNode()->getKey() == id)
return false;
}
return true;
} }
u16 getFreeClientActiveObjectId(
core::map<u16, ClientActiveObject*> &objects)
{
u16 new_id = 1;
for(;;)
{
if(isFreeClientActiveObjectId(new_id, objects))
return new_id;
if(new_id == 65535)
return 0;
new_id++;
}
}
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
{
assert(object);
if(object->getId() == 0)
{
u16 new_id = getFreeClientActiveObjectId(m_active_objects);
if(new_id == 0)
{
dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
<<"no free ids available"<<std::endl;
delete object;
return 0;
}
object->setId(new_id);
}
if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
{
dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
<<"id is not free ("<<object->getId()<<")"<<std::endl;
delete object;
return 0;
}
dstream<<"INGO: ClientEnvironment::addActiveObject(): "
<<"added (id="<<object->getId()<<")"<<std::endl;
m_active_objects.insert(object->getId(), object);
object->addToScene(m_smgr);
return object->getId();
}
void ClientEnvironment::addActiveObject(u16 id, u8 type)
{
ClientActiveObject* obj = ClientActiveObject::create(type);
if(obj == NULL)
{
dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
<<"id="<<id<<" type="<<type<<": Couldn't create object"
<<std::endl;
return;
}
obj->setId(id);
addActiveObject(obj);
}
void ClientEnvironment::removeActiveObject(u16 id)
{
dstream<<"ClientEnvironment::removeActiveObject(): "
<<"id="<<id<<std::endl;
ClientActiveObject* obj = getActiveObject(id);
if(obj == NULL)
{
dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
<<"id="<<id<<" not found"<<std::endl;
return;
}
obj->removeFromScene();
delete obj;
m_active_objects.remove(id);
}
void ClientEnvironment::processActiveObjectMessage(u16 id,
const std::string &data)
{
ClientActiveObject* obj = getActiveObject(id);
if(obj == NULL)
{
dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
<<" got message for id="<<id<<", which doesn't exist."
<<std::endl;
return;
}
obj->processMessage(data);
}
#endif // #ifndef SERVER

@ -26,7 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- The map - The map
- Players - Players
- Other objects - Other objects
- The current time in the game, etc. - The current time in the game (actually it only contains the brightness)
- etc.
*/ */
#include <list> #include <list>
@ -39,51 +40,175 @@ class Environment
{ {
public: public:
// Environment will delete the map passed to the constructor // Environment will delete the map passed to the constructor
Environment(Map *map, std::ostream &dout); Environment();
~Environment(); virtual ~Environment();
/*
This can do anything to the environment, such as removing
timed-out players.
Also updates Map's timers.
*/
void step(f32 dtime);
Map & getMap();
/* /*
Environment deallocates players after use. Step everything in environment.
- Move players
- Step mobs
- Run timers of map
*/ */
void addPlayer(Player *player); virtual void step(f32 dtime) = 0;
virtual Map & getMap() = 0;
virtual void addPlayer(Player *player);
void removePlayer(u16 peer_id); void removePlayer(u16 peer_id);
#ifndef SERVER
LocalPlayer * getLocalPlayer();
#endif
Player * getPlayer(u16 peer_id); Player * getPlayer(u16 peer_id);
Player * getPlayer(const char *name); Player * getPlayer(const char *name);
core::list<Player*> getPlayers(); core::list<Player*> getPlayers();
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 serializePlayers(const std::string &savedir);
// This loads players as ServerRemotePlayers
void deSerializePlayers(const std::string &savedir);
#ifndef SERVER
void updateMeshes(v3s16 blockpos);
void expireMeshes(bool only_daynight_diffed);
#endif
void setDayNightRatio(u32 r); void setDayNightRatio(u32 r);
u32 getDayNightRatio(); u32 getDayNightRatio();
private: protected:
Map *m_map; // 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;
// Debug output goes here // Brightness
std::ostream &m_dout;
u32 m_daynight_ratio; u32 m_daynight_ratio;
}; };
/*
The server-side environment.
This is not thread-safe. Server uses an environment mutex.
*/
#include "serverobject.h"
class ServerEnvironment : public Environment
{
public:
ServerEnvironment(ServerMap *map);
~ServerEnvironment();
Map & getMap()
{
return *m_map;
}
ServerMap & getServerMap()
{
return *m_map;
}
void step(f32 dtime);
void serializePlayers(const std::string &savedir);
void deSerializePlayers(const std::string &savedir);
/*
ActiveObjects
*/
ServerActiveObject* getActiveObject(u16 id);
/*
Adds an active object to the environment.
Environment handles deletion of object.
Object may be deleted by environment immediately.
If id of object is 0, assigns a free id to it.
Returns the id of the object.
Returns 0 if not added and thus deleted.
*/
u16 addActiveObject(ServerActiveObject *object);
/*
Finds out what new objects have been added to
inside a radius around a position
*/
void getAddedActiveObjects(v3s16 pos, s16 radius,
core::map<u16, bool> &current_objects,
core::map<u16, bool> &added_objects);
/*
Finds out what new objects have been removed from
inside a radius around a position
*/
void getRemovedActiveObjects(v3s16 pos, s16 radius,
core::map<u16, bool> &current_objects,
core::map<u16, bool> &removed_objects);
/*
Gets the next message emitted by some active object.
Returns a message with id=0 if no messages are available.
*/
ActiveObjectMessage getActiveObjectMessage();
private:
ServerMap *m_map;
core::map<u16, ServerActiveObject*> m_active_objects;
Queue<ActiveObjectMessage> m_active_object_messages;
float m_random_spawn_timer;
};
#ifndef SERVER
#include "clientobject.h"
/*
The client-side environment.
This is not thread-safe.
Must be called from main (irrlicht) thread (uses the SceneManager)
Client uses an environment mutex.
*/
class ClientEnvironment : public Environment
{
public:
ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr);
~ClientEnvironment();
Map & getMap()
{
return *m_map;
}
ClientMap & getClientMap()
{
return *m_map;
}
void step(f32 dtime);
virtual void addPlayer(Player *player);
LocalPlayer * getLocalPlayer();
void updateMeshes(v3s16 blockpos);
void expireMeshes(bool only_daynight_diffed);
/*
ActiveObjects
*/
ClientActiveObject* getActiveObject(u16 id);
/*
Adds an active object to the environment.
Environment handles deletion of object.
Object may be deleted by environment immediately.
If id of object is 0, assigns a free id to it.
Returns the id of the object.
Returns 0 if not added and thus deleted.
*/
u16 addActiveObject(ClientActiveObject *object);
void addActiveObject(u16 id, u8 type);
void removeActiveObject(u16 id);
void processActiveObjectMessage(u16 id, const std::string &data);
private:
ClientMap *m_map;
scene::ISceneManager *m_smgr;
core::map<u16, ClientActiveObject*> m_active_objects;
};
#endif
#endif #endif

@ -35,7 +35,7 @@
#endif // _WIN32_WCE #endif // _WIN32_WCE
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>
// CriticalSection is way faster than the alternative
#define JMUTEX_CRITICALSECTION #define JMUTEX_CRITICALSECTION
#else // using pthread #else // using pthread
#include <pthread.h> #include <pthread.h>

@ -160,32 +160,23 @@ TODO: Make fetching sector's blocks more efficient when rendering
TODO: Flowing water animation TODO: Flowing water animation
NOTE(FIXED): A lock condition is possible:
1) MapBlock::updateMesh() is called from client asynchronously:
- AsyncProcessData() -> Map::updateMeshes()
2) Asynchronous locks m_temp_mods_mutex
3) MapBlock::updateMesh() is called from client synchronously:
- Client::step() -> Environment::step()
4) Synchronous starts waiting for m_temp_mods_mutex
5) Asynchronous calls getTexture, which starts waiting for main thread
Configuration: Configuration:
-------------- --------------
TODO: Make the video backend selectable
Client: Client:
------- -------
TODO: Untie client network operations from framerate TODO: Untie client network operations from framerate
- Needs some input queues or something - Needs some input queues or something
TODO: Make morning and evening transition more smooth and maybe shorter SUGG: Make morning and evening transition more smooth and maybe shorter
TODO: Don't update all meshes always on single node changes, but SUGG: Don't update all meshes always on single node changes, but
check which ones should be updated check which ones should be updated
- implement Map::updateNodeMeshes() - implement Map::updateNodeMeshes()
TODO: Remove IrrlichtWrapper
Server: Server:
------- -------
@ -194,16 +185,13 @@ TODO: When player dies, throw items on map
TODO: Make an option to the server to disable building and digging near TODO: Make an option to the server to disable building and digging near
the starting position the starting position
TODO: Save players with inventories to disk
TODO: Players to be saved as text in map/players/<name>
TODO: Copy the text of the last picked sign to inventory in creative TODO: Copy the text of the last picked sign to inventory in creative
mode mode
TODO: Check what goes wrong with caching map to disk (Kray) TODO: Check what goes wrong with caching map to disk (Kray)
- Nothing? - Nothing?
TODO: When server sees that client is removing an inexistent block to TODO: When server sees that client is removing an inexistent block in
an existent position, resend the MapBlock. an existent position, resend the MapBlock.
FIXME: Server went into some infinite PeerNotFoundException loop FIXME: Server went into some infinite PeerNotFoundException loop
@ -237,6 +225,11 @@ Block object server side:
- TODO: For incoming blocks, time difference is calculated and - TODO: For incoming blocks, time difference is calculated and
objects are stepped according to it. objects are stepped according to it.
- When an active object goes far from a player, either delete
it or store it statically.
- When a statically stored active object comes near a player,
recreate the active object
Map: Map:
---- ----
@ -345,18 +338,18 @@ Doing now (most important at the top):
#include <fstream> #include <fstream>
#include <jmutexautolock.h> #include <jmutexautolock.h>
#include <locale.h> #include <locale.h>
#include "main.h"
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "debug.h" #include "debug.h"
#include "map.h" #include "map.h"
#include "player.h" #include "player.h"
#include "main.h"
#include "test.h" #include "test.h"
#include "environment.h" //#include "environment.h"
#include "server.h" #include "server.h"
#include "client.h" #include "client.h"
#include "serialization.h" //#include "serialization.h"
#include "constants.h" #include "constants.h"
#include "strfnd.h" //#include "strfnd.h"
#include "porting.h" #include "porting.h"
#include "irrlichtwrapper.h" #include "irrlichtwrapper.h"
#include "gettime.h" #include "gettime.h"
@ -905,8 +898,12 @@ class RandomInputHandler : public InputHandler
public: public:
RandomInputHandler() RandomInputHandler()
{ {
leftdown = false;
rightdown = false;
leftclicked = false; leftclicked = false;
rightclicked = false; rightclicked = false;
leftreleased = false;
rightreleased = false;
for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i) for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
keydown[i] = false; keydown[i] = false;
} }
@ -925,11 +922,11 @@ public:
virtual bool getLeftState() virtual bool getLeftState()
{ {
return false; return leftdown;
} }
virtual bool getRightState() virtual bool getRightState()
{ {
return false; return rightdown;
} }
virtual bool getLeftClicked() virtual bool getLeftClicked()
@ -951,37 +948,23 @@ public:
virtual bool getLeftReleased() virtual bool getLeftReleased()
{ {
return false; return leftreleased;
} }
virtual bool getRightReleased() virtual bool getRightReleased()
{ {
return false; return rightreleased;
} }
virtual void resetLeftReleased() virtual void resetLeftReleased()
{ {
leftreleased = false;
} }
virtual void resetRightReleased() virtual void resetRightReleased()
{ {
rightreleased = false;
} }
virtual void step(float dtime) virtual void step(float dtime)
{ {
{
static float counter1 = 0;
counter1 -= dtime;
if(counter1 < 0.0)
{
counter1 = 0.1*Rand(1,10);
/*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
g_selected_material++;
else
g_selected_material = 0;*/
if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
g_selected_item++;
else
g_selected_item = 0;
}
}
{ {
static float counter1 = 0; static float counter1 = 0;
counter1 -= dtime; counter1 -= dtime;
@ -1033,7 +1016,11 @@ public:
if(counter1 < 0.0) if(counter1 < 0.0)
{ {
counter1 = 0.1*Rand(1, 30); counter1 = 0.1*Rand(1, 30);
leftclicked = true; leftdown = !leftdown;
if(leftdown)
leftclicked = true;
if(!leftdown)
leftreleased = true;
} }
} }
{ {
@ -1041,8 +1028,12 @@ public:
counter1 -= dtime; counter1 -= dtime;
if(counter1 < 0.0) if(counter1 < 0.0)
{ {
counter1 = 0.1*Rand(1, 20); counter1 = 0.1*Rand(1, 15);
rightclicked = true; rightdown = !rightdown;
if(rightdown)
rightclicked = true;
if(!rightdown)
rightreleased = true;
} }
} }
mousepos += mousespeed; mousepos += mousespeed;
@ -1056,8 +1047,12 @@ private:
bool keydown[KEY_KEY_CODES_COUNT]; bool keydown[KEY_KEY_CODES_COUNT];
v2s32 mousepos; v2s32 mousepos;
v2s32 mousespeed; v2s32 mousespeed;
bool leftdown;
bool rightdown;
bool leftclicked; bool leftclicked;
bool rightclicked; bool rightclicked;
bool leftreleased;
bool rightreleased;
}; };
void updateViewingRange(f32 frametime_in, Client *client) void updateViewingRange(f32 frametime_in, Client *client)
@ -2486,11 +2481,9 @@ int main(int argc, char *argv[])
camera->setTarget(camera_position + camera_direction * 100.0); camera->setTarget(camera_position + camera_direction * 100.0);
if(FIELD_OF_VIEW_TEST){ if(FIELD_OF_VIEW_TEST){
//client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
client.updateCamera(v3f(0,0,0), v3f(0,0,1)); client.updateCamera(v3f(0,0,0), v3f(0,0,1));
} }
else{ else{
//client.m_env.getMap().updateCamera(camera_position, camera_direction);
//TimeTaker timer("client.updateCamera"); //TimeTaker timer("client.updateCamera");
client.updateCamera(camera_position, camera_direction); client.updateCamera(camera_position, camera_direction);
} }
@ -2585,7 +2578,7 @@ int main(int argc, char *argv[])
core::aabbox3d<f32> nodehilightbox; core::aabbox3d<f32> nodehilightbox;
f32 mindistance = BS * 1001; f32 mindistance = BS * 1001;
v3s16 pos_i = floatToInt(player_position); v3s16 pos_i = floatToInt(player_position, BS);
/*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")" /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
<<std::endl;*/ <<std::endl;*/
@ -2615,7 +2608,7 @@ int main(int argc, char *argv[])
} }
v3s16 np(x,y,z); v3s16 np(x,y,z);
v3f npf = intToFloat(np); v3f npf = intToFloat(np, BS);
f32 d = 0.01; f32 d = 0.01;
@ -2723,7 +2716,7 @@ int main(int argc, char *argv[])
const float d = 0.502; const float d = 0.502;
core::aabbox3d<f32> nodebox core::aabbox3d<f32> nodebox
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
v3f nodepos_f = intToFloat(nodepos); v3f nodepos_f = intToFloat(nodepos, BS);
nodebox.MinEdge += nodepos_f; nodebox.MinEdge += nodepos_f;
nodebox.MaxEdge += nodepos_f; nodebox.MaxEdge += nodepos_f;
nodehilightbox = nodebox; nodehilightbox = nodebox;

@ -1221,95 +1221,6 @@ void Map::removeNodeAndUpdate(v3s16 p,
} }
} }
#ifndef SERVER
void Map::expireMeshes(bool only_daynight_diffed)
{
TimeTaker timer("expireMeshes()");
core::map<v2s16, MapSector*>::Iterator si;
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
MapSector *sector = si.getNode()->getValue();
core::list< MapBlock * > sectorblocks;
sector->getBlocks(sectorblocks);
core::list< MapBlock * >::Iterator i;
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
{
MapBlock *block = *i;
if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
{
continue;
}
{
JMutexAutoLock lock(block->mesh_mutex);
if(block->mesh != NULL)
{
/*block->mesh->drop();
block->mesh = NULL;*/
block->setMeshExpired(true);
}
}
}
}
}
void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
{
assert(mapType() == MAPTYPE_CLIENT);
try{
v3s16 p = blockpos + v3s16(0,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
// Leading edge
try{
v3s16 p = blockpos + v3s16(-1,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,-1,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,-1);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
/*// Trailing edge
try{
v3s16 p = blockpos + v3s16(1,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,1,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,1);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}*/
}
#endif
bool Map::dayNightDiffed(v3s16 blockpos) bool Map::dayNightDiffed(v3s16 blockpos)
{ {
try{ try{
@ -4371,7 +4282,7 @@ continue_generating:
//if(!is_ground_content(block->getNode(cp).d)) //if(!is_ground_content(block->getNode(cp).d))
if(1) if(1)
{ {
RatObject *obj = new RatObject(NULL, -1, intToFloat(cp)); RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
block->addObject(obj); block->addObject(obj);
} }
} }
@ -5667,6 +5578,92 @@ bool ClientMap::clearTempMod(v3s16 p,
return changed; return changed;
} }
void ClientMap::expireMeshes(bool only_daynight_diffed)
{
TimeTaker timer("expireMeshes()");
core::map<v2s16, MapSector*>::Iterator si;
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
MapSector *sector = si.getNode()->getValue();
core::list< MapBlock * > sectorblocks;
sector->getBlocks(sectorblocks);
core::list< MapBlock * >::Iterator i;
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
{
MapBlock *block = *i;
if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
{
continue;
}
{
JMutexAutoLock lock(block->mesh_mutex);
if(block->mesh != NULL)
{
/*block->mesh->drop();
block->mesh = NULL;*/
block->setMeshExpired(true);
}
}
}
}
}
void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
{
assert(mapType() == MAPTYPE_CLIENT);
try{
v3s16 p = blockpos + v3s16(0,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
// Leading edge
try{
v3s16 p = blockpos + v3s16(-1,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,-1,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,-1);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
/*// Trailing edge
try{
v3s16 p = blockpos + v3s16(1,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,1,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,1);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
}
catch(InvalidPositionException &e){}*/
}
void ClientMap::PrintInfo(std::ostream &out) void ClientMap::PrintInfo(std::ostream &out)
{ {
out<<"ClientMap: "; out<<"ClientMap: ";

@ -33,7 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif #endif
#include "common_irrlicht.h" #include "common_irrlicht.h"
//#include "heightmap.h"
#include "mapnode.h" #include "mapnode.h"
#include "mapblock.h" #include "mapblock.h"
#include "mapsector.h" #include "mapsector.h"
@ -61,7 +60,10 @@ public:
{ {
return MAPTYPE_BASE; return MAPTYPE_BASE;
} }
/*
Drop (client) or delete (server) the map.
*/
virtual void drop() virtual void drop()
{ {
delete this; delete this;
@ -211,19 +213,6 @@ public:
void removeNodeAndUpdate(v3s16 p, void removeNodeAndUpdate(v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks); core::map<v3s16, MapBlock*> &modified_blocks);
#ifndef SERVER
void expireMeshes(bool only_daynight_diffed);
/*
Update the faces of the given block and blocks on the
leading edge.
*/
void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
// Update meshes that touch the node
//void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
#endif
/* /*
Takes the blocks at the edges into account Takes the blocks at the edges into account
*/ */
@ -628,6 +617,17 @@ public:
// Efficient implementation needs a cache of TempMods // Efficient implementation needs a cache of TempMods
//void clearTempMods(); //void clearTempMods();
void expireMeshes(bool only_daynight_diffed);
/*
Update the faces of the given block and blocks on the
leading edge.
*/
void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
// Update meshes that touch the node
//void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);

@ -822,7 +822,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
if(dir == v3s16(0,1,0)) if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXZBy(-45); vertices[i].Pos.rotateXZBy(-45);
vertices[i].Pos += intToFloat(p + getPosRelative()); vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
} }
// Set material // Set material
@ -1066,7 +1066,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
if(dir == v3s16(1,0,-0)) if(dir == v3s16(1,0,-0))
vertices[j].Pos.rotateXZBy(-90); vertices[j].Pos.rotateXZBy(-90);
vertices[j].Pos += intToFloat(p + getPosRelative()); vertices[j].Pos += intToFloat(p + getPosRelative(), BS);
} }
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
@ -1105,7 +1105,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
s32 j = corner_resolve[i]; s32 j = corner_resolve[i];
vertices[i].Pos.Y += corner_levels[j]; vertices[i].Pos.Y += corner_levels[j];
vertices[i].Pos += intToFloat(p + getPosRelative()); vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
} }
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
@ -1155,7 +1155,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
for(s32 i=0; i<4; i++) for(s32 i=0; i<4; i++)
{ {
vertices[i].Pos.Y += (-0.5+node_water_level)*BS; vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
vertices[i].Pos += intToFloat(p + getPosRelative()); vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
} }
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
@ -1222,7 +1222,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
for(u16 i=0; i<4; i++) for(u16 i=0; i<4; i++)
{ {
vertices[i].Pos += intToFloat(p + getPosRelative()); vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
} }
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
@ -1596,7 +1596,7 @@ void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
if(getNode(p).d == CONTENT_AIR if(getNode(p).d == CONTENT_AIR
&& getNode(p).getLightBlend(daynight_ratio) <= 11) && getNode(p).getLightBlend(daynight_ratio) <= 11)
{ {
RatObject *obj = new RatObject(NULL, -1, intToFloat(p)); RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
addObject(obj); addObject(obj);
} }
} }

@ -35,7 +35,7 @@ v3f MapBlockObject::getAbsolutePos()
return m_pos; return m_pos;
// getPosRelative gets nodepos relative to map origin // getPosRelative gets nodepos relative to map origin
v3f blockpos = intToFloat(m_block->getPosRelative()); v3f blockpos = intToFloat(m_block->getPosRelative(), BS);
return blockpos + m_pos; return blockpos + m_pos;
} }
@ -55,7 +55,7 @@ v3f MovingObject::getAbsoluteShowPos()
return m_pos; return m_pos;
// getPosRelative gets nodepos relative to map origin // getPosRelative gets nodepos relative to map origin
v3f blockpos = intToFloat(m_block->getPosRelative()); v3f blockpos = intToFloat(m_block->getPosRelative(), BS);
return blockpos + m_showpos; return blockpos + m_showpos;
} }
@ -71,7 +71,7 @@ void MovingObject::move(float dtime, v3f acceleration)
acceleration.X, acceleration.Y, acceleration.Z acceleration.X, acceleration.Y, acceleration.Z
); );
v3s16 oldpos_i = floatToInt(m_pos); v3s16 oldpos_i = floatToInt(m_pos, BS);
if(m_block->isValidPosition(oldpos_i) == false) if(m_block->isValidPosition(oldpos_i) == false)
{ {
@ -137,7 +137,7 @@ void MovingObject::move(float dtime, v3f acceleration)
Collision detection Collision detection
*/ */
v3s16 pos_i = floatToInt(position); v3s16 pos_i = floatToInt(position, BS);
// The loop length is limited to the object moving a distance // The loop length is limited to the object moving a distance
f32 d = (float)BS * 0.15; f32 d = (float)BS * 0.15;
@ -614,7 +614,7 @@ void MapBlockObjectList::update(std::istream &is, u8 version,
{ {
u8 light = LIGHT_MAX; u8 light = LIGHT_MAX;
try{ try{
v3s16 relpos_i = floatToInt(obj->m_pos); v3s16 relpos_i = floatToInt(obj->m_pos, BS);
MapNode n = m_block->getNodeParent(relpos_i); MapNode n = m_block->getNodeParent(relpos_i);
light = n.getLightBlend(daynight_ratio); light = n.getLightBlend(daynight_ratio);
} }
@ -772,7 +772,7 @@ void MapBlockObjectList::step(float dtime, bool server, u32 daynight_ratio)
// Update light // Update light
u8 light = LIGHT_MAX; u8 light = LIGHT_MAX;
try{ try{
v3s16 relpos_i = floatToInt(obj->m_pos); v3s16 relpos_i = floatToInt(obj->m_pos, BS);
MapNode n = m_block->getNodeParent(relpos_i); MapNode n = m_block->getNodeParent(relpos_i);
light = n.getLightBlend(daynight_ratio); light = n.getLightBlend(daynight_ratio);
} }
@ -824,7 +824,7 @@ void MapBlockObjectList::step(float dtime, bool server, u32 daynight_ratio)
{ {
MapBlockObject *obj = i.getNode()->getValue(); MapBlockObject *obj = i.getNode()->getValue();
v3s16 pos_i = floatToInt(obj->m_pos); v3s16 pos_i = floatToInt(obj->m_pos, BS);
if(m_block->isValidPosition(pos_i)) if(m_block->isValidPosition(pos_i))
{ {
@ -871,7 +871,7 @@ bool MapBlockObjectList::wrapObject(MapBlockObject *object)
// Calculate blockpos on map // Calculate blockpos on map
v3s16 oldblock_pos_i_on_map = m_block->getPosRelative(); v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
v3f pos_f_on_oldblock = object->m_pos; v3f pos_f_on_oldblock = object->m_pos;
v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock); v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock, BS);
v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map; v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map); v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
@ -905,9 +905,9 @@ bool MapBlockObjectList::wrapObject(MapBlockObject *object)
} }
// Calculate position on new block // Calculate position on new block
v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map); v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map, BS);
v3s16 newblock_pos_i_on_map = newblock->getPosRelative(); v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map); v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map, BS);
v3f pos_f_on_newblock = pos_f_on_oldblock v3f pos_f_on_newblock = pos_f_on_oldblock
- newblock_pos_f_on_map + oldblock_pos_f_on_map; - newblock_pos_f_on_map + oldblock_pos_f_on_map;

@ -683,32 +683,6 @@ struct MapNode
} }
}; };
/*
Returns integer position of the node in given
floating point position.
*/
inline v3s16 floatToInt(v3f p)
{
v3s16 p2(
(p.X + (p.X>0 ? BS/2 : -BS/2))/BS,
(p.Y + (p.Y>0 ? BS/2 : -BS/2))/BS,
(p.Z + (p.Z>0 ? BS/2 : -BS/2))/BS);
return p2;
}
/*
The same thing backwards
*/
inline v3f intToFloat(v3s16 p)
{
v3f p2(
p.X * BS,
p.Y * BS,
p.Z * BS
);
return p2;
}
#endif #endif

@ -273,7 +273,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
{ {
v3f position = getPosition(); v3f position = getPosition();
v3f oldpos = position; v3f oldpos = position;
v3s16 oldpos_i = floatToInt(oldpos); v3s16 oldpos_i = floatToInt(oldpos, BS);
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<"," /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
<<oldpos_i.Z<<")"<<std::endl;*/ <<oldpos_i.Z<<")"<<std::endl;*/
@ -296,7 +296,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
*/ */
// Player position in nodes // Player position in nodes
v3s16 pos_i = floatToInt(position); v3s16 pos_i = floatToInt(position, BS);
/* /*
Check if player is in water (the oscillating value) Check if player is in water (the oscillating value)
@ -305,13 +305,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
// If in water, the threshold of coming out is at higher y // If in water, the threshold of coming out is at higher y
if(in_water) if(in_water)
{ {
v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0)); v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
in_water = content_liquid(map.getNode(pp).d); in_water = content_liquid(map.getNode(pp).d);
} }
// If not in water, the threshold of going in is at lower y // If not in water, the threshold of going in is at lower y
else else
{ {
v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0)); v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
in_water = content_liquid(map.getNode(pp).d); in_water = content_liquid(map.getNode(pp).d);
} }
} }
@ -324,7 +324,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
Check if player is in water (the stable value) Check if player is in water (the stable value)
*/ */
try{ try{
v3s16 pp = floatToInt(position + v3f(0,0,0)); v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
in_water_stable = content_liquid(map.getNode(pp).d); in_water_stable = content_liquid(map.getNode(pp).d);
} }
catch(InvalidPositionException &e) catch(InvalidPositionException &e)
@ -363,7 +363,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
if(control.sneak && m_sneak_node_exists) if(control.sneak && m_sneak_node_exists)
{ {
f32 maxd = 0.5*BS + sneak_max; f32 maxd = 0.5*BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node); v3f lwn_f = intToFloat(m_sneak_node, BS);
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd); position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd); position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
@ -537,13 +537,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
player is sneaking from, if any. player is sneaking from, if any.
*/ */
{ {
v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0)); v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
v2f player_p2df(position.X, position.Z); v2f player_p2df(position.X, position.Z);
f32 min_distance_f = 100000.0*BS; f32 min_distance_f = 100000.0*BS;
// If already seeking from some node, compare to it. // If already seeking from some node, compare to it.
/*if(m_sneak_node_exists) /*if(m_sneak_node_exists)
{ {
v3f sneaknode_pf = intToFloat(m_sneak_node); v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z); v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df); f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y); f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
@ -556,7 +556,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
for(s16 z=-1; z<=1; z++) for(s16 z=-1; z<=1; z++)
{ {
v3s16 p = pos_i_bottom + v3s16(x,0,z); v3s16 p = pos_i_bottom + v3s16(x,0,z);
v3f pf = intToFloat(p); v3f pf = intToFloat(p, BS);
v2f node_p2df(pf.X, pf.Z); v2f node_p2df(pf.X, pf.Z);
f32 distance_f = player_p2df.getDistanceFrom(node_p2df); f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
f32 max_axis_distance_f = MYMAX( f32 max_axis_distance_f = MYMAX(

@ -132,6 +132,10 @@ protected:
v3f m_position; v3f m_position;
}; };
/*
Player on the server
*/
class ServerRemotePlayer : public Player class ServerRemotePlayer : public Player
{ {
public: public:
@ -150,12 +154,16 @@ public:
virtual void move(f32 dtime, Map &map, f32 pos_max_d) virtual void move(f32 dtime, Map &map, f32 pos_max_d)
{ {
} }
private: private:
}; };
#ifndef SERVER #ifndef SERVER
/*
All the other players on the client are these
*/
class RemotePlayer : public Player, public scene::ISceneNode class RemotePlayer : public Player, public scene::ISceneNode
{ {
public: public:

@ -283,21 +283,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
// Increment timers // Increment timers
{ m_nearest_unsent_reset_timer += dtime;
JMutexAutoLock lock(m_blocks_sent_mutex);
m_nearest_unsent_reset_timer += dtime;
}
// Won't send anything if already sending // Won't send anything if already sending
if(m_blocks_sending.size() >= g_settings.getU16
("max_simultaneous_block_sends_per_client"))
{ {
JMutexAutoLock lock(m_blocks_sending_mutex); //dstream<<"Not sending any blocks, Queue full."<<std::endl;
return;
if(m_blocks_sending.size() >= g_settings.getU16
("max_simultaneous_block_sends_per_client"))
{
//dstream<<"Not sending any blocks, Queue full."<<std::endl;
return;
}
} }
Player *player = server->m_env.getPlayer(peer_id); Player *player = server->m_env.getPlayer(peer_id);
@ -307,7 +300,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
v3f playerpos = player->getPosition(); v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed(); v3f playerspeed = player->getSpeed();
v3s16 center_nodepos = floatToInt(playerpos); v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos); v3s16 center = getNodeBlockPos(center_nodepos);
@ -323,29 +316,26 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
*/ */
s16 last_nearest_unsent_d; s16 last_nearest_unsent_d;
s16 d_start; s16 d_start;
if(m_last_center != center)
{ {
JMutexAutoLock lock(m_blocks_sent_mutex); m_nearest_unsent_d = 0;
m_last_center = center;
if(m_last_center != center)
{
m_nearest_unsent_d = 0;
m_last_center = center;
}
/*dstream<<"m_nearest_unsent_reset_timer="
<<m_nearest_unsent_reset_timer<<std::endl;*/
if(m_nearest_unsent_reset_timer > 5.0)
{
m_nearest_unsent_reset_timer = 0;
m_nearest_unsent_d = 0;
//dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
}
last_nearest_unsent_d = m_nearest_unsent_d;
d_start = m_nearest_unsent_d;
} }
/*dstream<<"m_nearest_unsent_reset_timer="
<<m_nearest_unsent_reset_timer<<std::endl;*/
if(m_nearest_unsent_reset_timer > 5.0)
{
m_nearest_unsent_reset_timer = 0;
m_nearest_unsent_d = 0;
//dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
}
last_nearest_unsent_d = m_nearest_unsent_d;
d_start = m_nearest_unsent_d;
u16 maximum_simultaneous_block_sends_setting = g_settings.getU16 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
("max_simultaneous_block_sends_per_client"); ("max_simultaneous_block_sends_per_client");
u16 maximum_simultaneous_block_sends = u16 maximum_simultaneous_block_sends =
@ -356,24 +346,15 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
Decrease send rate if player is building stuff. Decrease send rate if player is building stuff.
*/ */
m_time_from_building += dtime;
if(m_time_from_building < g_settings.getFloat(
"full_block_send_enable_min_time_from_building"))
{ {
SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock()); maximum_simultaneous_block_sends
m_time_from_building.m_value += dtime; = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
/*if(m_time_from_building.m_value
< FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
if(m_time_from_building.m_value < g_settings.getFloat(
"full_block_send_enable_min_time_from_building"))
{
maximum_simultaneous_block_sends
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
} }
u32 num_blocks_selected; u32 num_blocks_selected = m_blocks_sending.size();
{
JMutexAutoLock lock(m_blocks_sending_mutex);
num_blocks_selected = m_blocks_sending.size();
}
/* /*
next time d will be continued from the d from which the nearest next time d will be continued from the d from which the nearest
@ -384,11 +365,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
*/ */
s32 new_nearest_unsent_d = -1; s32 new_nearest_unsent_d = -1;
// Serialization version used
//u8 ser_version = serialization_version;
//bool has_incomplete_blocks = false;
s16 d_max = g_settings.getS16("max_block_send_distance"); s16 d_max = g_settings.getS16("max_block_send_distance");
s16 d_max_gen = g_settings.getS16("max_block_generate_distance"); s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
@ -398,20 +374,16 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
{ {
//dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl; //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
//if(has_incomplete_blocks == false) /*
If m_nearest_unsent_d was changed by the EmergeThread
(it can change it to 0 through SetBlockNotSent),
update our d to it.
Else update m_nearest_unsent_d
*/
if(m_nearest_unsent_d != last_nearest_unsent_d)
{ {
JMutexAutoLock lock(m_blocks_sent_mutex); d = m_nearest_unsent_d;
/* last_nearest_unsent_d = m_nearest_unsent_d;
If m_nearest_unsent_d was changed by the EmergeThread
(it can change it to 0 through SetBlockNotSent),
update our d to it.
Else update m_nearest_unsent_d
*/
if(m_nearest_unsent_d != last_nearest_unsent_d)
{
d = m_nearest_unsent_d;
last_nearest_unsent_d = m_nearest_unsent_d;
}
} }
/* /*
@ -443,23 +415,19 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
maximum_simultaneous_block_sends_setting; maximum_simultaneous_block_sends_setting;
} }
// Limit is dynamically lowered when building
if(num_blocks_selected
>= maximum_simultaneous_block_sends_now)
{ {
JMutexAutoLock lock(m_blocks_sending_mutex); /*dstream<<"Not sending more blocks. Queue full. "
<<m_blocks_sending.size()
// Limit is dynamically lowered when building <<std::endl;*/
if(num_blocks_selected goto queue_full;
>= maximum_simultaneous_block_sends_now)
{
/*dstream<<"Not sending more blocks. Queue full. "
<<m_blocks_sending.size()
<<std::endl;*/
goto queue_full;
}
if(m_blocks_sending.find(p) != NULL)
continue;
} }
if(m_blocks_sending.find(p) != NULL)
continue;
/* /*
Do not go over-limit Do not go over-limit
*/ */
@ -519,7 +487,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
#endif #endif
/* /*
Don't draw if not in sight Don't generate or send if not in sight
*/ */
if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false) if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
@ -531,8 +499,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
Don't send already sent blocks Don't send already sent blocks
*/ */
{ {
JMutexAutoLock lock(m_blocks_sent_mutex);
if(m_blocks_sent.find(p) != NULL) if(m_blocks_sent.find(p) != NULL)
continue; continue;
} }
@ -546,12 +512,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
bool block_is_invalid = false; bool block_is_invalid = false;
if(block != NULL) if(block != NULL)
{ {
/*if(block->isIncomplete())
{
has_incomplete_blocks = true;
continue;
}*/
if(block->isDummy()) if(block->isDummy())
{ {
surely_not_found_on_disk = true; surely_not_found_on_disk = true;
@ -567,11 +527,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
v2s16 chunkpos = map->sector_to_chunk(p2d); v2s16 chunkpos = map->sector_to_chunk(p2d);
if(map->chunkNonVolatile(chunkpos) == false) if(map->chunkNonVolatile(chunkpos) == false)
block_is_invalid = true; block_is_invalid = true;
/*MapChunk *chunk = map->getChunk(chunkpos);
if(chunk == NULL)
block_is_invalid = true;
else if(chunk->getIsVolatile() == true)
block_is_invalid = true;*/
} }
/* /*
@ -598,11 +553,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
*/ */
if(block == NULL || surely_not_found_on_disk || block_is_invalid) if(block == NULL || surely_not_found_on_disk || block_is_invalid)
{ {
//dstream<<"asd"<<std::endl;
/*SharedPtr<JMutexAutoLock> lock
(m_num_blocks_in_emerge_queue.getLock());*/
//TODO: Get value from somewhere //TODO: Get value from somewhere
// Allow only one block in emerge queue // Allow only one block in emerge queue
if(server->m_emerge_queue.peerItemCount(peer_id) < 1) if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
@ -624,7 +574,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
} }
/* /*
Add block to queue Add block to send queue
*/ */
PrioritySortedBlockTransfer q((float)d, p, peer_id); PrioritySortedBlockTransfer q((float)d, p, peer_id);
@ -638,7 +588,6 @@ queue_full:
if(new_nearest_unsent_d != -1) if(new_nearest_unsent_d != -1)
{ {
JMutexAutoLock lock(m_blocks_sent_mutex);
m_nearest_unsent_d = new_nearest_unsent_d; m_nearest_unsent_d = new_nearest_unsent_d;
} }
} }
@ -743,7 +692,7 @@ void RemoteClient::SendObjectData(
v3f playerpos = player->getPosition(); v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed(); v3f playerspeed = player->getSpeed();
v3s16 center_nodepos = floatToInt(playerpos); v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos); v3s16 center = getNodeBlockPos(center_nodepos);
s16 d_max = g_settings.getS16("active_object_range"); s16 d_max = g_settings.getS16("active_object_range");
@ -767,7 +716,6 @@ void RemoteClient::SendObjectData(
Ignore blocks that haven't been sent to the client Ignore blocks that haven't been sent to the client
*/ */
{ {
JMutexAutoLock sentlock(m_blocks_sent_mutex);
if(m_blocks_sent.find(p) == NULL) if(m_blocks_sent.find(p) == NULL)
continue; continue;
} }
@ -861,8 +809,6 @@ skip_subsequent:
void RemoteClient::GotBlock(v3s16 p) void RemoteClient::GotBlock(v3s16 p)
{ {
JMutexAutoLock lock(m_blocks_sending_mutex);
JMutexAutoLock lock2(m_blocks_sent_mutex);
if(m_blocks_sending.find(p) != NULL) if(m_blocks_sending.find(p) != NULL)
m_blocks_sending.remove(p); m_blocks_sending.remove(p);
else else
@ -876,13 +822,6 @@ void RemoteClient::GotBlock(v3s16 p)
void RemoteClient::SentBlock(v3s16 p) void RemoteClient::SentBlock(v3s16 p)
{ {
JMutexAutoLock lock(m_blocks_sending_mutex);
/*if(m_blocks_sending.size() > 15)
{
dstream<<"RemoteClient::SentBlock(): "
<<"m_blocks_sending.size()="
<<m_blocks_sending.size()<<std::endl;
}*/
if(m_blocks_sending.find(p) == NULL) if(m_blocks_sending.find(p) == NULL)
m_blocks_sending.insert(p, 0.0); m_blocks_sending.insert(p, 0.0);
else else
@ -892,9 +831,6 @@ void RemoteClient::SentBlock(v3s16 p)
void RemoteClient::SetBlockNotSent(v3s16 p) void RemoteClient::SetBlockNotSent(v3s16 p)
{ {
JMutexAutoLock sendinglock(m_blocks_sending_mutex);
JMutexAutoLock sentlock(m_blocks_sent_mutex);
m_nearest_unsent_d = 0; m_nearest_unsent_d = 0;
if(m_blocks_sending.find(p) != NULL) if(m_blocks_sending.find(p) != NULL)
@ -905,9 +841,6 @@ void RemoteClient::SetBlockNotSent(v3s16 p)
void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks) void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
{ {
JMutexAutoLock sendinglock(m_blocks_sending_mutex);
JMutexAutoLock sentlock(m_blocks_sent_mutex);
m_nearest_unsent_d = 0; m_nearest_unsent_d = 0;
for(core::map<v3s16, MapBlock*>::Iterator for(core::map<v3s16, MapBlock*>::Iterator
@ -964,7 +897,7 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
Server::Server( Server::Server(
std::string mapsavedir std::string mapsavedir
): ):
m_env(new ServerMap(mapsavedir), dout_server), m_env(new ServerMap(mapsavedir)),
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),
@ -1257,11 +1190,8 @@ void Server::AsyncRunStep()
} }
/* /*
Update digging Check added and deleted active objects
NOTE: Some of this could be moved to RemoteClient
*/ */
#if 0
{ {
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex); JMutexAutoLock conlock(m_con_mutex);
@ -1272,100 +1202,209 @@ void Server::AsyncRunStep()
{ {
RemoteClient *client = i.getNode()->getValue(); RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id); Player *player = m_env.getPlayer(client->peer_id);
v3s16 pos = floatToInt(player->getPosition(), BS);
s16 radius = 32;
JMutexAutoLock digmutex(client->m_dig_mutex); core::map<u16, bool> removed_objects;
core::map<u16, bool> added_objects;
if(client->m_dig_tool_item == -1) m_env.getRemovedActiveObjects(pos, radius,
continue; client->m_known_objects, removed_objects);
m_env.getAddedActiveObjects(pos, radius,
client->m_dig_time_remaining -= dtime; client->m_known_objects, added_objects);
if(client->m_dig_time_remaining > 0)
{
client->m_time_from_building.set(0.0);
continue;
}
v3s16 p_under = client->m_dig_position;
// Mandatory parameter; actually used for nothing // Ignore if nothing happened
core::map<v3s16, MapBlock*> modified_blocks; if(removed_objects.size() == 0 && added_objects.size() == 0)
continue;
u8 material;
try
{
// Get material at position
material = m_env.getMap().getNode(p_under).d;
// If it's not diggable, do nothing
if(content_diggable(material) == false)
{
derr_server<<"Server: Not finishing digging: Node not diggable"
<<std::endl;
client->m_dig_tool_item = -1;
break;
}
}
catch(InvalidPositionException &e)
{
derr_server<<"Server: Not finishing digging: Node not found"
<<std::endl;
client->m_dig_tool_item = -1;
break;
}
// Create packet std::string data_buffer;
u32 replysize = 8;
SharedBuffer<u8> reply(replysize); char buf[4];
writeU16(&reply[0], TOCLIENT_REMOVENODE);
writeS16(&reply[2], p_under.X); // Handle removed objects
writeS16(&reply[4], p_under.Y); writeU16((u8*)buf, removed_objects.size());
writeS16(&reply[6], p_under.Z); data_buffer.append(buf, 2);
for(core::map<u16, bool>::Iterator
i = removed_objects.getIterator();
i.atEnd()==false; i++)
{
// Get object
u16 id = i.getNode()->getKey();
ServerActiveObject* obj = m_env.getActiveObject(id);
// Add to data buffer for sending
writeU16((u8*)buf, i.getNode()->getKey());
data_buffer.append(buf, 2);
// Remove from known objects
client->m_known_objects.remove(i.getNode()->getKey());
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
// Handle added objects
writeU16((u8*)buf, added_objects.size());
data_buffer.append(buf, 2);
for(core::map<u16, bool>::Iterator
i = added_objects.getIterator();
i.atEnd()==false; i++)
{
// Get object
u16 id = i.getNode()->getKey();
ServerActiveObject* obj = m_env.getActiveObject(id);
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
dstream<<"WARNING: "<<__FUNCTION_NAME
<<": NULL object"<<std::endl;
else
type = obj->getType();
// Add to data buffer for sending
writeU16((u8*)buf, id);
data_buffer.append(buf, 2);
writeU8((u8*)buf, type);
data_buffer.append(buf, 1);
// Add to known objects
client->m_known_objects.insert(i.getNode()->getKey(), false);
if(obj)
obj->m_known_by_count++;
}
// Send packet
SharedBuffer<u8> reply(2 + data_buffer.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
memcpy((char*)&reply[2], data_buffer.c_str(),
data_buffer.size());
// Send as reliable // Send as reliable
m_con.SendToAll(0, reply, true); m_con.Send(client->peer_id, 0, reply, true);
if(g_settings.getBool("creative_mode") == false)
{
// Add to inventory and send inventory
InventoryItem *item = new MaterialItem(material, 1);
player->inventory.addItem("main", item);
SendInventory(player->peer_id);
}
/* dstream<<"INFO: Server: Sent object remove/add: "
Remove the node <<removed_objects.size()<<" removed, "
(this takes some time so it is done after the quick stuff) <<added_objects.size()<<" added, "
*/ <<"packet size is "<<reply.getSize()<<std::endl;
m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
/*
Update water
*/
// Update water pressure around modification
// This also adds it to m_flow_active_nodes if appropriate
MapVoxelManipulator v(&m_env.getMap());
v.m_disable_water_climb =
g_settings.getBool("disable_water_climb");
VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
try
{
v.updateAreaWaterPressure(area, m_flow_active_nodes);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached (1)"<<std::endl;
}
v.blitBack(modified_blocks);
} }
} }
#endif
// Send object positions /*
Send object messages
*/
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
// Key = object id
// Value = data sent by object
core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
// Get active object messages from environment
for(;;)
{
ActiveObjectMessage aom = m_env.getActiveObjectMessage();
if(aom.id == 0)
break;
core::list<ActiveObjectMessage>* message_list = NULL;
core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
n = buffered_messages.find(aom.id);
if(n == NULL)
{
message_list = new core::list<ActiveObjectMessage>;
buffered_messages.insert(aom.id, message_list);
}
else
{
message_list = n->getValue();
}
message_list->push_back(aom);
}
// Route data to every client
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd()==false; i++)
{
RemoteClient *client = i.getNode()->getValue();
std::string reliable_data;
std::string unreliable_data;
// Go through all objects in message buffer
for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
j = buffered_messages.getIterator();
j.atEnd()==false; j++)
{
// If object is not known by client, skip it
u16 id = j.getNode()->getKey();
if(client->m_known_objects.find(id) == NULL)
continue;
// Get message list of object
core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
// Go through every message
for(core::list<ActiveObjectMessage>::Iterator
k = list->begin(); k != list->end(); k++)
{
// Compose the full new data with header
ActiveObjectMessage aom = *k;
std::string new_data;
// Add header (object id + length)
char header[4];
writeU16((u8*)&header[0], aom.id);
writeU16((u8*)&header[2], aom.datastring.size());
new_data.append(header, 4);
// Add data
new_data += aom.datastring;
// Add data to buffer
if(aom.reliable)
reliable_data += new_data;
else
unreliable_data += new_data;
}
}
/*
reliable_data and unreliable_data are now ready.
Send them.
*/
if(reliable_data.size() > 0)
{
SharedBuffer<u8> reply(2 + reliable_data.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
memcpy((char*)&reply[2], reliable_data.c_str(),
reliable_data.size());
// Send as reliable
m_con.Send(client->peer_id, 0, reply, true);
}
if(unreliable_data.size() > 0)
{
SharedBuffer<u8> reply(2 + unreliable_data.size());
writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
memcpy((char*)&reply[2], unreliable_data.c_str(),
unreliable_data.size());
// Send as unreliable
m_con.Send(client->peer_id, 0, reply, false);
}
if(reliable_data.size() > 0 || unreliable_data.size() > 0)
{
dstream<<"INFO: Server: Size of object message data: "
<<"reliable: "<<reliable_data.size()
<<", unreliable: "<<unreliable_data.size()
<<std::endl;
}
}
// Clear buffered_messages
for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
i = buffered_messages.getIterator();
i.atEnd()==false; i++)
{
delete i.getNode()->getValue();
}
}
/*
Send object positions
*/
{ {
float &counter = m_objectdata_timer; float &counter = m_objectdata_timer;
counter += dtime; counter += dtime;
@ -1485,7 +1524,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return; return;
} }
//u8 peer_ser_ver = peer->serialization_version;
u8 peer_ser_ver = getClient(peer->id)->serialization_version; u8 peer_ser_ver = getClient(peer->id)->serialization_version;
try try
@ -1595,7 +1633,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
SharedBuffer<u8> reply(2+1+6); SharedBuffer<u8> reply(2+1+6);
writeU16(&reply[0], TOCLIENT_INIT); writeU16(&reply[0], TOCLIENT_INIT);
writeU8(&reply[2], deployed); writeU8(&reply[2], deployed);
writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0))); writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
// Send as reliable // Send as reliable
m_con.Send(peer_id, 0, reply, true); m_con.Send(peer_id, 0, reply, true);
@ -1892,6 +1930,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{ {
derr_server<<"Server: Not finishing digging: Node not diggable" derr_server<<"Server: Not finishing digging: Node not diggable"
<<std::endl; <<std::endl;
// Client probably has wrong data.
// Set block not sent, so that client will get
// a valid one.
dstream<<"Client "<<peer_id<<" tried to dig "
<<"node from invalid position; setting"
<<" MapBlock not sent."<<std::endl;
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(p_under);
client->SetBlockNotSent(blockpos);
return; return;
} }
// Get mineral // Get mineral
@ -2088,7 +2137,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
// Reset build time counter // Reset build time counter
getClient(peer->id)->m_time_from_building.set(0.0); getClient(peer->id)->m_time_from_building = 0.0;
// Create node data // Create node data
MaterialItem *mitem = (MaterialItem*)item; MaterialItem *mitem = (MaterialItem*)item;
@ -2166,9 +2215,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
v3s16 block_pos_i_on_map = block->getPosRelative(); v3s16 block_pos_i_on_map = block->getPosRelative();
v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map); v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
v3f pos = intToFloat(p_over); v3f pos = intToFloat(p_over, BS);
pos -= block_pos_f_on_map; pos -= block_pos_f_on_map;
/*dout_server<<"pos=" /*dout_server<<"pos="
@ -3060,6 +3109,7 @@ void Server::SendBlocks(float dtime)
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
//TimeTaker timer("Server::SendBlocks"); //TimeTaker timer("Server::SendBlocks");
@ -3087,8 +3137,6 @@ void Server::SendBlocks(float dtime)
// Lowest is most important. // Lowest is most important.
queue.sort(); queue.sort();
JMutexAutoLock conlock(m_con_mutex);
for(u32 i=0; i<queue.size(); i++) for(u32 i=0; i<queue.size(); i++)
{ {
//TODO: Calculate limit dynamically //TODO: Calculate limit dynamically
@ -3268,7 +3316,7 @@ Player *Server::emergePlayer(const char *name, const char *password,
0, 0,
45, //64, 45, //64,
0 0
))); ), BS));
#endif #endif
#if 0 #if 0
f32 groundheight = 0; f32 groundheight = 0;

@ -239,13 +239,6 @@ public:
pending_serialization_version = SER_FMT_VER_INVALID; pending_serialization_version = SER_FMT_VER_INVALID;
m_nearest_unsent_d = 0; m_nearest_unsent_d = 0;
m_nearest_unsent_reset_timer = 0.0; m_nearest_unsent_reset_timer = 0.0;
m_blocks_sent_mutex.Init();
m_blocks_sending_mutex.Init();
/*m_dig_mutex.Init();
m_dig_time_remaining = 0;
m_dig_tool_item = -1;*/
} }
~RemoteClient() ~RemoteClient()
{ {
@ -279,7 +272,6 @@ public:
s32 SendingCount() s32 SendingCount()
{ {
JMutexAutoLock lock(m_blocks_sending_mutex);
return m_blocks_sending.size(); return m_blocks_sending.size();
} }
@ -290,8 +282,6 @@ public:
void PrintInfo(std::ostream &o) void PrintInfo(std::ostream &o)
{ {
JMutexAutoLock l2(m_blocks_sent_mutex);
JMutexAutoLock l3(m_blocks_sending_mutex);
o<<"RemoteClient "<<peer_id<<": " o<<"RemoteClient "<<peer_id<<": "
<<", m_blocks_sent.size()="<<m_blocks_sent.size() <<", m_blocks_sent.size()="<<m_blocks_sent.size()
<<", m_blocks_sending.size()="<<m_blocks_sending.size() <<", m_blocks_sending.size()="<<m_blocks_sending.size()
@ -302,30 +292,21 @@ public:
} }
// Time from last placing or removing blocks // Time from last placing or removing blocks
MutexedVariable<float> m_time_from_building; float m_time_from_building;
/*JMutex m_dig_mutex; /*JMutex m_dig_mutex;
float m_dig_time_remaining; float m_dig_time_remaining;
// -1 = not digging // -1 = not digging
s16 m_dig_tool_item; s16 m_dig_tool_item;
v3s16 m_dig_position;*/ v3s16 m_dig_position;*/
/*
List of active objects that the client knows of.
Value is dummy.
*/
core::map<u16, bool> m_known_objects;
private: private:
/*
All members that are accessed by many threads should
obviously be behind a mutex. The threads include:
- main thread (calls step())
- server thread (calls AsyncRunStep() and Receive())
- emerge thread
*/
//TODO: core::map<v3s16, MapBlock*> m_active_blocks
//NOTE: Not here, it should be server-wide!
// Number of blocks in the emerge queue that have this client as
// a receiver. Used for throttling network usage.
//MutexedVariable<s16> m_num_blocks_in_emerge_queue;
/* /*
Blocks that have been sent to client. Blocks that have been sent to client.
- These don't have to be sent again. - These don't have to be sent again.
@ -339,7 +320,7 @@ private:
s16 m_nearest_unsent_d; s16 m_nearest_unsent_d;
v3s16 m_last_center; v3s16 m_last_center;
float m_nearest_unsent_reset_timer; float m_nearest_unsent_reset_timer;
JMutex m_blocks_sent_mutex;
/* /*
Blocks that are currently on the line. Blocks that are currently on the line.
This is used for throttling the sending of blocks. This is used for throttling the sending of blocks.
@ -349,7 +330,6 @@ private:
Value is time from sending. (not used at the moment) Value is time from sending. (not used at the moment)
*/ */
core::map<v3s16, float> m_blocks_sending; core::map<v3s16, float> m_blocks_sending;
JMutex m_blocks_sending_mutex;
/* /*
Count of excess GotBlocks(). Count of excess GotBlocks().
@ -361,15 +341,6 @@ private:
u32 m_excess_gotblocks; u32 m_excess_gotblocks;
}; };
/*struct ServerSettings
{
ServerSettings()
{
creative_mode = false;
}
bool creative_mode;
};*/
class Server : public con::PeerHandler class Server : public con::PeerHandler
{ {
public: public:
@ -470,7 +441,7 @@ 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; JMutex m_env_mutex;
Environment m_env; ServerEnvironment m_env;
JMutex m_con_mutex; JMutex m_con_mutex;
con::Connection m_con; con::Connection m_con;

75
src/serverobject.cpp Normal file

@ -0,0 +1,75 @@
/*
Minetest-c55
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
(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.
You should have received a copy of the GNU 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 "serverobject.h"
ServerActiveObject::ServerActiveObject(u16 id, v3f pos):
ActiveObject(id),
m_known_by_count(0),
m_removed(false),
m_base_position(pos)
{
}
ServerActiveObject::~ServerActiveObject()
{
}
TestSAO::TestSAO(u16 id, v3f pos):
ServerActiveObject(id, pos),
m_timer1(0),
m_age(0)
{
}
void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
{
m_age += dtime;
if(m_age > 10)
{
m_removed = true;
return;
}
m_base_position.Y += dtime * BS * 2;
if(m_base_position.Y > 8*BS)
m_base_position.Y = 2*BS;
m_timer1 -= dtime;
if(m_timer1 < 0.0)
{
m_timer1 += 0.125;
//dstream<<"TestSAO: id="<<getId()<<" sending data"<<std::endl;
std::string data;
data += itos(0); // 0 = position
data += " ";
data += itos(m_base_position.X);
data += " ";
data += itos(m_base_position.Y);
data += " ";
data += itos(m_base_position.Z);
//ActiveObjectMessage aom(getId(), true, data);
ActiveObjectMessage aom(getId(), false, data);
messages.push_back(aom);
}
}

87
src/serverobject.h Normal file

@ -0,0 +1,87 @@
/*
Minetest-c55
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
(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.
You should have received a copy of the GNU 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.
*/
#ifndef SERVEROBJECT_HEADER
#define SERVEROBJECT_HEADER
#include "common_irrlicht.h"
#include "activeobject.h"
#include "utility.h"
/*
Some planning
-------------
* Server environment adds an active object, which gets the id 1
* The active object list is scanned for each client once in a while,
and it finds out what objects have been added that are not known
by the client yet. This scan is initiated by the server and the
result ends up directly to the server.
* A network packet is created with the info and sent to the client.
*/
class ServerActiveObject : public ActiveObject
{
public:
ServerActiveObject(u16 id, v3f pos=v3f(0,0,0));
virtual ~ServerActiveObject();
v3f getBasePosition()
{
return m_base_position;
}
/*
Step object in time.
Messages added to messages are sent to client over network.
*/
virtual void step(float dtime, Queue<ActiveObjectMessage> &messages){}
// Number of players which know about this one
u16 m_known_by_count;
/*
Whether this object is to be removed when nobody knows about
it anymore.
Removal is delayed to preserve the id for the time during which
it could be confused to some other object by some client.
*/
bool m_removed;
protected:
v3f m_base_position;
};
class TestSAO : public ServerActiveObject
{
public:
TestSAO(u16 id, v3f pos);
u8 getType() const
{
return ACTIVEOBJECT_TYPE_TEST;
}
void step(float dtime, Queue<ActiveObjectMessage> &messages);
private:
float m_timer1;
float m_age;
};
#endif

@ -1738,5 +1738,30 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen)
#define MYMIN(a,b) ((a)<(b)?(a):(b)) #define MYMIN(a,b) ((a)<(b)?(a):(b))
#define MYMAX(a,b) ((a)>(b)?(a):(b)) #define MYMAX(a,b) ((a)>(b)?(a):(b))
/*
Returns integer position of node in given floating point position
*/
inline v3s16 floatToInt(v3f p, f32 d)
{
v3s16 p2(
(p.X + (p.X>0 ? BS/2 : -BS/2))/d,
(p.Y + (p.Y>0 ? BS/2 : -BS/2))/d,
(p.Z + (p.Z>0 ? BS/2 : -BS/2))/d);
return p2;
}
/*
Returns floating point position of node in given integer position
*/
inline v3f intToFloat(v3s16 p, f32 d)
{
v3f p2(
(f32)p.X * d,
(f32)p.Y * d,
(f32)p.Z * d
);
return p2;
}
#endif #endif