)
set(common_SRCS
+ serverobject.cpp
noise.cpp
mineral.cpp
porting.cpp
test.cpp
)
+# Client sources
set(minetest_SRCS
${common_SRCS}
+ clientobject.cpp
guiMainMenu.cpp
guiMessageMenu.cpp
guiTextInputMenu.cpp
main.cpp
)
+# Server sources
set(minetestserver_SRCS
${common_SRCS}
servermain.cpp
--- /dev/null
+/*
+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
+
const char *playername,
MapDrawControl &control):
m_thread(this),
- m_env(new ClientMap(this, control,
+ m_env(
+ new ClientMap(this, control,
device->getSceneManager()->getRootSceneNode(),
device->getSceneManager(), 666),
- dout_client),
+ device->getSceneManager()
+ ),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_device(device),
camera_position(0,0,0),
m_step_dtime_mutex.Init();
m_thread.Start();
-
+
+ /*
+ Add local player
+ */
{
JMutexAutoLock envlock(m_env_mutex);
- //m_env.getMap().StartUpdater();
Player *player = new LocalPlayer();
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);
}
+
+ // Add some active objects for testing
+ /*{
+ ClientActiveObject *obj = new TestCAO(0, v3f(0, 10*BS, 0));
+ m_env.addActiveObject(obj);
+ }*/
}
Client::~Client()
v3s16 playerpos_s16(0, BS*2+BS*20, 0);
if(datasize >= 2+1+6)
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
JMutexAutoLock envlock(m_env_mutex);
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)
else
{
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
{
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
- m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
+ m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
}
}
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
- m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
+ m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
}
}
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()
{
JMutexAutoLock envlock(m_env_mutex);
// Calculate from_pos relative to block
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;
block->getObjects(from_pos_f_on_block, max_d, objects);
// Calculate shootline relative to block
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(
shootline_on_map.start - block_pos_f_on_map,
shootline_on_map.end - block_pos_f_on_map
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
- v3s16 center_nodepos = floatToInt(playerpos);
+ v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
u32 counter = 0;
#include "common_irrlicht.h"
#include "jmutex.h"
#include <ostream>
+#include "clientobject.h"
class ClientNotReadyException : public BaseException
{
// Returns InvalidPositionException if not found
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();
// Prints a line or two of info
void printDebugInfo(std::ostream &os);
- //s32 getDayNightIndex();
u32 getDayNightRatio();
//void updateSomeExpiredMeshes();
}
}
-#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()
{
JMutexAutoLock lock(m_con_mutex);
// NOTE: If connection and environment are both to be locked,
// environment shall be locked first.
- Environment m_env;
+ ClientEnvironment m_env;
JMutex m_env_mutex;
con::Connection m_con;
JMutex m_con_mutex;
- /*core::map<v3s16, float> m_fetchblock_history;
- JMutex m_fetchblock_mutex;*/
-
core::list<IncomingPacket> m_incoming_queue;
JMutex m_incoming_queue_mutex;
--- /dev/null
+/*
+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();
+ }
+}
+
+
--- /dev/null
+/*
+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
+
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
#include "environment.h"
#include "filesys.h"
-Environment::Environment(Map *map, std::ostream &dout):
- m_dout(dout)
+Environment::Environment()
{
- m_map = map;
- m_daynight_ratio = 0.2;
+ m_daynight_ratio = 0.5;
}
Environment::~Environment()
{
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)
{
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.
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(player->peer_id != 0)
assert(getPlayer(player->peer_id) == NULL);
}
}
-#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)
{
for(core::list<Player*>::Iterator i = m_players.begin();
}
}
-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";
fs::CreateDir(players_path);
//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";
}
}
+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> ¤t_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> ¤t_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
-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);
}
-void Environment::expireMeshes(bool only_daynight_diffed)
+void ClientEnvironment::expireMeshes(bool 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
+
+
- The map
- Players
- Other objects
- - The current time in the game, etc.
+ - The current time in the game (actually it only contains the brightness)
+ - etc.
*/
#include <list>
{
public:
// 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.
+ Step everything in environment.
+ - Move players
+ - Step mobs
+ - Run timers of map
*/
- void step(f32 dtime);
+ virtual void step(f32 dtime) = 0;
- Map & getMap();
+ virtual Map & getMap() = 0;
- /*
- Environment deallocates players after use.
- */
- void addPlayer(Player *player);
+ virtual void addPlayer(Player *player);
void removePlayer(u16 peer_id);
-#ifndef SERVER
- LocalPlayer * getLocalPlayer();
-#endif
Player * getPlayer(u16 peer_id);
Player * getPlayer(const char *name);
core::list<Player*> getPlayers();
core::list<Player*> getPlayers(bool ignore_disconnected);
void printPlayers(std::ostream &o);
+ void setDayNightRatio(u32 r);
+ u32 getDayNightRatio();
+
+protected:
+ // peer_ids in here should be unique, except that there may be many 0s
+ core::list<Player*> m_players;
+ // Brightness
+ 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);
- // This loads players as ServerRemotePlayers
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> ¤t_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> ¤t_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);
-#endif
- void setDayNightRatio(u32 r);
- u32 getDayNightRatio();
+
+ /*
+ 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:
- Map *m_map;
- // peer_ids in here should be unique, except that there may be
- // many 0s
- core::list<Player*> m_players;
- // Debug output goes here
- std::ostream &m_dout;
- u32 m_daynight_ratio;
+ ClientMap *m_map;
+ scene::ISceneManager *m_smgr;
+ core::map<u16, ClientActiveObject*> m_active_objects;
};
#endif
+#endif
+
#endif // _WIN32_WCE
#include <winsock2.h>
#include <windows.h>
-
+ // CriticalSection is way faster than the alternative
#define JMUTEX_CRITICALSECTION
#else // using pthread
#include <pthread.h>
\r
TODO: Flowing water animation\r
\r
-NOTE(FIXED): A lock condition is possible:\r
- 1) MapBlock::updateMesh() is called from client asynchronously:\r
- - AsyncProcessData() -> Map::updateMeshes()\r
- 2) Asynchronous locks m_temp_mods_mutex\r
- 3) MapBlock::updateMesh() is called from client synchronously:\r
- - Client::step() -> Environment::step()\r
- 4) Synchronous starts waiting for m_temp_mods_mutex\r
- 5) Asynchronous calls getTexture, which starts waiting for main thread\r
-\r
Configuration:\r
--------------\r
\r
-TODO: Make the video backend selectable\r
-\r
Client:\r
-------\r
\r
TODO: Untie client network operations from framerate\r
- Needs some input queues or something\r
\r
-TODO: Make morning and evening transition more smooth and maybe shorter\r
+SUGG: Make morning and evening transition more smooth and maybe shorter\r
\r
-TODO: Don't update all meshes always on single node changes, but\r
+SUGG: Don't update all meshes always on single node changes, but\r
check which ones should be updated\r
- implement Map::updateNodeMeshes()\r
\r
+TODO: Remove IrrlichtWrapper\r
+\r
Server:\r
-------\r
\r
TODO: Make an option to the server to disable building and digging near\r
the starting position\r
\r
-TODO: Save players with inventories to disk\r
-TODO: Players to be saved as text in map/players/<name>\r
-\r
TODO: Copy the text of the last picked sign to inventory in creative\r
mode\r
\r
TODO: Check what goes wrong with caching map to disk (Kray)\r
- Nothing?\r
\r
-TODO: When server sees that client is removing an inexistent block to\r
+TODO: When server sees that client is removing an inexistent block in\r
an existent position, resend the MapBlock.\r
\r
FIXME: Server went into some infinite PeerNotFoundException loop\r
- TODO: For incoming blocks, time difference is calculated and\r
objects are stepped according to it.\r
\r
+- When an active object goes far from a player, either delete\r
+ it or store it statically.\r
+- When a statically stored active object comes near a player,\r
+ recreate the active object\r
+\r
Map:\r
----\r
\r
#include <fstream>\r
#include <jmutexautolock.h>\r
#include <locale.h>\r
+#include "main.h"\r
#include "common_irrlicht.h"\r
#include "debug.h"\r
#include "map.h"\r
#include "player.h"\r
-#include "main.h"\r
#include "test.h"\r
-#include "environment.h"\r
+//#include "environment.h"\r
#include "server.h"\r
#include "client.h"\r
-#include "serialization.h"\r
+//#include "serialization.h"\r
#include "constants.h"\r
-#include "strfnd.h"\r
+//#include "strfnd.h"\r
#include "porting.h"\r
#include "irrlichtwrapper.h"\r
#include "gettime.h"\r
public:\r
RandomInputHandler()\r
{\r
+ leftdown = false;\r
+ rightdown = false;\r
leftclicked = false;\r
rightclicked = false;\r
+ leftreleased = false;\r
+ rightreleased = false;\r
for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
keydown[i] = false;\r
}\r
\r
virtual bool getLeftState()\r
{\r
- return false;\r
+ return leftdown;\r
}\r
virtual bool getRightState()\r
{\r
- return false;\r
+ return rightdown;\r
}\r
\r
virtual bool getLeftClicked()\r
\r
virtual bool getLeftReleased()\r
{\r
- return false;\r
+ return leftreleased;\r
}\r
virtual bool getRightReleased()\r
{\r
- return false;\r
+ return rightreleased;\r
}\r
virtual void resetLeftReleased()\r
{\r
+ leftreleased = false;\r
}\r
virtual void resetRightReleased()\r
{\r
+ rightreleased = false;\r
}\r
\r
virtual void step(float dtime)\r
{\r
- {\r
- static float counter1 = 0;\r
- counter1 -= dtime;\r
- if(counter1 < 0.0)\r
- {\r
- counter1 = 0.1*Rand(1,10);\r
- /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)\r
- g_selected_material++;\r
- else\r
- g_selected_material = 0;*/\r
- if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
- g_selected_item++;\r
- else\r
- g_selected_item = 0;\r
- }\r
- }\r
{\r
static float counter1 = 0;\r
counter1 -= dtime;\r
if(counter1 < 0.0)\r
{\r
counter1 = 0.1*Rand(1, 30);\r
- leftclicked = true;\r
+ leftdown = !leftdown;\r
+ if(leftdown)\r
+ leftclicked = true;\r
+ if(!leftdown)\r
+ leftreleased = true;\r
}\r
}\r
{\r
counter1 -= dtime;\r
if(counter1 < 0.0)\r
{\r
- counter1 = 0.1*Rand(1, 20);\r
- rightclicked = true;\r
+ counter1 = 0.1*Rand(1, 15);\r
+ rightdown = !rightdown;\r
+ if(rightdown)\r
+ rightclicked = true;\r
+ if(!rightdown)\r
+ rightreleased = true;\r
}\r
}\r
mousepos += mousespeed;\r
bool keydown[KEY_KEY_CODES_COUNT];\r
v2s32 mousepos;\r
v2s32 mousespeed;\r
+ bool leftdown;\r
+ bool rightdown;\r
bool leftclicked;\r
bool rightclicked;\r
+ bool leftreleased;\r
+ bool rightreleased;\r
};\r
\r
void updateViewingRange(f32 frametime_in, Client *client)\r
camera->setTarget(camera_position + camera_direction * 100.0);\r
\r
if(FIELD_OF_VIEW_TEST){\r
- //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));\r
client.updateCamera(v3f(0,0,0), v3f(0,0,1));\r
}\r
else{\r
- //client.m_env.getMap().updateCamera(camera_position, camera_direction);\r
//TimeTaker timer("client.updateCamera");\r
client.updateCamera(camera_position, camera_direction);\r
}\r
core::aabbox3d<f32> nodehilightbox;\r
f32 mindistance = BS * 1001;\r
\r
- v3s16 pos_i = floatToInt(player_position);\r
+ v3s16 pos_i = floatToInt(player_position, BS);\r
\r
/*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
<<std::endl;*/\r
}\r
\r
v3s16 np(x,y,z);\r
- v3f npf = intToFloat(np);\r
+ v3f npf = intToFloat(np, BS);\r
\r
f32 d = 0.01;\r
\r
const float d = 0.502;\r
core::aabbox3d<f32> nodebox\r
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
- v3f nodepos_f = intToFloat(nodepos);\r
+ v3f nodepos_f = intToFloat(nodepos, BS);\r
nodebox.MinEdge += nodepos_f;\r
nodebox.MaxEdge += nodepos_f;\r
nodehilightbox = nodebox;\r
}
}
-#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)
{
try{
//if(!is_ground_content(block->getNode(cp).d))
if(1)
{
- RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
+ RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
block->addObject(obj);
}
}
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)
{
out<<"ClientMap: ";
#endif
#include "common_irrlicht.h"
-//#include "heightmap.h"
#include "mapnode.h"
#include "mapblock.h"
#include "mapsector.h"
{
return MAPTYPE_BASE;
}
-
+
+ /*
+ Drop (client) or delete (server) the map.
+ */
virtual void drop()
{
delete this;
void removeNodeAndUpdate(v3s16 p,
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
*/
// Efficient implementation needs a cache of TempMods
//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
virtual void PrintInfo(std::ostream &out);
if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXZBy(-45);
- vertices[i].Pos += intToFloat(p + getPosRelative());
+ vertices[i].Pos += intToFloat(p + getPosRelative(), BS);
}
// Set material
if(dir == v3s16(1,0,-0))
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};
//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
s32 j = corner_resolve[i];
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};
for(s32 i=0; i<4; i++)
{
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};
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};
if(getNode(p).d == CONTENT_AIR
&& 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);
}
}
return m_pos;
// 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 m_pos;
// getPosRelative gets nodepos relative to map origin
- v3f blockpos = intToFloat(m_block->getPosRelative());
+ v3f blockpos = intToFloat(m_block->getPosRelative(), BS);
return blockpos + m_showpos;
}
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)
{
Collision detection
*/
- v3s16 pos_i = floatToInt(position);
+ v3s16 pos_i = floatToInt(position, BS);
// The loop length is limited to the object moving a distance
f32 d = (float)BS * 0.15;
{
u8 light = LIGHT_MAX;
try{
- v3s16 relpos_i = floatToInt(obj->m_pos);
+ v3s16 relpos_i = floatToInt(obj->m_pos, BS);
MapNode n = m_block->getNodeParent(relpos_i);
light = n.getLightBlend(daynight_ratio);
}
// Update light
u8 light = LIGHT_MAX;
try{
- v3s16 relpos_i = floatToInt(obj->m_pos);
+ v3s16 relpos_i = floatToInt(obj->m_pos, BS);
MapNode n = m_block->getNodeParent(relpos_i);
light = n.getLightBlend(daynight_ratio);
}
{
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))
{
// Calculate blockpos on map
v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
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_blocks_on_map = getNodeBlockPos(pos_i_on_map);
}
// 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();
- 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
- newblock_pos_f_on_map + oldblock_pos_f_on_map;
}
};
-/*
- 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
{
v3f position = getPosition();
v3f oldpos = position;
- v3s16 oldpos_i = floatToInt(oldpos);
+ v3s16 oldpos_i = floatToInt(oldpos, BS);
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
<<oldpos_i.Z<<")"<<std::endl;*/
*/
// Player position in nodes
- v3s16 pos_i = floatToInt(position);
+ v3s16 pos_i = floatToInt(position, BS);
/*
Check if player is in water (the oscillating value)
// If in water, the threshold of coming out is at higher y
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);
}
// If not in water, the threshold of going in is at lower y
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);
}
}
Check if player is in water (the stable value)
*/
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);
}
catch(InvalidPositionException &e)
if(control.sneak && m_sneak_node_exists)
{
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.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
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);
f32 min_distance_f = 100000.0*BS;
// If already seeking from some node, compare to it.
/*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);
f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
for(s16 z=-1; z<=1; 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);
f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
f32 max_axis_distance_f = MYMAX(
v3f m_position;
};
+/*
+ Player on the server
+*/
+
class ServerRemotePlayer : public Player
{
public:
virtual void move(f32 dtime, Map &map, f32 pos_max_d)
{
}
-
+
private:
};
#ifndef SERVER
+/*
+ All the other players on the client are these
+*/
+
class RemotePlayer : public Player, public scene::ISceneNode
{
public:
DSTACK(__FUNCTION_NAME);
// Increment timers
- {
- JMutexAutoLock lock(m_blocks_sent_mutex);
- m_nearest_unsent_reset_timer += dtime;
- }
+ m_nearest_unsent_reset_timer += dtime;
// 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);
-
- if(m_blocks_sending.size() >= g_settings.getU16
- ("max_simultaneous_block_sends_per_client"))
- {
- //dstream<<"Not sending any blocks, Queue full."<<std::endl;
- return;
- }
+ //dstream<<"Not sending any blocks, Queue full."<<std::endl;
+ return;
}
Player *player = server->m_env.getPlayer(peer_id);
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
- v3s16 center_nodepos = floatToInt(playerpos);
+ v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
*/
s16 last_nearest_unsent_d;
s16 d_start;
- {
- JMutexAutoLock lock(m_blocks_sent_mutex);
- 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;
- }
+ if(m_last_center != center)
+ {
+ m_nearest_unsent_d = 0;
+ m_last_center = center;
+ }
- 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
("max_simultaneous_block_sends_per_client");
u16 maximum_simultaneous_block_sends =
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());
- m_time_from_building.m_value += dtime;
- /*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;
- }
+ maximum_simultaneous_block_sends
+ = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
- u32 num_blocks_selected;
- {
- JMutexAutoLock lock(m_blocks_sending_mutex);
- num_blocks_selected = m_blocks_sending.size();
- }
+ u32 num_blocks_selected = m_blocks_sending.size();
/*
next time d will be continued from the d from which the nearest
*/
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_gen = g_settings.getS16("max_block_generate_distance");
{
//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);
- /*
- 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;
- }
+ d = m_nearest_unsent_d;
+ last_nearest_unsent_d = m_nearest_unsent_d;
}
/*
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);
-
- // Limit is dynamically lowered when building
- if(num_blocks_selected
- >= 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;
+ /*dstream<<"Not sending more blocks. Queue full. "
+ <<m_blocks_sending.size()
+ <<std::endl;*/
+ goto queue_full;
}
-
+
+ if(m_blocks_sending.find(p) != NULL)
+ continue;
+
/*
Do not go over-limit
*/
#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)
Don't send already sent blocks
*/
{
- JMutexAutoLock lock(m_blocks_sent_mutex);
-
if(m_blocks_sent.find(p) != NULL)
continue;
}
bool block_is_invalid = false;
if(block != NULL)
{
- /*if(block->isIncomplete())
- {
- has_incomplete_blocks = true;
- continue;
- }*/
-
if(block->isDummy())
{
surely_not_found_on_disk = true;
v2s16 chunkpos = map->sector_to_chunk(p2d);
if(map->chunkNonVolatile(chunkpos) == false)
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;*/
}
/*
*/
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
// Allow only one block in emerge queue
if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
}
/*
- Add block to queue
+ Add block to send queue
*/
PrioritySortedBlockTransfer q((float)d, p, peer_id);
if(new_nearest_unsent_d != -1)
{
- JMutexAutoLock lock(m_blocks_sent_mutex);
m_nearest_unsent_d = new_nearest_unsent_d;
}
}
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
- v3s16 center_nodepos = floatToInt(playerpos);
+ v3s16 center_nodepos = floatToInt(playerpos, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
s16 d_max = g_settings.getS16("active_object_range");
Ignore blocks that haven't been sent to the client
*/
{
- JMutexAutoLock sentlock(m_blocks_sent_mutex);
if(m_blocks_sent.find(p) == NULL)
continue;
}
void RemoteClient::GotBlock(v3s16 p)
{
- JMutexAutoLock lock(m_blocks_sending_mutex);
- JMutexAutoLock lock2(m_blocks_sent_mutex);
if(m_blocks_sending.find(p) != NULL)
m_blocks_sending.remove(p);
else
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)
m_blocks_sending.insert(p, 0.0);
else
void RemoteClient::SetBlockNotSent(v3s16 p)
{
- JMutexAutoLock sendinglock(m_blocks_sending_mutex);
- JMutexAutoLock sentlock(m_blocks_sent_mutex);
-
m_nearest_unsent_d = 0;
if(m_blocks_sending.find(p) != NULL)
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;
for(core::map<v3s16, MapBlock*>::Iterator
Server::Server(
std::string mapsavedir
):
- m_env(new ServerMap(mapsavedir), dout_server),
+ m_env(new ServerMap(mapsavedir)),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this),
m_emergethread(this),
}
/*
- Update digging
-
- NOTE: Some of this could be moved to RemoteClient
+ Check added and deleted active objects
*/
-#if 0
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
{
RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id);
-
- JMutexAutoLock digmutex(client->m_dig_mutex);
-
- if(client->m_dig_tool_item == -1)
+ v3s16 pos = floatToInt(player->getPosition(), BS);
+ s16 radius = 32;
+
+ core::map<u16, bool> removed_objects;
+ core::map<u16, bool> added_objects;
+ m_env.getRemovedActiveObjects(pos, radius,
+ client->m_known_objects, removed_objects);
+ m_env.getAddedActiveObjects(pos, radius,
+ client->m_known_objects, added_objects);
+
+ // Ignore if nothing happened
+ if(removed_objects.size() == 0 && added_objects.size() == 0)
continue;
+
+ std::string data_buffer;
+
+ char buf[4];
+
+ // Handle removed objects
+ writeU16((u8*)buf, removed_objects.size());
+ 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());
- client->m_dig_time_remaining -= dtime;
+ if(obj && obj->m_known_by_count > 0)
+ obj->m_known_by_count--;
+ }
- if(client->m_dig_time_remaining > 0)
+ // 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++)
{
- client->m_time_from_building.set(0.0);
- continue;
+ // 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++;
}
- v3s16 p_under = client->m_dig_position;
-
- // Mandatory parameter; actually used for nothing
- core::map<v3s16, MapBlock*> modified_blocks;
+ // 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
+ m_con.Send(client->peer_id, 0, reply, true);
- u8 material;
+ dstream<<"INFO: Server: Sent object remove/add: "
+ <<removed_objects.size()<<" removed, "
+ <<added_objects.size()<<" added, "
+ <<"packet size is "<<reply.getSize()<<std::endl;
+ }
+ }
- try
+ /*
+ 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)
{
- // 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;
- }
+ message_list = new core::list<ActiveObjectMessage>;
+ buffered_messages.insert(aom.id, message_list);
}
- catch(InvalidPositionException &e)
+ else
{
- derr_server<<"Server: Not finishing digging: Node not found"
- <<std::endl;
- client->m_dig_tool_item = -1;
- break;
+ message_list = n->getValue();
}
-
- // Create packet
- u32 replysize = 8;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_REMOVENODE);
- writeS16(&reply[2], p_under.X);
- writeS16(&reply[4], p_under.Y);
- writeS16(&reply[6], p_under.Z);
- // Send as reliable
- m_con.SendToAll(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);
+ 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;
+ }
}
-
/*
- Remove the node
- (this takes some time so it is done after the quick stuff)
+ reliable_data and unreliable_data are now ready.
+ Send them.
*/
- 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
+ if(reliable_data.size() > 0)
{
- v.updateAreaWaterPressure(area, m_flow_active_nodes);
+ 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);
}
- catch(ProcessingLimitException &e)
+ if(unreliable_data.size() > 0)
{
- dstream<<"Processing limit reached (1)"<<std::endl;
+ 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);
}
-
- v.blitBack(modified_blocks);
+ 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();
}
}
-#endif
- // Send object positions
+ /*
+ Send object positions
+ */
{
float &counter = m_objectdata_timer;
counter += dtime;
return;
}
- //u8 peer_ser_ver = peer->serialization_version;
u8 peer_ser_ver = getClient(peer->id)->serialization_version;
try
SharedBuffer<u8> reply(2+1+6);
writeU16(&reply[0], TOCLIENT_INIT);
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
m_con.Send(peer_id, 0, reply, true);
{
derr_server<<"Server: Not finishing digging: Node not diggable"
<<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;
}
// Get mineral
}
// 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
MaterialItem *mitem = (MaterialItem*)item;
}
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;
/*dout_server<<"pos="
DSTACK(__FUNCTION_NAME);
JMutexAutoLock envlock(m_env_mutex);
+ JMutexAutoLock conlock(m_con_mutex);
//TimeTaker timer("Server::SendBlocks");
// Lowest is most important.
queue.sort();
- JMutexAutoLock conlock(m_con_mutex);
-
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
0,
45, //64,
0
- )));
+ ), BS));
#endif
#if 0
f32 groundheight = 0;
pending_serialization_version = SER_FMT_VER_INVALID;
m_nearest_unsent_d = 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()
{
s32 SendingCount()
{
- JMutexAutoLock lock(m_blocks_sending_mutex);
return m_blocks_sending.size();
}
void PrintInfo(std::ostream &o)
{
- JMutexAutoLock l2(m_blocks_sent_mutex);
- JMutexAutoLock l3(m_blocks_sending_mutex);
o<<"RemoteClient "<<peer_id<<": "
<<", m_blocks_sent.size()="<<m_blocks_sent.size()
<<", m_blocks_sending.size()="<<m_blocks_sending.size()
}
// Time from last placing or removing blocks
- MutexedVariable<float> m_time_from_building;
+ float m_time_from_building;
/*JMutex m_dig_mutex;
float m_dig_time_remaining;
// -1 = not digging
s16 m_dig_tool_item;
v3s16 m_dig_position;*/
-
-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
+ List of active objects that the client knows of.
+ Value is dummy.
*/
-
- //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;
+ core::map<u16, bool> m_known_objects;
+private:
/*
Blocks that have been sent to client.
- These don't have to be sent again.
s16 m_nearest_unsent_d;
v3s16 m_last_center;
float m_nearest_unsent_reset_timer;
- JMutex m_blocks_sent_mutex;
+
/*
Blocks that are currently on the line.
This is used for throttling the sending of blocks.
Value is time from sending. (not used at the moment)
*/
core::map<v3s16, float> m_blocks_sending;
- JMutex m_blocks_sending_mutex;
/*
Count of excess GotBlocks().
u32 m_excess_gotblocks;
};
-/*struct ServerSettings
-{
- ServerSettings()
- {
- creative_mode = false;
- }
- bool creative_mode;
-};*/
-
class Server : public con::PeerHandler
{
public:
// NOTE: If connection and environment are both to be locked,
// environment shall be locked first.
JMutex m_env_mutex;
- Environment m_env;
+ ServerEnvironment m_env;
JMutex m_con_mutex;
con::Connection m_con;
--- /dev/null
+/*
+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);
+ }
+}
+
--- /dev/null
+/*
+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
+
#define MYMIN(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