Example #1
0
void LuaMatUniforms::Parse(lua_State* L, const int tableIdx)
{
	auto uniformAndNames = GetUniformsAndPossibleNames();
	decltype(uniformAndNames) uniformAndNamesLower;
	for (auto& p: uniformAndNames) {
		uniformAndNamesLower[StringToLower(p.first)] = p.second;
	}

	for (lua_pushnil(L); lua_next(L, tableIdx) != 0; lua_pop(L, 1)) {
		if (!lua_israwstring(L, -2) || !lua_isnumber(L, -1))
			continue;

		// get the lua table key
		// and remove the "loc" at the end (cameraPosLoc -> camerapos)
		std::string uniformLocStr = luaL_tosstring(L, -2);
		StringToLowerInPlace(uniformLocStr);
		if (StringEndsWith(uniformLocStr, "loc")) {
			uniformLocStr.resize(uniformLocStr.size() - 3);
		}

		const auto uniformLocIt = uniformAndNamesLower.find(uniformLocStr);
		if (uniformLocIt == uniformAndNamesLower.end()) {
			LOG_L(L_WARNING, "LuaMaterial: unknown uniform \"%s\"", lua_tostring(L, -2));
			continue;
		}

		uniformLocIt->second->loc = static_cast<GLint>(lua_tonumber(L, -1));
	}
}
Example #2
0
bool CLuaHandleSynced::LightCopyTable(int dstIndex, int srcIndex)
{
	// use positive indices
	if (dstIndex < 0) { dstIndex = lua_gettop(L) + dstIndex + 1; }
	if (srcIndex < 0) { srcIndex = lua_gettop(L) + srcIndex + 1; }

	if (!lua_istable(L, dstIndex) || !lua_istable(L, srcIndex)) {
		return false;
	}

	for (lua_pushnil(L); lua_next(L, srcIndex) != 0; lua_pop(L, 1)) {
		if (!lua_israwstring(L, -2)) { // key must be a string
			continue;
		}
		if (lua_isstring(L, -1)   ||
				lua_isnumber(L, -1)   ||
				lua_isboolean(L, -1)  ||
				lua_isfunction(L, -1)) {
			lua_pushvalue(L, -2); // copy the key
			lua_pushvalue(L, -2); // copy the value
			lua_rawset(L, dstIndex);
		}
	}

	return true;
}
static void ParseTexture(lua_State* L, int index, LuaMatTexture& texUnit) {
	if (index < 0)
		index = lua_gettop(L) + index + 1;

	if (lua_isstring(L, index)) {
		LuaOpenGLUtils::ParseTextureImage(L, texUnit, lua_tostring(L, index));
		texUnit.enable = true;
		return;
	}

	if (!lua_istable(L, index))
		return;

	const int table = (index > 0) ? index : (lua_gettop(L) + index + 1);

	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
		if (!lua_israwstring(L, -2))
			continue;

		const string key = StringToLower(lua_tostring(L, -2));

		if (key == "tex") {
			LuaOpenGLUtils::ParseTextureImage(L, texUnit, lua_tostring(L, -1));
			texUnit.enable = true;
			continue;
		}

		if (key == "enable") {
			texUnit.enable = lua_isboolean(L, -1) && lua_toboolean(L, -1);
			continue;
		}
	}
}
Example #4
0
int LuaFonts::meta_index(lua_State* L)
{
	//! first check if there is a function
	luaL_getmetatable(L, "Font");
	lua_pushvalue(L, 2);
	lua_rawget(L, -2);
	if (!lua_isnil(L, -1)) {
		return 1;
	}
	lua_pop(L, 1);

	//! couldn't find a function, so check properties
	CglFont* font = tofont(L, 1);

	if (lua_israwstring(L, 2)) {
		const string key = lua_tostring(L, 2);

		if (key == "size") {
			lua_pushnumber(L, font->GetSize());
			return 1;
		//} else if (key == "outlinecolor") {
		//} else if (key == "textcolor") {
		} else if (key == "path") {
			const std::string& filepath = font->GetFilePath();
			lua_pushlstring(L, filepath.c_str(), filepath.length());
			return 1;
		} else if (key == "height" || key == "lineheight") {
			lua_pushnumber(L, font->GetLineHeight());
			return 1;
		} else if (key == "descender") {
			lua_pushnumber(L, font->GetDescender());
			return 1;
		} else if (key == "outlinewidth") {
			lua_pushnumber(L, font->GetOutlineWidth());
			return 1;
		} else if (key == "outlineweight") {
			lua_pushnumber(L, font->GetOutlineWeight());
			return 1;
		} else if (key == "family") {
			const std::string& family = font->GetFamily();
			lua_pushlstring(L, family.c_str(), family.length());
			return 1;
		} else if (key == "style") {
			const std::string& style = font->GetStyle();
			lua_pushlstring(L, style.c_str(), style.length());
			return 1;
		} else if (key == "texturewidth") {
			lua_pushnumber(L, font->GetTexWidth());
			return 1;
		} else if (key == "textureheight") {
			lua_pushnumber(L, font->GetTexHeight());
			return 1;
		}
	}

	return 0;
}
static int IndexHook(lua_State* L)
{
	CLuaHandle** addr = (CLuaHandle**) lua_touserdata(L, lua_upvalueindex(1));
	if (!lua_israwstring(L, 2)) {
		return 0; // missing string name for function
	}
	lua_pushlightuserdata(L, addr);
	lua_pushstring(L, lua_tostring(L, 2));
	lua_pushcclosure(L, HandleXCall, 2);	
	return 1;
}
Example #6
0
static bool LowerKeysReal(lua_State* L, int depth)
{
	lua_checkstack(L, lowerKeysTable + 8 + (depth * 3));

	const int table = lua_gettop(L);
	if (LowerKeysCheck(L, table)) {
		return true;
	}

	// a new table for changed values
	const int changed = table + 1;
	lua_newtable(L);

	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
		if (lua_istable(L, -1)) {
			LowerKeysReal(L, depth + 1);
		}
		if (lua_israwstring(L, -2)) {
			const string rawKey = lua_tostring(L, -2);
			const string lowerKey = StringToLower(rawKey);
			if (rawKey != lowerKey) {
				// removed the mixed case entry
				lua_pushvalue(L, -2); // the key
				lua_pushnil(L);
				lua_rawset(L, table);
				// does the lower case key alread exist in the table?
				lua_pushsstring(L, lowerKey);
				lua_rawget(L, table);
				if (lua_isnil(L, -1)) {
					// lower case does not exist, add it to the changed table
					lua_pushsstring(L, lowerKey);
					lua_pushvalue(L, -3); // the value
					lua_rawset(L, changed);
				}
				lua_pop(L, 1);
			}
		}
	}

	// copy the changed values into the table
	for (lua_pushnil(L); lua_next(L, changed) != 0; lua_pop(L, 1)) {
		lua_pushvalue(L, -2); // copy the key to the top
		lua_pushvalue(L, -2); // copy the value to the top
		lua_rawset(L, table);
	}

	lua_pop(L, 1); // pop the changed table

	return true;
}
Example #7
0
bool LuaTable::GetKeys(vector<string>& data) const
{
	if (!PushTable()) {
		return false;
	}
	const int table = lua_gettop(L);
	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
		if (lua_israwstring(L, -2)) {
			const string value = lua_tostring(L, -2);
			data.push_back(value);
		}
	}
	std::sort(data.begin(), data.end());
	return true;
}
Example #8
0
bool LuaTable::GetMap(map<string, string>& data) const
{
	if (!PushTable()) {
		return false;
	}
	const int table = lua_gettop(L);
	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
		if (lua_israwstring(L, -2) && lua_isstring(L, -1)) {
			const string key   = lua_tostring(L, -2);
			const string value = lua_tostring(L, -1);
			data[key] = value;
		}
	}
	return true;
}
Example #9
0
/*-
	Logs a msg to the logfile / console
	@param loglevel loglevel that will be used for the message
	@param msg string to be logged
	@fn Spring.Log(string logsection, int loglevel, ...)
	@fn Spring.Log(string logsection, string loglevel, ...)
*/
int LuaUtils::Log(lua_State* L)
{
	const int args = lua_gettop(L); // number of arguments
	if (args < 2)
		return luaL_error(L, "Incorrect arguments to Spring.Log(logsection, loglevel, ...)");
	if (args < 3)
		return 0;

	const std::string section = luaL_checkstring(L, 1);

	int loglevel;
	if (lua_israwnumber(L, 2)) {
		loglevel = lua_tonumber(L, 2);
	}
	else if (lua_israwstring(L, 2)) {
		std::string loglvlstr = lua_tostring(L, 2);
		StringToLowerInPlace(loglvlstr);
		if (loglvlstr == "debug") {
			loglevel = LOG_LEVEL_DEBUG;
		}
		else if (loglvlstr == "info") {
			loglevel = LOG_LEVEL_INFO;
		}
		else if (loglvlstr == "warning") {
			loglevel = LOG_LEVEL_WARNING;
		}
		else if (loglvlstr == "error") {
			loglevel = LOG_LEVEL_ERROR;
		}
		else if (loglvlstr == "fatal") {
			loglevel = LOG_LEVEL_FATAL;
		}
		else {
			return luaL_error(L, "Incorrect arguments to Spring.Log(logsection, loglevel, ...)");
		}
	}
	else {
		return luaL_error(L, "Incorrect arguments to Spring.Log(logsection, loglevel, ...)");
	}

	const std::string msg = getprintf_msg(L, 3);
	LOG_SI(section.c_str(), loglevel, "%s", msg.c_str());
	return 0;
}
Example #10
0
int CLuaRules::SetRulesInfoMap(lua_State* L)
{
	assert(luaRules != NULL);
	if (!lua_istable(L, 1)) {
		luaL_error(L, "Incorrect arguments to SetRulesInfoMap(teamID)");
	}
	map<string, string>& infoMap = luaRules->infoMap;
	infoMap.clear();
	const int table = 1;
	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
		if (lua_israwstring(L, -2) && lua_isstring(L, -1)) {
			const string key = lua_tostring(L, -2);
			const string value = lua_tostring(L, -1);
			infoMap[key] = value;
		}
	}
	lua_pushnumber(L, infoMap.size());
	return 2;
}
Example #11
0
static int HandleXCall(lua_State* L)
{
	const int addrIndex = lua_upvalueindex(1);
	const int nameIndex = lua_upvalueindex(2);

	if (!lua_isuserdata(L, addrIndex) || !lua_israwstring(L, nameIndex)) {
		luaL_error(L, "Bad function name type");
	}

	CLuaHandle** addr = (CLuaHandle**) lua_touserdata(L, addrIndex);
	if (*addr == NULL) {
		return 0; // handle is not currently active
	}
	CLuaHandle* lh = *addr;

	const char* funcName = lua_tostring(L, nameIndex);

	return lh->SyncedXCall(L, funcName);
}
Example #12
0
int LuaUtils::ParseFacing(lua_State* L, const char* caller, int index)
{
	if (lua_israwnumber(L, index)) {
		return std::max(0, std::min(3, lua_toint(L, index)));
	}
	else if (lua_israwstring(L, index)) {
		const string dir = StringToLower(lua_tostring(L, index));
		if (dir == "s") { return 0; }
		if (dir == "e") { return 1; }
		if (dir == "n") { return 2; }
		if (dir == "w") { return 3; }
		if (dir == "south") { return 0; }
		if (dir == "east")  { return 1; }
		if (dir == "north") { return 2; }
		if (dir == "west")  { return 3; }
		luaL_error(L, "%s(): bad facing string", caller);
	}
	luaL_error(L, "%s(): bad facing parameter", caller);
	return 0;
}
Example #13
0
static int CallHook(lua_State* L)
{
	CLuaHandle** addr = (CLuaHandle**) lua_touserdata(L, lua_upvalueindex(1));
	const int args = lua_gettop(L); // arg 1 is the table
	if (args <= 1) {
		// is the handle currently active?
		lua_pushboolean(L, (*addr != NULL));
		return 1;
	}
	else if ((args >= 2) && lua_israwstring(L, 2)) {
		// see if the specified function exists
		const string funcName = lua_tostring(L, 2);
		CLuaHandle* lh = *addr;
		if (lh == NULL) {
			return 0; // not running
		}
		lua_pushboolean(L, lh->HasSyncedXCall(funcName));
		return 1;
	}
	return 0;
}
Example #14
0
void CLuaRules::CreateRulesParams(lua_State* L, const char* caller, int offset,
		                              vector<float>& params,
		                              map<string, int>& paramsMap)
{
	const int table = offset + 1;
	if (!lua_istable(L, table)) {
		luaL_error(L, "Incorrect arguments to %s()", caller);
	}

	params.clear();
	paramsMap.clear();

	for (int i = 1; /* no test */; i++) {
		lua_rawgeti(L, table, i);
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);
			return;
		}
		else if (lua_israwnumber(L, -1)) {
			const float value = lua_tofloat(L, -1);
			params.push_back(value);
		}
		else if (lua_istable(L, -1)) {
			lua_pushnil(L);
			if (lua_next(L, -2)) {
				if (lua_israwstring(L, -2) && lua_isnumber(L, -1)) {
					const string name = lua_tostring(L, -2);
					const float value = lua_tonumber(L, -1);
					paramsMap[name] = params.size();
					params.push_back(value);
				}
				lua_pop(L, 2);
			}
		}
		lua_pop(L, 1);
	}

	return;
}
static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller)
{
	int numAssignedValues = 0;

	if (moveType == NULL) {
		luaL_error(L, "[%s] unit %d has incompatible movetype for %s", __FUNCTION__, lua_tonumber(L, 1), caller);
		return numAssignedValues;
	}

	switch (lua_gettop(L)) {
		case 2: {
			// two args (unitID, {"key1" = (number | bool) value1, ...})
			const int tableIdx = 2;

			if (lua_istable(L, tableIdx)) {
				for (lua_pushnil(L); lua_next(L, tableIdx) != 0; lua_pop(L, 1)) {
					if (lua_israwstring(L, -2) && SetMoveTypeValue(L, moveType, -2, -1)) {
						numAssignedValues++;
					} else {
						LOG_L(L_WARNING, "[%s] incompatible movetype key for %s", __FUNCTION__, caller);
					}
				}
			}
		} break;

		case 3: {
			// three args (unitID, "key", (number | bool) value)
			if (lua_isstring(L, 2) && SetMoveTypeValue(L, moveType, 2, 3)) {
				numAssignedValues++;
			} else {
				LOG_L(L_WARNING, "[%s] incompatible movetype key for %s", __FUNCTION__, caller);
			}
		} break;
	}

	lua_pushnumber(L, numAssignedValues);
	return 1;
}
Example #16
0
void CLuaRules::SetRulesParam(lua_State* L, const char* caller, int offset,
                              vector<float>& params,
		                          map<string, int>& paramsMap)
{
	const int index = offset + 1;
	const int valIndex = offset + 2;
	int pIndex = -1;

	if (lua_israwnumber(L, index)) {
		pIndex = lua_toint(L, index) - 1;
	}
	else if (lua_israwstring(L, index)) {
		const string pName = lua_tostring(L, index);
		map<string, int>::const_iterator it = paramsMap.find(pName);
		if (it != paramsMap.end()) {
			pIndex = it->second;
		}
		else {
			// create a new parameter
			pIndex = params.size();
			paramsMap[pName] = params.size();
			params.push_back(0.0f); // dummy value
		}
	}
	else {
		luaL_error(L, "Incorrect arguments to %s()", caller);
	}

	if ((pIndex < 0) || (pIndex >= (int)params.size())) {
		return;
	}

	if (!lua_isnumber(L, valIndex)) {
		luaL_error(L, "Incorrect arguments to %s()", caller);
	}
	params[pIndex] = lua_tofloat(L, valIndex);
	return;
}
Example #17
0
int LuaSyncedMoveCtrl::SetGunshipMoveTypeData(lua_State *L)
{
	CHoverAirMoveType* moveType = ParseMoveType<CHoverAirMoveType>(L, __FUNCTION__, 1);
	if (moveType == NULL) {
		luaL_error(L, "Unit does not have a compatible MoveType");
	}

	const int args = lua_gettop(L); // number of arguments

	if (args == 3 && lua_isstring(L, 2)) {
		// a single value
		SetSingleHoverAirMoveTypeValue(L, 2, 3, moveType);
	} else if (args == 2 && lua_istable(L, 2)) {
		// a table of values
		const int table = 2;
		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
			if (lua_israwstring(L, -2)) {
				SetSingleHoverAirMoveTypeValue(L, -2, -1, moveType);
			}
		}
	}

	return 0;
}
Example #18
0
void LuaMaterial::Parse(
	lua_State* L,
	const int tableIdx,
	std::function<void(lua_State*, int, LuaMatShader&)> ParseShader,
	std::function<void(lua_State*, int, LuaMatTexture&)> ParseTexture,
	std::function<GLuint(lua_State*, int)> ParseDisplayList
) {
	for (lua_pushnil(L); lua_next(L, tableIdx) != 0; lua_pop(L, 1)) {
		if (!lua_israwstring(L, -2))
			continue;

		const std::string key = StringToLower(lua_tostring(L, -2));

		// uniforms
		if (key.find("uniforms") != std::string::npos) {
			if (!lua_istable(L, -1))
				continue;

			if (key.find("standard") != std::string::npos) {
				uniforms[LuaMatShader::LUASHADER_PASS_FWD].Parse(L, lua_gettop(L));
				continue;
			}
			if (key.find("deferred") != std::string::npos) {
				uniforms[LuaMatShader::LUASHADER_PASS_DFR].Parse(L, lua_gettop(L));
				continue;
			}

			// fallback
			uniforms[LuaMatShader::LUASHADER_PASS_FWD].Parse(L, lua_gettop(L));
			continue;
		}

		// shaders
		if (key.find("shader") != std::string::npos) {
			if (key.find("standard") != std::string::npos) {
				ParseShader(L, -1, shaders[LuaMatShader::LUASHADER_PASS_FWD]);
				continue;
			}
			if (key.find("deferred") != std::string::npos) {
				ParseShader(L, -1, shaders[LuaMatShader::LUASHADER_PASS_DFR]);
				continue;
			}

			// fallback
			ParseShader(L, -1, shaders[LuaMatShader::LUASHADER_PASS_FWD]);
			continue;
		}

		// textures
		if (key.substr(0, 7) == "texunit") {
			if (key.size() < 8)
				continue;

			if (key[7] == 's') {
				// "texunits" = {[0] = string|table, ...}
				if (!lua_istable(L, -1))
					continue;

				const int texTable = lua_gettop(L);

				for (lua_pushnil(L); lua_next(L, texTable) != 0; lua_pop(L, 1)) {
					if (!lua_israwnumber(L, -2))
						continue;

					const unsigned int texUnit = lua_toint(L, -2);

					if (texUnit >= LuaMatTexture::maxTexUnits)
						continue;

					ParseTexture(L, -1, textures[texUnit]);
				}
			} else {
				// "texunitX" = string|table
				const unsigned int texUnit = atoi(key.c_str() + 7);

				if (texUnit >= LuaMatTexture::maxTexUnits)
					continue;

				ParseTexture(L, -1, textures[texUnit]);
			}

			continue;
		}

		// dlists
		if (key == "prelist") {
			preList = ParseDisplayList(L, -1);
			continue;
		}
		if (key == "postlist") {
			postList = ParseDisplayList(L, -1);
			continue;
		}

		// misc
		if (key == "order") {
			order = luaL_checkint(L, -1);
			continue;
		}
		if (key == "culling") {
			if (lua_isnumber(L, -1))
				cullingMode = (GLenum)lua_tonumber(L, -1);

			continue;
		}

		if (key == "usecamera") {
			useCamera = lua_isboolean(L, -1) && lua_toboolean(L, -1);
			continue;
		}

		LOG_L(L_WARNING, "LuaMaterial: incorrect key \"%s\"", key.c_str());
	}
}
Example #19
0
int CLuaHandleSynced::CallAsTeam(lua_State* L)
{
	const int args = lua_gettop(L);
	if ((args < 2) || !lua_isfunction(L, 2)) {
		luaL_error(L, "Incorrect arguments to CallAsTeam()");
	}

	// save the current access
	const bool prevFullCtrl    = CLuaHandle::GetHandleFullCtrl(L);
	const bool prevFullRead    = CLuaHandle::GetHandleFullRead(L);
	const int prevCtrlTeam     = CLuaHandle::GetHandleCtrlTeam(L);
	const int prevReadTeam     = CLuaHandle::GetHandleReadTeam(L);
	const int prevReadAllyTeam = CLuaHandle::GetHandleReadAllyTeam(L);
	const int prevSelectTeam   = CLuaHandle::GetHandleSelectTeam(L);

	// parse the new access
	if (lua_isnumber(L, 1)) {
		const int teamID = lua_toint(L, 1);
		if ((teamID < CEventClient::MinSpecialTeam) || (teamID >= teamHandler->ActiveTeams())) {
			luaL_error(L, "Bad teamID in SetCtrlTeam");
		}
		// ctrl
		CLuaHandle::SetHandleCtrlTeam(L, teamID);
		CLuaHandle::SetHandleFullCtrl(L, prevCtrlTeam == CEventClient::AllAccessTeam);
		// read
		CLuaHandle::SetHandleReadTeam(L, teamID);
		CLuaHandle::SetHandleReadAllyTeam(L, (teamID < 0) ? teamID : teamHandler->AllyTeam(teamID));
		CLuaHandle::SetHandleFullRead(L, prevReadAllyTeam == CEventClient::AllAccessTeam);
		// select
		CLuaHandle::SetHandleSelectTeam(L, teamID);
	}
	else if (lua_istable(L, 1)) {
		const int table = 1;
		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
			if (!lua_israwstring(L, -2) || !lua_isnumber(L, -1)) {
				continue;
			}
			const string key = lua_tostring(L, -2);
			const int teamID = lua_toint(L, -1);
			if ((teamID < CEventClient::MinSpecialTeam) || (teamID >= teamHandler->ActiveTeams())) {
				luaL_error(L, "Bad teamID in SetCtrlTeam");
			}

			if (key == "ctrl") {
				CLuaHandle::SetHandleCtrlTeam(L, teamID);
				CLuaHandle::SetHandleFullCtrl(L, prevCtrlTeam == CEventClient::AllAccessTeam);
			}
			else if (key == "read") {
				CLuaHandle::SetHandleReadTeam(L, teamID);
				CLuaHandle::SetHandleReadAllyTeam(L, (teamID < 0) ? teamID : teamHandler->AllyTeam(teamID));
				CLuaHandle::SetHandleFullRead(L, prevReadAllyTeam == CEventClient::AllAccessTeam);
			}
			else if (key == "select") {
				CLuaHandle::SetHandleSelectTeam(L, teamID);
			}
		}
	}
	else {
		luaL_error(L, "Incorrect arguments to CallAsTeam()");
	}

	// call the function
	const int funcArgs = lua_gettop(L) - 2;

	// protected call so that the permissions are always reverted
	const int error = lua_pcall(L, funcArgs, LUA_MULTRET, 0);

	// revert the permissions
	CLuaHandle::SetHandleFullCtrl(L, prevFullCtrl);
	CLuaHandle::SetHandleFullRead(L, prevFullRead);
	CLuaHandle::SetHandleCtrlTeam(L, prevCtrlTeam);
	CLuaHandle::SetHandleReadTeam(L, prevReadTeam);
	CLuaHandle::SetHandleReadAllyTeam(L, prevReadAllyTeam);
	CLuaHandle::SetHandleSelectTeam(L, prevSelectTeam);

	if (error != 0) {
		LOG_L(L_ERROR, "error = %i, %s, %s",
				error, "CallAsTeam", lua_tostring(L, -1));
		lua_error(L);
	}

	return lua_gettop(L) - 1;	// the teamID/table is still on the stack
}
Example #20
0
int CLuaHandleSynced::CallAsTeam(lua_State* L)
{
	CLuaHandleSynced* lhs = GetActiveHandle();
	if (lhs->teamsLocked) {
		luaL_error(L, "CallAsTeam() called when teams are locked");
	}
	const int args = lua_gettop(L);
	if ((args < 2) || !lua_isfunction(L, 2)) {
		luaL_error(L, "Incorrect arguments to CallAsTeam()");
	}

	// save the current access
	const bool prevFullCtrl    = lhs->fullCtrl;
	const bool prevFullRead    = lhs->fullRead;
	const int prevCtrlTeam     = lhs->ctrlTeam;
	const int prevReadTeam     = lhs->readTeam;
	const int prevReadAllyTeam = lhs->readAllyTeam;
	const int prevSelectTeam   = lhs->selectTeam;

	// parse the new access
	if (lua_isnumber(L, 1)) {
		const int teamID = lua_toint(L, 1);
		if ((teamID < MinSpecialTeam) || (teamID >= teamHandler->ActiveTeams())) {
			luaL_error(L, "Bad teamID in SetCtrlTeam");
		}
		// ctrl
		lhs->ctrlTeam = teamID;
		lhs->fullCtrl = (lhs->ctrlTeam == CEventClient::AllAccessTeam);
		// read
		lhs->readTeam = teamID;
		lhs->readAllyTeam = (teamID < 0) ? teamID : teamHandler->AllyTeam(teamID);
		lhs->fullRead = (lhs->readAllyTeam == CEventClient::AllAccessTeam);
		activeFullRead     = lhs->fullRead;
		activeReadAllyTeam = lhs->readAllyTeam;
		// select
		lhs->selectTeam = teamID;
	}
	else if (lua_istable(L, 1)) {
		const int table = 1;
		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
			if (!lua_israwstring(L, -2) || !lua_isnumber(L, -1)) {
				continue;
			}
			const string key = lua_tostring(L, -2);
			const int teamID = lua_toint(L, -1);
			if ((teamID < MinSpecialTeam) || (teamID >= teamHandler->ActiveTeams())) {
				luaL_error(L, "Bad teamID in SetCtrlTeam");
			}

			if (key == "ctrl") {
				lhs->ctrlTeam = teamID;
				lhs->fullCtrl = (lhs->ctrlTeam == CEventClient::AllAccessTeam);
			}
			else if (key == "read") {
				lhs->readTeam = teamID;
				lhs->readAllyTeam = (teamID < 0) ? teamID : teamHandler->AllyTeam(teamID);
				lhs->fullRead = (lhs->readAllyTeam == CEventClient::AllAccessTeam);
				activeFullRead     = lhs->fullRead;
				activeReadAllyTeam = lhs->readAllyTeam;
			}
			else if (key == "select") {
				lhs->selectTeam = teamID;
			}
		}
	}
	else {
		luaL_error(L, "Incorrect arguments to CallAsTeam()");
	}

	// call the function
	const int funcArgs = lua_gettop(L) - 2;

	// protected call so that the permissions are always reverted
	const int error = lua_pcall(lhs->L, funcArgs, LUA_MULTRET, 0);

	// revert the permissions
	lhs->fullCtrl      = prevFullCtrl;
	lhs->fullRead      = prevFullRead;
	lhs->ctrlTeam      = prevCtrlTeam;
	lhs->readTeam      = prevReadTeam;
	lhs->readAllyTeam  = prevReadAllyTeam;
	lhs->selectTeam    = prevSelectTeam;
	activeFullRead     = prevFullRead;
	activeReadAllyTeam = prevReadAllyTeam;

	if (error != 0) {
		logOutput.Print("error = %i, %s, %s\n",
		                error, "CallAsTeam", lua_tostring(L, -1));
		lua_error(L);
	}

	return lua_gettop(L) - 1;	// the teamID/table is still on the stack
}