From bc9a378521f2d63dc46b75579b9cce63f3b7e0e5 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 27 Oct 2023 20:17:27 +0200
Subject: [PATCH] Refactor userdata metatable registration

---
 src/lua_consolelib.c |   5 +-
 src/lua_hudlib.c     |  40 ++---------
 src/lua_infolib.c    | 107 +++-------------------------
 src/lua_inputlib.c   |  14 +---
 src/lua_maplib.c     | 163 ++++++-------------------------------------
 src/lua_mobjlib.c    |  39 ++---------
 src/lua_playerlib.c  |  33 +--------
 src/lua_polyobjlib.c |  29 +-------
 src/lua_script.c     |  29 ++++++++
 src/lua_script.h     |   8 +++
 src/lua_skinlib.c    |  35 ++--------
 11 files changed, 89 insertions(+), 413 deletions(-)

diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 0ab8ad9c34..b341050049 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -626,10 +626,7 @@ static int cvar_get(lua_State *L)
 int LUA_ConsoleLib(lua_State *L)
 {
 	// Metatable for consvar_t
-	luaL_newmetatable(L, META_CVAR);
-		lua_pushcfunction(L, cvar_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L,1);
+	LUA_RegisterUserdataMetatable(L, META_CVAR, cvar_get, NULL, NULL);
 
 	cvar_fields_ref = Lua_CreateFieldTable(L, cvar_opt);
 
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 6eec912733..ed0ec86f85 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1404,16 +1404,13 @@ int LUA_HudLib(lua_State *L)
 	luaL_register(L, NULL, lib_draw);
 	lib_draw_ref = luaL_ref(L, LUA_REGISTRYINDEX);
 
-	luaL_newmetatable(L, META_HUDINFO);
-		lua_pushcfunction(L, hudinfo_get);
-		lua_setfield(L, -2, "__index");
+	LUA_RegisterUserdataMetatable(L, META_HUDINFO, hudinfo_get, hudinfo_set, hudinfo_num);
+	LUA_RegisterUserdataMetatable(L, META_COLORMAP, colormap_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_PATCH, patch_get, patch_set, NULL);
+	LUA_RegisterUserdataMetatable(L, META_CAMERA, camera_get, camera_set, NULL);
 
-		lua_pushcfunction(L, hudinfo_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, hudinfo_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
+	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);
@@ -1425,31 +1422,6 @@ int LUA_HudLib(lua_State *L)
 		lua_setmetatable(L, -2);
 	lua_setglobal(L, "hudinfo");
 
-	luaL_newmetatable(L, META_COLORMAP);
-		lua_pushcfunction(L, colormap_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L,1);
-
-	luaL_newmetatable(L, META_PATCH);
-		lua_pushcfunction(L, patch_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, patch_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L,1);
-
-	patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
-
-	luaL_newmetatable(L, META_CAMERA);
-		lua_pushcfunction(L, camera_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, camera_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L,1);
-
-	camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
-
 	luaL_register(L, "hud", lib_hud);
 	return 0;
 }
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 3764acf6a4..6b15cc33f0 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1914,96 +1914,18 @@ int LUA_InfoLib(lua_State *L)
 	lua_newtable(L);
 	lua_setfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
 
