diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index ed0ec86f85a9ba6059c8bc494fd9a36c9e46a3c2..666a11abc0a8a5afa683452b91dfb7944c1407cb 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1412,15 +1412,7 @@ int LUA_HudLib(lua_State *L)
 	patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
 	camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getHudInfo);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_hudinfolen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "hudinfo");
+	LUA_RegisterGlobalUserdata(L, "hudinfo", lib_getHudInfo, NULL, lib_hudinfolen);
 
 	luaL_register(L, "hud", lib_hud);
 	return 0;
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 6b15cc33f0630fa005e8b11b1c3852ec686057bb..020eba15264605543e374dee98ec87be7ad3b119 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1926,105 +1926,16 @@ int LUA_InfoLib(lua_State *L)
 
 	mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSprname);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_sprnamelen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "sprnames");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSpr2name);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_spr2namelen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "spr2names");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSpr2default);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setSpr2default);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_spr2namelen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "spr2defaults");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getState);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setState);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_statelen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "states");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getMobjInfo);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setMobjInfo);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_mobjinfolen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "mobjinfo");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSkinColor);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setSkinColor);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_skincolorslen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "skincolors");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSfxInfo);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setSfxInfo);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_sfxlen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_pushvalue(L, -1);
-	lua_setglobal(L, "S_sfx");
-	lua_setglobal(L, "sfxinfo");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSpriteInfo);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_setSpriteInfo);
-			lua_setfield(L, -2, "__newindex");
-
-			lua_pushcfunction(L, lib_spriteinfolen);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "spriteinfo");
+	LUA_RegisterGlobalUserdata(L, "sprnames", lib_getSprname, NULL, lib_sprnamelen);
+	LUA_RegisterGlobalUserdata(L, "spr2names", lib_getSpr2name, NULL, lib_spr2namelen);
+	LUA_RegisterGlobalUserdata(L, "spr2defaults", lib_getSpr2default, lib_setSpr2default, lib_spr2namelen);
+	LUA_RegisterGlobalUserdata(L, "states", lib_getState, lib_setState, lib_statelen);
+	LUA_RegisterGlobalUserdata(L, "mobjinfo", lib_getMobjInfo, lib_setMobjInfo, lib_mobjinfolen);
+	LUA_RegisterGlobalUserdata(L, "skincolors", lib_getSkinColor, lib_setSkinColor, lib_skincolorslen);
+	LUA_RegisterGlobalUserdata(L, "spriteinfo", lib_getSpriteInfo, lib_setSpriteInfo, lib_spriteinfolen);
+	LUA_RegisterGlobalUserdata(L, "sfxinfo", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);
+	// TODO: 2.3: Delete this alias
+	LUA_RegisterGlobalUserdata(L, "S_sfx", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);
 
 	return 0;
 }
diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c
index 34b44e3af20ed3084ababc4cd5ef10faaf22356c..ef3a9011f1c47a27dc8c75b3e43dc2cd47b9a2d8 100644
--- a/src/lua_inputlib.c
+++ b/src/lua_inputlib.c
@@ -291,28 +291,11 @@ int LUA_InputLib(lua_State *L)
 	// Register the library, then add __index and __newindex
 	// metamethods to it to allow global variables
 	luaL_register(L, "input", lib);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_get);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_set);
-			lua_setfield(L, -2, "__newindex");
-		lua_setmetatable(L, -2);
-
-		lua_newuserdata(L, 0);
-			lua_createtable(L, 0, 2);
-				lua_pushcfunction(L, lib_getGameKeyDown);
-				lua_setfield(L, -2, "__index");
-
-				lua_pushcfunction(L, lib_setGameKeyDown);
-				lua_setfield(L, -2, "__newindex");
-
-				lua_pushcfunction(L, lib_lenGameKeyDown);
-				lua_setfield(L, -2, "__len");
-			lua_setmetatable(L, -2);
-		lua_pushvalue(L, -1); // TODO: 2.3: Delete (gamekeydown moved to input library)
-		lua_setglobal(L, "gamekeydown"); // Delete too
-		lua_setfield(L, -2, "gamekeydown");
+		LUA_CreateAndSetMetatable(L, lib_get, lib_set, NULL, false);
+
+		LUA_CreateAndSetUserdataField(L, -1, "gamekeydown", lib_getGameKeyDown, lib_setGameKeyDown, lib_lenGameKeyDown, false);
+		// TODO: 2.3: Delete this alias (moved to input library)
+		LUA_RegisterGlobalUserdata(L, "gamekeydown", lib_getGameKeyDown, lib_setGameKeyDown, lib_lenGameKeyDown);
 	lua_pop(L, 1);
 
 	return 0;
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index a0a7120a29a99f1a6e0e1ac82659d359c95446f0..2e1aab3eb4d9f19df84cde3ae5cbbdb384cd07bf 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -2868,21 +2868,10 @@ int LUA_MapLib(lua_State *L)
 	slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
 	mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
 
