diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f75c182bd0850476cd3e92ceb1139c1baa42ef2e..b41fc4a6cb5814cd42a534bd671081edda4d1160 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -94,6 +94,8 @@ configure_file(
 )
 
 set(common_SRCS
+	scriptapi.cpp
+	script.cpp
 	log.cpp
 	content_sao.cpp
 	mapgen.cpp
diff --git a/src/common_irrlicht.h b/src/common_irrlicht.h
index f4c2f76ecec26ab4257e5d925fb17a22266c2632..379c2d15a6fb101279402c390bf0bcdd9ea4e204 100644
--- a/src/common_irrlicht.h
+++ b/src/common_irrlicht.h
@@ -23,13 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define endSceneX(d){d->draw2DLine(v2s32(0,0),v2s32(1,0),\
 video::SColor(255,30,30,30));d->endScene();}
 
-#include <irrTypes.h>
-#include <vector2d.h>
-#include <vector3d.h>
-#include <irrMap.h>
-#include <irrList.h>
-#include <irrArray.h>
-#include <aabbox3d.h>
+#include "irrlichttypes.h"
+
 #ifndef SERVER
 #include <SColor.h>
 #include <IMesh.h>
@@ -43,26 +38,6 @@ video::SColor(255,30,30,30));d->endScene();}
 #include <IGUIElement.h>
 #include <IGUIEnvironment.h>
 #endif
-using namespace irr;
-typedef core::vector3df v3f;
-typedef core::vector3d<s16> v3s16;
-typedef core::vector3d<s32> v3s32;
-
-typedef core::vector2d<f32> v2f;
-typedef core::vector2d<s16> v2s16;
-typedef core::vector2d<s32> v2s32;
-typedef core::vector2d<u32> v2u32;
-typedef core::vector2d<f32> v2f32;
-
-#ifdef _MSC_VER
-	// Windows
-	typedef unsigned long long u64;
-#else
-	// Posix
-	#include <stdint.h>
-	typedef uint64_t u64;
-	//typedef unsigned long long u64;
-#endif
 
 #endif
 
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 6cb2cee385f53b2112656f826c3ea5d0963af91a..157a51886493ba952c65f8f20146f717bee835fa 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -1264,3 +1264,152 @@ void MobV2CAO::setLooks(const std::string &looks)
 			selection_size.X);
 }
 
+/*
+	LuaEntityCAO
+*/
+
+// Prototype
+LuaEntityCAO proto_LuaEntityCAO;
+
+LuaEntityCAO::LuaEntityCAO():
+	ClientActiveObject(0),
+	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))
+{
+	ClientActiveObject::registerType(getType(), create);
+}
+
+LuaEntityCAO::~LuaEntityCAO()
+{
+}
+
+ClientActiveObject* LuaEntityCAO::create()
+{
+	return new LuaEntityCAO();
+}
+
+void LuaEntityCAO::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);
+	// Initialize with the stick texture
+	buf->getMaterial().setTexture
+			(0, driver->getTexture(getTexturePath("mese.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 LuaEntityCAO::removeFromScene()
+{
+	if(m_node == NULL)
+		return;
+
+	m_node->remove();
+	m_node = NULL;
+}
+
+void LuaEntityCAO::updateLight(u8 light_at_pos)
+{
+	if(m_node == NULL)
+		return;
+
+	u8 li = decode_light(light_at_pos);
+	video::SColor color(255,li,li,li);
+	setMeshVerticesColor(m_node->getMesh(), color);
+}
+
+v3s16 LuaEntityCAO::getLightPosition()
+{
+	return floatToInt(m_position, BS);
+}
+
+void LuaEntityCAO::updateNodePos()
+{
+	if(m_node == NULL)
+		return;
+
+	m_node->setPosition(m_position);
+}
+
+void LuaEntityCAO::step(float dtime, ClientEnvironment *env)
+{
+	if(m_node)
+	{
+		/*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);
+	}
+}
+
+void LuaEntityCAO::processMessage(const std::string &data)
+{
+	infostream<<"LuaEntityCAO: Got message"<<std::endl;
+	std::istringstream is(data, std::ios::binary);
+	// command
+	u8 cmd = readU8(is);
+	if(cmd == 0)
+	{
+		// pos
+		m_position = readV3F1000(is);
+		updateNodePos();
+	}
+}
+
+void LuaEntityCAO::initialize(const std::string &data)
+{
+	infostream<<"LuaEntityCAO: Got init data"<<std::endl;
+	
+	{
+		std::istringstream is(data, std::ios::binary);
+		// version
+		u8 version = readU8(is);
+		// check version
+		if(version != 0)
+			return;
+		// pos
+		m_position = readV3F1000(is);
+	}
+	
+	updateNodePos();
+}
+
+
diff --git a/src/content_cao.h b/src/content_cao.h
index 963e1a4889aef425dd302546f5b81caa9a38453f..c8554e737cb0cc370823e37eee370eed7545091b 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -376,6 +376,46 @@ class MobV2CAO : public ClientActiveObject
 	Settings *m_properties;
 };
 
+/*
+	LuaEntityCAO
+*/
+
+class LuaEntityCAO : public ClientActiveObject
+{
+public:
+	LuaEntityCAO();
+	virtual ~LuaEntityCAO();
+	
+	u8 getType() const
+	{
+		return ACTIVEOBJECT_TYPE_LUAENTITY;
+	}
+	
+	static ClientActiveObject* create();
+
+	void addToScene(scene::ISceneManager *smgr);
+	void removeFromScene();
+	void updateLight(u8 light_at_pos);
+	v3s16 getLightPosition();
+	void updateNodePos();
+
+	void step(float dtime, ClientEnvironment *env);
+
+	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;
+};
+
 
 #endif
 
diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp
index 413ae8505276e349ae3f334b9bc6ce533cfb05da..1d5c6b35594214f483c5bbc981ec40eb8dc627a1 100644
--- a/src/content_inventory.cpp
+++ b/src/content_inventory.cpp
@@ -80,16 +80,16 @@ std::string item_craft_get_image_name(const std::string &subname)
 }
 
 ServerActiveObject* item_craft_create_object(const std::string &subname,
-		ServerEnvironment *env, u16 id, v3f pos)
+		ServerEnvironment *env, v3f pos)
 {
 	if(subname == "rat")
 	{
-		ServerActiveObject *obj = new RatSAO(env, id, pos);
+		ServerActiveObject *obj = new RatSAO(env, pos);
 		return obj;
 	}
 	else if(subname == "firefly")
 	{
-		ServerActiveObject *obj = new FireflySAO(env, id, pos);
+		ServerActiveObject *obj = new FireflySAO(env, pos);
 		return obj;
 	}
 
diff --git a/src/content_inventory.h b/src/content_inventory.h
index 91550bb9a42c0e46881337024bbf8b4d08be5351..9fd4cb03bd7114b21c4c01a82f4b162491a069eb 100644
--- a/src/content_inventory.h
+++ b/src/content_inventory.h
@@ -33,7 +33,7 @@ InventoryItem* item_material_create_cook_result(content_t content);
 
 std::string         item_craft_get_image_name(const std::string &subname);
 ServerActiveObject* item_craft_create_object(const std::string &subname,
-		ServerEnvironment *env, u16 id, v3f pos);
+		ServerEnvironment *env, v3f pos);
 s16                 item_craft_get_drop_count(const std::string &subname);
 bool                item_craft_is_cookable(const std::string &subname);
 InventoryItem*      item_craft_create_cook_result(const std::string &subname);
diff --git a/src/content_object.h b/src/content_object.h
index a5fea716351de6b7667d7a6dc76f26273e1be485..f092b2789139f043c957860c31af13d640d3c453 100644
--- a/src/content_object.h
+++ b/src/content_object.h
@@ -27,5 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define ACTIVEOBJECT_TYPE_FIREFLY 5
 #define ACTIVEOBJECT_TYPE_MOBV2 6
 
+#define ACTIVEOBJECT_TYPE_LUAENTITY 7
+
 #endif
 
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index f226371852c5ff22c2250e1fce75663487b06b68..d51e92a8c50738bafb9e6a83a8325df8f39c50e1 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -49,20 +49,20 @@ void accelerate_xz(v3f &speed, v3f target_speed, f32 max_increase)
 */
 
 // Prototype
-TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0));
+TestSAO proto_TestSAO(NULL, v3f(0,0,0));
 
-TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
-	ServerActiveObject(env, id, pos),
+TestSAO::TestSAO(ServerEnvironment *env, v3f pos):
+	ServerActiveObject(env, pos),
 	m_timer1(0),
 	m_age(0)
 {
 	ServerActiveObject::registerType(getType(), create);
 }
 
-ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* TestSAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
-	return new TestSAO(env, id, pos);
+	return new TestSAO(env, pos);
 }
 
 void TestSAO::step(float dtime, bool send_recommended)
@@ -107,11 +107,11 @@ void TestSAO::step(float dtime, bool send_recommended)
 */
 
 // Prototype
-ItemSAO proto_ItemSAO(NULL, 0, v3f(0,0,0), "");
+ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
 
-ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
+ItemSAO::ItemSAO(ServerEnvironment *env, v3f pos,
 		const std::string inventorystring):
