Some progress on transitioning from MapBlockObject to ActiveObject.

This commit is contained in:
Perttu Ahola 2011-04-08 00:47:14 +03:00
parent 2dba606e12
commit 4b54b291ae
13 changed files with 522 additions and 40 deletions

@ -38,6 +38,7 @@ struct ActiveObjectMessage
#define ACTIVEOBJECT_TYPE_INVALID 0
#define ACTIVEOBJECT_TYPE_TEST 1
#define ACTIVEOBJECT_TYPE_ITEM 2
/*
Parent class for ServerActiveObject and ClientActiveObject

@ -1970,6 +1970,48 @@ MapBlockObject * Client::getSelectedObject(
return NULL;
}
ClientActiveObject * Client::getSelectedActiveObject(
f32 max_d,
v3f from_pos_f_on_map,
core::line3d<f32> shootline_on_map
)
{
core::array<DistanceSortedActiveObject> objects;
m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
//dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
// Sort them.
// After this, the closest object is the first in the array.
objects.sort();
for(u32 i=0; i<objects.size(); i++)
{
ClientActiveObject *obj = objects[i].obj;
core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
if(selection_box == NULL)
continue;
v3f pos = obj->getPosition();
core::aabbox3d<f32> offsetted_box(
selection_box->MinEdge + pos,
selection_box->MaxEdge + pos
);
if(offsetted_box.intersectsWithLine(shootline_on_map))
{
//dstream<<"Returning selected object"<<std::endl;
return obj;
}
}
//dstream<<"No object selected; returning NULL."<<std::endl;
return NULL;
}
void Client::printDebugInfo(std::ostream &os)
{
//JMutexAutoLock lock1(m_fetchblock_mutex);

@ -316,6 +316,14 @@ public:
core::line3d<f32> shootline_on_map
);
// Gets closest object pointed by the shootline
// Returns NULL if not found
ClientActiveObject * getSelectedActiveObject(
f32 max_d,
v3f from_pos_f_on_map,
core::line3d<f32> shootline_on_map
);
// Prints a line or two of info
void printDebugInfo(std::ostream &os);

@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "utility.h"
core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
ClientActiveObject::ClientActiveObject(u16 id):
ActiveObject(id)
{
@ -35,41 +37,55 @@ ClientActiveObject::~ClientActiveObject()
ClientActiveObject* ClientActiveObject::create(u8 type)
{
if(type == ACTIVEOBJECT_TYPE_INVALID)
// Find factory function
core::map<u16, Factory>::Node *n;
n = m_types.find(type);
if(n == NULL)
{
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
{
dstream<<"ClientActiveObject::create(): passed "
<<"unknown type="<<type<<std::endl;
// If factory is not found, just return.
dstream<<"WARNING: ClientActiveObject: No factory for type="
<<type<<std::endl;
return NULL;
}
Factory f = n->getValue();
ClientActiveObject *object = (*f)();
return object;
}
void ClientActiveObject::registerType(u16 type, Factory f)
{
core::map<u16, Factory>::Node *n;
n = m_types.find(type);
if(n)
return;
m_types.insert(type, f);
}
/*
TestCAO
*/
TestCAO::TestCAO(u16 id):
ClientActiveObject(id),
// Prototype
TestCAO proto_TestCAO;
TestCAO::TestCAO():
ClientActiveObject(0),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
TestCAO::~TestCAO()
{
}
ClientActiveObject* TestCAO::create()
{
return new TestCAO();
}
void TestCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
@ -160,4 +176,184 @@ void TestCAO::processMessage(const std::string &data)
}
}
/*
ItemCAO
*/
#include "inventory.h"
// Prototype
ItemCAO proto_ItemCAO;
ItemCAO::ItemCAO():
ClientActiveObject(0),
m_selection_box(-BS*0.4,0.0,-BS*0.4, BS*0.4,BS*0.8,BS*0.4),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
ItemCAO::~ItemCAO()
{
}
ClientActiveObject* ItemCAO::create()
{
return new ItemCAO();
}
void ItemCAO::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),*/
video::S3DVertex(BS/3,0,0, 0,0,0, c, 0,1),
video::S3DVertex(-BS/3,0,0, 0,0,0, c, 1,1),
video::S3DVertex(-BS/3,0+BS*2/3,0, 0,0,0, c, 1,0),
video::S3DVertex(BS/3,0+BS*2/3,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, NULL);
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();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void ItemCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void ItemCAO::updateLight(u8 light_at_pos)
{
}
v3s16 ItemCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void ItemCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
}
void ItemCAO::step(float dtime)
{
if(m_node)
{
v3f rot = m_node->getRotation();
rot.Y += dtime * 120;
m_node->setRotation(rot);
}
}
void ItemCAO::processMessage(const std::string &data)
{
dstream<<"ItemCAO: 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();
}
}
void ItemCAO::initialize(const std::string &data)
{
dstream<<"ItemCAO: Got init data: "<<data<<std::endl;
Strfnd fn(data);
v3f newpos;
newpos.X = stoi(fn.next(","));
newpos.Y = stoi(fn.next(","));
newpos.Z = stoi(fn.next(":"));
m_position = newpos;
updateNodePos();
m_inventorystring = fn.next("");
if(m_node == NULL)
return;
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
if(buf == NULL)
return;
/*
Create an inventory item to see what is its image
*/
std::istringstream is(m_inventorystring, std::ios_base::binary);
video::ITexture *texture = NULL;
try{
InventoryItem *item = NULL;
item = InventoryItem::deSerialize(is);
dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
<<m_inventorystring<<"\" -> item="<<item
<<std::endl;
if(item)
{
texture = item->getImage();
delete item;
}
}
catch(SerializationError &e)
{
dstream<<"WARNING: "<<__FUNCTION_NAME
<<": error deSerializing inventorystring \""
<<m_inventorystring<<"\""<<std::endl;
}
// Set meshbuffer texture
buf->getMaterial().setTexture(0, texture);
}

