From fd7a0735c9aeaa7978190049319e3cdfe48920a4 Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Sun, 10 Apr 2011 04:15:10 +0300
Subject: [PATCH] new object system

---
 src/client.cpp       |  29 +++++-
 src/client.h         |   1 +
 src/clientobject.cpp |  22 +++--
 src/clientobject.h   |  12 ++-
 src/clientserver.h   |   9 ++
 src/environment.cpp  | 208 +++++++++++++++++++++++++++++++++++++++----
 src/main.cpp         |  26 +-----
 src/map.cpp          |  16 ++++
 src/mapblock.h       |   2 +
 src/nodemetadata.h   |   2 +-
 src/serialization.h  |   3 +-
 src/server.cpp       | 190 +++++++++++++++++++++++----------------
 src/serverobject.cpp |  91 +++++++++++++++++++
 src/serverobject.h   |  70 +++++++++------
 src/utility.h        |  14 +--
 15 files changed, 535 insertions(+), 160 deletions(-)

diff --git a/src/client.cpp b/src/client.cpp
index 1f2f7e1e8..fca7b1b02 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -1193,7 +1193,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 	}
 	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
 	{
-		if(g_settings.getBool("enable_experimental"))
+		//if(g_settings.getBool("enable_experimental"))
 		{
 			/*
 				u16 command
@@ -1252,7 +1252,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 	}
 	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
 	{
-		if(g_settings.getBool("enable_experimental"))
+		//if(g_settings.getBool("enable_experimental"))
 		{
 			/*
 				u16 command
@@ -1594,6 +1594,31 @@ void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
 	Send(0, data, true);
 }
 
+void Client::clickActiveObject(u8 button, u16 id, u16 item)
+{
+	if(connectedAndInitialized() == false){
+		dout_client<<DTIME<<"Client::clickActiveObject() "
+				"cancelled (not connected)"
+				<<std::endl;
+		return;
+	}
+	
+	/*
+		length: 7
+		[0] u16 command
+		[2] u8 button (0=left, 1=right)
+		[3] u16 id
+		[5] u16 item
+	*/
+	u8 datasize = 2 + 1 + 6 + 2 + 2;
+	SharedBuffer<u8> data(datasize);
+	writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
+	writeU8(&data[2], button);
+	writeU16(&data[3], id);
+	writeU16(&data[5], item);
+	Send(0, data, true);
+}
+
 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
 {
 	/*
diff --git a/src/client.h b/src/client.h
index 0616cc914..ef3dd435a 100644
--- a/src/client.h
+++ b/src/client.h
@@ -275,6 +275,7 @@ class Client : public con::PeerHandler, public InventoryManager
 	void groundAction(u8 action, v3s16 nodepos_undersurface,
 			v3s16 nodepos_oversurface, u16 item);
 	void clickObject(u8 button, v3s16 blockpos, s16 id, u16 item);
+	void clickActiveObject(u8 button, u16 id, u16 item);
 
 	void sendSignText(v3s16 blockpos, s16 id, std::string text);
 	void sendSignNodeText(v3s16 p, std::string text);
diff --git a/src/clientobject.cpp b/src/clientobject.cpp
index d95862d1d..a6aa033ef 100644
--- a/src/clientobject.cpp
+++ b/src/clientobject.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "constants.h"
 #include "utility.h"
+#include "environment.h"
 
 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
 
@@ -148,7 +149,7 @@ void TestCAO::updateNodePos()
 	//m_node->setRotation(v3f(0, 45, 0));
 }
 
-void TestCAO::step(float dtime)
+void TestCAO::step(float dtime, ClientEnvironment *env)
 {
 	if(m_node)
 	{
@@ -187,7 +188,7 @@ 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_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
 	m_node(NULL),
 	m_position(v3f(0,10*BS,0))
 {
@@ -219,10 +220,10 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr)
 		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),
+		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);
@@ -272,12 +273,17 @@ void ItemCAO::updateNodePos()
 	m_node->setPosition(m_position);
 }
 
-void ItemCAO::step(float dtime)
+void ItemCAO::step(float dtime, ClientEnvironment *env)
 {
 	if(m_node)
 	{
-		v3f rot = m_node->getRotation();
+		/*v3f rot = m_node->getRotation();
 		rot.Y += dtime * 120;
+		m_node->setRotation(rot);*/
+		LocalPlayer *player = env->getLocalPlayer();
+		assert(player);
+		v3f rot = m_node->getRotation();
+		rot.Y = 180.0 - (player->getYaw());
 		m_node->setRotation(rot);
 	}
 }
