diff --git a/data/scripts/default.lua b/data/scripts/default.lua
index 3ec6f84332e6bca72cf10f4adc0565eeff069cbf..dcc58097bc7ae1093a9873e71554111a79997e70 100644
--- a/data/scripts/default.lua
+++ b/data/scripts/default.lua
@@ -162,6 +162,8 @@ end
 -- Called when object is punched
 function TNT:on_punch(hitter)
 	print("TNT:on_punch()")
+	self.object:remove()
+	hitter:add_to_inventory("CraftItem testobject1 1")
 end
 
 -- Called when object is right-clicked
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index 0abeb0ef047e30675d235f44c72ea96d16681d51..eeb17bd3090975e5ced882359c144dad7f768275 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -237,14 +237,24 @@ InventoryItem * ItemSAO::createInventoryItem()
 	}
 }
 
-void ItemSAO::rightClick(Player *player)
+void ItemSAO::punch(ServerActiveObject *puncher)
+{
+	InventoryItem *item = createInventoryItem();
+	bool fits = puncher->addToInventory(item);
+	if(fits)
+		m_removed = true;
+	else
+		delete item;
+}
+
+void ItemSAO::rightClick(ServerActiveObject *clicker)
 {
 	infostream<<__FUNCTION_NAME<<std::endl;
 	InventoryItem *item = createInventoryItem();
 	if(item == NULL)
 		return;
 	
-	bool to_be_deleted = item->use(m_env, player);
+	bool to_be_deleted = item->use(m_env, clicker);
 
 	if(to_be_deleted)
 		m_removed = true;
@@ -252,7 +262,7 @@ void ItemSAO::rightClick(Player *player)
 		// Reflect changes to the item here
 		m_inventorystring = item->getItemString();
 	
-	delete item;
+	delete item; // Delete temporary item
 }
 
 /*
@@ -435,11 +445,15 @@ std::string RatSAO::getStaticData()
 	return os.str();
 }
 
-InventoryItem* RatSAO::createPickedUpItem()
+void RatSAO::punch(ServerActiveObject *puncher)
 {
 	std::istringstream is("CraftItem rat 1", std::ios_base::binary);
 	InventoryItem *item = InventoryItem::deSerialize(is);
-	return item;
+	bool fits = puncher->addToInventory(item);
+	if(fits)
+		m_removed = true;
+	else
+		delete item;
 }
 
 /*
@@ -684,9 +698,17 @@ std::string Oerkki1SAO::getStaticData()
 	return os.str();
 }
 
-u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir,
-		const std::string &playername)
+void Oerkki1SAO::punch(ServerActiveObject *puncher)
 {
+	v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
+
+	std::string toolname = "";
+	InventoryItem *item = puncher->getWieldedItem();
+	if(item && (std::string)item->getName() == "ToolItem"){
+		ToolItem *titem = (ToolItem*)item;
+		toolname = titem->getToolName();
+	}
+
 	m_speed_f += dir*12*BS;
 
 	u16 amount = 5;
@@ -704,7 +726,8 @@ u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir,
 	if(toolname == "SteelPick")
 		amount = 7;
 	doDamage(amount);
-	return 65536/100;
+	
+	puncher->damageWieldedItem(65536/100);
 }
 
 void Oerkki1SAO::doDamage(u16 d)
@@ -1351,10 +1374,20 @@ void MobV2SAO::step(float dtime, bool send_recommended)
 	}
 }
 
-u16 MobV2SAO::punch(const std::string &toolname, v3f dir,
-		const std::string &playername)
+void MobV2SAO::punch(ServerActiveObject *puncher)
 {
-	assert(m_env);
+	v3f dir = (getBasePosition() - puncher->getBasePosition()).normalize();
+
+	std::string toolname = "";
+	InventoryItem *item = puncher->getWieldedItem();
+	if(item && (std::string)item->getName() == "ToolItem"){
+		ToolItem *titem = (ToolItem*)item;
+		toolname = titem->getToolName();
+	}
+	
+	// A quick hack; SAO description is player name for player
+	std::string playername = puncher->getDescription();
+
 	Map *map = &m_env->getMap();
 	
 	actionstream<<playername<<" punches mob id="<<m_id
@@ -1396,7 +1429,8 @@ u16 MobV2SAO::punch(const std::string &toolname, v3f dir,
 	if(toolname == "SteelPick")
 		amount = 3;
 	doDamage(amount);
-	return 65536/100;
+	
+	puncher->damageWieldedItem(65536/100);
 }
 
 bool MobV2SAO::isPeaceful()
@@ -1629,18 +1663,20 @@ InventoryItem* LuaEntitySAO::createPickedUpItem()
 	return item;
 }
 
-u16 LuaEntitySAO::punch(const std::string &toolname, v3f dir,
-		const std::string &playername)
+void LuaEntitySAO::punch(ServerActiveObject *puncher)
 {
-	return 0;
+	if(!m_registered)
+		return;
+	lua_State *L = m_env->getLua();
+	scriptapi_luaentity_punch(L, m_id, puncher);
 }
 
-void LuaEntitySAO::rightClick(Player *player)
+void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
 {
 	if(!m_registered)
 		return;
 	lua_State *L = m_env->getLua();
-	scriptapi_luaentity_rightclick_player(L, m_id, player->getName());
+	scriptapi_luaentity_rightclick(L, m_id, clicker);
 }
 
 void LuaEntitySAO::setPos(v3f pos)
diff --git a/src/content_sao.h b/src/content_sao.h
index 1d72392324bad3d3b21427569275011556576807..51911fe052fa1be9ca42b5c4524199d013dfecc9 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -50,9 +50,8 @@ class ItemSAO : public ServerActiveObject
 	std::string getClientInitializationData();
 	std::string getStaticData();
 	InventoryItem* createInventoryItem();
-	InventoryItem* createPickedUpItem(){return createInventoryItem();}
-	void rightClick(Player *player);
-
+	void punch(ServerActiveObject *puncher);
+	void rightClick(ServerActiveObject *clicker);
 	float getMinimumSavedMovement(){ return 0.1*BS; }
 private:
 	std::string m_inventorystring;
@@ -72,7 +71,7 @@ class RatSAO : public ServerActiveObject
 	void step(float dtime, bool send_recommended);
 	std::string getClientInitializationData();
 	std::string getStaticData();
-	InventoryItem* createPickedUpItem();
+	void punch(ServerActiveObject *puncher);
 private:
 	bool m_is_active;
 	IntervalLimiter m_inactive_interval;
@@ -98,8 +97,7 @@ class Oerkki1SAO : public ServerActiveObject
 	std::string getClientInitializationData();
 	std::string getStaticData();
 	InventoryItem* createPickedUpItem(){return NULL;}
-	u16 punch(const std::string &toolname, v3f dir,
-			const std::string &playername);
+	void punch(ServerActiveObject *puncher);
 	bool isPeaceful(){return false;}
 private:
 	void doDamage(u16 d);
@@ -159,8 +157,7 @@ class MobV2SAO : public ServerActiveObject
 	std::string getClientInitializationData();
 	void step(float dtime, bool send_recommended);
 	InventoryItem* createPickedUpItem(){return NULL;}
-	u16 punch(const std::string &toolname, v3f dir,
-			const std::string &playername);
+	void punch(ServerActiveObject *puncher);
 	bool isPeaceful();
 private:
 	void sendPosition();
@@ -214,10 +211,8 @@ class LuaEntitySAO : public ServerActiveObject
 	std::string getClientInitializationData();
 	std::string getStaticData();
 	InventoryItem* createPickedUpItem();
-	u16 punch(const std::string &toolname, v3f dir,
-			const std::string &playername);
-	void rightClick(Player *player);
-
+	void punch(ServerActiveObject *puncher);
+	void rightClick(ServerActiveObject *clicker);
 	void setPos(v3f pos);
 	void moveTo(v3f pos, bool continuous);
 	float getMinimumSavedMovement();
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 45646a69a40791b73e683ad319e917baa5f21954..c9ba9b4e58901512740c66a977f9c80ad0b52f46 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -205,22 +205,23 @@ InventoryItem *CraftItem::createCookResult() const
 	return item_craft_create_cook_result(m_subname);
 }
 
-bool CraftItem::use(ServerEnvironment *env, Player *player)
+bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
 {
-	if(item_craft_is_eatable(m_subname))
-	{
-		u16 result_count = getCount() - 1; // Eat one at a time
-		s16 hp_change = item_craft_eat_hp_change(m_subname);
-		if(player->hp + hp_change > 20)
-			player->hp = 20;
-		else
-			player->hp += hp_change;
+	if(!item_craft_is_eatable(m_subname))
+		return false;
+	
+	u16 result_count = getCount() - 1; // Eat one at a time
+	s16 hp_change = item_craft_eat_hp_change(m_subname);
+	s16 hp = user->getHP();
+	hp += hp_change;
+	if(hp < 0)
+		hp = 0;
+	user->setHP(hp);
+	
+	if(result_count < 1)
+		return true;
 		
-		if(result_count < 1)
-			return true;
-		else
-			setCount(result_count);
-	}
+	setCount(result_count);
 	return false;
 }
 
diff --git a/src/inventory.h b/src/inventory.h
index 490cab73ef9eb239ae43241cbdf0954c998b0ea6..dc5b04ff7cc4bfa9053e2427b379fc842b2abc9f 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -36,7 +36,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class ServerActiveObject;
 class ServerEnvironment;
-class Player;
 
 class InventoryItem
 {
@@ -117,7 +116,7 @@ class InventoryItem
 	// Called when item is right-clicked when lying on ground.
 	// If returns true, item shall be deleted.
 	virtual bool use(ServerEnvironment *env,
-			Player *player){return false;}
+			ServerActiveObject *user){return false;}
 
 protected:
 	u16 m_count;
@@ -263,7 +262,7 @@ class CraftItem : public InventoryItem
 	bool isCookable() const;
 	InventoryItem *createCookResult() const;
 
-	bool use(ServerEnvironment *env, Player *player);
+	bool use(ServerEnvironment *env, ServerActiveObject *user);
 	
 	/*
 		Special methods
diff --git a/src/player.cpp b/src/player.cpp
index 396ce2494537224a794ab32e4cb3f50603ccfdee..f0a395f498e6233d77fd09249c61e83aae667871 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -164,6 +164,70 @@ void Player::deSerialize(std::istream &is)
 	inventory.deSerialize(is);
 }
 
+/*
+	ServerRemotePlayer
+*/
+
+/* ServerActiveObject interface */
+
+InventoryItem* ServerRemotePlayer::getWieldedItem()
+{
+	InventoryList *list = inventory.getList("main");
+	if (list)
+		return list->getItem(m_selected_item);
+	return NULL;
+}
+void ServerRemotePlayer::damageWieldedItem(u16 amount)
+{
+	infostream<<"Damaging "<<getName()<<"'s wielded item for amount="
+			<<amount<<std::endl;
+	InventoryList *list = inventory.getList("main");
+	if(!list)
+		return;
+	InventoryItem *item = list->getItem(m_selected_item);
+	if(item && (std::string)item->getName() == "ToolItem"){
+		ToolItem *titem = (ToolItem*)item;
+		bool weared_out = titem->addWear(amount);
+		if(weared_out)
+			list->deleteItem(m_selected_item);
+	}
+}
+bool ServerRemotePlayer::addToInventory(InventoryItem *item)
+{
+	infostream<<"Adding "<<item->getName()<<" into "<<getName()
+			<<"'s inventory"<<std::endl;
+	
+	InventoryList *ilist = inventory.getList("main");
+	if(ilist == NULL)
+		return false;
+	
+	// In creative mode, just delete the item
+	if(g_settings->getBool("creative_mode")){
+		return false;
+	}
+
+	// Skip if inventory has no free space
+	if(ilist->roomForItem(item) == false)
+	{
+		infostream<<"Player inventory has no free space"<<std::endl;
+		return false;
+	}
+
+	// Add to inventory
+	InventoryItem *leftover = ilist->addItem(item);
+	assert(!leftover);
+
+	return true;
+}
+void ServerRemotePlayer::setHP(s16 hp_)
+{
+	hp = hp_;
+}
+s16 ServerRemotePlayer::getHP()
+{
+	return hp;
+}
+
 /*
 	RemotePlayer
 */
