서버 스크립트

Chain2D.Data

세 개의 서랍 — 게임 전체, 이 렐름, 한 사람. 무엇을 어디에 두는지가 곧 누가 그것을 볼 수 있는지입니다. 세 함수는 모두 같은 모양의 문서를 돌려주고, 문서에는 `get`, `set`, `remove`, `save` 네 가지가 있습니다. `set` 과 `remove` 는 런타임이 들고 있는 사본만 고치고 문서를 바뀐 것으로 표시할 뿐입니다 — 실제로 중앙에 쓰는 것은 5초마다 도는 자동 저장과 `save` 입니다. 자동 저장은 바뀐 문서를 전부 저장하고, 실패하면 로그에 남기고 문서를 바뀐 채로 두어 다음 번에 다시 시도합니다. 사람이 나갈 때는 `playerRemoving` 이 끝난 뒤 그 사람의 문서를 한 번 저장하고, 서버가 정상 종료할 때는 남은 문서를 전부 저장합니다.

Data.game

선언
Chain2D.Data.game() -> Document
반환

Document게임 하나에 중앙 문서 하나. 이 게임의 모든 렐름이 그것을 나눠 쓰고, 채널마다 자기 사본을 들고 있습니다.

게임 전체가 함께 쓰는 문서를 돌려줍니다. 랭킹, 공지, 시즌처럼 렐름이 달라도 같아야 하는 것들이 여기에 있습니다. 처음 부를 때 한 번 중앙에서 읽어 오고 그 뒤로는 같은 문서를 돌려줍니다 — 부를 때마다 왕복하지 않습니다. 그 첫 한 번은 네트워크를 건너가므로 부른 스레드가 멈추지만, 멈추는 것은 그 스레드뿐이고 시뮬레이션과 다른 스레드는 계속 돕니다. 읽어 온 뒤로 이 채널의 사본이 중앙과 다시 맞춰지는 것은 `save` 가 병합 결과를 받아 올 때뿐입니다. 그래서 아래처럼 읽어서 더하는 것은 정확한 카운터가 아닙니다 — 여러 채널이 같은 키를 함께 올리면 마지막으로 끝난 저장의 값이 남습니다. 인자를 하나라도 주면 "Data.game expects no arguments" 로 거부됩니다.

local game = Chain2D.Data.game()
local visits = game:get("visits", 0)
game:set("visits", visits + 1)
game:save()

Data.realm

선언
Chain2D.Data.realm() -> Document
반환

Document이 렐름의 문서. 같은 렐름의 채널들이 중앙의 같은 문서를 나눠 쓰지만, 채널마다 자기 사본을 들고 있습니다.

이 렐름 안에서만 공유하는 문서를 돌려줍니다. `Data.game` 이 돌려주는 것과 같은 모양의 문서이고, 범위만 다른 별개의 문서입니다 — 처음 부를 때 읽어 오고, 그 뒤로는 같은 것을 돌려주며, 인자를 주면 "Data.realm expects no arguments" 로 거부됩니다. 다른 채널이 같은 문서를 고쳐도 이 채널의 사본은 저장하기 전까지 예전 값을 들고 있습니다. 채널끼리 지금 무슨 일이 일어났는지를 알리는 수단으로는 쓰지 마세요.

local realm = Chain2D.Data.realm()
realm:set("weather", "rain")
realm:save()

Data.player

선언
Chain2D.Data.player(player: Player) -> Document
매개변수
playerPlayer지금 접속해 있는 사람. `Chain2D.Players` 나 콜백이 건넨 그 객체여야 합니다. 흉내 낸 표나 이미 나간 사람은 거부됩니다.
반환

Document이 사람의 문서. `player.data` 와 같은 것입니다.

한 사람의 문서를 돌려줍니다. 이 문서는 그 사람이 접속할 때 이미 읽혀 있으므로 여기서는 왕복이 없습니다 — `playerAdded` 가 불리는 시점에 `player.data` 는 준비되어 있습니다. 읽어 오지 못했다면 빈 문서를 정상인 것처럼 건네지 않습니다. 오류는 문서를 만드는 접속 처리에서 나고 그 접속 자체가 실패하므로, 그 사람에 대해서는 `playerAdded` 도 불리지 않습니다. 그 사람이 나가면 문서는 `playerRemoving` 이 끝난 뒤 한 번 저장되고 자동 저장 목록에서 빠지며, 그 뒤로는 이 함수가 그 사람을 알지 못합니다.

언제접속 중인 Player 에 대해서만. `playerRemoving` 안에서는 아직 유효하고, 그 콜백이 끝난 뒤부터 아닙니다. 나간 사람이나 Player 가 아닌 값을 건네면 "Data.player expects a connected Player" 로 거부됩니다.

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

Document.get