diff --git a/src/clientobject.h b/src/clientobject.h
index ebdcb948e..50fae67c2 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -35,6 +35,8 @@ Some planning
 
 */
 
+class ClientEnvironment;
+
 class ClientActiveObject : public ActiveObject
 {
 public:
@@ -51,11 +53,13 @@ class ClientActiveObject : public ActiveObject
 	virtual v3f getPosition(){return v3f(0,0,0);}
 	
 	// Step object in time
-	virtual void step(float dtime){}
+	virtual void step(float dtime, ClientEnvironment *env){}
 	
 	// Process a message sent by the server side object
 	virtual void processMessage(const std::string &data){}
 
+	virtual std::string infoText() {return "";}
+
 	/*
 		This takes the return value of
 		ServerActiveObject::getClientInitializationData
@@ -66,9 +70,11 @@ class ClientActiveObject : public ActiveObject
 	static ClientActiveObject* create(u8 type);
 
 protected:
+	// Used for creating objects based on type
 	typedef ClientActiveObject* (*Factory)();
 	static void registerType(u16 type, Factory f);
 private:
+	// Used for creating objects based on type
 	static core::map<u16, Factory> m_types;
 };
 
@@ -112,7 +118,7 @@ class TestCAO : public ClientActiveObject
 	v3s16 getLightPosition();
 	void updateNodePos();
 
-	void step(float dtime);
+	void step(float dtime, ClientEnvironment *env);
 
 	void processMessage(const std::string &data);
 
@@ -144,7 +150,7 @@ class ItemCAO : public ClientActiveObject
 	v3s16 getLightPosition();
 	void updateNodePos();
 
-	void step(float dtime);
+	void step(float dtime, ClientEnvironment *env);
 
 	void processMessage(const std::string &data);
 
diff --git a/src/clientserver.h b/src/clientserver.h
index 7baa79fa6..fadafed5f 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -249,6 +249,15 @@ enum ToServerCommand
 		textdata
 	*/
 
+	TOSERVER_CLICK_ACTIVEOBJECT = 0x34,
+	/*
+		length: 7
+		[0] u16 command
+		[2] u8 button (0=left, 1=right)
+		[3] u16 id
+		[5] u16 item
+	*/
+
 };
 
 inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
diff --git a/src/environment.cpp b/src/environment.cpp
index f4afb28bf..7e1d268df 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -189,7 +189,7 @@ u32 Environment::getDayNightRatio()
 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
 	m_map(map),
 	m_server(server),
-	m_random_spawn_timer(0)
+	m_random_spawn_timer(3)
 {
 }
 
@@ -422,7 +422,7 @@ void ServerEnvironment::step(float dtime)
 		}
 	}
 	