diff --git a/src/player.h b/src/player.h
index c3be0789415ce8b9009d75d96d3a91a3eba8f9e8..e6dfb81991450569753ae626942243dac677e388 100644
--- a/src/player.h
+++ b/src/player.h
@@ -215,12 +215,20 @@ class ServerRemotePlayer : public Player, public ServerActiveObject
 		Player::setPosition(position);
 		ServerActiveObject::setBasePosition(position);
 	}
+	
+	/* ServerActiveObject interface */
 
-	/*
-		ServerActiveObject interface
-	*/
 	u8 getType() const
 		{return ACTIVEOBJECT_TYPE_PLAYER;}
+	virtual std::string getDescription(){return getName();}
+	// Returns a reference
+	virtual InventoryItem* getWieldedItem();
+	virtual void damageWieldedItem(u16 amount);
+	// If all fits, eats item and returns true. Otherwise returns false.
+	virtual bool addToInventory(InventoryItem *item);
+	virtual void setHP(s16 hp_);
+	virtual s16 getHP();
+
 private:
 };
 
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index f8875c0e36d53e41eb149477d6fa126499013c3b..530c1719e10eb0b6d053a40103e0fe6a5f811107 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -368,6 +368,14 @@ class ObjectRef
 	
 	// Exported functions
 	
