From 57f422d32d82a1e4c78e91717462158b106f6f30 Mon Sep 17 00:00:00 2001
From: nobody_ <>
Date: Mon, 19 Jan 2004 23:24:40 +0000
Subject: [PATCH] Better META_LUA. Shorter code, moved more stuff intro
 script.c

---
 src/include/script.h             | 55 ++++++++++++++------
 src/include/stratagus.h          |  9 +---
 src/stratagus/script.cpp         | 86 ++++++++++++++++++--------------
 src/stratagus/script_missile.cpp | 82 +++++++++++++-----------------
 src/stratagus/script_spell.cpp   | 79 +++++++++++++----------------
 5 files changed, 162 insertions(+), 149 deletions(-)

diff --git a/src/include/script.h b/src/include/script.h
index 72814cfbc..a5fff4b3b 100644
--- a/src/include/script.h
+++ b/src/include/script.h
@@ -90,30 +90,39 @@ extern void CleanCclCredits();            /// Free Ccl Credits Memory
 --  Functions and data structures.
 ----------------------------------------------------------------------------*/
 
-	/// Script get/set function prototype. The values is on the lua stack.
-typedef int ScriptGetSetFunction(void* object, const char* key, lua_State* l);
+	/// Script get/set function prototype with string key. The value is on the lua stack.
+typedef int ScriptGetSetStrFunction(void* object, const char* key, lua_State* l);
+	/// Script get/set function prototype with int index. The value is on the lua stack.
+typedef int ScriptGetSetIntFunction(void* object, int index, lua_State* l);
+	/// Script garbage collector function prototype.
+typedef int ScriptCollectFunction(void* object);
+
+	/// Structure for a script proxy type. Make one of those for every scriptable struct.
+typedef struct {
+	ScriptGetSetStrFunction* GetStr;        /// Get function with strings.
+	ScriptGetSetStrFunction* SetStr;        /// Set function with strings.
+	ScriptGetSetIntFunction* GetInt;        /// Get function with int index.
+	ScriptGetSetIntFunction* SetInt;        /// Set function with int index.
+	ScriptCollectFunction* Collect;         /// Garbage collection function.
+} ScriptProxyType;
 
 	/// Structure for a script proxy. Don't mess with this outside of scripting
 typedef struct {
-	void* Object;                          /// The actual Object
-	ScriptGetSetFunction* GetFunction;        /// Get function
-	ScriptGetSetFunction* SetFunction;        /// Set function
+	void* Object;                                   /// The actual Object
+	ScriptProxyType* Type;                          /// Type information
 } ScriptProxy;
 
-	/// Userdata Constructor.
-extern void ScriptDoCreateUserdata(lua_State* l, void* Object,
-		ScriptGetSetFunction* GetFunction, ScriptGetSetFunction* SetFunction);
-	/// Really dumb set function that always goes into an error.
-extern int ScriptSetValueBlock(lua_State* l);
+	/// Userdata Constructor. Push userdata on the stack.
+extern void ScriptCreateUserdata(lua_State* l, void* object, ScriptProxyType* type);
+	/// Really dumb set function that always goes into an error, with string key
+extern int ScriptGetSetStrBlock(void* object, const char* key, lua_State* l);
+	/// Really dumb set function that always goes into an error, with int index
+extern int ScriptGetSetIntBlock(void* object, int index, lua_State* l);
 
 /*----------------------------------------------------------------------------
 --  Quick macros for meta lua. Use them in well-formed get/set functions.
 ----------------------------------------------------------------------------*/
 
-	/// Macro to call ScriptDoCreateUserdata w/o casts.
-#define ScriptCreateUserdata(l, o, g, s) (ScriptDoCreateUserdata(l, o, \
-		(ScriptGetSetFunction*)(g), (ScriptGetSetFunction*)(s)))
-
     /// Quick way to fail in a function. You can use _C_ like in DebugLevelx
 #ifdef DEBUG
 #define LuaError(l, args) \
@@ -182,6 +191,24 @@ extern int ScriptSetValueBlock(lua_State* l);
 	} \
 }
 