-	if(g_settings.getBool("enable_experimental"))
+	//if(g_settings.getBool("enable_experimental"))
 	{
 
 	/*
@@ -438,7 +438,7 @@ void ServerEnvironment::step(float dtime)
 	}
 
 	/*
-		Remove (m_removed && m_known_by_count==0) objects
+		Remove objects that satisfy (m_removed && m_known_by_count==0)
 	*/
 	{
 		core::list<u16> objects_to_remove;
@@ -457,16 +457,173 @@ void ServerEnvironment::step(float dtime)
 				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);
+		}
+	}
+	
+
+	const s16 to_active_max_blocks = 3;
+	const f32 to_static_max_f = (to_active_max_blocks+1)*MAP_BLOCKSIZE*BS;
+
+	/*
+		Convert stored objects from blocks near the players to active.
+	*/
+	for(core::list<Player*>::Iterator i = m_players.begin();
+			i != m_players.end(); i++)
+	{
+		Player *player = *i;
+		v3f playerpos = player->getPosition();
+		
+		v3s16 blockpos0 = getNodeBlockPos(floatToInt(playerpos, BS));
+		v3s16 bpmin = blockpos0 - v3s16(1,1,1)*to_active_max_blocks;
+		v3s16 bpmax = blockpos0 + v3s16(1,1,1)*to_active_max_blocks;
+		// Loop through all nearby blocks
+		for(s16 x=bpmin.X; x<=bpmax.X; x++)
+		for(s16 y=bpmin.Y; y<=bpmax.Y; y++)
+		for(s16 z=bpmin.Z; z<=bpmax.Z; z++)
+		{
+			v3s16 blockpos(x,y,z);
+			MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+			if(block==NULL)
+				continue;
+			// Ignore if no stored objects (to not set changed flag)
+			if(block->m_static_objects.m_stored.size() == 0)
+				continue;
+			// This will contain the leftovers of the stored list
+			core::list<StaticObject> new_stored;
+			// Loop through stored static objects
+			for(core::list<StaticObject>::Iterator
+					i = block->m_static_objects.m_stored.begin();
+					i != block->m_static_objects.m_stored.end(); i++)
 			{
-				// If not m_removed, don't remove.
-				if(obj->m_removed == false)
+				dstream<<"INFO: Server: Creating an active object from "
+						<<"static data"<<std::endl;
+				StaticObject &s_obj = *i;
+				// Create an active object from the data
+				ServerActiveObject *obj = ServerActiveObject::create
+						(s_obj.type, this, 0, s_obj.pos, s_obj.data);
+				if(obj==NULL)
+				{
+					// This is necessary to preserve stuff during bugs
+					// and errors
+					new_stored.push_back(s_obj);
 					continue;
-				// Delete
-				delete obj;
-				// Id to be removed from m_active_objects
-				objects_to_remove.push_back(id);
+				}
+				// This will also add the object to the active static list
+				addActiveObject(obj);
+				//u16 id = addActiveObject(obj);
+			}
+			// Clear stored list
+			block->m_static_objects.m_stored.clear();
+			// Add leftover stuff to stored list
+			for(core::list<StaticObject>::Iterator
+					i = new_stored.begin();
+					i != new_stored.end(); i++)
+			{
+				StaticObject &s_obj = *i;
+				block->m_static_objects.m_stored.push_back(s_obj);
+			}
+			block->setChangedFlag();
+		}
+	}
+
+	/*
+		Convert objects that are far away from all the players to static.
+	*/
+	{
+		core::list<u16> objects_to_remove;
+		for(core::map<u16, ServerActiveObject*>::Iterator
+				i = m_active_objects.getIterator();
+				i.atEnd()==false; i++)
+		{
+			ServerActiveObject* obj = i.getNode()->getValue();
+			u16 id = i.getNode()->getKey();
+			v3f objectpos = obj->getBasePosition();
+
+			// This shouldn't happen but check it
+			if(obj == NULL)
+			{
+				dstream<<"WARNING: NULL object found in ServerEnvironment"
+						<<std::endl;
+				continue;
 			}
+			// If known by some client, don't convert to static.
+			if(obj->m_known_by_count > 0)
+				continue;
+
+			// If close to some player, don't convert to static.
+			bool close_to_player = false;
+			for(core::list<Player*>::Iterator i = m_players.begin();
+					i != m_players.end(); i++)
+			{
+				Player *player = *i;
+				v3f playerpos = player->getPosition();
+				f32 d = playerpos.getDistanceFrom(objectpos);
+				if(d < to_static_max_f)
+				{
+					close_to_player = true;
+					break;
+				}
+			}
+
+			if(close_to_player)
+				continue;
+
+			/*
+				Update the static data and remove the active object.
+			*/
+
+			// Delete old static object
+			MapBlock *oldblock = NULL;
+			if(obj->m_static_exists)
+			{
+				MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
+				if(block)
+				{
+					block->m_static_objects.remove(id);
+					oldblock = block;
+				}
+			}
+			// Add new static object
+			std::string staticdata = obj->getStaticData();
+			StaticObject s_obj(obj->getType(), objectpos, staticdata);
+			// Add to the block where the object is located in
+			v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+			MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+			if(block)
+			{
+				block->m_static_objects.insert(0, s_obj);
+				block->setChangedFlag();
+			}
+			// If not possible, add back to previous block
+			else if(oldblock)
+			{
+				oldblock->m_static_objects.insert(0, s_obj);
+				oldblock->setChangedFlag();
+			}
+			else{
+				dstream<<"WARNING: Server: Could not find a block for "
+						<<"storing static object"<<std::endl;
+				continue;
+			}
+			// Delete active object
+			dstream<<"INFO: Server: Stored static data. Deleting object."
+					<<std::endl;
+			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();
@@ -479,11 +636,13 @@ void ServerEnvironment::step(float dtime)
 	/*
 		TEST CODE
 	*/
+#if 1
 	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 += 2.0;
+		//m_random_spawn_timer += 2.0;
+		m_random_spawn_timer += 200.0;
 
 		/*
 			Find some position
@@ -508,11 +667,10 @@ void ServerEnvironment::step(float dtime)
 		*/
 
 		//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);
+		//ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
+		//addActiveObject(obj);
 	}
+#endif
 
 	} // enable_experimental
 }