선언
document:get(key: string, defaultValue: any?) -> any
매개변수
keystring1바이트에서 128바이트 사이의 문자열. 그 밖의 것은 "Game Data key must be a string between 1 and 128 bytes" 로 거부됩니다. 네 가지가 모두 같은 규칙을 씁니다.
defaultValueany?그 키가 문서에 없을 때 대신 돌려줄 값. 생략하면 `nil` 입니다.
반환

any저장된 값의 복사본. 키가 없으면 `defaultValue` 의 복사본.

문서에서 값을 읽습니다. 런타임이 들고 있는 사본에서 읽으므로 왕복이 없고 멈추지도 않습니다 — 매 틱 불러도 네트워크로 나가는 것은 없습니다. 돌려주는 것은 복사본입니다. 받은 표를 고쳐도 문서는 변하지 않으니, 고쳤으면 `set` 으로 다시 넣어야 합니다. 네 가지 모두 점이 아니라 콜론으로 부릅니다 — `document.get("gold")` 처럼 부르면 첫 인자가 문서 자리로 밀려 들어가고 키 자리는 비게 되므로, 문서가 자기 자신인지 보는 검사에 걸려 "Data document get expects key and optional default" 로 거부됩니다.

local data = Chain2D.Data.player(player)
local gold = data:get("gold", 0)
print(player.id, gold)

Document.set

선언
document:set(key: string, value: any)
매개변수
keystring1바이트에서 128바이트 사이의 문자열.
valueanyJSON 으로 쓸 수 있는 값. `nil` 은 "Data document set does not accept nil; use remove" 로 거부됩니다 — 지우는 것은 `remove` 이고, 둘은 다른 일입니다. 표는 넣는 그 자리에서 JSON 을 거쳐 복사되므로, 함수처럼 JSON 으로 쓸 수 없는 것이 표 안에 있으면 `set` 에서 바로 오류가 납니다.

값을 넣습니다. 사본을 지금 바꾸고 문서를 바뀐 것으로 표시할 뿐이고, 중앙에 쓰는 것은 자동 저장이나 `save` 입니다. 넣는 값도 복사됩니다 — 넣은 뒤에 그 표를 고쳐도 문서는 그대로입니다. game 과 realm 문서는 여기서 바뀐 키를 따로 적어 둡니다. 저장할 때 문서 전체가 아니라 그 키만 보내기 위해서이고, 그래야 다른 채널이 그 사이에 바꾼 키가 이 채널의 오래된 사본으로 되돌아가지 않습니다.

local data = Chain2D.Data.player(player)
data:set("gold", data:get("gold", 0) + 10)
data:set("lastMap", "town")

Document.remove

선언
document:remove(key: string)
매개변수
keystring1바이트에서 128바이트 사이의 문자열.

키를 문서에서 지웁니다. 값을 `nil` 로 두는 것과 지우는 것을 구분하기 위해 따로 있습니다. 없던 키를 지우면 아무 일도 일어나지 않고 문서는 바뀐 것으로 표시되지도 않습니다. `set` 과 마찬가지로 사본만 고치고, 중앙에서 사라지는 것은 저장이 끝난 뒤입니다.

local data = Chain2D.Data.player(player)
data:remove("temporaryQuest")
data:save()

Document.save

선언
document:save()

지금 저장하고, 중앙 저장이 끝난 뒤에 돌아옵니다. 기다리는 것은 부른 스레드뿐이고 시뮬레이션과 다른 스레드는 계속 돕니다. 보상이나 결제처럼 저장이 끝난 것을 확인해야 하는 자리에 씁니다 — 나머지는 자동 저장이 합니다. 바뀐 것이 없으면 아무것도 하지 않고 바로 돌아옵니다. player 문서는 읽어 온 version 과 함께 문서 전체를 보내고, 그 사이 중앙의 version 이 달라졌으면 거부됩니다. game 과 realm 문서는 바뀐 키만 보내며, 중앙이 최신 문서 위에 그 키들을 병합한 결과를 돌려주고 이 채널의 사본은 그것으로 갈아치워집니다. 실패는 오류로 납니다. 그리고 실패한 문서는 바뀐 채로 남아 다음 자동 저장 때 다시 시도됩니다 — 쓰지 못한 변경을 조용히 버리지 않습니다. 크기 한도는 보내는 것에 걸립니다 — player 문서는 JSON 으로 만든 문서 전체가 64KiB 를 넘으면, game 과 realm 문서는 한 번에 보내는 변경 키 묶음이 128KiB 를 넘으면 거부되고, 중앙이 돌려주는 문서도 64KiB 까지만 읽습니다. 데이터 소켓 없이 띄운 런타임에서는 문서가 빈 채로 시작하고 이 호출은 아무 곳에도 보내지 않습니다 — 그러면서도 문서의 바뀐 표시는 지우므로, 그 런타임에서 쓴 것은 어디에도 남지 않습니다.

local data = Chain2D.Data.player(player)
data:set("gold", data:get("gold", 0) + 100)
data:save()
Chain2D.Network.send(player, "reward", { gold = data:get("gold") })