-	ServerActiveObject(env, id, pos),
+	ServerActiveObject(env, pos),
 	m_inventorystring(inventorystring),
 	m_speed_f(0,0,0),
 	m_last_sent_position(0,0,0)
@@ -119,7 +119,7 @@ ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
 	ServerActiveObject::registerType(getType(), create);
 }
 
-ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* ItemSAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
 	std::istringstream is(data, std::ios::binary);
@@ -133,7 +133,7 @@ ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
 	std::string inventorystring = deSerializeString(is);
 	infostream<<"ItemSAO::create(): Creating item \""
 			<<inventorystring<<"\""<<std::endl;
-	return new ItemSAO(env, id, pos, inventorystring);
+	return new ItemSAO(env, pos, inventorystring);
 }
 
 void ItemSAO::step(float dtime, bool send_recommended)
@@ -260,10 +260,10 @@ void ItemSAO::rightClick(Player *player)
 */
 
 // Prototype
-RatSAO proto_RatSAO(NULL, 0, v3f(0,0,0));
+RatSAO proto_RatSAO(NULL, v3f(0,0,0));
 
-RatSAO::RatSAO(ServerEnvironment *env, u16 id, v3f pos):
-	ServerActiveObject(env, id, pos),
+RatSAO::RatSAO(ServerEnvironment *env, v3f pos):
+	ServerActiveObject(env, pos),
 	m_is_active(false),
 	m_speed_f(0,0,0)
 {
@@ -278,7 +278,7 @@ RatSAO::RatSAO(ServerEnvironment *env, u16 id, v3f pos):
 	m_touching_ground = false;
 }
 
-ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* RatSAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
 	std::istringstream is(data, std::ios::binary);
@@ -289,7 +289,7 @@ ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos,
 	// check if version is supported
 	if(version != 0)
 		return NULL;
-	return new RatSAO(env, id, pos);
+	return new RatSAO(env, pos);
 }
 
 void RatSAO::step(float dtime, bool send_recommended)
@@ -447,10 +447,10 @@ InventoryItem* RatSAO::createPickedUpItem()
 */
 
 // Prototype
-Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0));
+Oerkki1SAO proto_Oerkki1SAO(NULL, v3f(0,0,0));
 
-Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos):
-	ServerActiveObject(env, id, pos),
+Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, v3f pos):
+	ServerActiveObject(env, pos),
 	m_is_active(false),
 	m_speed_f(0,0,0)
 {
@@ -467,7 +467,7 @@ Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos):
 	m_after_jump_timer = 0;
 }
 
-ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
 	std::istringstream is(data, std::ios::binary);
@@ -478,7 +478,7 @@ ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos,
 	// check if version is supported
 	if(version != 0)
 		return NULL;
-	Oerkki1SAO *o = new Oerkki1SAO(env, id, pos);
+	Oerkki1SAO *o = new Oerkki1SAO(env, pos);
 	o->m_hp = hp;
 	return o;
 }
@@ -739,10 +739,10 @@ void Oerkki1SAO::doDamage(u16 d)
 */
 
 // Prototype
-FireflySAO proto_FireflySAO(NULL, 0, v3f(0,0,0));
+FireflySAO proto_FireflySAO(NULL, v3f(0,0,0));
 
-FireflySAO::FireflySAO(ServerEnvironment *env, u16 id, v3f pos):
-	ServerActiveObject(env, id, pos),
+FireflySAO::FireflySAO(ServerEnvironment *env, v3f pos):
+	ServerActiveObject(env, pos),
 	m_is_active(false),
 	m_speed_f(0,0,0)
 {
@@ -757,7 +757,7 @@ FireflySAO::FireflySAO(ServerEnvironment *env, u16 id, v3f pos):
 	m_touching_ground = false;
 }
 
-ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* FireflySAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
 	std::istringstream is(data, std::ios::binary);
@@ -768,7 +768,7 @@ ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos,
 	// check if version is supported
 	if(version != 0)
 		return NULL;
-	return new FireflySAO(env, id, pos);
+	return new FireflySAO(env, pos);
 }
 
 void FireflySAO::step(float dtime, bool send_recommended)
@@ -918,11 +918,11 @@ InventoryItem* FireflySAO::createPickedUpItem()
 */
 
 // Prototype
-MobV2SAO proto_MobV2SAO(NULL, 0, v3f(0,0,0), NULL);
+MobV2SAO proto_MobV2SAO(NULL, v3f(0,0,0), NULL);
 
-MobV2SAO::MobV2SAO(ServerEnvironment *env, u16 id, v3f pos,
+MobV2SAO::MobV2SAO(ServerEnvironment *env, v3f pos,
 		Settings *init_properties):
-	ServerActiveObject(env, id, pos),
+	ServerActiveObject(env, pos),
 	m_move_type("ground_nodes"),
 	m_speed(0,0,0),
 	m_last_sent_position(0,0,0),
@@ -961,13 +961,13 @@ MobV2SAO::~MobV2SAO()
 	delete m_properties;
 }
 
-ServerActiveObject* MobV2SAO::create(ServerEnvironment *env, u16 id, v3f pos,
+ServerActiveObject* MobV2SAO::create(ServerEnvironment *env, v3f pos,
 		const std::string &data)
 {
 	std::istringstream is(data, std::ios::binary);
 	Settings properties;
 	properties.parseConfigLines(is, "MobArgsEnd");
-	MobV2SAO *o = new MobV2SAO(env, id, pos, &properties);
+	MobV2SAO *o = new MobV2SAO(env, pos, &properties);
 	return o;
 }
 
@@ -1174,7 +1174,7 @@ void MobV2SAO::step(float dtime, bool send_recommended)
 				properties.set("player_hit_damage", "9");
 				properties.set("player_hit_distance", "2");
 				properties.set("player_hit_interval", "1");
-				ServerActiveObject *obj = new MobV2SAO(m_env, 0,
+				ServerActiveObject *obj = new MobV2SAO(m_env,
 						pos, &properties);
 				//m_env->addActiveObjectAsStatic(obj);
 				m_env->addActiveObject(obj);
@@ -1490,3 +1490,95 @@ void MobV2SAO::doDamage(u16 d)
 }
 
 
+/*
+	LuaEntitySAO
+*/
+
+#include "scriptapi.h"
+
+// Prototype
+LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
+
+LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
+		const std::string &name, const std::string &state):
+	ServerActiveObject(env, pos),
+	m_init_name(name),
+	m_init_state(state),
+	m_registered(false)
+{
+	if(env == NULL){
+		ServerActiveObject::registerType(getType(), create);
+		return;
+	}
+}
+
+LuaEntitySAO::~LuaEntitySAO()
+{
+	if(m_registered){
+		lua_State *L = m_env->getLua();
+		scriptapi_luaentity_deregister(L, m_id);
+	}
+}
+
+void LuaEntitySAO::addedToEnvironment(u16 id)
+{
+	ServerActiveObject::addedToEnvironment(id);
+	
+	// Create entity by name and state
+	m_registered = true;
+	lua_State *L = m_env->getLua();
+	scriptapi_luaentity_register(L, id, m_init_name.c_str(), m_init_state.c_str());
+}
+
+ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
+		const std::string &data)
+{
+	std::istringstream is(data, std::ios::binary);
+	// read version
+	u8 version = readU8(is);
+	// check if version is supported
+	if(version != 0)
+		return NULL;
+	// read name
+	std::string name = deSerializeString(is);
+	// read state
+	std::string state = deSerializeLongString(is);
+	// create object
+	infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
+			<<state<<"\")"<<std::endl;
+	return new LuaEntitySAO(env, pos, name, state);
+}
+
+void LuaEntitySAO::step(float dtime, bool send_recommended)
+{
+	if(m_registered){
+		lua_State *L = m_env->getLua();
+		scriptapi_luaentity_step(L, m_id, dtime, send_recommended);
+	}
+}
+
+std::string LuaEntitySAO::getClientInitializationData()
+{
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	// pos
+	writeV3F1000(os, m_base_position);
+	return os.str();
+}
+
+std::string LuaEntitySAO::getStaticData()
+{
+	infostream<<__FUNCTION_NAME<<std::endl;
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	// name
+	os<<serializeString(m_init_name);
+	// state
+	std::string state = scriptapi_luaentity_get_state(L, m_id);
+	os<<serializeString(state);
+	return os.str();
+}
+
+
diff --git a/src/content_sao.h b/src/content_sao.h
index f0ebf4f6fbe3b02822ba35d3e74f08b785835b84..44c0b7172c77d7c274bea18bb087d2e5db97cce4 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class TestSAO : public ServerActiveObject
 {
 public:
-	TestSAO(ServerEnvironment *env, u16 id, v3f pos);
+	TestSAO(ServerEnvironment *env, v3f pos);
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_TEST;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	void step(float dtime, bool send_recommended);
 private:
@@ -40,11 +40,11 @@ class TestSAO : public ServerActiveObject
 class ItemSAO : public ServerActiveObject
 {
 public:
-	ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
+	ItemSAO(ServerEnvironment *env, v3f pos,
 			const std::string inventorystring);
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_ITEM;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	void step(float dtime, bool send_recommended);
 	std::string getClientInitializationData();
@@ -62,10 +62,10 @@ class ItemSAO : public ServerActiveObject
 class RatSAO : public ServerActiveObject
 {
 public:
-	RatSAO(ServerEnvironment *env, u16 id, v3f pos);
+	RatSAO(ServerEnvironment *env, v3f pos);
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_RAT;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	void step(float dtime, bool send_recommended);
 	std::string getClientInitializationData();
@@ -87,10 +87,10 @@ class RatSAO : public ServerActiveObject
 class Oerkki1SAO : public ServerActiveObject
 {
 public:
-	Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos);
+	Oerkki1SAO(ServerEnvironment *env, v3f pos);
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_OERKKI1;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	void step(float dtime, bool send_recommended);
 	std::string getClientInitializationData();
@@ -119,10 +119,10 @@ class Oerkki1SAO : public ServerActiveObject
 class FireflySAO : public ServerActiveObject
 {
 public:
-	FireflySAO(ServerEnvironment *env, u16 id, v3f pos);
+	FireflySAO(ServerEnvironment *env, v3f pos);
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_FIREFLY;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	void step(float dtime, bool send_recommended);
 	std::string getClientInitializationData();
@@ -146,12 +146,12 @@ class Settings;
 class MobV2SAO : public ServerActiveObject
 {
 public:
-	MobV2SAO(ServerEnvironment *env, u16 id, v3f pos,
+	MobV2SAO(ServerEnvironment *env, v3f pos,
 			Settings *init_properties);
 	virtual ~MobV2SAO();
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_MOBV2;}
-	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	std::string getStaticData();
 	std::string getClientInitializationData();
@@ -195,5 +195,27 @@ class MobV2SAO : public ServerActiveObject
 	Settings *m_properties;
 };
 
+struct LuaState;
+
+class LuaEntitySAO : public ServerActiveObject
+{
+public:
+	LuaEntitySAO(ServerEnvironment *env, v3f pos,
+			const std::string &name, const std::string &state);
+	~LuaEntitySAO();
+	u8 getType() const
+		{return ACTIVEOBJECT_TYPE_LUAENTITY;}
+	virtual void addedToEnvironment(u16 id);
+	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
+			const std::string &data);
+	void step(float dtime, bool send_recommended);
+	std::string getClientInitializationData();
+	std::string getStaticData();
+private:
+	std::string m_init_name;
+	std::string m_init_state;
+	bool m_registered;
+};
+
 #endif
 
diff --git a/src/environment.cpp b/src/environment.cpp
index 80e9d5c78bc3eeadf3affabf6f57ff7a05588a77..d7a647ac841e927e10d1516a9d71592d617889ae 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "log.h"
 #include "profiler.h"
+#include "scriptapi.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -267,9 +268,9 @@ void ActiveBlockList::update(core::list<v3s16> &active_positions,
 	ServerEnvironment
 */
 
-ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
+ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L):
 	m_map(map),
-	m_server(server),
+	m_lua(L),
 	m_random_spawn_timer(3),
 	m_send_recommended_timer(0),
 	m_game_time(0),
@@ -674,6 +675,8 @@ void ServerEnvironment::clearAllObjects()
 			obj->m_removed = true;
 			continue;
 		}
+		// Deregister in scripting api
+		scriptapi_rm_object_reference(m_lua, obj);
 		// Delete active object
 		delete obj;
 		// Id to be removed from m_active_objects
@@ -1043,7 +1046,7 @@ void ServerEnvironment::step(float dtime)
 								n1.getContent() == CONTENT_AIR)
 						{
 							v3f pos = intToFloat(p1, BS);
-							ServerActiveObject *obj = new RatSAO(this, 0, pos);
+							ServerActiveObject *obj = new RatSAO(this, pos);
 							addActiveObject(obj);
 						}
 					}