-	luaL_newmetatable(L, META_STATE);
-		lua_pushcfunction(L, state_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, state_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, state_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_MOBJINFO);
-		lua_pushcfunction(L, mobjinfo_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, mobjinfo_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, mobjinfo_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
+	LUA_RegisterUserdataMetatable(L, META_STATE, state_get, state_set, state_num);
+	LUA_RegisterUserdataMetatable(L, META_MOBJINFO, mobjinfo_get, mobjinfo_set, mobjinfo_num);
+	LUA_RegisterUserdataMetatable(L, META_SKINCOLOR, skincolor_get, skincolor_set, skincolor_num);
+	LUA_RegisterUserdataMetatable(L, META_COLORRAMP, colorramp_get, colorramp_set, colorramp_len);
+	LUA_RegisterUserdataMetatable(L, META_SFXINFO, sfxinfo_get, sfxinfo_set, sfxinfo_num);
+	LUA_RegisterUserdataMetatable(L, META_SPRITEINFO, spriteinfo_get, spriteinfo_set, spriteinfo_num);
+	LUA_RegisterUserdataMetatable(L, META_PIVOTLIST, pivotlist_get, pivotlist_set, pivotlist_num);
+	LUA_RegisterUserdataMetatable(L, META_FRAMEPIVOT, framepivot_get, framepivot_set, framepivot_num);
+	LUA_RegisterUserdataMetatable(L, META_LUABANKS, lib_getluabanks, lib_setluabanks, lib_luabankslen);
 
 	mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
 
-	luaL_newmetatable(L, META_SKINCOLOR);
-		lua_pushcfunction(L, skincolor_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, skincolor_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, skincolor_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_COLORRAMP);
-		lua_pushcfunction(L, colorramp_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, colorramp_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, colorramp_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
-
-	luaL_newmetatable(L, META_SFXINFO);
-		lua_pushcfunction(L, sfxinfo_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, sfxinfo_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, sfxinfo_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_SPRITEINFO);
-		lua_pushcfunction(L, spriteinfo_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, spriteinfo_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, spriteinfo_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_PIVOTLIST);
-		lua_pushcfunction(L, pivotlist_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, pivotlist_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, pivotlist_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_FRAMEPIVOT);
-		lua_pushcfunction(L, framepivot_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, framepivot_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, framepivot_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
 			lua_pushcfunction(L, lib_getSprname);
@@ -2104,16 +2026,5 @@ int LUA_InfoLib(lua_State *L)
 		lua_setmetatable(L, -2);
 	lua_setglobal(L, "spriteinfo");
 
-	luaL_newmetatable(L, META_LUABANKS);
-		lua_pushcfunction(L, lib_getluabanks);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, lib_setluabanks);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, lib_luabankslen);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	return 0;
 }
diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c
index eeb61fed7e..34b44e3af2 100644
--- a/src/lua_inputlib.c
+++ b/src/lua_inputlib.c
@@ -285,18 +285,8 @@ static int mouse_num(lua_State *L)
 
 int LUA_InputLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_KEYEVENT);
-		lua_pushcfunction(L, keyevent_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_MOUSE);
-		lua_pushcfunction(L, mouse_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, mouse_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
+	LUA_RegisterUserdataMetatable(L, META_KEYEVENT, keyevent_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_MOUSE, mouse_get, NULL, mouse_num);
 
 	// Register the library, then add __index and __newindex
 	// metamethods to it to allow global variables
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index e343979939..9986158509 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -2843,120 +2843,37 @@ static int mapheaderinfo_get(lua_State *L)
 
 int LUA_MapLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_SECTORLINES);
