🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

how to unload the script from memory to properly write the implementation of reloading scripts

Started by
3 comments, last by egor230 5 years ago

For a long time wanted to write a plugin that allows you to run lua scripts for vc. In this plugin lies Plugin-SDK DK https://github.com/DK22Pac/plugin-sdk/tree/8d4d2ff5502ffcb3a741cbcac238d49664689808 
the sdk has its main thread, which is an infinite loop, in a way. If there put delay, for example, 10 seconds, then at this time will stop the game itself. Therefore, we launch a new independent stream with a flag, so that it is only one.
Searching all lua files in a folder, run in a new thread with a new lua state.  Threads and lua States are added to vectors.
 There is a check for errors, run the script, the counter of running scripts increases, get the stack function main, run it.
As follows.
        lua_pcall(L, 0, 0, 0);// run the file.
        lua_getglobal(L, "main");
        if (LUA_TFUNCTION == lua_type(L, -1)){ luastate.push_back(L); counts++;
        lua_pcall(L, 0, 0, 0);


Lua contains the main function, which has an infinite loop.  Everything works, but there is one but.  How do I reboot all scripts?
 Press ctrl, go through the cycle for vector c lua States, remove from the stack all, but lua state is not unloaded from memory, so it has an infinite loop.
   Please tell me how to unload the script from memory to properly write the implementation of reloading scripts?.

plugin.cpp

main.lua

Advertisement

As you always have the problem in a multithreaded environment, you need to have some kind of trigger state that will end your loops. For example


while(!loopClose)
{
    ... do
}

This state should be something global and properly synced e.g. through interlocked arithmetic.

Then you need to end in your main functions body, that function should maintain a second state that indicates if reload was triggered. If so, loop again and start all the state mashines again, otherwise leave and shutdown the application

It's hard to tell, but I think you have a pattern where each of your lua scripts defines a global function called "main", and you invoke that in each of them?

So, the issue right now is that (a) all your Lua code runs infinite busy loops, and (b) you have no way of signalling to your threads that they should stop processing, whether because you are shutting down the application or just restarting your scripts.

 

What you probably want to do is change paradigms. For each file that successfully loads as a Lua script, you want to create and register a Lua Module object to communicate with that script. The module is responsible for the lifetime of the Lua State. Lua code can register event listeners and callbacks and such with the Module which they live inside.

 

When your application generates an event (including the "run this code 30 times per second" event, or whatever), you iterate through all modules and raise the relevant event on them. The module wrapper translates the C++ event into a Lua function call, and calls the handler code inside their Lua module. You'll want to take care of threading concerns, and handle runaway Lua code, at this point.

 

This way, when you want to "reload" scripts, all you have to do is tell each Lua Module wrapper to scrap their current state and re-load it from file.



class LuaModule {
private:
	std::string m_path;
	lua_State   *L;

	void        load();
	void        unload();

public:
	LuaModule(std::string path);
	LuaModule(LuaModule &&other);
	~LuaModule();

	void      reload();

	/* ok(); returns true if module is ready to use */
	bool      ok() const;

	/* callGlobal(name); really simple example invoke, returns true if the method exists and completed without error */
	bool      callGlobal(const char *name);
};


void
LuaModule::load()
{
	if( ok() ) {
		// We are already loaded; we have to be unloaded first.
		return;
	}
	if( m_path == "" ) {
		// We can't be loaded, on purpose. See the move-constructor.
		return;
	}

	// Compile the Lua source file in question.
	lua_State* L = luaL_newstate();
	int status = luaL_loadfile(L, m_path.c_str());

	if( status == 0 ) {
		// Link the libraries (which were not necessary to just parse the file).
		luaL_openlibs(L);
		getGlobalNamespace(L)
			.beginClass<CPed>("cped")
			.endClass()                             // закрыть регистрацию класса.
			.beginClass<CVehicle>("CVehicle")       // имя класса авто в lua.
			.endClass()                             // закрыть регистрацию класса.
			.addCFunction("findplayer", findplayer) // возвращает указатель игрока.
			.addCFunction("sethealth",  sethealth)  // название функции в lua и c++. уст здоровье.
			.addCFunction("setarmour",  setarmour)  // название функции в lua и c++. уст броню.
			;

		// Call the top-level block function, to initialize this module.
		lua_pcall(L, 0, 0, 0);

	} else {
		// Error during loadfile; close the state.
		lua_close(L);
		L = nullptr;
	}
}

void
LuaModule::unload()
{
	if( ! ok() ) return;
	
	lua_close(L);
	L = nullptr;
}

LuaModule::LuaModule(std::string path)
	: m_path(path), L(nullptr)
{
	load();
}

LuaModule::LuaModule(LuaModule &&other)
	: m_path(other.m_path), L(other.L)
{
	// Detach the other from its LuaState, and empty its filename to make sure it can't be re-used.
	other.L = nullptr;
	other.m_path = "";
}

LuaModule::~LuaModule()
{
	unload();
}

void
LuaModule::reload()
{
	unload();
	load();
}

bool
LuaModule::ok()
{
	return L != nullptr;
}

bool
LuaModule::callGlobal(const char *name)
{
	if( ! ok() ) {
		// This LuaModule was not ready to be called.
		return false;
	}

	lua_getglobal(L, name);
	if( LUA_TFUNCTION != lua_type(L, -1) ) {
		// Value was not a function; pop whatever it was and return false.
		lua_pop(L, 1);
		return false;
	}

	if( 0 != lua_pcall(L, 0, 0, 0) ) {
		// Call failed with an error (now on-stack)
		lua_pop(L, 1);
		return false;
	}

	// State OK, method existed, and method exited without error.
	return true;
}


// In your game's main loop somewhere...
	switch(event.type) {
		case PLAYER_DAMAGED:
			broadcast("onPlayerDamaged");
			break;
		//...
	}


//...
	std::vector<LuaModule> allModules;
//...
	void loadFile(std::string filename) {
		LuaModule tmp(filename);
		if( tmp.ok() ) {
			allModules.push_back(std::move(tmp));
		}
	}
//...
	void broadcast(const char* event) {
		for(auto& module: allModules) {
			module.callGlobal(event);
		}
	}
//...

///////////////////////////////////////////////////////////////////////
// player-invulnerable.lua

-- Find the player during script init
player = findplayer()

function onPlayerDamaged()
	-- Make the player near-invulnerable by
	-- resetting their health whenever harmed.
	sethealth(player, 200)
	setarmour(player, 250)
end

 

RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.

Thank you very much for your reply, Sharingan. I decided to make a trigger, turned off all scripts. Thank You for your explanations, I will think how to do it.    

Quote


void second(bool& reload) {
	vector<thread>t;//вектор для lua потоков.
	list<lua_State*>luastate;// вектор для lua состояний.
	for (auto const& de : fs::recursive_directory_iterator{ fs::current_path() / "lualoader" }) { // папка для поиска
		if (de.path().extension() == ".lua" || de.path().extension() == ".LUA") {
			string res = de.path().string();// перевод имя файла в строку. 
			t.push_back(move((std::thread(search, res, std::ref(luastate), std::ref(counts)))));// добавить поток в вектор.
		}
	};
			static unsigned int time = 0;// обнулить таймер.

	while (true) {
		if (KeyPressed(VK_CONTROL)) {// перезагрузка скрипта		
			this_thread::sleep_for(chrono::milliseconds(1));
				break;
		};
	};
	for (auto L : luastate){
		if (LUA_TFUNCTION == lua_type(L, -1)) {
			lua_setglobal(L, "lualoader");// уст значение переменной в lua.
			lua_pushinteger(L, 110);  luastate.remove(L);
		}
		else {
			counts--; lua_close(L);  luastate.remove(L);	}
		};	
	if (!luastate.empty()) {
		for (auto L : luastate) {
			 luastate.pop_front();
		};
	};
	while (true) {
		static char message[256];
		snprintf(message, 256, "test = %d", counts);//	CMessages::AddMessageJumpQ(message, 1000, 3);
	if (counts == 0) {
	break;	};	
	};	//};		
	if (CTimer::m_snTimeInMilliseconds - time > 500) {
	time = 0;// обнулить таймер
	CMessages::AddMessageJumpQ(L"Script reloaded", 2000, 3);	
	for (auto& i : t) {
		i.detach();};  // Ждать завершения потоков.
};		
//};
//reload = false; // флаг, что можно запускать новый поток.
};

 


require("lualoader/mod")
model = MODEL_HOTRINB
function main()
while lualoader == nil do
 wait()
 player = findplayer()-- получить игрока
 if keypress(VK_H)-- клавиша h.
 then 
 loadmodel(model)
while keypress(VK_H) do wait(1) end
while not availablemodel(model) do  wait(1) end
x,y,z =getcoordes(player)
y=y+10
car = createcar(model, x,y,z)
releasemodel(model)
setarmour(player, 300)
printmessage("create car and give armour ", 1000)
wait(1900)
end  end
end

Thank you very much Warframe for such a detailed full answer. Yes, You are absolutely right, each lua script has its own state and then, each has a main function. Yes, it can not stop the endless loop in lua scripts, complete the flow. This is a very good way that You suggest, you need to analyze it carefully to understand it better.

This topic is closed to new replies.

Advertisement