+#define META_GET_FUNC(keyval, v) \
+{ \
+	if (!strcmp(key, keyval)) { \
+		lua_pushcfunction(l, v); \
+		return 1; \
+	} \
+}
+
+/*
+#define META_SET_FUNC(keyval, v) \
+{ \
+	if (!strcmp(key, keyval)) { \
+		luaL_checktype(l, -1, LUA_TBOOLEAN); \
+		v = lua_toboolean(l, -1); \
+		return 0; \
+	} \
+}*/
+
 #endif // META_LUA
 
 //@}
diff --git a/src/include/stratagus.h b/src/include/stratagus.h
index f570e840b..2ac90a19c 100644
--- a/src/include/stratagus.h
+++ b/src/include/stratagus.h
@@ -44,7 +44,7 @@
 #define NEW_UNIT_CACHE
 
 // New Lua scripting.
-//#define META_LUA
+// #define META_LUA
 
 // Dynamic loading.
 //#define DYNAMIC_LOAD
@@ -52,12 +52,7 @@
 	//
 	//		Default speed for many things, set it higher for faster actions.
 	//
-#define SPEED_MINE		1				/// Speed factor for mine gold
-#define SPEED_GOLD		1				/// Speed factor for getting gold
-#define SPEED_CHOP		1				/// Speed factor for chop
-#define SPEED_WOOD		1				/// Speed factor for getting wood
-#define SPEED_HAUL		1				/// Speed factor for haul oil
-#define SPEED_OIL		1				/// Speed factor for getting oil
+#define SPEED_RESOURCE	1				/// Speed factor for resources
 #define SPEED_BUILD		1				/// Speed factor for building
 #define SPEED_TRAIN		1				/// Speed factor for training
 #define SPEED_UPGRADE	1				/// Speed factor for upgrading
diff --git a/src/stratagus/script.cpp b/src/stratagus/script.cpp
index 5c047a65b..360ce4520 100644
--- a/src/stratagus/script.cpp
+++ b/src/stratagus/script.cpp
@@ -959,14 +959,17 @@ global int CclCommand(const char* command)
 local int ScriptGet(lua_State* l)
 {
 	ScriptProxy* sp;
-	const char* key;
 
 	sp = (ScriptProxy*)lua_touserdata(l, -2);
-	key = LuaToString(l, -1);
-	DebugCheck((!sp) || (!key) || (!sp->Object) || (!sp->GetFunction));
-	DebugLevel3Fn("%p->(%s)\n" _C_ sp _C_ key);
+	DebugCheck((!sp) || (!sp->Type));
+	if (sp->Type->GetInt && lua_isnumber(l, -1)) {
+		return sp->Type->GetInt(sp->Object, lua_tonumber(l, -1), l);
+	}
+	if (sp->Type->GetStr && lua_isstring(l, -1)) {
+		return sp->Type->GetStr(sp->Object, lua_tostring(l, -1), l);
+	}
 
-	return sp->GetFunction(sp->Object, key, l);
+	LuaError(l, "Only int or string indexing available for userdata, sorry.\n");
 }
 
 /**
@@ -978,53 +981,56 @@ local int ScriptGet(lua_State* l)
 local int ScriptSet(lua_State* l)
 {
 	ScriptProxy* sp;
-	const char* key;
 
 	sp = (ScriptProxy*)lua_touserdata(l, -3);
-	key = LuaToString(l, -2);
-	DebugCheck((!sp) || (!key) || (!sp->Object) || (!sp->SetFunction));
-	DebugLevel3Fn("%p->(%s)\n" _C_ sp _C_ key);
 
-	return sp->SetFunction(sp->Object, key, l);
+	if (sp->Type->SetInt && lua_isnumber(l, -2)) {
+		return sp->Type->SetInt(sp->Object, lua_tonumber(l, -2), l);
+	}
+	if (sp->Type->SetStr && lua_isstring(l, -2)) {
+		return sp->Type->SetStr(sp->Object, lua_tostring(l, -2), l);
+	}
+
+	LuaError(l, "Only int or string indexing available for userdata, sorry.\n");
 }
 
 /**
-**  Garbage collector function for normal userdata. This is only used inside
-**	scripting, getting called when lua decides to collect the proxy for a C
-**  structure.
+**  Generic Collect Function for a script proxy. Delegate the call to the object's
+**  Collection function.
 **
-**  @param l            The lua state.
-**
-**	@see ScriptCreateUserdata for garbage details.
+**	@param l            The lua state.
 */
