사용자:Senouis/위키스크립트 규격 제안
위키스크립트 설계 v.0.0.1 by Senouis
미디어위키 문법은 기본적으로 HTML만 대체한다. 따라서 프로그래밍 언어가 아니며, 위키 문법 만으로는 죽었다 깨어나도 실시간 렌더링이 안 된다.
그러나 CSS로 상태 머신(State Machine)을 구현하면 프로그래밍 언어로 만들 수 있으며, 따라서 적절한 변환을 거쳐 위키 문법과 유사한 형태의 스크립팅이 가능하다.
다만, CSS 상태 머신만 이용하게 하면 페이지 레이아웃을 해칠 가능성이 크다. 가능하면 HTML canvas 요소(Element)를 사용해 게임을 만들어야 한다.
이에 따라 미디어위키 문법을 살짝 변형하여 완벽한 프로그래밍이 가능한 위키스크립트를 제안한다.
사용법
게임에 {{위키스크립트|(위키스크립트 문서 이름)}}을 삽입한다. 즉 틀을 삽입하면 소도구 등으로 스크립트가 정의된 다른 문서를 파싱한다.
위키스크립트 기본 구조
<noinclude>[[분류:위키스크립트 문서]]</noinclude>를 부착한 문서를 정의하고, 각 문서마다 다음 순서대로 이름을 가진 단락을 정의한다.
0번째 단락
- 문단 제목이 없는 0번째 단락은 메타데이터 문단으로, 분류를 삽입하거나 게임 타입을 정의
- 첫 줄에는 메타데이터를 한 줄로 삽입
- 제작자 이름 문자열과, 게임의 렌더링 방식, 커스텀 이벤트(또는 커스텀 API) 관련 데이터를 삽입(예: Senouis 2 3 -> Senouis가 만든 모듈이며, 2d canvas 게임이고, 3개의 커스텀 이벤트/API가 있음)
- 게임의 렌더링 방식은 다음과 같음
- 0: 정하지 않음(기본값, 파싱 에러 발생)
- 1: canvas 사용하지 않음(다른 HTML element만 조작)
- 2: canvas를 하나 삽입하며, 2d 컨텍스트로 로딩
- 3: canvas를 하나 삽입하며, webgl 컨텍스트로 로딩
- 제작자 이름 문자열과, 게임의 렌더링 방식, 커스텀 이벤트(또는 커스텀 API) 관련 데이터를 삽입(예: Senouis 2 3 -> Senouis가 만든 모듈이며, 2d canvas 게임이고, 3개의 커스텀 이벤트/API가 있음)
- 두 번째 줄부터 무시하며, 여기에 적절하게 분류 문법을 삽입
- 첫 줄에는 메타데이터를 한 줄로 삽입
시작(1번째)
- '시작' 문단은 스크립트의 초기값 정의를 다룬다.
프레임(2번째)
- Javascript의 requestAnimatedFrame( )을 사용하여 매 프레임마다 모든 모듈 및 메인 모듈의 이 문단의 내용을 실행
렌더링(3번째)
- 이 단락은 사실 없어도 되긴 하나('프레임' 단락에서 필요한 일을 할 수 있음), 리버
이벤트-키보드
이벤트-마우스
이벤트-조이스틱
이벤트-충돌
이벤트-사운드
이벤트-읽고쓰기
이벤트-네트워크
이벤트 네트워크에는 다음과 같은 매핑 API가 있다. {{특수:위키스크립트#message|(가상 포트 번호)|(콜백이 될 커스텀 이벤트 ID)}}
- 이벤트 콜백 ID는 커스텀 이벤트 문단에 대한 설명에서 결정
커스텀 이벤트
- '이벤트-네트워크' 다음의 2단계 문단부터는 커스텀 이벤트 정의로, 첫 줄에 명시한 갯수만큼 문단을 확인하고 파싱
- ID를 정하는 방법으로 맨 처음에 매핑할 콜백 ID를 하나의 숫자로 쓴다.
- 그러면 그 다음 줄부터 콜백 스크립트를 작성하면 실행 컨텍스트에 해당 스크립트의 raw data가 저장된다.
API
호출법
- 리버티게임에서 제공하는 기본 게임 제작용 위키스크립트 기능은 {{특수:위키스크립트#(API 이름)|(파라미터 1)|(파라미터 2)|...}} 형태로 호출한다.
- 기본 연산이 되는 기본 API 호출이 없으면(빈 줄) 무시하고 다음 줄로 넘어간다.
변수 정의
- #var와 #vardefine은 JSON 형태로 값을 저장한다(대소문자 구분 있는 변수 명칭을 키로 삼음).
- #struct는 리버티게임에 작성된 JSON 문서를 불러와 구조체(structure) 데이터로 삼는다.
- {{특수:위키스크립트#struct|(JSON 문서 이름)|구조체 키}}
조건문, 반복문(흐름 제어)
- #if, #ifexpr, #ifexist, #switch 지원: 리버티게임:특수 함수에 따름
- 미디어위키 문법에 없는 반복문은 각각 #while(일반 반복문)과 #for(이터레이션)라는 이름을 가지며, 파라미터에 반복 처리할 문구를 삽입한다.
- {{특수:위키스크립트#while|(루프 유지 조건)|(루프 내용)}}
- {{특수:위키스크립트#for|인덱스 이름|인덱스 시작 값|인덱스 끝 값|(루프 내용)}}
- #for의 인덱스 이름은 루프 내용 안에서 전역 변수보다 높은 우선순위를 가진다. 따라서 겹칠 수 없다.
- 자바스크립트로 구현할 때에는 스택을 이용하여 시작할 때 컨텍스트 push 후 조건에 따라 끝나기 전까지 로컬 컨텍스트 파싱을 유지하다 끝나면 pop
기본 API
- 기본 API 이름은 기본적으로 위에서 언급하지 않은 리버티게임:특수 함수 내 키워드를 따른다.
- 소도구의 기능을 다음과 같이 래핑한다.
- CGI 값 변경: #offset-read, #offset-write
- #offset-read
- #offset-write
- DB: LocalStorage API
- #db-read
- #db-write
- #db-checksum
- DB2: 리버티게임 전용 LocalStorage API
- 미디어위키:Gadget-DB2.js의 기능을 끌어옴
- File: IndexedDB API
- #file-open
- #file-read
- #file-write
- #file-close
- CGI 값 변경: #offset-read, #offset-write
- 형변환(casting)
- #string2JSON
- #JSON2string
- #int2string
- #string2int
- #trunc
- Vibrate(진동)
- 미디어위키:Gadget-Vibrate.js의 기능을 끌어옴
- #vibrate
- TTS(음성 읽어주기)
- 미디어위키:Gadget-TTS.js의 기능을 끌어옴
- #tts
- 키보드 이벤트 래핑
- 마우스 이벤트 래핑
- 게임패드 이벤트 래핑
- Sound API 래핑
- 그 외 사이트 연동 목적 mw.Api 래핑 및 프론트엔드 네트워킹 구현을 위한 WebSocket 래핑
- #mwapi-***
- #websocket-***
- 가상 포트라는 개념을 도입하여 통신을 구분
- 가상 포트 0은 리버티게임 사이트와의 통신으로 고정
- 가상 포트 1번부터 websocket 연결을 담당
- websocket 서버/리버티게임 사이트로부터 응답받은 데이터는 가상 포트 번호와 함께 '이벤트-네트워킹'으로 전달됨
기타
- invoke 파서(Scribunto 루아 모듈 호출자)의 이름은 추후 모듈 호출 기능을 위해 예약한다.
커스텀 스크립트 호출법(모듈화)
- '분류:위키스크립트 라이브러리'가 0번째 문단에 있으면 단순히 기능만 가져오는 모듈로 간주, 커스텀 이벤트가 곧 API 콜백이 됨
- 순수한 라이브러리를 만들어야 하면 '시작' 단락부터 '이벤트-네트워크' 단락을 만들되 각 단락을 비우면 된다.
- '분류:위키스크립트 오브젝트'가 0번째 문단에 있으면 메인 스크립트에 전체를 붙이는 오브젝트로 간주
- 사용자가 직접 만든 API는 {{특수:위키스크립트/(사용자 위키스크립트 문서 디렉토리)|(파라미터 1)|(파라미터 2)|...}} 형태로 호출한다.
- 성능 향상을 위해 메모이제이션을 사용한다(이전에 호출한 사용자 위키스크립트 문서의 파싱 결과를 기억)
구현 방향
- 소도구 측 스크립트(프론트엔드)
- 먼저, '위키스크립트' 틀의 파라미터에 삽입된 문서에 '분류:위키스크립트 문서'가 0번째 단락(문서 맨 위부터 '시작' 문단 이전)에 붙어 있는지 확인한다. mw.Api.getCategories(title)로 가져온 배열에 '위키스크립트 문서'가 있는지 확인하면 끝.
- '위키스크립트 오브젝트' 분류와 '위키스크립트 라이브러리' 분류도 확인한다.
- 그 다음index.php?title=(문서 이름)&action=raw§ions=(스크립트 문서 단락 번호)를 사용하여 문서 내용을 가져온다.
- 1번째 문단부터 한 줄씩 파싱하며 스크립트를 실행한다.
- {{특수:위키스크립트 구문을 볼 때마다 API로 인식한다.
- 구조체/클래스는 별도 정의된 JSON 문서를 가져오는 방식으로 구현
- 실제로 게임 스크립트를 실행할 실행 컨텍스트는 매핑된 배열을 1회성 순회하되, tick은 request
- 실행 컨텍스트의 자료구조는 다음과 같은 JSON 포맷을 사용
{ "module": { "main": ...(메인 문서 raw data), "두게임/렌더링라이브러리": ...(라이브러리 raw data), ... }, "structure": { "한게임/DataStructure.json": { (JSON 객체) }, ... }, "variables": { "var1": 0, "var2": "예시", ... }, "current-context": { "start": [ ... , ... , ..., ... ], "tick": [ [] , [], [] , ... ], "event-keyboard": [ ... , {}, ..., ... ], ... "callback-0": [..., ..., ], ... } }
- 오브젝트/라이브러리 로드 시 '시작' 문단은 즉시 실행, '프레임' 문단과 '이벤트' 문단들은 순회 큐에 추가
- 실행 컨텍스트의 자료구조는 다음과 같은 JSON 포맷을 사용
- 먼저, '위키스크립트' 틀의 파라미터에 삽입된 문서에 '분류:위키스크립트 문서'가 0번째 단락(문서 맨 위부터 '시작' 문단 이전)에 붙어 있는지 확인한다. mw.Api.getCategories(title)로 가져온 배열에 '위키스크립트 문서'가 있는지 확인하면 끝.
- PHP 확장 기능 측(백엔드)
- '특수:위키스크립트'라는 빈 문서를 생성한다.
- {{특수:위키스크립트#...}} 구문을 웹 페이지로 직접 볼 경우 해당 구문은 아무 것도 출력하지 않는다.