+	// garbage collector
+	static int gc_object(lua_State *L) {
+		ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
+		//infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
+		delete o;
+		return 0;
+	}
+
 	// remove(self)
 	static int l_remove(lua_State *L)
 	{
@@ -427,12 +435,25 @@ class ObjectRef
 		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;
+	// add_to_inventory(self, itemstring)
+	// returns: true if item was added, false otherwise
+	static int l_add_to_inventory(lua_State *L)
+	{
+		ObjectRef *ref = checkobject(L, 1);
+		luaL_checkstring(L, 2);
+		ServerActiveObject *co = getobject(ref);
+		if(co == NULL) return 0;
+		// itemstring
+		const char *itemstring = lua_tostring(L, 2);
+		infostream<<"ObjectRef::l_add_to_inventory(): id="<<co->getId()
+				<<" itemstring=\""<<itemstring<<"\""<<std::endl;
+		// Do it
+		std::istringstream is(itemstring, std::ios::binary);
+		InventoryItem *item = InventoryItem::deSerialize(is);
+		bool fits = co->addToInventory(item);
+		// Return
+		lua_pushboolean(L, fits);
+		return 1;
 	}
 
 public:
@@ -502,9 +523,20 @@ const luaL_reg ObjectRef::methods[] = {
 	method(ObjectRef, getpos),
 	method(ObjectRef, setpos),
 	method(ObjectRef, moveto),
+	method(ObjectRef, add_to_inventory),
 	{0,0}
 };
 
+// Creates a new anonymous reference if id=0
+static void objectref_get_or_create(lua_State *L, ServerActiveObject *cobj)
+{
+	if(cobj->getId() == 0){
+		ObjectRef::create(L, cobj);
+	} else {
+		objectref_get(L, cobj->getId());
+	}
+}
+
 /*
 	Main export function
 */
@@ -570,6 +602,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
 	lua_setfield(L, -2, "env");
 }
 