@@ -1071,21 +1074,21 @@ void ServerEnvironment::step(float dtime)
 									Settings properties;
 									getMob_dungeon_master(properties);
 									ServerActiveObject *obj = new MobV2SAO(
-											this, 0, pos, &properties);
+											this, pos, &properties);
 									addActiveObject(obj);
 								} else if(i == 2 || i == 3){
 									actionstream<<"Rats spawn at "
 											<<PP(p1)<<std::endl;
 									for(int j=0; j<3; j++){
 										ServerActiveObject *obj = new RatSAO(
-												this, 0, pos);
+												this, pos);
 										addActiveObject(obj);
 									}
 								} else {
 									actionstream<<"An oerkki spawns at "
 											<<PP(p1)<<std::endl;
 									ServerActiveObject *obj = new Oerkki1SAO(
-											this, 0, pos);
+											this, pos);
 									addActiveObject(obj);
 								}
 							}
@@ -1228,18 +1231,18 @@ void ServerEnvironment::step(float dtime)
 			Create a ServerActiveObject
 		*/
 
-		//TestSAO *obj = new TestSAO(this, 0, pos);
-		//ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
-		//ServerActiveObject *obj = new RatSAO(this, 0, pos);
-		//ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
-		//ServerActiveObject *obj = new FireflySAO(this, 0, pos);
+		//TestSAO *obj = new TestSAO(this, pos);
+		//ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
+		//ServerActiveObject *obj = new RatSAO(this, pos);
+		//ServerActiveObject *obj = new Oerkki1SAO(this, pos);
+		//ServerActiveObject *obj = new FireflySAO(this, pos);
 
 		infostream<<"Server: Spawning MobV2SAO at "
 				<<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
 		
 		Settings properties;
 		getMob_dungeon_master(properties);
-		ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
+		ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
 		addActiveObject(obj);
 	}
 #endif
@@ -1493,6 +1496,11 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
 				<<"could not find block for storing id="<<object->getId()
 				<<" statically"<<std::endl;
 	}
+	
+	// Post-initialize object
+	object->addedToEnvironment(object->getId());
+	// Register reference in scripting api
+	scriptapi_add_object_reference(m_lua, object);
 
 	return object->getId();
 }
@@ -1544,6 +1552,9 @@ void ServerEnvironment::removeRemovedObjects()
 		if(obj->m_known_by_count > 0)
 			continue;
 		
+		// Deregister in scripting api
+		scriptapi_rm_object_reference(m_lua, obj);
+
 		// Delete
 		delete obj;
 		// Id to be removed from m_active_objects
@@ -1815,6 +1826,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 		verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
 				<<"object id="<<id<<" is not known by clients"
 				<<"; deleting"<<std::endl;
+
+		// Deregister in scripting api
+		scriptapi_rm_object_reference(m_lua, obj);
+
 		// Delete active object
 		delete obj;
 		// Id to be removed from m_active_objects
diff --git a/src/environment.h b/src/environment.h
index 5d2fe55515d785e83399199f49b624f2686c09fa..0e0a5510e960120d12fe5f8b6f9a33964fc6ba4f 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class Server;
 class ActiveBlockModifier;
 class ServerActiveObject;
+typedef struct lua_State lua_State;
 
 class Environment
 {
@@ -126,7 +127,7 @@ class ActiveBlockList
 class ServerEnvironment : public Environment
 {
 public:
-	ServerEnvironment(ServerMap *map, Server *server);
+	ServerEnvironment(ServerMap *map, lua_State *L);
 	~ServerEnvironment();
 
 	Map & getMap()
@@ -139,13 +140,11 @@ class ServerEnvironment : public Environment
 		return *m_map;
 	}
 
-	Server * getServer()
+	lua_State* getLua()
 	{
-		return m_server;
+		return m_lua;
 	}
 
-	void step(f32 dtime);
-	
 	/*
 		Save players
 	*/
@@ -222,7 +221,9 @@ class ServerEnvironment : public Environment
 	
 	// Clear all objects, loading and going through every MapBlock
 	void clearAllObjects();
-
+	
+	void step(f32 dtime);
+	
 private:
 
 	/*
@@ -269,8 +270,8 @@ class ServerEnvironment : public Environment
 	
 	// The map
 	ServerMap *m_map;
-	// Pointer to server (which is handling this environment)
-	Server *m_server;
+	// Lua state
+	lua_State *m_lua;
 	// Active object list
 	core::map<u16, ServerActiveObject*> m_active_objects;
 	// Outgoing network message buffer for active objects
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 92ef3b011f708c5fbb744d90355b3172093c9919..5d4a6e4089e09f967025e637c36ae5dcfe18960d 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -136,7 +136,7 @@ ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, u16 id, v3f
 		Create an ItemSAO
 	*/
 	// Create object
-	ServerActiveObject *obj = new ItemSAO(env, 0, pos, getItemString());
+	ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
 	return obj;
 }
 
