서버 스크립트

Chain2D.Players

이 채널에 접속해 있는 사람들. 채널마다 이 스크립트가 한 벌씩 돌기 때문에, 여기서 보이는 것은 이 채널의 사람들뿐입니다. 건네받는 Player 는 얼린 표이고 `id`, `channelOrdinal`, 이 사람의 Data 문서를 들고 있습니다. Player 를 받는 호출이 그 표를 얼마나 따지는지는 호출마다 다릅니다. `Chain2D.Network.send` 와 `Chain2D.Channels.move` 는 이 표 자체를 요구해서, 같은 `id` 를 담아 새로 만든 표는 거부됩니다. `Chain2D.Data.player` 는 표가 들고 있는 문서가 그 `id` 의 문서인지를 봅니다. `Chain2D.Maps` 쪽 호출들(`of`, `move`, `bodyOf`, `setOwner`)은 표이고 `id` 가 문자열인지만 보고 그 `id` 를 엔진에 넘깁니다 — 여기서는 손으로 만든 표도 통과합니다.

Players.getPlayerById

선언
Chain2D.Players.getPlayerById(playerId: string) -> Player?
매개변수
playerIdstring찾을 사람의 id. 문자열이 아니면 거부됩니다.
반환

Player?이 채널에 접속해 있으면 그 Player, 아니면 `nil`.

id 로 사람을 찾습니다. 없으면 `nil` 이고 오류가 아닙니다 — 방금 나간 사람의 id 를 아직 들고 있는 일은 흔하고, 그때 받아야 할 답은 "없음"입니다.

local player = Chain2D.Players.getPlayerById(id)
if player == nil then
    return
end
Chain2D.Network.send(player, "hello", { gold = player.data:get("gold", 0) })

Players.getPlayers

선언
Chain2D.Players.getPlayers() -> {Player}
반환

{Player}부른 순간 접속해 있던 사람들. 들어온 순서대로이고, 얼려서 옵니다.

지금 이 채널에 있는 사람 전부를 배열로 돌려줍니다. 프레임워크가 들고 있는 표를 그대로 주지 않고 사본을 떠서 얼립니다 — 이 배열을 도는 동안 누가 들어오거나 나가도 배열은 그대로입니다. 사본인 것은 배열이고, 안에 든 Player 는 프레임워크가 쥐고 있는 그 표 그대로입니다.

local ids = {}
for _, player in ipairs(Chain2D.Players.getPlayers()) do
    table.insert(ids, player.id)
end
Chain2D.Network.broadcast("roster", { ids = ids })

Players.playerAdded

선언
Chain2D.Players.playerAdded(handler: (player: Player) -> ())
매개변수
handler(player: Player) -> ()사람이 이 채널에 들어올 때마다 불립니다. 불릴 때는 이미 `getPlayerById` 와 `getPlayers` 가 이 사람을 답으로 내놓고, 이 사람의 Data 문서도 열려 있습니다.

사람이 들어오는 것을 듣습니다. 핸들러가 불리는 시점에 이 사람은 이미 기본 맵(`Chain2D.Map`)에 들어가 있습니다 — 맵을 한 번도 말하지 않는 게임이 맵 하나짜리 게임인 것은 이 때문입니다. 핸들러는 하나만 등록할 수 있고, 함수가 아닌 것을 건네거나 이미 등록된 뒤에 다시 부르면 거부됩니다.

Chain2D.Players.playerAdded(function(player)
    local gold = player.data:get("gold", 0)
    Chain2D.Network.send(player, "wallet", { gold = gold })
end)

Players.playerRemoving

선언
Chain2D.Players.playerRemoving(handler: (player: Player) -> ())
매개변수
handler(player: Player) -> ()사람이 나가기 직전에 불립니다. 아직 접속한 사람으로 취급되므로 `Chain2D.Data.player(player)` 도, `Chain2D.Maps.of(player)` 도, 이 사람의 몸도 여기서는 답을 줍니다.

