'루아 전역 함수'에 해당되는 글 1건

  1. 2013.10.11 LUA require

LUA require

개발 2013. 10. 11. 01:41

와우 초창기 시절 애드온 만들어본답시고 깔짝대던 게 사용기의 전부였던 루아가 갑자기 눈앞에 닥쳤다. 일이 닥친 후에 공부해둘걸 하는 후회는 언제나 한발 늦다. ㅅㅂ



문제가 된 코드는 require 함수. 다른 루아 파일을 레퍼런스로 포함해주는 파일로 C의 include 같은 쓰임새를 보이는데 평소라면 문제가 안 되겠지만 내가 닥친 상황에서는 문제가 되었다.


프로젝트에서는 lua_tinker를 사용하였는데 일반적인 파일 입출력으로 다음과 같은 코드를 썼다.


lua_State* state = lua->getLuaState();

lua_tinker::dofile(state, "initialize.lua");


그런데 문제는 저 initialize.lua 라는 파일이 개발 중에는 파일로 있지만 데이터가 패킹된 후에는 파일이 아니라는 것. 저 정도는 다음과 같은 함수로 금방 대체 가능하다.


std::string script = ::getScript("initialize.lua");

lua_tinker::dostring(state, script);


문제는 그 다음이었는데,


lua_tinker::dofile(state, "common.lua");


initialize야 어차피 환경 설정과 경로 설정 등을 제하면 별 일을 하지 않는데 common이란 놈은 앞으로 사용될 녀석들의 바탕이 되는 녀석들이 정의된 파일이기 때문에 그 안에서 require 함수를 호출하고 있었다.


common.lua


require "a"

require "b"

require "c"

...


자, 여기서 require는 루아가 먼저 정의해둔 함수로 내부적으로 알아서 loadfile 등을 이용해 '파일'을 불러오는 동작을 한다. 그런데 우리 파일들은 전부 패킹되어 있잖아? 당연히 경로를 찾을 수 없다는 에러가 와르르 뜨게 되었다.



인고의 몇 시간을 보내고 나서 (별 것 아니라고 생각했는데 의외로 해당 상황에 대한 레퍼런스가 부실했을 뿐 아니라 키워드를 제대로 못 맞춘 건지 버전이 안 맞는 건지 찾은 코드들도 노화되었거나 제대로 동작하지 않는 게 부지기수였다) 다음과 같은 코드를 완성해낼 수 있었다.


int my_require(lua_State* state)

{

const char* name = lua_tostring(state, 1);    // require 다음의 파일을 가져온다

const char* relpaths[] =                      // 상대 경로 조합을 위한

{

"Lua/SubPath/%s.lua",

"Lua/TowPath/%s.lua",

};


for (int i = 0; i < _countof(relpaths); ++i)

{

char bf[256] = {0};

_stprintf(bf, relpaths[i], name);          // 상대 경로를 조합하여 경로 생성


std::string script = ::getScript(bf);

auto err = luaL_loadbuffer(state, script.c_str(), script.size(), bf);

if (err)

lua_error(state);

break;

}


lua_call(state, 0, 1);    // 핵심. 이걸 몰라서 2시간 헤맸다.

return 1;

}


int luaInitialize()

{

...


lua_tinker::def(state, "require", my_require);    // 루아가 만든 함수도 재정의가 가능


lua_tinker::dostring(state, script);

...


return 0;

}


루아가 제공하는 require 함수를 내가 만든 함수로 재정의했으며 직접 파일 내용을 가져와서 버퍼를 셋팅해주게 하였다. 대충 처리한 뒤에는 lua_call 로 마무리를 해주어야 문제가 일어나지 않았다. 최신화된 코드를 못 찾아서 별다른 레퍼런스 없이 어셈블리 코드를 까서 (지금 생각해보면 pdb라도 얻어다 할 걸 하는 생각이 든다) 노가다한 결과물이 되었다.


물론 실제 서비스할 코드는 위와 같이 이상하지 않도록 여러 가지를 더 신경써야 한다. 상대 경로도 하드코딩하는 것이 아니라 루아 전역 변수로부터 가져와야 하고, 기존 require처럼 로딩된 리소스는 다시 로딩하지 않도록 처리해주는 것도 효율을 중시한다면 필요하다.


하지만 난 하지 않겠어...


올리기는 부끄럽지만 한글로 된 관련 내용은 잘 없는 것 같아서 올려본다.


본격 루아를 잘 몰라서 헤맨 오늘의 일기 끝


Posted by OOJJRS
,