@@ -174,7 +174,7 @@ video::ITexture * CraftItem::getImage() const
 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
 {
 	// Special cases
-	ServerActiveObject *obj = item_craft_create_object(m_subname, env, id, pos);
+	ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
 	if(obj)
 		return obj;
 	// Default
diff --git a/src/irrlichttypes.h b/src/irrlichttypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ab83bb7d4b14eea92ad5dc4a0dba072d08e688a
--- /dev/null
+++ b/src/irrlichttypes.h
@@ -0,0 +1,52 @@
+/*
+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 IRRLICHTTYPES_HEADER
+#define IRRLICHTTYPES_HEADER
+
+#include <irrTypes.h>
+#include <vector2d.h>
+#include <vector3d.h>
+#include <irrMap.h>
+#include <irrList.h>
+#include <irrArray.h>
+#include <aabbox3d.h>
+using namespace irr;
+typedef core::vector3df v3f;
+typedef core::vector3d<s16> v3s16;
+typedef core::vector3d<s32> v3s32;
+
+typedef core::vector2d<f32> v2f;
+typedef core::vector2d<s16> v2s16;
+typedef core::vector2d<s32> v2s32;
+typedef core::vector2d<u32> v2u32;
+typedef core::vector2d<f32> v2f32;
+
+#ifdef _MSC_VER
+	// Windows
+	typedef unsigned long long u64;
+#else
+	// Posix
+	#include <stdint.h>
+	typedef uint64_t u64;
+	//typedef unsigned long long u64;
+#endif
+
+#endif
+
diff --git a/src/porting.cpp b/src/porting.cpp
index 00595b06ca0c8ce0dd922c8f0283fbbb37f47cde..0adc955a94f77b28861a5bd8b4c00e118feeab09 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -56,9 +56,10 @@ void sigint_handler(int sig)
 		dstream<<DTIME<<"INFO: sigint_handler(): "
 				<<"Ctrl-C pressed, shutting down."<<std::endl;
 		
-		dstream<<DTIME<<"INFO: sigint_handler(): "
+		// Comment out for less clutter when testing scripts
+		/*dstream<<DTIME<<"INFO: sigint_handler(): "
 				<<"Printing debug stacks"<<std::endl;
-		debug_stacks_print();
+		debug_stacks_print();*/
 
 		g_killed = true;
 	}
@@ -91,9 +92,10 @@ void signal_handler_init(void)
 			{
 				dstream<<DTIME<<"INFO: event_handler(): "
 						<<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
-				dstream<<DTIME<<"INFO: event_handler(): "
+				// Comment out for less clutter when testing scripts
+				/*dstream<<DTIME<<"INFO: event_handler(): "
 						<<"Printing debug stacks"<<std::endl;
-				debug_stacks_print();
+				debug_stacks_print();*/
 
 				g_killed = true;
 			}