-local int ScriptCollectUserdata(lua_State* l)
+local int ScriptCollect(lua_State* l)
 {
 	ScriptProxy* sp;
 	char key[20];
 
 	sp = (ScriptProxy*)lua_touserdata(l, -1);
 	DebugLevel3Fn("Collecting ScriptProxy at %p for obj at %p.\n" _C_ sp _C_ sp->Object);
-
 	// Remove the key from the table.
 	lua_pushstring(l, "StratagusReferences");
 	lua_gettable(l, LUA_REGISTRYINDEX);
-	sprintf(key, "%p%p%p", sp->Object, sp->GetFunction, sp->SetFunction);
+	sprintf(key, "%p%p", sp->Object, sp->Type);
 	lua_pushstring(l, key);
 	lua_pushnil(l);
 	lua_settable(l, -3);
 	lua_remove(l, -1);
+
+	/// Call custom garbage collector, if any.
+	if (sp->Type->Collect) {
+		return sp->Type->Collect(sp);
+	}
 	return 0;
 }
 
 /**
-**  Create a lua proxy for a C structure. This will not always create new
+**  Push a lua proxy on the stack for a C structure. This will not always create new
 **  userdata, but use an old one for the same structure. The userdata is pushed on
 **  the lua stack anyway.
 **
 **  @param l            The lua state
-**  @param Object       The Object to create userdata for.
-**  @param GetFunction  The function called to "Get" a value from the structure.
-**	@param SetFunction  The function called to "Set" a value from the structure.
+**  @param object       The Object to create userdata for.
+**	@param Type         Type info for the object
 **
 **	@notes The Object is sent to the get/set functions, otherwise it is not touched.
 **	@notes Internals.  All lua proxies are kept inside a weak table inside the registry.
@@ -1033,15 +1039,13 @@ local int ScriptCollectUserdata(lua_State* l)
 **	Otherwise it create a new userdata, and sets it's metatable. A garbage collection
 **  proc is called to remove it from the table.
 */