사람이 나가는 것을 듣습니다. 게임이 할 말을 다 한 뒤에 잊습니다 — 핸들러가 끝난 뒤에야 이 사람의 Data 문서가 저장되고 표에서 지워지고, 맵도 이 사람을 잊습니다. 맵이 잊는 것은 이 사람이 어느 맵에 있었는지뿐입니다. 몸은 그대로 남습니다 — 내리는 것은 이 핸들러의 일이고, 엔진은 주인이 나갔다는 이유로 몸을 치우지 않습니다. 순서가 반대였을 때, 즉 먼저 잊고 핸들러를 부르던 때에는, 어느 맵에 있었는지를 묻는 답이 `nil` 이 되어 디스폰이 실행되지 않았고, 몸은 채널이 사는 동안 맵에 남아 뒤에 들어온 모두에게 보였습니다. 저장은 프레임워크가 합니다. 핸들러에서 `set` 만 해 두면, 핸들러가 돌아온 직후 그 문서가 저장됩니다. 핸들러는 하나만 등록할 수 있습니다.

Chain2D.Players.playerRemoving(function(player)
    local map = Chain2D.Maps.of(player)
    local body = map and map.bodyOf(player)
    if body ~= nil then
        map.despawn(body)
    end
end)

Players.playerDeleted

선언
Chain2D.Players.playerDeleted(handler: (playerId: string) -> ())
매개변수
playerIdstring계정이 지워진 사람의 id. Player 표가 아니라 문자열입니다 — 그 사람은 여기 없습니다.

계정이 삭제된 사람을 듣습니다. 접속 종료가 아닙니다 — 그 사람은 이 채널에 없고, 몇 달 전부터 없었을 수도 있습니다. Chain2D 는 자기가 가진 것(세이브, 정체성, 세션)을 지웁니다. 게임의 공유 Data 에 박힌 playerId 는 게임만 지울 수 있습니다 — 랭킹에서 이름을 지울지 익명으로 바꿀지, 길드장이면 누구에게 넘길지를 아는 쪽은 게임뿐입니다. 핸들러가 끝나야 처리된 것으로 칩니다. 처리했다는 보고는 핸들러가 돌아온 뒤에 나가고, 중간에 실패하면 그 사람은 처리되지 않은 채로 남아 다음에 다시 옵니다 — 메시지 하나를 처리된 것으로 세어 버려서 아무도 지우지 않는 이름이 남는 것보다 낫습니다. 핸들러는 하나만 등록할 수 있습니다.

Chain2D.Players.playerDeleted(function(playerId)
    local board = Chain2D.Data.game()
    board:remove("score:" .. playerId)
    board:save()
end)

Player.id

선언
player.id: string

이 사람의 id. `getPlayerById` 가 찾는 것이고, 몸의 주인을 정할 때 엔진에 건네는 것도 이 문자열입니다. 계정이 지워지면 `playerDeleted` 가 들고 오는 것도 Player 가 아니라 이 문자열 하나입니다.

Player.channelOrdinal

선언
player.channelOrdinal: number

이 사람이 지금 있는 채널 번호. 1 부터 셉니다. 프레임워크는 들어오는 일들의 채널을 이 값과 대조해서, 예상하지 못한 채널에서 온 접속 종료나 메시지를 조용히 넘기지 않고 오류로 만듭니다.

Player.data

선언
player.data: Document

이 사람만의 Data 문서. `Chain2D.Data.player(player)` 가 돌려주는 것과 같은 표이고, `get`/`set`/`remove`/`save` 를 콜론으로 부릅니다. 저장은 대체로 프레임워크가 맡습니다 — 바뀐 문서는 5초마다 저장되고, 이 사람이 나갈 때 `playerRemoving` 이 끝난 직후 한 번 더 저장됩니다.

Chain2D.Players.playerAdded(function(player)
    local visits = player.data:get("visits", 0)
    player.data:set("visits", visits + 1)
end)