diff --git a/src/script.cpp b/src/script.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..edfc596b8f4a114f045c32565d323695fa9359c5
--- /dev/null
+++ b/src/script.cpp
@@ -0,0 +1,130 @@
+/*
+Minetest-c55
+Copyright (C) 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 "script.h"
+#include <cstdarg>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include "log.h"
+#include <iostream>
+
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+}
+
+void script_error(lua_State *L, const char *fmt, ...)
+{
+	va_list argp;
+	va_start(argp, fmt);
+	vfprintf(stderr, fmt, argp);
+	va_end(argp);
+	lua_close(L);
+	exit(EXIT_FAILURE);
+}
+
+void script_call_va(lua_State *L, const char *func, const char *sig, ...)
+{
+	va_list vl;
+	int narg, nres; /* number of arguments and results */
+
+	va_start(vl, sig);
+	lua_getglobal(L, func); /* push function */
+
+	for (narg = 0; *sig; narg++) {
+		/* repeat for each argument */
+		/* check stack space */
+		luaL_checkstack(L, 1, "too many arguments");
+		switch (*sig++) {
+		case 'd': /* double argument */
+			lua_pushnumber(L, va_arg(vl, double));
+			break;
+		case 'i': /* int argument */
+			lua_pushinteger(L, va_arg(vl, int));
+			break;
+		case 's': /* string argument */
+			lua_pushstring(L, va_arg(vl, char *));
+			break;
+		case '>': /* end of arguments */
+			goto endargs;
+		default:
+			script_error(L, "invalid option (%c)", *(sig - 1));
+		}
+	}
+endargs:
+
+	nres = strlen(sig); /* number of expected results */
+
+	if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
+		script_error(L, "error calling '%s': %s", func, lua_tostring(L, -1));
+	
+	nres = -nres; /* stack index of first result */
+	while (*sig) { /* repeat for each result */
+		switch (*sig++) {
+		case 'd': /* double result */
+			if (!lua_isnumber(L, nres))
+			script_error(L, "wrong result type");
+			*va_arg(vl, double *) = lua_tonumber(L, nres);
+			break;
+		case 'i': /* int result */
+			if (!lua_isnumber(L, nres))
+			script_error(L, "wrong result type");
+			*va_arg(vl, int *) = lua_tointeger(L, nres);
+			break;
+		case 's': /* string result */
+			if (!lua_isstring(L, nres))
+			script_error(L, "wrong result type");
+			*va_arg(vl, const char **) = lua_tostring(L, nres);
+			break;
+		default:
+			script_error(L, "invalid option (%c)", *(sig - 1));
+		}
+		nres++;
+	}
+
+	va_end(vl);
+}
+
+bool script_load(lua_State *L, const char *path)
+{
+	infostream<<"Loading and running script from "<<path<<std::endl;
+	int ret = luaL_loadfile(L, path) || lua_pcall(L, 0, 0, 0);
+	if(ret){
+		errorstream<<"Failed to load and run script from "<<path<<": "<<lua_tostring(L, -1)<<std::endl;
+		lua_pop(L, 1); // Pop error message from stack
+		return false;
+	}
+	return true;
+}
+
+lua_State* script_init()
+{
+	lua_State *L = luaL_newstate();
+	luaL_openlibs(L);
+	return L;
+}
+
+lua_State* script_deinit(lua_State *L)
+{
+	lua_close(L);
+}
+
+
diff --git a/src/script.h b/src/script.h
new file mode 100644
index 0000000000000000000000000000000000000000..82254744f76569bd4b9296681b55ab68403f572e
--- /dev/null
+++ b/src/script.h
@@ -0,0 +1,33 @@
+/*
+Minetest-c55
+Copyright (C) 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 SCRIPT_HEADER
+#define SCRIPT_HEADER
+
+typedef struct lua_State lua_State;
+//#include <string>
+
+lua_State* script_init();
+lua_State* script_deinit(lua_State *L);
+void script_error(lua_State *L, const char *fmt, ...);
+void script_call_va(lua_State *L, const char *func, const char *sig, ...);
+bool script_load(lua_State *L, const char *path);
+
+#endif
+
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fbe383075ed7494c97bbb8ed9efbf330586227cf
--- /dev/null
+++ b/src/scriptapi.cpp
@@ -0,0 +1,422 @@
+/*
+Minetest-c55
+Copyright (C) 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 "scriptapi.h"
+
+#include <iostream>
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+}
+
+#include "log.h"
+#include "server.h"
+#include "porting.h"
+#include "filesys.h"
+#include "serverobject.h"
+#include "script.h"
+//#include "luna.h"
+
+static void stackDump(lua_State *L, std::ostream &o)
+{
+  int i;
+  int top = lua_gettop(L);
+  for (i = 1; i <= top; i++) {  /* repeat for each level */
+	int t = lua_type(L, i);
+	switch (t) {
+
+	  case LUA_TSTRING:  /* strings */
+	  	o<<"\""<<lua_tostring(L, i)<<"\"";
+		break;
+
+	  case LUA_TBOOLEAN:  /* booleans */
+		o<<(lua_toboolean(L, i) ? "true" : "false");
+		break;
+
+	  case LUA_TNUMBER:  /* numbers */ {
+	  	char buf[10];
+		snprintf(buf, 10, "%g", lua_tonumber(L, i));
+		o<<buf;
+		break; }
+
+	  default:  /* other values */
+		o<<lua_typename(L, t);
+		break;
+
+	}
+	o<<" ";
+  }
+  o<<std::endl;
+}
+
+static void realitycheck(lua_State *L)
+{
+	int top = lua_gettop(L);
+	if(top >= 30){
+		dstream<<"Stack is over 30:"<<std::endl;
+		stackDump(L, dstream);
+		script_error(L, "Stack is over 30 (reality check)");
+	}
+}
+
+// Register new object prototype (must be based on entity)
+static int l_register_object(lua_State *L)
+{
+	const char *name = luaL_checkstring(L, 1);
+	luaL_checkany(L, 2);
+	infostream<<"register_object: "<<name<<std::endl;
+	// Get the minetest table
+	lua_getglobal(L, "minetest");
+	// Get field "registered_objects"
+	lua_getfield(L, -1, "registered_objects");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	// Object is in param 2
+	lua_pushvalue(L, 2); // Copy object to top of stack
+	lua_setfield(L, objectstable, name); // registered_objects[name] = object
+
+	return 0; /* number of results */
+}
+
+static int l_new_entity(lua_State *L)
+{
+	/* o = o or {}
+	   setmetatable(o, self)
+	   self.__index = self
+	   return o */
+	if(lua_isnil(L, -1))
+		lua_newtable(L);
+	luaL_checktype(L, -1, LUA_TTABLE);
+	luaL_getmetatable(L, "minetest.entity");
+	lua_pushvalue(L, -1); // duplicate metatable
+	lua_setfield(L, -2, "__index");
+	lua_setmetatable(L, -2);
+	// return table
+	return 1;
+}
+
+static const struct luaL_Reg minetest_f [] = {
+	{"register_object", l_register_object},
+	{"new_entity", l_new_entity},
+	{NULL, NULL}
+};
+
+static int l_entity_set_deleted(lua_State *L)
+{
+	return 0;
+}
+
+static const struct luaL_Reg minetest_entity_m [] = {
+	{"set_deleted", l_entity_set_deleted},
+	{NULL, NULL}
+};
+
+class ObjectRef
+{
+private:
+	ServerActiveObject *m_object;
+
+	static const char className[];
+	static const luaL_reg methods[];
+
+	static ObjectRef *checkobject(lua_State *L, int narg)
+	{
+		luaL_checktype(L, narg, LUA_TUSERDATA);
+		void *ud = luaL_checkudata(L, narg, className);
+		if(!ud) luaL_typerror(L, narg, className);
+		return *(ObjectRef**)ud;  // unbox pointer
+	}
+	
+	// Exported functions
+
+	static int l_remove(lua_State *L)
+	{
+		ObjectRef *o = checkobject(L, 1);
+		ServerActiveObject *co = o->m_object;
+		if(co == NULL) return 0;
+		infostream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
+		co->m_removed = true;
+		return 0;
+	}
+
+	static int gc_object(lua_State *L) {
+		//ObjectRef *o = checkobject(L, 1);
+		ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
+		//infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
+		delete o;
+		return 0;
+	}
+
+public:
+	ObjectRef(ServerActiveObject *object):
+		m_object(object)
+	{
+		infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl;
+	}
+
+	~ObjectRef()
+	{
+		if(m_object)
+			infostream<<"ObjectRef destructing for id="<<m_object->getId()<<std::endl;
+		else
+			infostream<<"ObjectRef destructing for id=unknown"<<std::endl;
+	}
+
+	// Creates an ObjectRef and leaves it on top of stack
+	// Not callable from Lua; all references are created on the C side.
+	static void create(lua_State *L, ServerActiveObject *object)
+	{
+		ObjectRef *o = new ObjectRef(object);
+		//infostream<<"ObjectRef::create: o="<<o<<std::endl;
+		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+		luaL_getmetatable(L, className);
+		lua_setmetatable(L, -2);
+	}
+
+	static void set_null(lua_State *L)
+	{
+		ObjectRef *o = checkobject(L, -1);
+		ServerActiveObject *co = o->m_object;
+		if(co == NULL)
+			return;
+		o->m_object = NULL;
+	}
+	
+	static void Register(lua_State *L)
+	{
+		lua_newtable(L);
+		int methodtable = lua_gettop(L);
+		luaL_newmetatable(L, className);
+		int metatable = lua_gettop(L);
+
+		lua_pushliteral(L, "__metatable");
+		lua_pushvalue(L, methodtable);
+		lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+		lua_pushliteral(L, "__index");
+		lua_pushvalue(L, methodtable);
+		lua_settable(L, metatable);
+
+		lua_pushliteral(L, "__gc");
+		lua_pushcfunction(L, gc_object);
+		lua_settable(L, metatable);
+
+		lua_pop(L, 1);  // drop metatable
+
+		luaL_openlib(L, 0, methods, 0);  // fill methodtable
+		lua_pop(L, 1);  // drop methodtable
+
+		// Cannot be created from Lua
+		//lua_register(L, className, create_object);
+	}
+};
+
+const char ObjectRef::className[] = "ObjectRef";
+
+#define method(class, name) {#name, class::l_##name}
+
+const luaL_reg ObjectRef::methods[] = {
+	method(ObjectRef, remove),
+	{0,0}
+};
+
+void scriptapi_export(lua_State *L, Server *server)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_export"<<std::endl;
+	
+	// Register global functions in table minetest
+	lua_newtable(L);
+	luaL_register(L, NULL, minetest_f);
+	lua_setglobal(L, "minetest");
+	
+	// Get the main minetest table
+	lua_getglobal(L, "minetest");
+
+	// Add registered_objects table in minetest
+	lua_newtable(L);
+	lua_setfield(L, -2, "registered_objects");
+
+	// Add object_refs table in minetest
+	lua_newtable(L);
+	lua_setfield(L, -2, "object_refs");
+
+	// Add luaentities table in minetest
+	lua_newtable(L);
+	lua_setfield(L, -2, "luaentities");
+
+	// Load and run some base Lua stuff
+	/*script_load(L, (porting::path_data + DIR_DELIM + "scripts"
+			+ DIR_DELIM + "base.lua").c_str());*/
+	
+	// Create entity reference metatable
+	luaL_newmetatable(L, "minetest.entity_reference");
+	lua_pop(L, 1);
+	
+	// Create entity prototype
+	luaL_newmetatable(L, "minetest.entity");
+	// metatable.__index = metatable
+	lua_pushvalue(L, -1); // Duplicate metatable
+	lua_setfield(L, -2, "__index");
+	// Put functions in metatable
+	luaL_register(L, NULL, minetest_entity_m);
+	// Put other stuff in metatable
+
+	// Entity C reference
+	ObjectRef::Register(L);
+}
+
+void scriptapi_luaentity_register(lua_State *L, u16 id, const char *name,
+		const char *init_state)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_luaentity_register: id="<<id<<std::endl;
+	
+	// Create object as a dummy string (TODO: Create properly)
+	lua_pushstring(L, "dummy object string");
+	int object = lua_gettop(L);
+
+	// Get minetest.luaentities table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	
+	// luaentities[id] = object
+	lua_pushnumber(L, id); // Push id
+	lua_pushvalue(L, object); // Copy object to top of stack
+	lua_settable(L, objectstable);
+	
+	lua_pop(L, 3); // pop luaentities, minetest and the object
+}
+
+void scriptapi_luaentity_deregister(lua_State *L, u16 id)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_luaentity_deregister: id="<<id<<std::endl;
+
+	// Get minetest.luaentities table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	
+	/*// Get luaentities[id]
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_gettable(L, objectstable);
+	// Object is at stack top
+	lua_pop(L, 1); // pop object*/
+
+	// Set luaentities[id] = nil
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_pushnil(L);
+	lua_settable(L, objectstable);
+	
+	lua_pop(L, 2); // pop luaentities, minetest
+}
+
+void scriptapi_luaentity_step(lua_State *L, u16 id,
+		float dtime, bool send_recommended)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+
+	// Get minetest.luaentities table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "luaentities");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	
+	// Get luaentities[id]
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_gettable(L, objectstable);
+
+	// TODO: Call step function
+
+	lua_pop(L, 1); // pop object
+	lua_pop(L, 2); // pop luaentities, minetest
+}
+
+std::string scriptapi_luaentity_get_state(lua_State *L, u16 id)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_luaentity_get_state: id="<<id<<std::endl;
+	
+	return "";
+}
+
+void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
+
+	// Create object on stack
+	ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
+	int object = lua_gettop(L);
+
+	// Get minetest.object_refs table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "object_refs");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	
+	// object_refs[id] = object
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_pushvalue(L, object); // Copy object to top of stack
+	lua_settable(L, objectstable);
+	
+	// pop object_refs, minetest and the object
+	lua_pop(L, 3);
+}
+
+void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
+
+	// Get minetest.object_refs table
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "object_refs");
+	luaL_checktype(L, -1, LUA_TTABLE);
+	int objectstable = lua_gettop(L);
+	
+	// Get object_refs[id]
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_gettable(L, objectstable);
+	// Set object reference to NULL
+	ObjectRef::set_null(L);
+	lua_pop(L, 1); // pop object
+
+	// Set object_refs[id] = nil
+	lua_pushnumber(L, cobj->getId()); // Push id
+	lua_pushnil(L);
+	lua_settable(L, objectstable);
+	
+	// pop object_refs, minetest
+	lua_pop(L, 2);
+}
+
diff --git a/src/scriptapi.h b/src/scriptapi.h
new file mode 100644
index 0000000000000000000000000000000000000000..a97d707a831d8cc884fd7d306feeb86f5849f303
--- /dev/null
+++ b/src/scriptapi.h
@@ -0,0 +1,43 @@
+/*
+Minetest-c55
+Copyright (C) 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 SCRIPTAPI_HEADER
+#define SCRIPTAPI_HEADER
+
+#include "irrlichttypes.h"
+#include <string>
+
+class Server;
+class ServerActiveObject;
+typedef struct lua_State lua_State;
+
+void scriptapi_export(lua_State *L, Server *server);
+	
+void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj);
+void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj);
+
+void scriptapi_luaentity_register(lua_State *L, u16 id, const char *name,
+		const char *init_state);
+void scriptapi_luaentity_deregister(lua_State *L, u16 id);
+void scriptapi_luaentity_step(lua_State *L, u16 id,
+		float dtime, bool send_recommended);
+std::string scriptapi_luaentity_get_state(lua_State *L, u16 id);
+
+#endif
+
diff --git a/src/server.cpp b/src/server.cpp
index e7cfc9afcfd363ede0e57db66d75e6ded7ef6df5..1c5d8d937b070c5943abf23e311cb7e9fc146106 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -39,6 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "log.h"
+#include "script.h"
+#include "scriptapi.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -185,7 +187,7 @@ void * EmergeThread::Thread()
 					<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
 					<<"only_from_disk="<<only_from_disk<<std::endl;
 		
-		ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
+		ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
 			
 		//core::map<v3s16, MapBlock*> changed_blocks;
 		//core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
@@ -250,7 +252,7 @@ void * EmergeThread::Thread()
 					MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
 					
 					// Activate objects and stuff
-					m_server->m_env.activateBlock(block, 3600);
+					m_server->m_env->activateBlock(block, 3600);
 				}
 			}
 			else
@@ -371,7 +373,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
 	//TimeTaker timer("RemoteClient::GetNextBlocks");
 	
-	Player *player = server->m_env.getPlayer(peer_id);
+	Player *player = server->m_env->getPlayer(peer_id);
 
 	assert(player != NULL);
 
@@ -572,7 +574,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 					MAP_BLOCKSIZE*p.Z);
 				
 				// Get ground height in nodes
-				s16 gh = server->m_env.getServerMap().findGroundLevel(
+				s16 gh = server->m_env->getServerMap().findGroundLevel(
 						p2d_nodes_center);
 
 				// If differs a lot, don't generate
@@ -611,7 +613,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			/*
 				Check if map has this block
 			*/