-global void ScriptDoCreateUserdata(lua_State* l, void* Object,
-		ScriptGetSetFunction* GetFunction, ScriptGetSetFunction* SetFunction)
+global void ScriptCreateUserdata(lua_State* l, void* object, ScriptProxyType* type)
 {
 	char key[40];
 	ScriptProxy* sp;
 
 	// FIXME: FASTER?
-	sprintf(key, "%p%p%p", Object, GetFunction, SetFunction);
-
+	sprintf(key, "%p%p", object, type);
 	lua_pushstring(l, "StratagusReferences");
 	lua_gettable(l, LUA_REGISTRYINDEX);
 	lua_pushstring(l, key);
@@ -1051,9 +1055,8 @@ global void ScriptDoCreateUserdata(lua_State* l, void* Object,
 		lua_remove(l, -1);
 		// Create userdata.
 		sp = (ScriptProxy*)lua_newuserdata(l, sizeof(ScriptProxy));
-		sp->Object = Object;
-		sp->GetFunction = GetFunction;
-		sp->SetFunction = SetFunction;
+		sp->Object = object;
+		sp->Type = type;
 		// Get the standard metatable
 		lua_pushstring(l, "StratagusStandardMetatable");
 		lua_gettable(l, LUA_REGISTRYINDEX);
@@ -1064,18 +1067,27 @@ global void ScriptDoCreateUserdata(lua_State* l, void* Object,
 		lua_settable(l, -4);
 		// Remove StratagusReferences reference
 		lua_remove(l, -2);
-		DebugLevel3Fn("Creating ScriptProxy at %p for obj at %p.\n" _C_ lua_touserdata(l, -1) _C_ Object);
+		DebugLevel3Fn("Creating ScriptProxy at %p for obj at %p.\n" _C_ lua_touserdata(l, -1) _C_ object);
 	} else {
 		lua_remove(l, -2);
-		DebugLevel3Fn("Reusing ScriptProxy at %p for obj at %p.\n" _C_ lua_touserdata(l, -1) _C_ Object);
+		DebugLevel3Fn("Reusing ScriptProxy at %p for obj at %p.\n" _C_ lua_touserdata(l, -1) _C_ object);
 	}
 }
 
-global int ScriptSetValueBlock(lua_State* l)
+/**
+**  Really dumb set function that always goes into an error, with string key
+*/
+extern int ScriptGetSetStrBlock(void* object, const char* key, lua_State* l)
 {
-	lua_pushstring(l, "Structure is read-only, sorry.\n");
-	lua_error(l);
-	return 0;
+	LuaError(l, "Access denied");
+}
+
+/**
+**  Really dumb set function that always goes into an error, with int index
+*/
+extern int ScriptGetSetIntBlock(void* object, int index, lua_State* l)
+{
+	LuaError(l, "Access denied");
 }
 
 /**
@@ -1149,7 +1161,7 @@ local void InitScript(void)
 	lua_pushcfunction(Lua, ScriptSet);
 	lua_settable(Lua, -3);
 	lua_pushstring(Lua, "__gc");
-	lua_pushcfunction(Lua, ScriptCollectUserdata);
+	lua_pushcfunction(Lua, ScriptCollect);
 	lua_settable(Lua, -3);
 	lua_settable(Lua, LUA_REGISTRYINDEX);
 	
diff --git a/src/stratagus/script_missile.cpp b/src/stratagus/script_missile.cpp
index 4b98b15be..18aa21452 100644
--- a/src/stratagus/script_missile.cpp
+++ b/src/stratagus/script_missile.cpp
@@ -427,17 +427,17 @@ global void MissileCclRegister(void)
 
 #ifdef META_LUA
 
-	/// Get func for SpellType 
-local int ScriptMissileTypeGet(MissileType* missiletype, const char* key, lua_State* l);
-	/// Set func for SpellType 
-local int ScriptMissileTypeSet(MissileType* missiletype, const char* key, lua_State* l);
+	/// Proxy type for MissileType
+local ScriptProxyType ScriptProxyMissileType;
+	/// Proxy type for the SpellType array
+local ScriptProxyType ScriptProxyMissileTypes;
 
 /**
 **  Create a new missile Type
 **
 **	@param l    Lua state
 */
-local int ScriptMissileTypesCreate(lua_State* l)
+local int ScriptMissileTypeCreate(lua_State* l)
 {
 	const char* name;
 	MissileType* mtype;
@@ -458,8 +458,7 @@ local int ScriptMissileTypesCreate(lua_State* l)
 		mtype->Flip = 1;
 		mtype->SplashFactor = 100;
 
-		ScriptCreateUserdata(l, mtype,
-				ScriptMissileTypeGet, ScriptMissileTypeSet);
+		ScriptCreateUserdata(l, mtype, &ScriptProxyMissileType);
 		return 1;
 	}
 }
@@ -549,32 +548,28 @@ local int ScriptMissileTypeSet(MissileType* mtype, const char* key, lua_State* l
 }
 
 /**
-**	Get function for the big spell namespace.
+**	Get function for the big missile types namespace, with int index
 */
