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?| playerId | string | 찾을 사람의 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) -> ())| playerId | string | 계정이 지워진 사람의 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)