Lua 스크립트
Connect Base 게임 서버는 Lua 스크립트로 서버 권위(server-authoritative) 게임 로직을 정의할 수 있습니다.
샌드박스 안에서 실행되며, 룸의 category_id 에 매핑된 스크립트가 자동으로 로드됩니다.
⚠️ 이 페이지는
backend/cmd/game-server/app/scripting/engine.go가 노출하는 실제 전역과 1:1 일치합니다. 다른 글에서 본getState,broadcastEvent,setTimer같은 함수는 존재하지 않으니 사용하지 마세요.
이벤트 핸들러
스크립트 최상위에 다음 함수를 정의하면 게임 서버가 자동으로 호출합니다.
-- 플레이어 입장: clientID 와 userID 가 인자로 전달됩니다
function onJoin(clientID, userID)
log("player joined: " .. clientID)
-- room.state 는 룸의 상태 테이블 그대로 — 직접 읽기 가능
local players = room.state.players or {}
players[clientID] = {
x = 0,
y = 0,
health = 100,
score = 0,
}
room.setState("players", players)
end
-- 플레이어 퇴장
function onLeave(clientID)
local players = room.state.players
if players then
players[clientID] = nil
room.setState("players", players)
end
end
-- 매 틱마다 호출 (deltaTime 단위: 초)
function onTick(deltaTime)
local players = room.state.players or {}
for id, p in pairs(players) do
-- 게임 로직...
end
end
-- 클라이언트가 보낸 액션 처리
-- action 테이블 필드: type, client_id, client_timestamp, sequence, data
function onAction(action)
if action.type == "move" then
handleMove(action.client_id, action.data)
elseif action.type == "shoot" then
handleShoot(action.client_id, action.data)
end
end📌
onJoin,onLeave,onTick,onAction— 이 4개가 게임 서버가 호출하는 유일한 라이프사이클 훅입니다.onRoomCreated,onPlayerJoined,onPlayerLeft같은 이름은 호출되지 않습니다.
room 전역
룸 자체에 대한 접근은 모두 room 테이블을 통해 이루어집니다.
room.id -- 룸 ID (string, read-only)
room.state -- 현재 상태 테이블 (직접 읽기 가능)
room.setState(path, value) -- 상태 필드 수정 (path 는 dot notation, 예: "players.abc.health")
room.getPlayers() -- 현재 플레이어 배열 반환
-- 각 entry: { client_id, user_id, metadata }
room.getPlayer(clientID) -- 단일 플레이어 조회 (없으면 nil)
room.broadcast(data) -- data 를 JSON 직렬화하여 모든 클라이언트에 전송function handleMove(clientID, data)
local player = room.state.players[clientID]
if not player then return end
-- 속도 검증
local dx = data.x - player.x
local dy = data.y - player.y
local distance = math.sqrt(dx * dx + dy * dy)
local maxSpeed = 5
if distance > maxSpeed then
local ratio = maxSpeed / distance
data.x = player.x + dx * ratio
data.y = player.y + dy * ratio
end
player.x = data.x
player.y = data.y
room.setState("players." .. clientID, player)
-- 모든 클라이언트에 알림
room.broadcast({ type = "moved", client_id = clientID, x = player.x, y = player.y })
end유틸리티 전역
-- 로깅
log("hello") -- 1KB 초과 시 잘림
-- JSON
local s = json.encode({ a = 1, b = "x" })
local v, err = json.decode(s)
-- 시간
local nowSec = time.now() -- Unix epoch (초)
-- Lua 표준 라이브러리
math.random(1, 10) -- 표준 math 모듈 사용⚠️
setTimer,setInterval,cancelTimer,broadcastEvent,sendEvent,kickPlayer,getState,setState,deleteState,random,getServerTime같은 전역은 등록되어 있지 않습니다. 주기적인 작업이 필요하면onTick안에서 직접 카운터를 누적하세요.
서비스 바인딩
scripting/services.go 에서 등록하는 추가 전역들 — Connect Base 의 다른 기능을 Lua 안에서 호출할 수 있습니다.
| 전역 | 설명 |
|---|---|
db | 같은 앱의 데이터베이스 read/write |
kv | NATS JetStream KV 스토어 |
http | 외부 HTTP 호출 (timeout/허용 도메인 정책 적용) |
member | 앱 멤버 조회 |
storage | 파일 스토리지 read/write |
function | 다른 Connect Base 함수 호출 |
websocket | 외부 WebSocket 푸시 |
spectator | 관전자 시스템 (spectator_api.go) |
triggers | 트리거 볼륨 (trigger_api.go) |
console | 디버그 로그 (debug.go) |
physics | 물리 엔진 (다음 섹션 참고) |
스크립트 등록
스크립트는 콘솔 → 게임 → 카테고리 메뉴에서 카테고리에 첨부합니다.
같은 category_id 의 룸이 생성되면 해당 스크립트가 자동으로 로드되고 핫 리로드도 지원됩니다.
⚠️ 외부에서 호출 가능한
POST /v1/game/scriptsHTTP 엔드포인트는 존재하지 않습니다. 콘솔 UI 또는 카테고리 관리 API 를 사용하세요.
보안 고려사항
- Lua 스크립트는 샌드박스 환경에서 실행됩니다
- 파일 시스템, 네트워크 접근이 제한됩니다
- CPU 시간과 메모리 사용량이 제한됩니다
- 무한 루프 감지 및 중단됩니다