+#if 0
 // Dump stack top with the dump2 function
 static void dump2(lua_State *L, const char *name)
 {
@@ -581,6 +614,7 @@ static void dump2(lua_State *L, const char *name)
 	if(lua_pcall(L, 2, 0, 0))
 		script_error(L, "error: %s\n", lua_tostring(L, -1));
 }
+#endif
 
 /*
 	object_reference
@@ -815,8 +849,9 @@ void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
 		script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
 }
 
-void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
-		const char *playername)
+// Calls entity:on_punch(ObjectRef puncher)
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+		ServerActiveObject *puncher)
 {
 	realitycheck(L);
 	assert(lua_checkstack(L, 20));
@@ -827,12 +862,36 @@ void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
 	luaentity_get(L, id);
 	int object = lua_gettop(L);
 	// State: object is at top of stack
-	// Get step function
+	// Get function
+	lua_getfield(L, -1, "on_punch");
+	luaL_checktype(L, -1, LUA_TFUNCTION);
+	lua_pushvalue(L, object); // self
+	objectref_get_or_create(L, puncher); // Clicker reference
+	// Call with 2 arguments, 0 results
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_rightclick(ObjectRef clicker)
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+		ServerActiveObject *clicker)
+{
+	realitycheck(L);
+	assert(lua_checkstack(L, 20));
+	//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+	StackUnroller stack_unroller(L);
+
+	// Get minetest.luaentities[id]
+	luaentity_get(L, id);
+	int object = lua_gettop(L);
+	// State: object is at top of stack
+	// Get function
 	lua_getfield(L, -1, "on_rightclick");
 	luaL_checktype(L, -1, LUA_TFUNCTION);
 	lua_pushvalue(L, object); // self
-	// Call with 1 arguments, 0 results
-	if(lua_pcall(L, 1, 0, 0))
-		script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
+	objectref_get_or_create(L, clicker); // Clicker reference
+	// Call with 2 arguments, 0 results
+	if(lua_pcall(L, 2, 0, 0))
+		script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1));
 }
 
diff --git a/src/scriptapi.h b/src/scriptapi.h
index 2a5eb25a96458490310ef21557ed017ac522cfdf..458f90acbb2cc35ec331b8062353de52f59ee227 100644
--- a/src/scriptapi.h
+++ b/src/scriptapi.h
@@ -42,8 +42,10 @@ std::string scriptapi_luaentity_get_state(lua_State *L, u16 id);
 void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
 		LuaEntityProperties *prop);
 void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
-void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
-		const char *playername);
+void scriptapi_luaentity_punch(lua_State *L, u16 id,
+		ServerActiveObject *clicker);
+void scriptapi_luaentity_rightclick(lua_State *L, u16 id,
+		ServerActiveObject *clicker);
 
 #endif
 
diff --git a/src/server.cpp b/src/server.cpp
index 330ce21c2c912cdeccd8e6e8c78d74f7df6c3538..08a26adc6165981f97c4438577c72e38e1c1347f 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -2299,10 +2299,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			return;
 		
 		//TODO: Check that object is reasonably close
+	
+		// Get ServerRemotePlayer
+		ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+
+		// Update wielded item
+		srp->wieldItem(item_i);
 		
-		// Left click, pick object up (usually)
+		// Left click, pick/punch
 		if(button == 0)
 		{
+			actionstream<<player->getName()<<" punches object "
+					<<obj->getId()<<std::endl;
+			
+			// Do stuff
+			obj->punch(srp);
+			
+#if 0
 			/*
 				Try creating inventory item
 			*/