@@ -582,7 +740,25 @@ u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
 	}
 	dstream<<"INGO: ServerEnvironment::addActiveObject(): "
 			<<"added (id="<<object->getId()<<")"<<std::endl;
+			
 	m_active_objects.insert(object->getId(), object);
+
+	// Add static object to active static list of the block
+	v3f objectpos = object->getBasePosition();
+	std::string staticdata = object->getStaticData();
+	StaticObject s_obj(object->getType(), objectpos, staticdata);
+	// Add to the block where the object is located in
+	v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+	MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+	if(block)
+	{
+		block->m_static_objects.m_active.insert(object->getId(), s_obj);
+	}
+	else{
+		dstream<<"WARNING: Server: Could not find a block for "
+				<<"storing newly added static active object"<<std::endl;
+	}
+
 	return object->getId();
 }
 
@@ -912,7 +1088,7 @@ void ClientEnvironment::step(float dtime)
 	{
 		ClientActiveObject* obj = i.getNode()->getValue();
 		// Step object
-		obj->step(dtime);
+		obj->step(dtime, this);
 	}
 }
 
diff --git a/src/main.cpp b/src/main.cpp
index d3f979cac..7749266d9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2620,10 +2620,8 @@ int main(int argc, char *argv[])
 			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
+				client.clickActiveObject(0,
+						selected_active_object->getId(), g_selected_item);
 			}
 			else if(g_input->getRightClicked())
 			{
@@ -2634,26 +2632,6 @@ int main(int argc, char *argv[])
 				*/
 				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
diff --git a/src/map.cpp b/src/map.cpp
index 49ed2f5fe..59cf937c0 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -5239,6 +5239,14 @@ void ServerMap::saveBlock(MapBlock *block)
 		block->serializeObjects(o, version);
 	}
 	
+	/*
+		Versions up from 15 have static objects.
+	*/
+	if(version >= 15)
+	{
+		block->m_static_objects.serialize(o);
+	}
+	
 	// We just wrote it to the disk
 	block->resetChangedFlag();
 }
@@ -5296,6 +5304,14 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
 			block->updateObjects(is, version, NULL, 0);
 		}
 
+		/*
+			Versions up from 15 have static objects.
+		*/
+		if(version >= 15)
+		{
+			block->m_static_objects.deSerialize(is);
+		}
+		
 		if(created_new)
 			sector->insertBlock(block);
 		
diff --git a/src/mapblock.h b/src/mapblock.h
index a77bf40c0..ce5682568 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblockobject.h"
 #include "voxel.h"
 #include "nodemetadata.h"
+#include "staticobject.h"
 
 
 // Named by looking towards z+
@@ -681,6 +682,7 @@ class MapBlock : public NodeContainer
 #endif
 	
 	NodeMetadataList m_node_metadata;