-			MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
+			MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
 			
 			bool surely_not_found_on_disk = false;
 			bool block_is_invalid = false;
@@ -640,7 +642,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 
 #if 0
 				v2s16 p2d(p.X, p.Z);
-				ServerMap *map = (ServerMap*)(&server->m_env.getMap());
+				ServerMap *map = (ServerMap*)(&server->m_env->getMap());
 				v2s16 chunkpos = map->sector_to_chunk(p2d);
 				if(map->chunkNonVolatile(chunkpos) == false)
 					block_is_invalid = true;
@@ -802,7 +804,7 @@ void RemoteClient::SendObjectData(
 	*/
 	
 	// Get connected players
-	core::list<Player*> players = server->m_env.getPlayers(true);
+	core::list<Player*> players = server->m_env->getPlayers(true);
 
 	// Write player count
 	u16 playercount = players.size();
@@ -948,10 +950,12 @@ Server::Server(
 		std::string mapsavedir,
 		std::string configpath
 	):
-	m_env(new ServerMap(mapsavedir), this),
+	m_env(NULL),
 	m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
 	m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
 	m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
+	m_lua(NULL),
+	//m_scriptapi(NULL),
 	m_thread(this),
 	m_emergethread(this),
 	m_time_counter(0),
@@ -973,20 +977,38 @@ Server::Server(
 	m_con_mutex.Init();
 	m_step_dtime_mutex.Init();
 	m_step_dtime = 0.0;
+
+	JMutexAutoLock envlock(m_env_mutex);
+	JMutexAutoLock conlock(m_con_mutex);
+	
+	// Initialize scripting
+	
+	infostream<<"Server: Initializing scripting"<<std::endl;
+	m_lua = script_init();
+	assert(m_lua);
+	// Export API
+	scriptapi_export(m_lua, this);
+	// Load and run scripts
+	script_load(m_lua, (porting::path_data + DIR_DELIM + "scripts"
+			+ DIR_DELIM + "default.lua").c_str());
+	
+	// Initialize Environment
+	
+	m_env = new ServerEnvironment(new ServerMap(mapsavedir), m_lua);
 	
 	// Register us to receive map edit events
-	m_env.getMap().addEventReceiver(this);
+	m_env->getMap().addEventReceiver(this);
 
 	// If file exists, load environment metadata
 	if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
 	{
 		infostream<<"Server: Loading environment metadata"<<std::endl;
-		m_env.loadMeta(m_mapsavedir);
+		m_env->loadMeta(m_mapsavedir);
 	}
 
 	// Load players
 	infostream<<"Server: Loading players"<<std::endl;
-	m_env.deSerializePlayers(m_mapsavedir);
+	m_env->deSerializePlayers(m_mapsavedir);
 }
 
 Server::~Server()
@@ -1029,13 +1051,13 @@ Server::~Server()
 			Save players
 		*/
 		infostream<<"Server: Saving players"<<std::endl;
-		m_env.serializePlayers(m_mapsavedir);
+		m_env->serializePlayers(m_mapsavedir);
 
 		/*
 			Save environment metadata
 		*/
 		infostream<<"Server: Saving environment metadata"<<std::endl;
-		m_env.saveMeta(m_mapsavedir);
+		m_env->saveMeta(m_mapsavedir);
 	}
 		
 	/*
@@ -1058,13 +1080,20 @@ Server::~Server()
 			{
 				u16 peer_id = i.getNode()->getKey();
 				JMutexAutoLock envlock(m_env_mutex);
-				m_env.removePlayer(peer_id);
+				m_env->removePlayer(peer_id);
 			}*/
 			
 			// Delete client
 			delete i.getNode()->getValue();
 		}
 	}
+
+	// Delete Environment
+	delete m_env;
+	
+	// Deinitialize scripting
+	infostream<<"Server: Deinitializing scripting"<<std::endl;
+	script_deinit(m_lua);
 }
 
 void Server::start(unsigned short port)
@@ -1173,7 +1202,7 @@ void Server::AsyncRunStep()
 		u32 units = (u32)(m_time_counter*speed);
 		m_time_counter -= (f32)units / speed;
 		
-		m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
+		m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
 		
 		//infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
 
@@ -1194,10 +1223,10 @@ void Server::AsyncRunStep()
 				i.atEnd() == false; i++)
 			{
 				RemoteClient *client = i.getNode()->getValue();
-				//Player *player = m_env.getPlayer(client->peer_id);
+				//Player *player = m_env->getPlayer(client->peer_id);
 				
 				SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-						m_env.getTimeOfDay());
+						m_env->getTimeOfDay());
 				// Send as reliable
 				m_con.Send(client->peer_id, 0, data, true);
 			}
@@ -1209,7 +1238,7 @@ void Server::AsyncRunStep()
 		// Step environment
 		ScopeProfiler sp(g_profiler, "SEnv step");
 		ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
-		m_env.step(dtime);
+		m_env->step(dtime);
 	}
 		
 	const float map_timer_and_unload_dtime = 5.15;
@@ -1218,7 +1247,7 @@ void Server::AsyncRunStep()
 		JMutexAutoLock lock(m_env_mutex);
 		// Run Map's timers and unload unused data
 		ScopeProfiler sp(g_profiler, "Server: map timer and unload");
-		m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
+		m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
 				g_settings->getFloat("server_unload_unused_data_timeout"));
 	}
 	
@@ -1239,13 +1268,13 @@ void Server::AsyncRunStep()
 		ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
 		core::map<v3s16, MapBlock*> modified_blocks;
-		m_env.getMap().transformLiquids(modified_blocks);
+		m_env->getMap().transformLiquids(modified_blocks);
 #if 0		
 		/*
 			Update lighting
 		*/
 		core::map<v3s16, MapBlock*> lighting_modified_blocks;
-		ServerMap &map = ((ServerMap&)m_env.getMap());
+		ServerMap &map = ((ServerMap&)m_env->getMap());
 		map.updateLighting(modified_blocks, lighting_modified_blocks);
 		
 		// Add blocks modified by lighting to modified_blocks
@@ -1295,7 +1324,7 @@ void Server::AsyncRunStep()
 			{
 				//u16 peer_id = i.getNode()->getKey();
 				RemoteClient *client = i.getNode()->getValue();
-				Player *player = m_env.getPlayer(client->peer_id);
+				Player *player = m_env->getPlayer(client->peer_id);
 				if(player==NULL)
 					continue;
 				infostream<<"* "<<player->getName()<<"\t";
@@ -1326,7 +1355,7 @@ void Server::AsyncRunStep()
 			i.atEnd() == false; i++)
 		{
 			RemoteClient *client = i.getNode()->getValue();
-			Player *player = m_env.getPlayer(client->peer_id);
+			Player *player = m_env->getPlayer(client->peer_id);
 			if(player==NULL)
 			{
 				// This can happen if the client timeouts somehow
@@ -1339,9 +1368,9 @@ void Server::AsyncRunStep()
 
 			core::map<u16, bool> removed_objects;
 			core::map<u16, bool> added_objects;
-			m_env.getRemovedActiveObjects(pos, radius,
+			m_env->getRemovedActiveObjects(pos, radius,
 					client->m_known_objects, removed_objects);
-			m_env.getAddedActiveObjects(pos, radius,
+			m_env->getAddedActiveObjects(pos, radius,
 					client->m_known_objects, added_objects);
 			
 			// Ignore if nothing happened
@@ -1364,7 +1393,7 @@ void Server::AsyncRunStep()
 			{
 				// Get object
 				u16 id = i.getNode()->getKey();
-				ServerActiveObject* obj = m_env.getActiveObject(id);
+				ServerActiveObject* obj = m_env->getActiveObject(id);
 
 				// Add to data buffer for sending
 				writeU16((u8*)buf, i.getNode()->getKey());
@@ -1386,7 +1415,7 @@ void Server::AsyncRunStep()
 			{
 				// Get object
 				u16 id = i.getNode()->getKey();
-				ServerActiveObject* obj = m_env.getActiveObject(id);
+				ServerActiveObject* obj = m_env->getActiveObject(id);
 				
 				// Get object type
 				u8 type = ACTIVEOBJECT_TYPE_INVALID;
@@ -1452,7 +1481,7 @@ void Server::AsyncRunStep()
 			}
 		}
 		
-		m_env.setKnownActiveObjects(whatever);
+		m_env->setKnownActiveObjects(whatever);
 #endif
 
 	}
@@ -1473,7 +1502,7 @@ void Server::AsyncRunStep()
 		// Get active object messages from environment
 		for(;;)
 		{
-			ActiveObjectMessage aom = m_env.getActiveObjectMessage();
+			ActiveObjectMessage aom = m_env->getActiveObjectMessage();
 			if(aom.id == 0)
 				break;
 			
@@ -1662,7 +1691,7 @@ void Server::AsyncRunStep()
 				{
 					v3s16 p = i.getNode()->getKey();
 					modified_blocks2.insert(p,
-							m_env.getMap().getBlockNoCreateNoEx(p));
+							m_env->getMap().getBlockNoCreateNoEx(p));
 				}
 				// Set blocks not sent
 				for(core::list<u16>::Iterator
@@ -1749,15 +1778,15 @@ void Server::AsyncRunStep()
 			JMutexAutoLock lock(m_env_mutex);
 
 			/*// Unload unused data (delete from memory)
-			m_env.getMap().unloadUnusedData(
+			m_env->getMap().unloadUnusedData(
 					g_settings->getFloat("server_unload_unused_sectors_timeout"));
 					*/
-			/*u32 deleted_count = m_env.getMap().unloadUnusedData(
+			/*u32 deleted_count = m_env->getMap().unloadUnusedData(
 					g_settings->getFloat("server_unload_unused_sectors_timeout"));
 					*/
 
 			// Save only changed parts
-			m_env.getMap().save(true);
+			m_env->getMap().save(true);
 
 			/*if(deleted_count > 0)
 			{
@@ -1766,10 +1795,10 @@ void Server::AsyncRunStep()
 			}*/
 
 			// Save players
-			m_env.serializePlayers(m_mapsavedir);
+			m_env->serializePlayers(m_mapsavedir);
 			
 			// Save environment metadata
-			m_env.saveMeta(m_mapsavedir);
+			m_env->saveMeta(m_mapsavedir);
 		}
 	}
 }
@@ -1811,7 +1840,7 @@ void Server::Receive()
 				<<" has apparently closed connection. "
 				<<"Removing player."<<std::endl;
 
-		m_env.removePlayer(peer_id);*/
+		m_env->removePlayer(peer_id);*/
 	}
 }
 