-		lua_pushcfunction(L, sectorlines_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, sectorlines_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_SECTOR);
-		lua_pushcfunction(L, sector_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, sector_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, sector_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
+	LUA_RegisterUserdataMetatable(L, META_SECTORLINES, sectorlines_get, NULL, sectorlines_num);
+	LUA_RegisterUserdataMetatable(L, META_SECTOR, sector_get, sector_set, sector_num);
+	LUA_RegisterUserdataMetatable(L, META_SUBSECTOR, subsector_get, NULL, subsector_num);
+	LUA_RegisterUserdataMetatable(L, META_LINE, line_get, NULL, line_num);
+	LUA_RegisterUserdataMetatable(L, META_LINEARGS, lineargs_get, NULL, lineargs_len);
+	LUA_RegisterUserdataMetatable(L, META_LINESTRINGARGS, linestringargs_get, NULL, linestringargs_len);
+	LUA_RegisterUserdataMetatable(L, META_SIDENUM, sidenum_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_SIDE, side_get, side_set, side_num);
+	LUA_RegisterUserdataMetatable(L, META_VERTEX, vertex_get, NULL, vertex_num);
+	LUA_RegisterUserdataMetatable(L, META_FFLOOR, ffloor_get, ffloor_set, NULL);
+	LUA_RegisterUserdataMetatable(L, META_BBOX, bbox_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_SLOPE, slope_get, slope_set, NULL);
+	LUA_RegisterUserdataMetatable(L, META_VECTOR2, vector2_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_VECTOR3, vector3_get, NULL, NULL);
+	LUA_RegisterUserdataMetatable(L, META_MAPHEADER, mapheaderinfo_get, NULL, NULL);
 
 	sector_fields_ref = Lua_CreateFieldTable(L, sector_opt);
-
-	luaL_newmetatable(L, META_SUBSECTOR);
-		lua_pushcfunction(L, subsector_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, subsector_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	subsector_fields_ref = Lua_CreateFieldTable(L, subsector_opt);
-
-	luaL_newmetatable(L, META_LINE);
-		lua_pushcfunction(L, line_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, line_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	line_fields_ref = Lua_CreateFieldTable(L, line_opt);
-
-	luaL_newmetatable(L, META_LINEARGS);
-		lua_pushcfunction(L, lineargs_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, lineargs_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_LINESTRINGARGS);
-		lua_pushcfunction(L, linestringargs_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, linestringargs_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_SIDENUM);
-		lua_pushcfunction(L, sidenum_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_SIDE);
-		lua_pushcfunction(L, side_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, side_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, side_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	side_fields_ref = Lua_CreateFieldTable(L, side_opt);
-
-	luaL_newmetatable(L, META_VERTEX);
-		lua_pushcfunction(L, vertex_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, vertex_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	vertex_fields_ref = Lua_CreateFieldTable(L, vertex_opt);
-
-	luaL_newmetatable(L, META_FFLOOR);
-		lua_pushcfunction(L, ffloor_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, ffloor_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L, 1);
-
 	ffloor_fields_ref = Lua_CreateFieldTable(L, ffloor_opt);
+	slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
+	mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
 
 #ifdef HAVE_LUA_SEGS
-	luaL_newmetatable(L, META_SEG);
-		lua_pushcfunction(L, seg_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, seg_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
+	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);
-
-	luaL_newmetatable(L, META_NODE);
-		lua_pushcfunction(L, node_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, node_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
 	node_fields_ref = Lua_CreateFieldTable(L, node_opt);
 
 	luaL_newmetatable(L, META_NODEBBOX);
@@ -2965,48 +2882,8 @@ int LUA_MapLib(lua_State *L)
 		lua_pushcfunction(L, nodebbox_call);
 		lua_setfield(L, -2, "__call");
 	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_NODECHILDREN);
-		lua_pushcfunction(L, nodechildren_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
 #endif
 
-	luaL_newmetatable(L, META_BBOX);
-		lua_pushcfunction(L, bbox_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_SLOPE);
-		lua_pushcfunction(L, slope_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, slope_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L, 1);
-
-	slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
-
-	luaL_newmetatable(L, META_VECTOR2);
-		lua_pushcfunction(L, vector2_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_VECTOR3);
-		lua_pushcfunction(L, vector3_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_MAPHEADER);
-		lua_pushcfunction(L, mapheaderinfo_get);
-		lua_setfield(L, -2, "__index");
-
-		//lua_pushcfunction(L, mapheaderinfo_num);
-		//lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
-
 	LUA_PushTaggableObjectArray(L, "sectors",
 			lib_iterateSectors,
 			lib_getSector,
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index fddf958beb..19f30b70e0 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1163,43 +1163,12 @@ static int lib_nummapthings(lua_State *L)
 
 int LUA_MobjLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_MOBJ);
-		lua_pushcfunction(L, mobj_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, mobj_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L,1);
+	LUA_RegisterUserdataMetatable(L, META_MOBJ, mobj_get, mobj_set, NULL);
+	LUA_RegisterUserdataMetatable(L, META_THINGARGS, thingargs_get, NULL, thingargs_len);
+	LUA_RegisterUserdataMetatable(L, META_THINGSTRINGARGS, thingstringargs_get, NULL, thingstringargs_len);
+	LUA_RegisterUserdataMetatable(L, META_MAPTHING, mapthing_get, mapthing_set, mapthing_num);
 
 	mobj_fields_ref = Lua_CreateFieldTable(L, mobj_opt);
-
-	luaL_newmetatable(L, META_THINGARGS);
-		lua_pushcfunction(L, thingargs_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, thingargs_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_THINGSTRINGARGS);
-		lua_pushcfunction(L, thingstringargs_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, thingstringargs_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_MAPTHING);
-		lua_pushcfunction(L, mapthing_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, mapthing_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, mapthing_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
-
 	mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
 
 	LUA_PushTaggableObjectArray(L, "mapthings",
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 827e5a405f..975025c44c 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1523,38 +1523,11 @@ static int ticcmd_set(lua_State *L)
 
 int LUA_PlayerLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_PLAYER);
-		lua_pushcfunction(L, player_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, player_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, player_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
+	LUA_RegisterUserdataMetatable(L, META_PLAYER, player_get, player_set, player_num);
+	LUA_RegisterUserdataMetatable(L, META_POWERS, power_get, power_set, power_len);
+	LUA_RegisterUserdataMetatable(L, META_TICCMD, ticcmd_get, ticcmd_set, NULL);
 
 	player_fields_ref = Lua_CreateFieldTable(L, player_opt);
-
-	luaL_newmetatable(L, META_POWERS);
-		lua_pushcfunction(L, power_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, power_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, power_len);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
-
-	luaL_newmetatable(L, META_TICCMD);
-		lua_pushcfunction(L, ticcmd_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, ticcmd_set);
-		lua_setfield(L, -2, "__newindex");
-	lua_pop(L,1);
-
 	ticcmd_fields_ref = Lua_CreateFieldTable(L, ticcmd_opt);
 
 	lua_newuserdata(L, 0);
diff --git a/src/lua_polyobjlib.c b/src/lua_polyobjlib.c
index c3d9d9d1ad..d011ccfaa3 100644
--- a/src/lua_polyobjlib.c
+++ b/src/lua_polyobjlib.c
@@ -447,32 +447,9 @@ static int lib_numPolyObjects(lua_State *L)
 
 int LUA_PolyObjLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_POLYOBJVERTICES);
-		lua_pushcfunction(L, polyobjvertices_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, polyobjvertices_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_POLYOBJLINES);
-		lua_pushcfunction(L, polyobjlines_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, polyobjlines_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L, 1);
-
-	luaL_newmetatable(L, META_POLYOBJ);
-		lua_pushcfunction(L, polyobj_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, polyobj_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, polyobj_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
+	LUA_RegisterUserdataMetatable(L, META_POLYOBJVERTICES, polyobjvertices_get, NULL, polyobjvertices_num);
+	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);
diff --git a/src/lua_script.c b/src/lua_script.c
index 9e106999dd..562114583e 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1832,3 +1832,32 @@ void LUA_PushTaggableObjectArray
 		lua_setmetatable(L, -2);
 	lua_setglobal(L, field);
 }
+
+void LUA_RegisterUserdataMetatable(
+	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);
+		lua_setfield(L, -2, "__index");
+	}
+	if (set)
+	{
+		lua_pushcfunction(L, set);
+		lua_setfield(L, -2, "__newindex");
+	}
+	if (len)
+	{
+		lua_pushcfunction(L, len);
+		lua_setfield(L, -2, "__len");
+	}
+
+	lua_pop(L, 1);
+}
diff --git a/src/lua_script.h b/src/lua_script.h
index d0b06a719e..8a4eb9f132 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -73,6 +73,14 @@ void LUA_PushTaggableObjectArray
 		size_t sizeof_element,
 		const char *meta);
 
+void LUA_RegisterUserdataMetatable(
+	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 041c5d5985..6c41758b1a 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -373,40 +373,13 @@ static int sprite_get(lua_State *L)
 
 int LUA_SkinLib(lua_State *L)
 {
-	luaL_newmetatable(L, META_SKIN);
-		lua_pushcfunction(L, skin_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, skin_set);
-		lua_setfield(L, -2, "__newindex");
-
-		lua_pushcfunction(L, skin_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
+	LUA_RegisterUserdataMetatable(L, META_SKIN, skin_get, skin_set, skin_num);
+	LUA_RegisterUserdataMetatable(L, META_SOUNDSID, soundsid_get, NULL, soundsid_num);
+	LUA_RegisterUserdataMetatable(L, META_SKINSPRITES, lib_getSkinSprite, NULL, lib_numSkinsSprites);
+	LUA_RegisterUserdataMetatable(L, META_SKINSPRITESLIST, sprite_get, NULL, NULL);
 
 	skin_fields_ref = Lua_CreateFieldTable(L, skin_opt);
 
-	luaL_newmetatable(L, META_SOUNDSID);
-		lua_pushcfunction(L, soundsid_get);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, soundsid_num);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
-
-	luaL_newmetatable(L, META_SKINSPRITES);
-		lua_pushcfunction(L, lib_getSkinSprite);
-		lua_setfield(L, -2, "__index");
-
-		lua_pushcfunction(L, lib_numSkinsSprites);
-		lua_setfield(L, -2, "__len");
-	lua_pop(L,1);
-
-	luaL_newmetatable(L, META_SKINSPRITESLIST);
-		lua_pushcfunction(L, sprite_get);
-		lua_setfield(L, -2, "__index");
-	lua_pop(L,1);
-
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
 			lua_pushcfunction(L, lib_getSkin);
-- 
GitLab