+	StaticObjectList m_static_objects;
 	
 private:
 	/*
diff --git a/src/nodemetadata.h b/src/nodemetadata.h
index e56bff17f..ae02cfc3c 100644
--- a/src/nodemetadata.h
+++ b/src/nodemetadata.h
@@ -54,7 +54,7 @@ class NodeMetadata
 	virtual u16 typeId() const = 0;
 	virtual NodeMetadata* clone() = 0;
 	virtual void serializeBody(std::ostream &os) = 0;
-	virtual std::string infoText() {return "<todo: remove this text>";}
+	virtual std::string infoText() {return "";}
 	virtual Inventory* getInventory() {return NULL;}
 	// This is called always after the inventory is modified, before
 	// the changes are copied elsewhere
diff --git a/src/serialization.h b/src/serialization.h
index fed5bb522..7b5c7d418 100644
--- a/src/serialization.h
+++ b/src/serialization.h
@@ -47,11 +47,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 	12: (dev) UnlimitedHeightmap now uses interpolated areas
 	13: (dev) Mapgen v2
 	14: (dev) NodeMetadata
+	15: (dev) StaticObjects
 */
 // This represents an uninitialized or invalid format
 #define SER_FMT_VER_INVALID 255
 // Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 14
+#define SER_FMT_VER_HIGHEST 15
 // Lowest supported serialization version
 #define SER_FMT_VER_LOWEST 2
 
diff --git a/src/server.cpp b/src/server.cpp
index 20a6a21c3..ab4032743 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1228,7 +1228,7 @@ void Server::AsyncRunStep()
 		}
 	}
 