@@ -2371,6 +2384,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 					SendInventory(player->peer_id);
 				}
 			}
+#endif
 		}
 		// Right click, do something with object
 		if(button == 1)
@@ -2378,18 +2392,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 			actionstream<<player->getName()<<" right clicks object "
 					<<obj->getId()<<std::endl;
 
-			// Track hp changes super-crappily
-			u16 oldhp = player->hp;
-			
 			// Do stuff
-			obj->rightClick(player);
-			
-			// Send back stuff
-			if(player->hp != oldhp)
-			{
-				SendPlayerHP(player);
-			}
+			obj->rightClick(srp);
 		}
+
+		/*
+			Update player state to client
+		*/
+		SendPlayerHP(player);
+		UpdateCrafting(player->peer_id);
+		SendInventory(player->peer_id);
 	}
 	else if(command == TOSERVER_GROUND_ACTION)
 	{
diff --git a/src/serverobject.h b/src/serverobject.h
index 66118cca021460985eb146e8702f4e4b51f9200d..7e767188a9f688436755aa21140cf0a65949597c 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -71,6 +71,7 @@ class ServerActiveObject : public ActiveObject
 	/*
 		Some more dynamic interface
 	*/
+	
 	virtual void setPos(v3f pos)
 		{ setBasePosition(pos); }
 	// continuous: if true, object does not stop immediately at pos
@@ -80,7 +81,11 @@ class ServerActiveObject : public ActiveObject
 	// saving to disk may be omitted
 	virtual float getMinimumSavedMovement()
 		{ return 2.0*BS; }
+	
+	virtual bool isPeaceful(){return true;}
 
+	virtual std::string getDescription(){return "SAO";}
+	
 	/*
 		Step object in time.
 		Messages added to messages are sent to client over network.
@@ -106,26 +111,22 @@ class ServerActiveObject : public ActiveObject
 	*/
 	virtual std::string getStaticData(){return "";}
 	
-	/*
-		Item that the player gets when this object is picked up.
-		If NULL, object cannot be picked up.
-	*/
-	virtual InventoryItem* createPickedUpItem(){return NULL;}
+	virtual void punch(ServerActiveObject *puncher){}
+	virtual void rightClick(ServerActiveObject *clicker){}
 	
-	/*
-		If the object doesn't return an item, this will be called.
-		Return value is tool wear.
-	*/
-	virtual u16 punch(const std::string &toolname, v3f dir,
-			const std::string &playername)
-	{return 0;}
-
-	/*
-	*/
-	virtual void rightClick(Player *player){}
+	// Returns a reference
+	virtual InventoryItem* getWieldedItem()
+		{ return NULL; }
+	virtual void damageWieldedItem(u16 amount)
+		{}
+	// If all fits, eats item and returns true. Otherwise returns false.
+	virtual bool addToInventory(InventoryItem *item)
+		{return false;}
+	virtual void setHP(s16 hp)
+		{}
+	virtual s16 getHP()
+		{return 0;}
 
-	virtual bool isPeaceful(){return true;}
-	
 	/*
 		Number of players which know about this object. Object won't be
 		deleted until this is 0 to keep the id preserved for the right