-local int ScriptMissileTypesNamespaceGet(lua_State* l)
+local int ScriptMissileTypesGetInt(void* object, int index, lua_State* l)
+{
+	if (index < 0 || index >= NumMissileTypes) {
+		LuaError(l, "Missile type index out of range");
+	}
+	ScriptCreateUserdata(l, MissileTypes[index], &ScriptProxyMissileType);
+	return 1;
+}
+
+/**
+**	Get function for the big missile types namespace, with string key
+*/
+local int ScriptMissileTypesGetStr(void* object, const char* key, lua_State* l)
 {
-	int i;
-	const char* key;
 	MissileType* mtype;
 
-	//  Index with number
-	if (lua_isnumber(l, 2)) {
-		i = LuaToNumber(l, 2);
-		DebugLevel3Fn("(%d)\n" _C_ i);
-		if (i < 0 || i >= NumMissileTypes) {
-			LuaError(l, "Missile type index out of range");
-		}
-		ScriptCreateUserdata(l, MissileTypes[i], ScriptMissileTypeGet, ScriptMissileTypeSet);
-		return 1;
-	}
-
-	//  Index with string.
-	key = LuaToString(l, 2);
-
 	META_GET_INT("n", NumMissileTypes);
-
+	META_GET_FUNC("Create", ScriptMissileTypeCreate);
 	if ((mtype = MissileTypeByIdent(key))) {
-		ScriptCreateUserdata(l, mtype, ScriptMissileTypeGet, ScriptMissileTypeSet);
+		ScriptCreateUserdata(l, mtype, &ScriptProxyMissileType);
 		return 1;
 	}
 
@@ -588,26 +583,21 @@ local int ScriptMissileTypesNamespaceGet(lua_State* l)
 */
 global void ScriptMissileTypesInit(void)
 {
-	// Create Stratagus.Missiles namespace.
-	// No userdata, there's no data. And no finalizer
+	ScriptProxyMissileType.GetStr = (ScriptGetSetStrFunction *)ScriptMissileTypeGet;
+	ScriptProxyMissileType.SetStr = (ScriptGetSetStrFunction *)ScriptMissileTypeSet;
+	ScriptProxyMissileType.GetInt = ScriptGetSetIntBlock;
+	ScriptProxyMissileType.SetInt = ScriptGetSetIntBlock;
+	ScriptProxyMissileType.Collect = 0;
+
+	ScriptProxyMissileTypes.GetStr = (ScriptGetSetStrFunction *)ScriptMissileTypesGetStr;
+	ScriptProxyMissileTypes.SetStr = ScriptGetSetStrBlock;
+	ScriptProxyMissileTypes.GetInt = (ScriptGetSetIntFunction *)ScriptMissileTypesGetInt;
+	ScriptProxyMissileTypes.SetInt = ScriptGetSetIntBlock;
+	ScriptProxyMissileTypes.Collect = 0;
+
+	// Create Stratagus.MissileTypes namespace.
 	lua_pushstring(Lua, "MissileTypes");
-	lua_newtable(Lua);
-
-	// Generate the metatable
-	lua_newtable(Lua);
-	lua_pushstring(Lua, "__index");
-	lua_pushcfunction(Lua, ScriptMissileTypesNamespaceGet);
-	lua_settable(Lua, -3);
-	lua_pushstring(Lua, "__newindex");
-	lua_pushcfunction(Lua, ScriptSetValueBlock); // Read-Only
-	lua_settable(Lua, -3);
-	lua_setmetatable(Lua, -2);
-
-	// Add functions.
-	lua_pushstring(Lua, "Create");
-	lua_pushcfunction(Lua, ScriptMissileTypesCreate);
-	lua_rawset(Lua, -3);
-
+	ScriptCreateUserdata(Lua, 0, &ScriptProxyMissileTypes);
 	lua_rawset(Lua, -3);
 }
 
diff --git a/src/stratagus/script_spell.cpp b/src/stratagus/script_spell.cpp
index 73e490aea..f98af35b3 100644
--- a/src/stratagus/script_spell.cpp
+++ b/src/stratagus/script_spell.cpp
@@ -1013,10 +1013,10 @@ global void SaveSpells(CLFile* file)
 
 #ifdef META_LUA
 
-	/// Get func for SpellType 
-local int ScriptSpellGet(SpellType* spell, const char* key, lua_State* l);
-	/// Set func for SpellType 
-local int ScriptSpellSet(SpellType* spell, const char* key, lua_State* l);
+	/// Proxy type for SpellType
+local ScriptProxyType ScriptProxySpell;
+	/// Proxy type for the SpellType array
+local ScriptProxyType ScriptProxySpellTypes;
 
 //
 //	Functions directly acessible from lua. Placed in the stratagus namespace.
@@ -1045,7 +1045,7 @@ local int ScriptSpellCreate(lua_State* l)
 		spell->Slot = SpellTypeCount - 1;
 		spell->Ident = strdup(name);
 		spell->DependencyId = -1;
-		ScriptCreateUserdata(l, spell, ScriptSpellGet, ScriptSpellSet);
+		ScriptCreateUserdata(l, spell, &ScriptProxySpell);
 		return 1;
 	}
 }
