플레이어 계정
게임의 플레이어에게 모든 Aukimi 게임에서 유지되는 계정을 제공하세요. 세이브 데이터는 한 기기에서 다른 기기로 따라오며, Aukimi의 서비스에서든 자체 서버에서든 작동합니다.
한 줄로#
플레이어는 play.aukimi.com에서 한 번 로그인하면 Aukimi로 만들어진 모든 게임에서 그 계정을 발견합니다. 세이브 데이터도 포함해서 말이죠. 게임은 계정을 요청할 뿐, 비밀번호를 다루는 일은 절대 없습니다.
게임이 절대 비밀번호를 보지 않는 이유#
누구나 게임을 게시할 수 있습니다. Aukimi 비밀번호를 요구하는 게임이 있다면 그것은 완벽하게 그럴듯한 피싱 페이지가 될 것이며, 사용자의 선한 의도가 아무리 크더라도 다른 개발자가 무엇을 만들어 게시할 수 있는지를 바꿀 수는 없습니다.
그래서 비밀번호는 play.aukimi.com에서만 입력됩니다. 게임은 PlayerLoginAsync()를 호출하고, Aukimi 고유의 색으로 된 브라우저 창이 열리며, 플레이어가 승인하면 그 창이 게임에 토큰을 건네줍니다.
그 토큰은 오직 그 게임만을 위해 발급됩니다. 게임 A를 위한 토큰을 게임 B에 제시하면 거부됩니다. 무시되는 것이 아니라 거부됩니다. 바로 그것이 핵심입니다: 계정은 게임들 사이에 공유되지만, 접근 권한은 그렇지 않습니다. 이것이 없다면 악의적인 게임 하나가 다른 모든 게임에서 플레이어의 세이브 데이터를 읽을 수 있을 것입니다.
플레이어에 대해 받게 되는 것#
딱 두 가지, 그 이상은 없습니다.
| 받는 것 | 절대 받지 않는 것 |
|---|---|
표시 이름 (PlayerName()) | 이메일 주소 |
불투명한 id, 게임마다 다름 (PlayerID()) | 다른 곳에서 그 사람을 식별할 수 있는 그 무엇이든 |
이 id가 게임마다 바뀌는 것은 의도적입니다. 두 개발자가 각자의 플레이어 목록을 비교해도 같은 사람을 보고 있다는 것을 알 수 없습니다. 이는 또한 사용자를 그 데이터의 컨트롤러가 아니라 프로세서로 유지하는데, 이는 법적인 구분이지 장식적인 것이 아닙니다.
게임 루프는 아무것도 막지 않습니다#
네트워크와 통신하는 모든 명령은 결과가 아니라 job 번호를 돌려줍니다. 게임 루프는 요청이 진행되는 동안에도 초당 60프레임으로 계속 실행됩니다. 이것이 애초에 게임이 "서버를 그냥 기다릴" 수 없는 이유 전체입니다.
job = PlayerLoginAsync()
그다음, 매 프레임마다:
etat$ = PlatformJobState(job)
if etat$ = "done"
resultat$ = PlatformJobResult(job)
PlatformJobRelease(job)
endif
if etat$ = "error"
Print(PlatformJobError(job))
PlatformJobRelease(job)
endif
결과를 읽은 뒤에는 job을 해제하세요. 한 번도 해제되지 않은 job은 세션이 끝날 때까지 그 결과를 메모리에 계속 붙들고 있습니다. 아무것도 깨지지 않고 아무 경고도 없으며, 이 누수는 긴 플레이 세션에서만 드러납니다.
계정 없이 플레이하기#
모두가 게임을 시도해보기 전에 가입하고 싶어하는 것은 아닙니다. PlayerGuestAsync(name$)는 이메일이 없는, 이 브라우저에만 남는 계정을 만듭니다.
이는 진짜 계정입니다: 저장되고, 불러와지며, 나중에 클레임될 수 있습니다. 플레이어가 이메일과 비밀번호를 추가하면, 기존 세이브 데이터는 이동 없이 그대로 따라옵니다.
솔직한 단점은 이렇습니다: 브라우저의 사이트 데이터를 지우면 그 계정을 잃게 되고, 복구할 방법이 없습니다. 그것이 "가입 없음"의 대가이며, 게임은 플레이어가 나중에 스스로 알아차리게 두는 대신 이를 알려야 합니다. PlayerIsGuest()는 정확히 그런 문장을 위해 존재합니다.
저장하기#
job = PlayerSaveAsync(donnees$)
job = PlayerLoadAsync()
세이브 데이터는 원하는 형태의 JSON이며, (이 플레이어, 이 게임) 쌍을 기준으로 저장됩니다. 다른 브라우저, 다른 기기, 다른 플랫폼까지 따라옵니다.
몇 개의 값만 다룬다면 SetCloudDataVariable()과 CloudDataVariable()이 더 간단합니다. job도 없고 JSON도 없습니다. 먼저 CloudDataAllowed()를 확인하세요. 로그인한 플레이어가 없으면 0을 돌려주며, 그런데도 쓰기를 시도하면 조용히 아무 곳에도 가지 않습니다.
화면 그리기#
Aukimi는 로그인 폼을 그리지 않습니다. 엔진이 구성한 폼은 언제나 픽셀 아트 게임 위에 떨어진 웹페이지처럼 보일 것이며, 그것은 사용자가 다시 스타일링할 수 없는 게임의 유일한 부분이 될 것입니다.
직접 그리세요. 브라우저에서 반드시 와야 하는 유일한 부분은 텍스트 입력이므로, CreateEditBox()는 사용자가 직접 배치하는 진짜 입력 필드를 제공합니다.
CreateEditBox(1)
EditBoxPosition(1, 20, 42)
EditBoxSize(1, 60, 8)
nom$ = EditBoxText(1)
Engine의 샘플 목록에 있는 Player Account 데모는 약 백 줄로 이루어진 완전히 작동하는 화면입니다. 열어서 읽어보고, 모든 Print()를 사용자 자신의 아트워크로 바꾸세요.
이것이 작동하기 전에#
게임은 사용자의 Aukimi 계정에서 My games(aukimi.com/app/games) 아래에 선언되어 있어야 합니다: 제목, 플레이될 수 있는 주소, 그리고 게임이 돌아갈 수 있도록 허용된 주소들입니다. 그전까지는 PlayerAvailable()이 0을 돌려주며, 좋은 게임은 멈춘 것처럼 보이는 대신 "계정을 사용할 수 없습니다"라고 말합니다.
같은 화면에서 누가 플레이하고 있는지도 볼 수 있습니다. 이 숫자들을 있는 그대로 읽으세요: 로그인한 플레이어와 시작된 세션을 세는 것이지, 플레이된 게임 수를 세는 것이 결코 아닙니다. 아무에게도 로그인을 요구하지 않는 게임은 여기에 아무것도 보여주지 않으며, 화면은 조용한 0을 보여주는 대신 그렇다고 말해줍니다.
자체 계정 서버 사용하기#
지금까지의 모든 내용은 Aukimi의 계정 서비스와 통신합니다. 게임을 자체 서버에서 호스팅한다면, 같은 열두 개의 명령이 사용자의 서비스와 대신 통신할 수 있으며, 스크립트는 한 줄도 바뀌지 않습니다.
Engine에서: Multiplayer 패널 → Player accounts 섹션 → My own server 선택 → 필드 하나를 채웁니다, 사용자 서버의 주소입니다. 그 주소는 씬의 설정이므로, 두 게임이 두 개의 서로 다른 서비스를 쓸 수 있습니다.
플레이어를 로그인시키는 두 가지 방법#
선택은 설정이 아니라 스크립트 안에서 이루어집니다. 어떤 서버를 가리키든 두 형태 모두 존재합니다.
창 (이미 알고 있는 방식)#
job = PlayerLoginAsync()
창이 열리고, 플레이어가 그 안에서 로그인하며, 그것이 게임에게 대신 행동할 권리를 넘겨줍니다. Aukimi의 서비스를 쓴다면 그 창은 play.aukimi.com입니다. 자체 서버를 쓴다면, 그 페이지는 사용자가 직접 만드는 것입니다.
직접, 사용자 이름과 비밀번호로#
job = PlayerLoginAsync(username$, password$)
창이 없습니다. 게임은 사용자 자신의 화면에서 플레이어가 입력한 것을 읽어 서버로 보냅니다. 작성하기 더 간단하며, 처음부터 끝까지 게임의 모습을 그대로 유지할 수 있게 해줍니다.
경고: 이 형태에서는 비밀번호가 게임에서 서버로 입력된 그대로 이동합니다. 두 가지 결과가 확고하게 따라옵니다: 서버의 주소는
https://로 시작해야 하며(자신의 기기에서의 테스트만 예외입니다), 이 형태는 오직 사용자 자신이 소유한 서버를 위한 것입니다.
play.aukimi.com은 이를 의도적으로 거부합니다. Aukimi는 플레이어의 비밀번호를 볼 수 있는 위치에 절대 있고 싶어하지 않으며, 사용자가 작성하지 않은 게임도 마찬가지여야 합니다.
두 선택지는 설정과 무관합니다: 자체 제작 서버는 창 방식만 제공할 수도 있고, 비밀번호를 직접 받는 것을 결코 허용하지 않을 수도 있습니다.
구체적인 예시#
자체 "로그인" 화면이 있는 게임: 게임이 그린 두 개의 텍스트 필드, 버튼 하나, 그리고 그 버튼 뒤에 있는 다음 내용입니다.
job = PlayerLoginAsync(typedName$, typedPassword$)
DO
etat$ = PlatformJobState(job)
if etat$ = "done"
PlatformJobRelease(job)
Print("Welcome " + PlayerName())
endif
if etat$ = "error"
PlatformJobRelease(job)
Print(PlayerLastError())
endif
Sync()
LOOP
로그인 이후의 모든 것은 이 페이지의 나머지 부분과 동일합니다: PlayerSaveAsync, PlayerLoadAsync, PlayerName과 그 외의 것들은 어떤 서버가 응답했는지 알지 못하고, 신경 쓰지도 않습니다.
서버가 응답해야 하는 것#
어떤 로그인 방식을 제공하든, 네 개의 주소가 필요합니다.
| 무엇을 위한 것인가 | 게임이 기대하는 것 |
|---|---|
| 게스트로 플레이 | 이메일이 없는 계정을 돌려줌 |
| 이 플레이어는 누구인가 | 표시 이름, id, 게스트 여부 |
| 세이브 읽기 | 저장된 데이터 |
| 세이브 쓰기 | 게임이 보낸 것을 저장함 |
그리고, 제공하기로 선택한 것에 따라:
- 창 형태는 서버에 로그인 페이지가 필요하며, 완료되면 누가 로그인했는지 게임에 알려줘야 합니다.
- 직접 형태는 사용자 이름과 비밀번호를 받아 예/아니오로 답하는 주소가 하나 더 필요합니다.
정확한 주소, 각 응답의 형태, 에러 코드는 저장소의 docs/engine-player-accounts.md에 있습니다. 그 파일이 계약이고, 이 페이지는 지도입니다.
참고: 네이티브 내보내기에서는 이 중 아무것도 작동하지 않습니다.
Player*명령들은 통신할 서버와 그것을 통할 브라우저를 필요로 하며, 네이티브 빌드에는 둘 다 없습니다.
명령들#
| 명령 | 하는 일 |
|---|---|
PlayerAvailable() | 이 게임에 계정 서비스가 설정되어 있는가 |
PlayerLoginAsync() | 로그인 창을 엽니다. job을 돌려줌 |
PlayerLoginAsync(user, pass) | 직접 로그인합니다, 오직 자체 서버에서만. job을 돌려줌 |
PlayerGuestAsync() | 이메일 없는 계정을 만듭니다. job을 돌려줌 |
PlayerLoggedIn() | 지금 누군가 로그인해 있는가 |
PlayerName() | 표시 이름 |
PlayerID() | 이 게임에 한정된 불투명한 id |
PlayerIsGuest() | 이메일이 없는 계정인가 |
PlayerSaveAsync() | 세이브를 씁니다. job을 돌려줌 |
PlayerLoadAsync() | 다시 읽어옵니다. job을 돌려줌 |
PlayerRefreshAsync() | 토큰이 만료되기 전에 갱신함 |
PlayerLogout() | 이 기기에서 로그아웃함 |
PlayerLastError() | 마지막 호출이 실패한 이유 |