@ -46,6 +46,9 @@ public:
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;}
virtual v3f getPosition(){return v3f(0,0,0);}
// Step object in time
virtual void step(float dtime){}
@ -54,8 +57,8 @@ public:
virtual void processMessage(const std::string &data){}
/*
This takes the return value of getClientInitializationData
TODO: Usage of this
This takes the return value of
ServerActiveObject::getClientInitializationData
*/
virtual void initialize(const std::string &data){}
@ -63,12 +66,37 @@ public:
static ClientActiveObject* create(u8 type);
protected:
typedef ClientActiveObject* (*Factory)();
static void registerType(u16 type, Factory f);
private:
static core::map<u16, Factory> m_types;
};
struct DistanceSortedActiveObject
{
ClientActiveObject *obj;
f32 d;
DistanceSortedActiveObject(ClientActiveObject *a_obj, f32 a_d)
{
obj = a_obj;
d = a_d;
}
bool operator < (DistanceSortedActiveObject &other)
{
return d < other.d;
}
};
/*
TestCAO
*/
class TestCAO : public ClientActiveObject
{
public:
TestCAO(u16 id);
TestCAO();
virtual ~TestCAO();
u8 getType() const
@ -76,6 +104,8 @@ public:
return ACTIVEOBJECT_TYPE_TEST;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
@ -91,5 +121,46 @@ private:
v3f m_position;
};
/*
ItemCAO
*/
class ItemCAO : public ClientActiveObject
{
public:
ItemCAO();
virtual ~ItemCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_ITEM;
}
static ClientActiveObject* create();
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);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
private:
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
std::string m_inventorystring;
};
#endif