-#ifdef HAVE_LUA_SEGS
-	LUA_RegisterUserdataMetatable(L, META_SEG, seg_get, NULL, seg_num);
-	LUA_RegisterUserdataMetatable(L, META_NODE, node_get, NULL, node_num);
-	LUA_RegisterUserdataMetatable(L, META_NODECHILDREN, nodechildren_get, NULL, NULL);
-
-	seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
-	node_fields_ref = Lua_CreateFieldTable(L, node_opt);
-
-	luaL_newmetatable(L, META_NODEBBOX);
-		//lua_pushcfunction(L, nodebbox_get);
-		//lua_setfield(L, -2, "__index");
-		lua_pushcfunction(L, nodebbox_call);
-		lua_setfield(L, -2, "__call");
-	lua_pop(L, 1);
-#endif
+	LUA_RegisterGlobalUserdata(L, "subsectors", lib_getSubsector, NULL, lib_numsubsectors);
+	LUA_RegisterGlobalUserdata(L, "sides", lib_getSide, NULL, lib_numsides);
+	LUA_RegisterGlobalUserdata(L, "vertexes", lib_getVertex, NULL, lib_numvertexes);
+	LUA_RegisterGlobalUserdata(L, "mapheaderinfo", lib_getMapheaderinfo, NULL, lib_nummapheaders);
 
 	LUA_PushTaggableObjectArray(L, "sectors",
 			lib_iterateSectors,
@@ -2892,16 +2881,6 @@ int LUA_MapLib(lua_State *L)
 			&numsectors, &sectors,
 			sizeof (sector_t), META_SECTOR);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSubsector);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numsubsectors);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "subsectors");
-
 	LUA_PushTaggableObjectArray(L, "lines",
 			lib_iterateLines,
 			lib_getLine,
@@ -2910,56 +2889,24 @@ int LUA_MapLib(lua_State *L)
 			&numlines, &lines,
 			sizeof (line_t), META_LINE);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSide);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numsides);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "sides");
+#ifdef HAVE_LUA_SEGS
+	LUA_RegisterUserdataMetatable(L, META_SEG, seg_get, NULL, seg_num);
+	LUA_RegisterUserdataMetatable(L, META_NODE, node_get, NULL, node_num);
+	LUA_RegisterUserdataMetatable(L, META_NODECHILDREN, nodechildren_get, NULL, NULL);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getVertex);
-			lua_setfield(L, -2, "__index");
+	seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
+	node_fields_ref = Lua_CreateFieldTable(L, node_opt);
 
-			lua_pushcfunction(L, lib_numvertexes);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "vertexes");
+	luaL_newmetatable(L, META_NODEBBOX);
+		//lua_pushcfunction(L, nodebbox_get);
+		//lua_setfield(L, -2, "__index");
+		lua_pushcfunction(L, nodebbox_call);
+		lua_setfield(L, -2, "__call");
+	lua_pop(L, 1);
 
-#ifdef HAVE_LUA_SEGS
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSeg);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numsegs);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "segs");
-
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getNode);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numnodes);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "nodes");
+	LUA_RegisterGlobalUserdata(L, "segs", lib_getSeg, NULL, lib_numsegs);
+	LUA_RegisterGlobalUserdata(L, "nodes", lib_getNode, NULL, lib_numnodes);
 #endif
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getMapheaderinfo);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_nummapheaders);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "mapheaderinfo");
 	return 0;
 }
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 975025c44c178d1f261baa4bffdbd25572483ccc..ac829d0c4ddd563e49f291e38d8b7c61a4634839 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1530,14 +1530,6 @@ int LUA_PlayerLib(lua_State *L)
 	player_fields_ref = Lua_CreateFieldTable(L, player_opt);
 	ticcmd_fields_ref = Lua_CreateFieldTable(L, ticcmd_opt);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getPlayer);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_lenPlayer);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "players");
+	LUA_RegisterGlobalUserdata(L, "players", lib_getPlayer, NULL, lib_lenPlayer);
 	return 0;
 }
diff --git a/src/lua_polyobjlib.c b/src/lua_polyobjlib.c
index d011ccfaa31d151ad13def5571131501a22525fe..6c016f8b2cb24c8bcf993f4c3250e02e020c2f54 100644
--- a/src/lua_polyobjlib.c
+++ b/src/lua_polyobjlib.c
@@ -451,14 +451,6 @@ int LUA_PolyObjLib(lua_State *L)
 	LUA_RegisterUserdataMetatable(L, META_POLYOBJLINES, polyobjlines_get, NULL, polyobjlines_num);
 	LUA_RegisterUserdataMetatable(L, META_POLYOBJ, polyobj_get, polyobj_set, polyobj_num);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getPolyObject);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numPolyObjects);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "polyobjects");
+	LUA_RegisterGlobalUserdata(L, "polyobjects", lib_getPolyObject, NULL, lib_numPolyObjects);
 	return 0;
 }
diff --git a/src/lua_script.c b/src/lua_script.c
index 562114583ef9ea6396473537b509ede5a169ec0d..9aea96ac73b59928367f63efc7b649fb030e3b06 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1833,16 +1833,13 @@ void LUA_PushTaggableObjectArray
 	lua_setglobal(L, field);
 }
 