@@ -2026,7 +2055,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			writeU16(&reply[0], TOCLIENT_INIT);
 			writeU8(&reply[2], deployed);
 			writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
-			writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
+			writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
 			
 			// Send as reliable
 			m_con.Send(peer_id, 0, reply, true);
@@ -2063,7 +2092,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		// Send player items to all players
 		SendPlayerItems();
 
-		Player *player = m_env.getPlayer(peer_id);
+		Player *player = m_env->getPlayer(peer_id);
 
 		// Send HP
 		SendPlayerHP(player);
@@ -2071,7 +2100,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		// Send time of day
 		{
 			SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
-					m_env.getTimeOfDay());
+					m_env->getTimeOfDay());
 			m_con.Send(peer_id, 0, data, true);
 		}
 		
@@ -2081,7 +2110,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		// Send information about joining in chat
 		{
 			std::wstring name = L"unknown";
-			Player *player = m_env.getPlayer(peer_id);
+			Player *player = m_env->getPlayer(peer_id);
 			if(player != NULL)
 				name = narrow_to_wide(player->getName());
 			
@@ -2117,7 +2146,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				if(client->serialization_version == SER_FMT_VER_INVALID)
 					continue;
 				// Get player
-				Player *player = m_env.getPlayer(client->peer_id);
+				Player *player = m_env->getPlayer(client->peer_id);
 				if(!player)
 					continue;
 				// Get name of player
@@ -2139,7 +2168,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		return;
 	}
 	