@ -482,7 +482,8 @@ void ServerEnvironment::step(float dtime)
m_random_spawn_timer -= dtime;
if(m_random_spawn_timer < 0)
{
m_random_spawn_timer += myrand_range(2.0, 20.0);
//m_random_spawn_timer += myrand_range(2.0, 20.0);
m_random_spawn_timer += 2.0;
/*
Find some position
@ -503,11 +504,11 @@ void ServerEnvironment::step(float dtime)
);
/*
Create a TestSAO object
Create a ServerActiveObject
*/
TestSAO *obj = new TestSAO(this, 0,
v3f(myrand_range(-2*BS,2*BS), BS*5, myrand_range(-2*BS,2*BS)));
//TestSAO *obj = new TestSAO(this, 0, pos);
ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
// Add the object to the environment
addActiveObject(obj);
@ -1044,6 +1045,27 @@ void ClientEnvironment::processActiveObjectMessage(u16 id,
obj->processMessage(data);
}
void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
core::array<DistanceSortedActiveObject> &dest)
{
for(core::map<u16, ClientActiveObject*>::Iterator
i = m_active_objects.getIterator();
i.atEnd()==false; i++)
{
ClientActiveObject* obj = i.getNode()->getValue();
f32 d = (obj->getPosition() - origin).getLength();
if(d > max_d)
continue;
DistanceSortedActiveObject dso(obj, d);
dest.push_back(dso);
}
}
#endif // #ifndef SERVER

@ -211,7 +211,11 @@ public:
void removeActiveObject(u16 id);
void processActiveObjectMessage(u16 id, const std::string &data);
// Get all nearby objects
void getActiveObjects(v3f origin, f32 max_d,
core::array<DistanceSortedActiveObject> &dest);
private:
ClientMap *m_map;
scene::ISceneManager *m_smgr;

@ -109,17 +109,19 @@ Gaming ideas:
Game content:
-------------
- When furnace is destroyed, move items to player's inventory
- Add lots of stuff, no matter if they have really no real purpose.
- Add lots of stuff
- Glass blocks
- Growing grass, decaying leaves
- This can be done in the active blocks I guess.
- Lots of stuff can be done in the active blocks.
- Uh, is there an active block list somewhere?
- This can be done in the active blocks I guess.
- Lots of stuff can be done in the active blocks.
- Uh, is there an active block list somewhere? I think not. Add it.
- Player health points
- When player dies, throw items on map
- When player dies, throw items on map (needs better item-on-map
implementation)
- Cobble to get mossy if near water
- More slots in furnace source list, so that multiple ingredients
are possible.
- Keys to chests?
Documentation:
--------------
@ -200,7 +202,7 @@ FIXME: If something is removed from craftresult with a right click,
Objects:
--------
TODO: Get rid of MapBlockObjects
TODO: Get rid of MapBlockObjects and use ActiveObjects
Map:
----
@ -2534,9 +2536,9 @@ int main(int argc, char *argv[])
MapBlockObject *selected_object = client.getSelectedObject
(d*BS, camera_position, shootline);
/*
If it's pointing to a MapBlockObject
*/
ClientActiveObject *selected_active_object
= client.getSelectedActiveObject
(d*BS, camera_position, shootline);
if(selected_object != NULL)
{
@ -2594,6 +2596,76 @@ int main(int argc, char *argv[])
}
}
}
else if(selected_active_object != NULL)
{
//dstream<<"Client returned selected_active_object != NULL"<<std::endl;
core::aabbox3d<f32> *selection_box
= selected_active_object->getSelectionBox();
// Box should exist because it was returned in the first place
assert(selection_box);
v3f pos = selected_active_object->getPosition();
core::aabbox3d<f32> box_on_map(
selection_box->MinEdge + pos,
selection_box->MaxEdge + pos
);
hilightboxes.push_back(box_on_map);
infotext = narrow_to_wide("A ClientActiveObject");
//infotext = narrow_to_wide(selected_object->infoText());
if(g_input->getLeftClicked())
{
std::cout<<DTIME<<"Left-clicked object"<<std::endl;
#if 0
client.clickObject(0, selected_object->getBlock()->getPos(),
selected_object->getId(), g_selected_item);
#endif
}
else if(g_input->getRightClicked())
{
std::cout<<DTIME<<"Right-clicked object"<<std::endl;
#if 0
/*
Check if we want to modify the object ourselves
*/
if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
{
dstream<<"Sign object right-clicked"<<std::endl;
if(random_input == false)
{
// Get a new text for it
TextDest *dest = new TextDestSign(
selected_object->getBlock()->getPos(),
selected_object->getId(),
&client);
SignObject *sign_object = (SignObject*)selected_object;
std::wstring wtext =
narrow_to_wide(sign_object->getText());
(new GUITextInputMenu(guienv, guiroot, -1,
&g_menumgr, dest,
wtext))->drop();
}
}
/*
Otherwise pass the event to the server as-is
*/
else
{
client.clickObject(1, selected_object->getBlock()->getPos(),
selected_object->getId(), g_selected_item);
}
#endif
}
}
else // selected_object == NULL
{

@ -99,6 +99,9 @@ void NodeMetadata::registerType(u16 id, Factory f)
SignNodeMetadata
*/
// Prototype
SignNodeMetadata proto_SignNodeMetadata("");
SignNodeMetadata::SignNodeMetadata(std::string text):
m_text(text)
{
@ -130,6 +133,9 @@ std::string SignNodeMetadata::infoText()
ChestNodeMetadata
*/
// Prototype
ChestNodeMetadata proto_ChestNodeMetadata;
ChestNodeMetadata::ChestNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);
@ -182,6 +188,9 @@ bool ChestNodeMetadata::nodeRemovalDisabled()
FurnaceNodeMetadata
*/
// Prototype
FurnaceNodeMetadata proto_FurnaceNodeMetadata;
FurnaceNodeMetadata::FurnaceNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);