@@ -1120,34 +1120,28 @@ local int ScriptSpellSet(SpellType* spell, const char* key, lua_State* l)
 }
 
 /**
-**	Get function for the big spell namespace.
+**	Get function for the big spell namespace, with int index
 */
-local int ScriptSpellNamespaceGet(lua_State* l)
+local int ScriptSpellTypesGetInt(void* object, int index, lua_State* l)
+{
+	if (index < 0 || index >= SpellTypeCount) {
+		LuaError(l, "Spell index out of range");
+	}
+	ScriptCreateUserdata(l, SpellTypeTable[index], &ScriptProxySpell);
+	return 1;
+}
+
+/**
+**	Get function for the big spell namespace, with string key
+*/
+local int ScriptSpellTypesGetStr(void* object, const char* key, lua_State* l)
 {
-	int i;
-	const char* key;
 	SpellType* spell;
 
-	//  Index with number
-	if (lua_isnumber(l, 2)) {
-		i = LuaToNumber(l, 2);
-		DebugLevel3Fn("(%d)\n" _C_ i);
-		if (i < 0 || i >= SpellTypeCount) {
-			LuaError(l, "Spell index out of range");
-		}
-		ScriptCreateUserdata(l, SpellTypeTable[i], ScriptSpellGet, ScriptSpellSet);
-		return 1;
-	}
-
-	//  Index with string. FIXME: hashtable? :)
-	key = LuaToString(l, 2);
-	DebugCheck(!key);
-	DebugLevel3Fn("(%s)\n" _C_ key);
-
 	META_GET_INT("n", SpellTypeCount);
-
+	META_GET_FUNC("Create", ScriptSpellCreate);
 	if ((spell = SpellTypeByIdent(key))) {
-		ScriptCreateUserdata(l, spell, ScriptSpellGet, ScriptSpellSet);
+		ScriptCreateUserdata(l, spell, &ScriptProxySpell);
 		return 1;
 	}
 
@@ -1159,26 +1153,21 @@ local int ScriptSpellNamespaceGet(lua_State* l)
 */
 global void ScriptSpellInit(void)
 {
+	ScriptProxySpell.GetStr = (ScriptGetSetStrFunction *)ScriptSpellGet;
+	ScriptProxySpell.SetStr = (ScriptGetSetStrFunction *)ScriptSpellSet;
+	ScriptProxySpell.GetInt = ScriptGetSetIntBlock;
+	ScriptProxySpell.SetInt = ScriptGetSetIntBlock;
+	ScriptProxySpell.Collect = 0;
+
+	ScriptProxySpellTypes.GetStr = (ScriptGetSetStrFunction *)ScriptSpellTypesGetStr;
+	ScriptProxySpellTypes.SetStr = ScriptGetSetStrBlock;
+	ScriptProxySpellTypes.GetInt = (ScriptGetSetIntFunction *)ScriptSpellTypesGetInt;
+	ScriptProxySpellTypes.SetInt = ScriptGetSetIntBlock;
+	ScriptProxySpellTypes.Collect = 0;
+
 	// Create Stratagus.Spells namespace.
-	// No userdata, there's no data. And no finalizer
 	lua_pushstring(Lua, "Spells");
-	lua_newtable(Lua);
-
-	// Generate the metatable
-	lua_newtable(Lua);
-	lua_pushstring(Lua, "__index");
-	lua_pushcfunction(Lua, ScriptSpellNamespaceGet);
-	lua_settable(Lua, -3);
-	lua_pushstring(Lua, "__newindex");
-	lua_pushcfunction(Lua, ScriptSetValueBlock); // Read-Only
-	lua_settable(Lua, -3);
-	lua_setmetatable(Lua, -2);
-
-	// Add functions.
-	lua_pushstring(Lua, "Create");
-	lua_pushcfunction(Lua, ScriptSpellCreate);
-	lua_rawset(Lua, -3);
-
+	ScriptCreateUserdata(Lua, 0, &ScriptProxySpellTypes);
 	lua_rawset(Lua, -3);
 }