-	if(g_settings.getBool("enable_experimental"))
+	//if(g_settings.getBool("enable_experimental"))
 	{
 
 	/*
@@ -1345,6 +1345,33 @@ void Server::AsyncRunStep()
 					<<added_objects.size()<<" added, "
 					<<"packet size is "<<reply.getSize()<<std::endl;
 		}
+
+#if 0
+		/*
+			Collect a list of all the objects known by the clients
+			and report it back to the environment.
+		*/
+
+		core::map<u16, bool> all_known_objects;
+
+		for(core::map<u16, RemoteClient*>::Iterator
+			i = m_clients.getIterator();
+			i.atEnd() == false; i++)
+		{
+			RemoteClient *client = i.getNode()->getValue();
+			// Go through all known objects of client
+			for(core::map<u16, bool>::Iterator
+					i = client->m_known_objects.getIterator();
+					i.atEnd()==false; i++)
+			{
+				u16 id = i.getNode()->getKey();
+				all_known_objects[id] = true;
+			}
+		}
+		
+		m_env.setKnownActiveObjects(whatever);
+#endif
+
 	}
 
 	/*
@@ -1978,6 +2005,70 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			block->removeObject(id);
 		}
 	}
+	else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
+	{
+		if(datasize < 7)
+			return;
+
+		/*
+			length: 7
+			[0] u16 command
+			[2] u8 button (0=left, 1=right)
+			[3] u16 id
+			[5] u16 item
+		*/
+		u8 button = readU8(&data[2]);
+		u16 id = readS16(&data[3]);
+		//u16 item_i = readU16(&data[11]);
+	
+		ServerActiveObject *obj = m_env.getActiveObject(id);
+
+		if(obj == NULL)
+		{
+			derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
+					<<std::endl;
+			return;
+		}
+
+		//TODO: Check that object is reasonably close
+		
+		// Left click, pick object up (usually)
+		if(button == 0)
+		{
+			InventoryList *ilist = player->inventory.getList("main");
+			if(g_settings.getBool("creative_mode") == false && ilist != NULL)
+			{
+			
+				// Skip if inventory has no free space
+				if(ilist->getUsedSlots() == ilist->getSize())
+				{
+					dout_server<<"Player inventory has no free space"<<std::endl;
+					return;
+				}
+				
+				/*
+					Create the inventory item
+				*/
+				InventoryItem *item = NULL;
+				// If it is an item-object, take the item from it
+				if(obj->getType() == ACTIVEOBJECT_TYPE_ITEM
+						&& obj->m_removed == false)
+				{
+					item = ((ItemSAO*)obj)->createInventoryItem();
+				}
+				
+				if(item)
+				{
+					// Add to inventory and send inventory
+					ilist->addItem(item);
+					SendInventory(player->peer_id);
+				}
+			}
+
+			// Remove object from environment
+			obj->m_removed = true;
+		}
+	}
 	else if(command == TOSERVER_GROUND_ACTION)
 	{
 		if(datasize < 17)
@@ -2327,68 +2418,40 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				}*/
 			}
 			/*
-				Handle other items
+				Place other item (not a block)
 			*/
 			else
 			{
 				v3s16 blockpos = getNodeBlockPos(p_over);
-
-				MapBlock *block = NULL;
-				try
-				{
-					block = m_env.getMap().getBlockNoCreate(blockpos);
-				}
-				catch(InvalidPositionException &e)
+				
+				/*
+					Check that the block is loaded so that the item
+					can properly be added to the static list too
+				*/
+				MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
+				if(block==NULL)
 				{
 					derr_server<<"Error while placing object: "
 							"block not found"<<std::endl;
 					return;
 				}
 
-				v3s16 block_pos_i_on_map = block->getPosRelative();
-				v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
-
 				v3f pos = intToFloat(p_over, BS);
-				pos -= block_pos_f_on_map;
-				
-				/*dout_server<<"pos="
-						<<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
-						<<std::endl;*/
+				pos.Y -= BS*0.45;
 
-				MapBlockObject *obj = NULL;
-
-				/*
-					Handle block object items
-				*/
-				if(std::string("MBOItem") == item->getName())
-				{
-					MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
-
-					/*dout_server<<"Trying to place a MapBlockObjectItem: "
-							"inventorystring=\""
-							<<oitem->getInventoryString()
-							<<"\""<<std::endl;*/
-							
-					obj = oitem->createObject
-							(pos, player->getYaw(), player->getPitch());
-				}
+				dout_server<<"Placing a miscellaneous item on map"
+						<<std::endl;
+						
 				/*
-					Handle other items
+					Create an ItemSAO
 				*/
-				else
-				{
-					dout_server<<"Placing a miscellaneous item on map"
-							<<std::endl;
-					/*
-						Create an ItemObject that contains the item.
-					*/
-					ItemObject *iobj = new ItemObject(NULL, -1, pos);
-					std::ostringstream os(std::ios_base::binary);
-					item->serialize(os);
-					dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
-					iobj->setItemString(os.str());
-					obj = iobj;
-				}
+				// Get item string
+				std::ostringstream os(std::ios_base::binary);
+				item->serialize(os);
+				dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
+				// Create object
+				ServerActiveObject *obj = new ItemSAO
+						(&m_env, 0, pos, os.str());
 
 				if(obj == NULL)
 				{
@@ -2398,8 +2461,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				}
 				else
 				{
-					block->addObject(obj);
-
+					// Add the object to the environment
+					m_env.addActiveObject(obj);
+					
 					dout_server<<"Placed object"<<std::endl;
 
 					InventoryList *ilist = player->inventory.getList("main");
@@ -3874,30 +3938,6 @@ Player *Server::emergePlayer(const char *name, const char *password,
 	} // create new player
 }
 
-#if 0
-void Server::UpdateBlockWaterPressure(MapBlock *block,
-			core::map<v3s16, MapBlock*> &modified_blocks)
-{
-	MapVoxelManipulator v(&m_env.getMap());
-	v.m_disable_water_climb =
-			g_settings.getBool("disable_water_climb");
-	
-	VoxelArea area(block->getPosRelative(),
-			block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
-
-	try
-	{
-		v.updateAreaWaterPressure(area, m_flow_active_nodes);
-	}
-	catch(ProcessingLimitException &e)
-	{
-		dstream<<"Processing limit reached (1)"<<std::endl;
-	}
-	
-	v.blitBack(modified_blocks);
-}
-#endif
-
 void Server::handlePeerChange(PeerChange &c)
 {
 	JMutexAutoLock envlock(m_env_mutex);
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index 3645f7666..f0ef7d8d6 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -20,11 +20,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serverobject.h"
 #include <fstream>
 #include "environment.h"
+#include "inventory.h"
+
+core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
 
 ServerActiveObject::ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos):
 	ActiveObject(id),
 	m_known_by_count(0),
 	m_removed(false),
+	m_static_exists(false),
+	m_static_block(1337,1337,1337),
 	m_env(env),
 	m_base_position(pos)
 {
@@ -34,15 +39,55 @@ ServerActiveObject::~ServerActiveObject()
 {
 }
 
+ServerActiveObject* ServerActiveObject::create(u8 type,
+		ServerEnvironment *env, u16 id, v3f pos,
+		const std::string &data)
+{
+	// Find factory function
+	core::map<u16, Factory>::Node *n;
+	n = m_types.find(type);
+	if(n == NULL)
+	{
+		// If factory is not found, just return.
+		dstream<<"WARNING: ServerActiveObject: No factory for type="
+				<<type<<std::endl;
+		return NULL;
+	}
+
+	Factory f = n->getValue();
+	ServerActiveObject *object = (*f)(env, id, pos, data);
+	return object;
+}
+
+void ServerActiveObject::registerType(u16 type, Factory f)
+{
+	core::map<u16, Factory>::Node *n;
+	n = m_types.find(type);
+	if(n)
+		return;
+	m_types.insert(type, f);
+}
+
+
 /*
 	TestSAO
 */
 
+// Prototype
+TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0));
+
 TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
 	ServerActiveObject(env, id, pos),
 	m_timer1(0),
 	m_age(0)
 {
+	ServerActiveObject::registerType(getType(), create);
+}
+
+ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos,
+		const std::string &data)
+{
+	return new TestSAO(env, id, pos);
 }
 
 void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