-	Player *player = m_env.getPlayer(peer_id);
+	Player *player = m_env->getPlayer(peer_id);
 
 	if(player == NULL){
 		infostream<<"Server::ProcessData(): Cancelling: "
@@ -2247,7 +2276,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		u16 id = readS16(&data[3]);
 		u16 item_i = readU16(&data[5]);
 	
-		ServerActiveObject *obj = m_env.getActiveObject(id);
+		ServerActiveObject *obj = m_env->getActiveObject(id);
 
 		if(obj == NULL)
 		{
@@ -2421,7 +2450,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
 			try
 			{
-				MapNode n = m_env.getMap().getNode(p_under);
+				MapNode n = m_env->getMap().getNode(p_under);
 				// Get mineral
 				mineral = n.getMineral();
 				// Get material at position
@@ -2442,7 +2471,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				if(cannot_remove_node == false)
 				{
 					// Get node metadata
-					NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
+					NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
 					if(meta && meta->nodeRemovalDisabled() == true)
 					{
 						infostream<<"Server: Not finishing digging: "
@@ -2608,7 +2637,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			{
 				MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
-				m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+				m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
 			}
 			/*
 				Set blocks not sent to far players
@@ -2649,7 +2678,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			{
 				try{
 					// Don't add a node if this is not a free space
-					MapNode n2 = m_env.getMap().getNode(p_over);
+					MapNode n2 = m_env->getMap().getNode(p_over);
 					bool no_enough_privs =
 							((getPlayerPrivs(player) & PRIV_BUILD)==0);
 					if(no_enough_privs)
@@ -2750,7 +2779,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 					MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
 					std::string p_name = std::string(player->getName());
-					m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
+					m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
 				}
 				/*
 					Set blocks not sent to far players
@@ -2792,7 +2821,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 					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);
+				MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
 				if(block==NULL)
 				{
 					infostream<<"Error while placing object: "
@@ -2823,7 +2852,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				/*
 					Create the object
 				*/
-				ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
+				ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
 
 				if(obj == NULL)
 				{
@@ -2837,7 +2866,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 							<<" at "<<PP(p_over)<<std::endl;
 				
 					// Add the object to the environment
-					m_env.addActiveObject(obj);
+					m_env->addActiveObject(obj);
 					
 					infostream<<"Placed object"<<std::endl;
 
@@ -2924,7 +2953,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			text += (char)buf[0];
 		}
 
-		NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+		NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
 		if(!meta)
 			return;
 		if(meta->typeId() != CONTENT_SIGN_WALL)
@@ -2936,7 +2965,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				<<" at "<<PP(p)<<std::endl;
 				
 		v3s16 blockpos = getNodeBlockPos(p);
-		MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
+		MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
 		if(block)
 		{
 			block->setChangedFlag();
@@ -3052,7 +3081,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 						p.X = stoi(fn.next(","));
 						p.Y = stoi(fn.next(","));
 						p.Z = stoi(fn.next(","));
-						NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+						NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
 						if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
 							LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
 							if (lcm->getOwner() != player->getName())
@@ -3070,7 +3099,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 						p.X = stoi(fn.next(","));
 						p.Y = stoi(fn.next(","));
 						p.Z = stoi(fn.next(","));
-						NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+						NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
 						if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
 							LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
 							if (lcm->getOwner() != player->getName())
@@ -3153,7 +3182,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				str_split(message, L' '),
 				paramstring,
 				this,
-				&m_env,
+				m_env,
 				player,
 				privs);
 
@@ -3356,7 +3385,7 @@ Inventory* Server::getInventory(InventoryContext *c, std::string id)
 		p.X = stoi(fn.next(","));
 		p.Y = stoi(fn.next(","));
 		p.Z = stoi(fn.next(","));
-		NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+		NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
 		if(meta)
 			return meta->getInventory();
 		infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
@@ -3389,7 +3418,7 @@ void Server::inventoryModified(InventoryContext *c, std::string id)
 		p.Z = stoi(fn.next(","));
 		v3s16 blockpos = getNodeBlockPos(p);
 
-		NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
+		NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
 		if(meta)
 			meta->inventoryModified();
 
@@ -3415,7 +3444,7 @@ core::list<PlayerInfo> Server::getPlayerInfo()
 	
 	core::list<PlayerInfo> list;
 
-	core::list<Player*> players = m_env.getPlayers();
+	core::list<Player*> players = m_env->getPlayers();
 	
 	core::list<Player*>::Iterator i;
 	for(i = players.begin();
@@ -3559,7 +3588,7 @@ void Server::SendPlayerInfos()
 	//JMutexAutoLock envlock(m_env_mutex);
 	
 	// Get connected players
-	core::list<Player*> players = m_env.getPlayers(true);
+	core::list<Player*> players = m_env->getPlayers(true);
 	
 	u32 player_count = players.getSize();
 	u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
@@ -3593,7 +3622,7 @@ void Server::SendInventory(u16 peer_id)
 {
 	DSTACK(__FUNCTION_NAME);
 	
-	Player* player = m_env.getPlayer(peer_id);
+	Player* player = m_env->getPlayer(peer_id);
 	assert(player);
 
 	/*
@@ -3650,7 +3679,7 @@ void Server::SendPlayerItems()
 	DSTACK(__FUNCTION_NAME);
 
 	std::ostringstream os(std::ios_base::binary);
-	core::list<Player *> players = m_env.getPlayers(true);
+	core::list<Player *> players = m_env->getPlayers(true);
 
 	writeU16(os, TOCLIENT_PLAYERITEM);
 	writeU16(os, players.size());
@@ -3779,7 +3808,7 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
 		if(far_players)
 		{
 			// Get player
-			Player *player = m_env.getPlayer(client->peer_id);
+			Player *player = m_env->getPlayer(client->peer_id);
 			if(player)
 			{
 				// If player is far away, only set modified blocks not sent
@@ -3820,7 +3849,7 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
 		if(far_players)
 		{
 			// Get player
-			Player *player = m_env.getPlayer(client->peer_id);
+			Player *player = m_env->getPlayer(client->peer_id);
 			if(player)
 			{
 				// If player is far away, only set modified blocks not sent
@@ -3960,7 +3989,7 @@ void Server::SendBlocks(float dtime)
 		MapBlock *block = NULL;
 		try
 		{
-			block = m_env.getMap().getBlockNoCreate(q.pos);
+			block = m_env->getMap().getBlockNoCreate(q.pos);
 		}
 		catch(InvalidPositionException &e)
 		{
@@ -4019,7 +4048,7 @@ void Server::HandlePlayerHP(Player *player, s16 damage)
 
 void Server::RespawnPlayer(Player *player)
 {
-	v3f pos = findSpawnPos(m_env.getServerMap());
+	v3f pos = findSpawnPos(m_env->getServerMap());
 	player->setPosition(pos);
 	player->hp = 20;
 	SendMovePlayer(player);
@@ -4030,7 +4059,7 @@ void Server::UpdateCrafting(u16 peer_id)
 {
 	DSTACK(__FUNCTION_NAME);
 	
-	Player* player = m_env.getPlayer(peer_id);
+	Player* player = m_env->getPlayer(peer_id);
 	assert(player);
 
 	/*
@@ -4096,7 +4125,7 @@ std::wstring Server::getStatusString()
 		if(client->serialization_version == SER_FMT_VER_INVALID)
 			continue;
 		// Get player
-		Player *player = m_env.getPlayer(client->peer_id);
+		Player *player = m_env->getPlayer(client->peer_id);
 		// Get name of player
 		std::wstring name = L"unknown";
 		if(player != NULL)
@@ -4105,7 +4134,7 @@ std::wstring Server::getStatusString()
 		os<<name<<L",";
 	}
 	os<<L"}";
-	if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
+	if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
 		os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
 	if(g_settings->get("motd") != "")
 		os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
@@ -4121,7 +4150,7 @@ void Server::saveConfig()
 
 void Server::notifyPlayer(const char *name, const std::wstring msg)
 {
-	Player *player = m_env.getPlayer(name);
+	Player *player = m_env->getPlayer(name);
 	if(!player)
 		return;
 	SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
@@ -4200,7 +4229,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
 	/*
 		Try to get an existing player
 	*/
-	Player *player = m_env.getPlayer(name);
+	Player *player = m_env->getPlayer(name);
 	if(player != NULL)
 	{
 		// If player is already connected, cancel
@@ -4230,7 +4259,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
 	/*
 		If player with the wanted peer_id already exists, cancel.
 	*/
-	if(m_env.getPlayer(peer_id) != NULL)
+	if(m_env->getPlayer(peer_id) != NULL)
 	{
 		infostream<<"emergePlayer(): Player with wrong name but same"
 				" peer_id already exists"<<std::endl;
@@ -4258,7 +4287,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
 		infostream<<"Server: Finding spawn place for player \""
 				<<player->getName()<<"\""<<std::endl;
 
-		v3f pos = findSpawnPos(m_env.getServerMap());
+		v3f pos = findSpawnPos(m_env->getServerMap());
 
 		player->setPosition(pos);
 
@@ -4266,7 +4295,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
 			Add player to environment
 		*/
 
-		m_env.addPlayer(player);
+		m_env->addPlayer(player);
 
 		/*
 			Add stuff to inventory
@@ -4337,7 +4366,7 @@ void Server::handlePeerChange(PeerChange &c)
 		{
 			// Get object
 			u16 id = i.getNode()->getKey();
-			ServerActiveObject* obj = m_env.getActiveObject(id);
+			ServerActiveObject* obj = m_env->getActiveObject(id);
 			
 			if(obj && obj->m_known_by_count > 0)
 				obj->m_known_by_count--;
@@ -4346,7 +4375,7 @@ void Server::handlePeerChange(PeerChange &c)
 		// Collect information about leaving in chat
 		std::wstring message;
 		{
-			Player *player = m_env.getPlayer(c.peer_id);
+			Player *player = m_env->getPlayer(c.peer_id);
 			if(player != NULL)
 			{
 				std::wstring name = narrow_to_wide(player->getName());
@@ -4360,12 +4389,12 @@ void Server::handlePeerChange(PeerChange &c)
 
 		/*// Delete player
 		{
-			m_env.removePlayer(c.peer_id);
+			m_env->removePlayer(c.peer_id);
 		}*/
 
 		// Set player client disconnected
 		{
-			Player *player = m_env.getPlayer(c.peer_id);
+			Player *player = m_env->getPlayer(c.peer_id);
 			if(player != NULL)
 				player->peer_id = 0;
 			
@@ -4384,7 +4413,7 @@ void Server::handlePeerChange(PeerChange &c)
 					if(client->serialization_version == SER_FMT_VER_INVALID)
 						continue;
 					// Get player
-					Player *player = m_env.getPlayer(client->peer_id);
+					Player *player = m_env->getPlayer(client->peer_id);
 					if(!player)
 						continue;
 					// Get name of player
diff --git a/src/server.h b/src/server.h
index b238bec26e86029b191c03101ce776808d4fb6f0..ab8c31a00a0a57186692d91eba573269d64874c1 100644
--- a/src/server.h
+++ b/src/server.h
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventory.h"
 #include "auth.h"
 #include "ban.h"
+struct LuaState;
+typedef struct lua_State lua_State;
 
 /*
 	Some random functions
@@ -391,7 +393,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	// Environment must be locked when called
 	void setTimeOfDay(u32 time)
 	{
-		m_env.setTimeOfDay(time);
+		m_env->setTimeOfDay(time);
 		m_time_of_day_send_timer = 0;
 	}
 
@@ -476,6 +478,9 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	// Envlock and conlock should be locked when calling this
 	void notifyPlayer(const char *name, const std::wstring msg);
 	void notifyPlayers(const std::wstring msg);
+	
+	// Envlock and conlock should be locked when using Lua
+	lua_State *getLua(){ return m_lua; }
 
 private:
 
@@ -543,7 +548,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	// When called, environment mutex should be locked
 	std::string getPlayerName(u16 peer_id)
 	{
-		Player *player = m_env.getPlayer(peer_id);
+		Player *player = m_env->getPlayer(peer_id);
 		if(player == NULL)
 			return "[id="+itos(peer_id);
 		return player->getName();
@@ -582,7 +587,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	// environment shall be locked first.
 
 	// Environment
-	ServerEnvironment m_env;
+	ServerEnvironment *m_env;
 	JMutex m_env_mutex;
 	
 	// Connection
@@ -596,6 +601,10 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 
 	// Bann checking
 	BanManager m_banmanager;
+
+	// Scripting
+	// Envlock and conlock should be locked when using Lua
+	lua_State *m_lua;
 	
 	/*
 		Threads
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index ce19ea34f10a85d7b46aa1a8038e0dde24b7d777..2a007bda5a7d8e0795cae4524cba6fbbacd01d6a 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -21,8 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <fstream>
 #include "inventory.h"
 
-ServerActiveObject::ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos):
-	ActiveObject(id),
+ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos):
+	ActiveObject(0),
 	m_known_by_count(0),
 	m_removed(false),
 	m_pending_deactivation(false),
@@ -37,6 +37,11 @@ ServerActiveObject::~ServerActiveObject()
 {
 }
 
+void ServerActiveObject::addedToEnvironment(u16 id)
+{
+	setId(id);
+}
+
 ServerActiveObject* ServerActiveObject::create(u8 type,
 		ServerEnvironment *env, u16 id, v3f pos,
 		const std::string &data)
@@ -53,7 +58,7 @@ ServerActiveObject* ServerActiveObject::create(u8 type,
 	}
 
 	Factory f = n->getValue();
-	ServerActiveObject *object = (*f)(env, id, pos, data);
+	ServerActiveObject *object = (*f)(env, pos, data);
 	return object;
 }
 
diff --git a/src/serverobject.h b/src/serverobject.h
index cbb50949cf8a4ff0064098fd03cbed5d98d47245..8d6bfd6e8b5982264e0056b200c9c758fd10c273 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -51,9 +51,11 @@ class ServerActiveObject : public ActiveObject
 		NOTE: m_env can be NULL, but step() isn't called if it is.
 		Prototypes are used that way.
 	*/
-	ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos);
+	ServerActiveObject(ServerEnvironment *env, v3f pos);
 	virtual ~ServerActiveObject();
 
+	virtual void addedToEnvironment(u16 id);
+	
 	// Create a certain type of ServerActiveObject
 	static ServerActiveObject* create(u8 type,
 			ServerEnvironment *env, u16 id, v3f pos,
@@ -160,7 +162,7 @@ class ServerActiveObject : public ActiveObject
 protected:
 	// Used for creating objects based on type
 	typedef ServerActiveObject* (*Factory)
-			(ServerEnvironment *env, u16 id, v3f pos,
+			(ServerEnvironment *env, v3f pos,
 			const std::string &data);
 	static void registerType(u16 type, Factory f);