-void LUA_RegisterUserdataMetatable(
+static void SetBasicMetamethods(
 	lua_State *L,
-	const char *name,
 	lua_CFunction get,
 	lua_CFunction set,
 	lua_CFunction len
 )
 {
-	luaL_newmetatable(L, name);
-
 	if (get)
 	{
 		lua_pushcfunction(L, get);
@@ -1858,6 +1855,73 @@ void LUA_RegisterUserdataMetatable(
 		lua_pushcfunction(L, len);
 		lua_setfield(L, -2, "__len");
 	}
+}
 
+void LUA_RegisterUserdataMetatable(
+	lua_State *L,
+	const char *name,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len
+)
+{
+	luaL_newmetatable(L, name);
+	SetBasicMetamethods(L, get, set, len);
 	lua_pop(L, 1);
 }
+
+// If keep is true, leaves the metatable on the stack.
+// Otherwise, the stack size remains unchanged.
+void LUA_CreateAndSetMetatable(
+	lua_State *L,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len,
+	boolean keep
+)
+{
+	lua_newtable(L);
+	SetBasicMetamethods(L, get, set, len);
+
+	lua_pushvalue(L, -1);
+	lua_setmetatable(L, -3);
+
+	if (!keep)
+		lua_pop(L, 1);
+}
+
+// If keep is true, leaves the userdata and metatable on the stack.
+// Otherwise, the stack size remains unchanged.
+void LUA_CreateAndSetUserdataField(
+	lua_State *L,
+	int index,
+	const char *name,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len,
+	boolean keep
+)
+{
+	if (index < 0 && index > LUA_REGISTRYINDEX)
+		index -= 3;
+
+	lua_newuserdata(L, 0);
+	LUA_CreateAndSetMetatable(L, get, set, len, true);
+
+	lua_pushvalue(L, -2);
+	lua_setfield(L, index, name);
+
+	if (!keep)
+		lua_pop(L, 2);
+}
+
+void LUA_RegisterGlobalUserdata(
+	lua_State *L,
+	const char *name,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len
+)
+{
+	LUA_CreateAndSetUserdataField(L, LUA_GLOBALSINDEX, name, get, set, len, false);
+}
diff --git a/src/lua_script.h b/src/lua_script.h
index 8a4eb9f132b7d87ca0b643c73b19a5a7a7bac09f..dd9a2568e0d197a9c53841cd1c7b165160cf4827 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -81,6 +81,32 @@ void LUA_RegisterUserdataMetatable(
 	lua_CFunction len
 );
 
+void LUA_CreateAndSetMetatable(
+	lua_State *L,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len,
+	boolean keep
+);
+
+void LUA_CreateAndSetUserdataField(
+	lua_State *L,
+	int index,
+	const char *name,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len,
+	boolean keep
+);
+
+void LUA_RegisterGlobalUserdata(
+	lua_State *L,
+	const char *name,
+	lua_CFunction get,
+	lua_CFunction set,
+	lua_CFunction len
+);
+
 void LUA_InsertTaggroupIterator
 (		lua_State *L,
 		taggroup_t *garray[],
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 6c41758b1ab1568a638caf7dd49953490db26514..3debd3746947a252be8e27f0f2608a21739049d2 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -380,15 +380,7 @@ int LUA_SkinLib(lua_State *L)
 
 	skin_fields_ref = Lua_CreateFieldTable(L, skin_opt);
 
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_pushcfunction(L, lib_getSkin);
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numSkins);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "skins");
+	LUA_RegisterGlobalUserdata(L, "skins", lib_getSkin, NULL, lib_numSkins);
 
 	return 0;
 }
diff --git a/src/lua_taglib.c b/src/lua_taglib.c
index 2ba60df998cf88831ec85618f88f57af2746f594..5ed457534ca24c25f55e6a54f0702eb859f7cdf6 100644
--- a/src/lua_taglib.c
+++ b/src/lua_taglib.c
@@ -426,17 +426,12 @@ set_taglist_metatable(lua_State *L, const char *meta)
 
 int LUA_TagLib(lua_State *L)
 {
-	lua_newuserdata(L, 0);
-		lua_createtable(L, 0, 2);
-			lua_createtable(L, 0, 1);
-				lua_pushcfunction(L, lib_iterateTags);
-				lua_setfield(L, -2, "iterate");
-			lua_setfield(L, -2, "__index");
-
-			lua_pushcfunction(L, lib_numTags);
-			lua_setfield(L, -2, "__len");
-		lua_setmetatable(L, -2);
-	lua_setglobal(L, "tags");
+	LUA_CreateAndSetUserdataField(L, LUA_GLOBALSINDEX, "tags", NULL, NULL, lib_numTags, true);
+		lua_createtable(L, 0, 1);
+			lua_pushcfunction(L, lib_iterateTags);
+			lua_setfield(L, -2, "iterate");
+		lua_setfield(L, -2, "__index");
+	lua_pop(L, 2);
 
 	open_taglist(L);