@ -1222,7 +1222,7 @@ void Server::AsyncRunStep()
//u16 peer_id = i.getNode()->getKey();
RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id);
std::cout<<player->getName()<<" ";
std::cout<<player->getName()<<"\t";
client->PrintInfo(std::cout);
}
}
@ -1235,6 +1235,8 @@ void Server::AsyncRunStep()
Check added and deleted active objects
*/
{
//dstream<<"Server: Checking added and deleted active objects"<<std::endl;
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
@ -1248,7 +1250,11 @@ void Server::AsyncRunStep()
RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id);
if(player==NULL)
{
dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
<<" has no associated player"<<std::endl;
continue;
}
v3s16 pos = floatToInt(player->getPosition(), BS);
core::map<u16, bool> removed_objects;
@ -1260,7 +1266,10 @@ void Server::AsyncRunStep()
// Ignore if nothing happened
if(removed_objects.size() == 0 && added_objects.size() == 0)
{
//dstream<<"INFO: active objects: none changed"<<std::endl;
continue;
}
std::string data_buffer;

@ -285,7 +285,7 @@ public:
void PrintInfo(std::ostream &o)
{
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_nearest_unsent_d="<<m_nearest_unsent_d
<<", m_excess_gotblocks="<<m_excess_gotblocks

@ -80,4 +80,35 @@ void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
}
/*
ItemSAO
*/
ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring):
ServerActiveObject(env, id, pos),
m_inventorystring(inventorystring)
{
dstream<<"Server: ItemSAO created with inventorystring=\""
<<m_inventorystring<<"\""<<std::endl;
}
void ItemSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
{
}
std::string ItemSAO::getClientInitializationData()
{
dstream<<__FUNCTION_NAME<<std::endl;
std::string data;
data += itos(m_base_position.X);
data += ",";
data += itos(m_base_position.Y);
data += ",";
data += itos(m_base_position.Z);
data += ":";
data += m_inventorystring;
return data;
}

@ -87,10 +87,12 @@ public:
// Number of players which know about this object
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.
- 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.
- This is set to true by the step() method when the object wants
to be deleted.
*/
bool m_removed;
@ -113,5 +115,20 @@ private:
float m_age;
};
class ItemSAO : public ServerActiveObject
{
public:
ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring);
u8 getType() const
{
return ACTIVEOBJECT_TYPE_ITEM;
}
void step(float dtime, Queue<ActiveObjectMessage> &messages);
std::string getClientInitializationData();
private:
std::string m_inventorystring;
};
#endif