여기선 팩토리오 core에서 제공하는 기본 event_handler 루아 모듈하고,
많이 사용하는 comfy, redmew 시나리오의 이벤트 핸들러 사용법을 알아본다.
모드와 시나리오는 일부 공유된 부분을 제외한 나머지는 샌드박스화된 별도의 공간을 가지고 있기 때문에 단순하게 만들 모드는 전혀 신경쓸 필요가 없기도 하다.
https://lua-api.factorio.com/latest/LuaBootstrap.html
script 객체에는 이벤트를 등록하는 함수 on_event라는게 있는데,
on_event(event, f, filters)
이 함수를 사용해서 어떤 이벤트ID(event)에 함수=핸들러(f)를 등록하게 되면 그 이벤트ID에 붙어있던 이전 핸들러는 지워지고 새로운 핸들러로 대체되게 된다.
(f 자리에 nil을 쓰면 아예 등록해제.)
script객체는 각 모드와 시나리오마다 따로 지정이 되어있기 때문에 각 모드나 시나리오 자신의 영역으로 한정된다.
lua파일을 모듈화해서(여러개 써서) 같은 이벤트ID에 대해서 소스코드 이곳저곳에서 여러가지 핸들러를 붙여주려고할 때엔 기존것이 지워지지 않게 핸들러를 덧붙이기 위해서 이벤트 핸들러를 처리해주는 lua 모듈을 만들어쓰게 된다.
팩토리오에서 기본적으로 제공해주는것은 core/lualib/event_handler.lua 이다.
----------------------------------------------
기본 core/lualib/event_handler.lua 의 사용법
----------------------------------------------
사용법은 대충 이벤트 핸들러 등록이 필요한 내용이 있는 각 lua파일의 상단에
local handler = require("event_handler")
또는
local handler = require("__core__.lualib.event_handler")
이렇게 지역변수로 호출해온다음에, (변수명 handler도 암거나 해도 된다.)
local anytable = {}
아무이름으로 테이블을 하나 만들어주고,
anytable.events = {}
그 안에 events라는 이름의 테이블을 또만들고
anytable.events[defines.events.on_player_created] = function(e) 블라블라 end
그 events안에 이벤트ID(숫자형)를 키이름으로하는 함수를 넣어주는데,
한번에 테이블을 바로 작성할 땐,
local 함수2 = function(e) 블라블라 end
anytable.events = {
[defines.events.on_tick] = function(e) 두두두두 end,
[defines.events.on_player_created] = 함수2,
...
}
이런식으로 이벤트ID 하나당 함수 하나씩 대입시켜준다.
이외에 아래같은 이벤트 처리용 키가 있다.
anytable.on_load = 어떤함수1
anytable.on_configuration_changed = 어떤함수2
anytable.on_init = 어떤함수3
anytable.on_nth_tick = {
[3] = 3의배수틱마다실행할함수,
[60] = 60의배수틱마다실행할함수,
...
}
그렇게 만든 anytable이라는 테이블을
handler.add_lib(anytable)
handler.add_lib(anytable2)
handler.add_lib(anytable3)
...
이런식으로 등록해주면 저 event_handler.lua의 내부에 있는 libraries라는 지역변수 테이블에 저장이 된다.
require된 event_handler.lua의 지역변수는 하나로 통일되어 있는 듯해서
시나리오의 control.lua에서 event_handler.lua를 require를 하고 add_lib을 수행했다가,
하위 lua에서 따로 추가로 event_handler.lua를 require를 하고 add_lib을 수행하더라도
require되고 사용될 때마다 이벤트 테이블 정보는 덧씌워지지않고 정상적으로 누적되는거 같더라.
즉, 이벤트 등록을 해주고 싶은 시점 아무때나 event_handler.lua를 require해서 포맷을 맞춰서 만든 테이블을 add_lib으로 등록해주면 같은 이벤트ID라도 이전정보를 지우지 않고 연달아서 실행하게끔 예약하게 된다.
event_handler.lua 소스코드를 보면 알 수 있겠지만,
스크립트가 완전히 다 읽어지고 나서 on_load같은 것들이 실행될 시점에 보관해둔 핸들러들을 순차적으로 실행하는 함수를 만들어서 그 함수를
script.on_event(event, f)
에서 f위치에 넣고 등록시키는 내용이 있다.
----------------------------------------------------
comfy, redmew 시나리오의 이벤트 핸들러 사용법
----------------------------------------------------
이 개쩌는 시나리오 제작팀 분들은 기본 이벤트 핸들러 모듈이 뭔가가 맘에 안드셨는지 따로 만들어서 쓰고 계신다.
누가 가장 처음 만들었는지는 모르겠지만 기본 포맷은 똑같은듯.
이벤트를 없앴다 추가했다 하는게 가능하게 하려고 한것 같은데 게임중에 굳이 시나리오 시작부터 등록되있던 이벤트 등록을 해제할 일이 있을까... 세이브파일에 저장도 안되는 영역이라 디싱크 위험성 때문에 싱크를 맞춰주는 전후처리가 더 귀찮을거 같은데.
아무튼 자세한 사용법은 각 시나리오의 utils 폴더안의 event.lua 파일의 코멘트에 잘 정리해둔 것 같다.
사용법을 요약하자면 기본 event_handler와 큰 차이는 없는데,
local Event = require("utils.event")
이렇게 해준다음에, (변수명 Event도 암거나 해도 된다.)
local 함수2 = function(e) 블라블라 end
Event.add(defines.events.on_tick, function(e) 두두두두 end)
Event.add(defines.events.on_player_created, 함수2)
...
이런식으로 쓰면 될거 같다.
script.on_event 이외의 이벤트는
Event.on_load(어떤함수1)
Event.on_init(어떤함수3)
Event.on_nth_tick(3, 3의배수틱마다실행할함수)
Event.on_nth_tick(60, 60의배수틱마다실행할함수)
...
대강 이런식으로 쓰라는 것 같다. 쓰고보니 좀 더 단순하게 써도 되는듯...?
Event.add_removable_function(event_name, func)으로 떼었다 붙였다 할 수 있다는거 같은데, 솔직히 말해서 중간에 서버에 참여하는 일이 잦은 멀티환경에서 세이브파일에 저장이 안되는 함수데이터같은것들은 중간에 변경하면 중간에 참여한 사람이 기존 사람과 같은 함수 데이터를 가지도록 싱크맞추는게 100배는 더 귀찮아서 겜 중간에 뗄 필요가 없다고 생각함. 이벤트 등록 상태를 중간에 변조한 시점 이후에 서버에 참여하는 사람은 비동기로 튕길 위험성이 존재하게 된다.
utils 폴더의 token.lua라는걸 써서 global 테이블안에 함수를 작성하라고 코멘트가 적혀있는게 보이던데, 옛날 버전엔 어땠는지 모르겠는데, 지금 버전의 팩토리오는 global 테이블안에 함수는 세이브파일에 저장이 안되고 증발한다.
지금의 팩토리오는 함수, 이벤트 등록상태, 커맨드 등록상태, remote등록상태 등등 같은건 세이브가 안되는 휘발성 데이터라 on_load단계에서 다시 빌드업해서 세이브 직전 상태와 싱크를 맞추게끔 권장되어있다. 세이브가능한 데이터는 global테이블안에 저장하라고 권장하고 있고.
이 시나리오들에서 token은 이제는 쓸 필요가 없어진 옛날의 유물같은거라고 생각한다.
휘발성 데이터는 그냥 표기없이 전역변수 선언해버리면되고, 비휘발성데이터는 global 테이블안에 이쁘게 이름지어서 넣어두면 되니까.
옛날에는 팩토리오의 이벤트 처리과정이 어땠는지 잘 모르지만 옛날에 만들어서 오랜기간 써온터라 옛날 시나리오나 모듈과의 호환성을 위해서 그냥 사용하는 듯.
기본 event_handler와 마찬가지로 하위 lua 파일 어느곳에서든 새로 require해서 이벤트 핸들러를 등록해줘도 되는거 같다.
------------------------------------------------------------------
이벤트를 게임 도중에 켜고 끄려면 그냥 global 테이블안에 스위치 역할을 하는 값을 하나 넣어두고 각 핸들러가 실행이 될때, 첫줄에서 그 스위치를 읽고 바로 return하게 하거나 계속 실행하게하면 된다.
진짜로 이벤트 등록상태를 도중에 삭제하는걸 구현하려면 이벤트 핸들러 테이블이 중간에 참여한 사람과 서버가 동기화 되도록 도중에 일어난 변조과정이 on_load단계에서 동일하게 수행되도록 만들어야한다.
댓글 0