@@ -84,6 +129,9 @@ void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
 	ItemSAO
 */
 
+// Prototype
+ItemSAO proto_ItemSAO(NULL, 0, v3f(0,0,0), "");
+
 ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
 		const std::string inventorystring):
 	ServerActiveObject(env, id, pos),
@@ -91,6 +139,19 @@ ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
 {
 	dstream<<"Server: ItemSAO created with inventorystring=\""
 			<<m_inventorystring<<"\""<<std::endl;
+	ServerActiveObject::registerType(getType(), create);
+}
+
+ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
+		const std::string &data)
+{
+	std::istringstream is(data, std::ios::binary);
+	char buf[1];
+	is.read(buf, 1); // read version
+	std::string inventorystring = deSerializeString(is);
+	dstream<<"ItemSAO::create(): Creating item \""
+			<<inventorystring<<"\""<<std::endl;
+	return new ItemSAO(env, id, pos, inventorystring);
 }
 
 void ItemSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
@@ -111,4 +172,34 @@ std::string ItemSAO::getClientInitializationData()
 	return data;
 }
 
+std::string ItemSAO::getStaticData()
+{
+	dstream<<__FUNCTION_NAME<<std::endl;
+	std::ostringstream os(std::ios::binary);
+	char buf[1];
+	buf[0] = 0; //version
+	os.write(buf, 1);
+	os<<serializeString(m_inventorystring);
+	return os.str();
+}
+
+InventoryItem * ItemSAO::createInventoryItem()
+{
+	try{
+		std::istringstream is(m_inventorystring, std::ios_base::binary);
+		InventoryItem *item = InventoryItem::deSerialize(is);
+		dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
+				<<m_inventorystring<<"\" -> item="<<item
+				<<std::endl;
+		return item;
+	}
+	catch(SerializationError &e)
+	{
+		dstream<<__FUNCTION_NAME<<": serialization error: "
+				<<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
+		return NULL;
+	}
+}
+
+
 
diff --git a/src/serverobject.h b/src/serverobject.h
index 241458193..a307c421f 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -39,27 +39,28 @@ Some planning
 */
 
 class ServerEnvironment;
+class InventoryItem;
 
 class ServerActiveObject : public ActiveObject
 {
 public:
-	ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos=v3f(0,0,0));
+	ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos);
 	virtual ~ServerActiveObject();
 
-	v3f getBasePosition()
-	{
-		return m_base_position;
-	}
+	// Create a certain type of ServerActiveObject
+	static ServerActiveObject* create(u8 type,
+			ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
 	
+	/*
+		Some simple getters/setters
+	*/
+	v3f getBasePosition()
+		{return m_base_position;}
 	void setBasePosition(v3f pos)
-	{
-		m_base_position = pos;
-	}
-
+		{m_base_position = pos;}
 	ServerEnvironment* getEnv()
-	{
-		return m_env;
-	}
+		{return m_env;}
 	
 	/*
 		Step object in time.
@@ -75,14 +76,10 @@ class ServerActiveObject : public ActiveObject
 	
 	/*
 		The return value of this is passed to the server-side object
-		when it is loaded from disk or from a static object
-	*/
-	virtual std::string getServerInitializationData(){return "";}
-	
-	/*
-		This takes the return value of getServerInitializationData
+		when it is created (converted from static to active - actually
+		the data is the static form)
 	*/
-	virtual void initialize(const std::string &data){}
+	virtual std::string getStaticData(){return "";}
 	
 	// Number of players which know about this object
 	u16 m_known_by_count;
@@ -93,12 +90,33 @@ class ServerActiveObject : public ActiveObject
 		  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.
+		- This can be set to true by anything else too.
 	*/
 	bool m_removed;
 	
+	/*
+		Whether the object's static data has been stored to a block
+	*/
+	bool m_static_exists;
+	/*
+		The block from which the object was loaded from, and in which
+		a copy of the static data resides.
+	*/
+	v3s16 m_static_block;
+	
 protected:
+	// Used for creating objects based on type
+	typedef ServerActiveObject* (*Factory)
+			(ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
+	static void registerType(u16 type, Factory f);
+
 	ServerEnvironment *m_env;
 	v3f m_base_position;
+
+private:
+	// Used for creating objects based on type
+	static core::map<u16, Factory> m_types;
 };
 
 class TestSAO : public ServerActiveObject
@@ -106,9 +124,9 @@ class TestSAO : public ServerActiveObject
 public:
 	TestSAO(ServerEnvironment *env, u16 id, v3f pos);
 	u8 getType() const
-	{
-		return ACTIVEOBJECT_TYPE_TEST;
-	}
+		{return ACTIVEOBJECT_TYPE_TEST;}
+	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
 	void step(float dtime, Queue<ActiveObjectMessage> &messages);
 private:
 	float m_timer1;
@@ -121,11 +139,13 @@ class ItemSAO : public ServerActiveObject
 	ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
 			const std::string inventorystring);
 	u8 getType() const
-	{
-		return ACTIVEOBJECT_TYPE_ITEM;
-	}
+		{return ACTIVEOBJECT_TYPE_ITEM;}
+	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
 	void step(float dtime, Queue<ActiveObjectMessage> &messages);
 	std::string getClientInitializationData();
+	std::string getStaticData();
+	InventoryItem* createInventoryItem();
 private:
 	std::string m_inventorystring;
 };
diff --git a/src/utility.h b/src/utility.h
index 3640b4b51..2b143f0ba 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -1839,15 +1839,17 @@ inline std::string serializeString(const std::string plain)
 	return s;
 }
 
-// Reads a string with the length as the first two bytes
+/*// Reads a string with the length as the first two bytes
 inline std::string deSerializeString(const std::string encoded)
 {
 	u16 s_size = readU16((u8*)&encoded.c_str()[0]);
+	if(s_size > encoded.length() - 2)
+		return "";
 	std::string s;
 	s.reserve(s_size);
 	s.append(&encoded.c_str()[2], s_size);
 	return s;
-}
+}*/
 
 // Reads a string with the length as the first two bytes
 inline std::string deSerializeString(std::istream &is)
@@ -1878,15 +1880,17 @@ inline std::string serializeLongString(const std::string plain)
 	return s;
 }
 
-// Reads a string with the length as the first four bytes
+/*// Reads a string with the length as the first four bytes
 inline std::string deSerializeLongString(const std::string encoded)
 {
 	u32 s_size = readU32((u8*)&encoded.c_str()[0]);
+	if(s_size > encoded.length() - 4)
+		return "";
 	std::string s;
 	s.reserve(s_size);
-	s.append(&encoded.c_str()[2], s_size);
+	s.append(&encoded.c_str()[4], s_size);
 	return s;
-}
+}*/
 
 // Reads a string with the length as the first four bytes
 inline std::string deSerializeLongString(std::istream &is)
-- 
GitLab