사용자:Senouis/위키스크립트 규격 제안
위키스크립트 설계 v.0.1.2 by Senouis
미디어위키 문법은 기본적으로 HTML만 대체한다. 따라서 프로그래밍 언어가 아니며, 위키 문법 만으로는 죽었다 깨어나도 실시간 렌더링이 안 된다.
그러나 CSS로 상태 머신(State Machine)을 구현하면 프로그래밍 언어로 만들 수 있으며, 따라서 적절한 변환을 거쳐 위키 문법과 유사한 형태의 스크립팅이 가능하다.
다만, CSS 상태 머신만 이용하게 하면 페이지 레이아웃을 해칠 가능성이 크다. 가능하면 HTML canvas 요소(Element)를 사용해 게임을 만들어야 한다.
이에 따라 미디어위키 문법을 살짝 변형하여 완벽한 프로그래밍이 가능한 위키스크립트를 제안한다.
사용법[편집 | 원본 편집]
게임에 {{위키스크립트|(위키스크립트 문서 이름)}}을 삽입한다. 즉 틀을 삽입하면 소도구 등으로 스크립트가 정의된 다른 문서를 파싱한다.
위키스크립트 기본 구조[편집 | 원본 편집]
위키스크립트 문서를 어떻게 구분할 것인가? 다음 두 가지 중 하나를 사용할 수 있다.
- <noinclude>[[분류:위키스크립트 문서]]</noinclude>를 부착한 문서를 일반 이름공간에 정의한다.
- '위키스크립트' 이름공간의 문서를 작성한다.
각 문서마다 다음 둘 중 한 가지 방법으로 이름을 가진 2단계 문단을 정의한다.
- 특수:위키스크립트를 틀로 인용하는 방식으로 스크립트를 작성하며, 그렇지 않은 부분은 기본적으로 파싱하지 않는다(주석/코드 레이아웃 설정 용도)
- {{특수:위키스크립트/(스크립트 문서 경로)#(연산)|(파라미터 1)|(파라미터 2)|...}}
- 틀 형태 래핑 없이 # 문자로 각 줄마다 연산을 명시하고, 파라미터는 | 기호를 사용해 구분하여 서술한다. -> 보기 페이지에서 줄 번호 비슷하게 나오나, action=raw 파라미터를 사용할 것이므로 무의미하다.
- /(스크립트 문서 경로)#(연산)|(파라미터 1)|(파라미터 2)|...
- 이 경우 주석은 <!-- -->로 제한한다.
아래 표준안은 임시로 첫 번째 규격을 사용해 서술한다.
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가 있음)
- 두번째 줄은 화면 크기 (가로, 세로)를 한 칸 띄어쓰기로 설정(예: 640 480)
- 1인 경우 div 엘리먼트를 하나 만든 다음 width 값만 첫번째 값으로 설정, 2~3은 canvas size로 정함
- 세 번째 줄부터 무시하며, 여기에 적절하게 분류 문법을 삽입하고 모듈/오브젝트/스크립트에 대한 설명을 부착한다.
- 첫 줄에는 메타데이터를 한 줄로 삽입
시작(1번째)[편집 | 원본 편집]
- '시작' 문단은 스크립트의 초기값 정의를 다룬다.
- 여기서 정의된 변수는 전역 변수(global variable)이다.
프레임(2번째)[편집 | 원본 편집]
- Javascript의 requestAnimatedFrame( )을 사용하여 매 프레임마다 모든 모듈 및 메인 모듈의 이 문단의 내용을 실행
렌더링(3번째)[편집 | 원본 편집]
- 모든 렌더링은 프레임 액션 이후 마지막에 실행된다.
- 이 단락은 사실 없어도 되긴 하나('프레임' 단락에서 필요한 일을 할 수 있음), 추후 API 규격 확장이 필요할 경우 이 문단 전후로 추가할 것.
- '위키스크립트 라이브러리' 분류가 붙은 문서는 이 단락의 내용을 무시한다(실행 컨텍스트 삽입 X).
이벤트-입력(4번째)[편집 | 원본 편집]
- 마우스, 키보드, 조이스틱 입력
- 이 이벤트는 내부적으로 0을 마우스, 1을 키보드, 2를 게임패드로 인식한다.
- 미디어위키 주석 문법(<!-- -->)을 제외한 문단 첫 줄에 입력 종류 파라미터의 이름, 입력값 파라미터의 이름을 원하는 대로 한 칸 띄어쓰기로 구분해 적는다.
- 그 다음 줄부터 로직 시작
이벤트-충돌(5번째)[편집 | 원본 편집]
- 모든 스크립트 문서에는 내부적으로 대응되는 충돌 판정 자료 구조를 가지나, 기본적으로 비활성화되어 있다.
- 콜리전 관련 자료 구조 역시 JSON 객체로, boolean 타입의 "enabled" 키의 값을 true로 설정해야 활성화된다.
이벤트-사운드(6번째)[편집 | 원본 편집]
이벤트-읽고쓰기(7번째)[편집 | 원본 편집]
이벤트-네트워크(8번째)[편집 | 원본 편집]
이벤트 네트워크에는 다음과 같은 매핑 API가 있다. {{특수:위키스크립트#message|(가상 포트 번호)|(콜백이 될 커스텀 이벤트 ID)}}
- 이벤트 콜백 ID는 커스텀 이벤트 문단에 대한 설명에서 결정
함수 커스텀 이벤트[편집 | 원본 편집]
- '이벤트-네트워크' 다음의 2단계 문단부터는 커스텀 이벤트 정의로, 문서 첫 줄(메타데이터)에 명시한 갯수만큼 문단을 확인하고 파싱(갯수 명시가 없으면 커스텀 이벤트 갯수 파악 불가)
- ID를 정하는 방법으로 맨 처음에 매핑할 콜백 ID를 하나의 문자열로 쓰고, 띄어쓰기 한 칸 후 콜백에 전달될 파라미터 이름 리스트를 띄어쓰기 한 칸으로 적는다.
- 그러면 그 다음 줄부터 콜백 스크립트를 작성하면 실행 컨텍스트에 해당 스크립트의 raw data가 저장된다.
- #call로 이벤트 콜백을 함수처럼 호출할 수 있다.
- {{특수:위키스크립트#call|(문단 이름)|(파라미터 1)|(파라미터 2)|...}}
- 이 경우 반환값은 매핑한 콜백 ID에 대입한다. 즉 콜백 ID도 하나의 변수가 된다.
- {{특수:위키스크립트#vardefine|(콜백 ID)|(반환할 값)}}
문법[편집 | 원본 편집]
호출법[편집 | 원본 편집]
- 리버티게임에서 제공하는 기본 게임 제작용 위키스크립트 기능은 {{특수:위키스크립트#(API 이름)|(파라미터 1)|(파라미터 2)|...}} 형태로 호출한다.
- 기본 연산이 되는 기본 API 호출이 없으면(빈 줄) 무시하고 다음 줄로 넘어간다.
변수 정의[편집 | 원본 편집]
- #vardefine은 값을 저장한다
- 내부적으로 JSON 객체인 variables를 실행 컨텍스트 자료구조에 추가해 대소문자 구분 있는 변수 명칭을 키로 삼음
- 담을 수 있는 값(value)은 단일 값, 배열, JSON 객체
- {{특수:위키스크립트#vardefine|(타입: 단일값/배열/객체 중 하나)|(이름)|(값)}}
- 단일 값은 다시 숫자와 문자열로 구분한다
- 문자열은 큰따옴표(")로 감싼다.
- #var는 값을 반환한다.
- 다른 곳에 대입되는 경우 타입이 맞지 않아도 에러를 내지 않고 자바스크립트처럼 그냥 대입 실행
- 대입되지 않는 경우에는 출력하며, 형변환은 자동으로 시행한다.
- {{특수:위키스크립트#var|(이름)}}
- #struct는 리버티게임에 작성된 JSON 문서를 불러와 구조체(structure) 데이터로 삼는다.
- {{특수:위키스크립트#struct|(JSON 문서 이름)|구조체 변수 이름}}
- 배열/객체 변수 접근은 다음 구분으로 한다. 변수가 배열이라면, 인덱스는 숫자값, 키는 문자열이다.
- {{특수:위키스크립트#id|(변수 이름)|(인덱스 또는 키)}}
- 재귀 호출 가능: 이 구문을 파싱하는 함수는 반환값이 있어야 하며, 파서 재귀 호출인지, 단순 값인지는 역시 {{특수:위키스크립트를 인식하여 파싱한다.
조건문, 반복문(흐름 제어)[편집 | 원본 편집]
- #if, #ifexpr, #ifexist, #switch 지원: 리버티게임:특수 함수에 따르되, : 대신 |을 사용한다.
- {{특수:위키스크립트#ifexpr|{{특수:위키스크립트#var|liberty}} = 0 |<!-- 참 --> | <!-- 거짓 --> }}
- 미디어위키 문법에 없는 반복문은 각각 #while(일반 반복문)과 #for(이터레이션)라는 이름을 가지며, 파라미터에 반복 처리할 문구를 삽입한다.
- {{특수:위키스크립트#while|(루프 유지 조건)|(루프 내용)}}
- {{특수:위키스크립트#for|인덱스 이름|인덱스 시작 값|인덱스 끝 값|(루프 내용)}}
- #for의 인덱스 이름은 루프 내용 안에서 전역 변수보다 높은 우선순위를 가진다. 따라서 겹칠 수 없다.
- 자바스크립트로 구현할 때에는 스택을 이용하여 시작할 때 컨텍스트 push 후 조건에 따라 끝나기 전까지 로컬 컨텍스트 파싱을 유지하다 끝나면 pop
- 조건문과 반복문은 유일하게 줄바꿈을 허용한다.
- 실행 스택으로 줄바꿈이 필요한 상황 감지 가능
- TODO: 실행 스택에 삽입될 스코프 자료 구조?
API[편집 | 원본 편집]
기본[편집 | 원본 편집]
- 기본 API 이름은 기본적으로 위에서 언급하지 않은 리버티게임:특수 함수 내 키워드를 따른다.
HTML element 조작[편집 | 원본 편집]
- #getElementById(가칭)
- #getElementsByClassname(가칭)
- #appendChild(가칭)
- #showElement(가칭): 엘리먼트 css display를 변경
- #setSize(가칭): 엘리먼트 css top, left, right, bottom 변경
- #getSize(가칭): 엘리먼트 css의 {position 속성, top, left, right, bottom} 순으로 JSON 반환
2D 렌더링[편집 | 원본 편집]
(추가 예정)
3D 렌더링[편집 | 원본 편집]
(추가 예정, 자바스크립트 엔진 계획도 참조)
소도구 래핑[편집 | 원본 편집]
- 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
- 형변환(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 루아 모듈 호출자)의 이름은 추후 모듈 호출 기능을 위해 예약한다.
- TODO: 특수:위키스크립트#call로 커스텀 이벤트 콜백을 내부에서 직접 (평범한 함수처럼) 호출?
- 오류 발생 시 오류가 발생한 줄을 찾는다.
- }}로 닫지 않았다면 실행 컨텍스트의 현재 실행 스택 높이로 가장 최근에 올라온 줄을 표시하고 에러가 난 이유를 적절하게 표시한다.
커스텀 스크립트 호출법(모듈화)[편집 | 원본 편집]
- '분류:위키스크립트 라이브러리'가 0번째 문단에 있으면 단순히 기능만 가져오는 모듈로 간주, 커스텀 이벤트가 곧 API 콜백이 됨
- 순수한 라이브러리를 만들어야 하면 '시작' 단락부터 '이벤트-네트워크' 단락을 만들되 각 단락을 비우면 된다.
- '분류:위키스크립트 오브젝트'가 0번째 문단에 있으면 메인 스크립트에 전체를 붙이는 오브젝트로 간주
- 사용자가 직접 만든 API는 {{특수:위키스크립트/(사용자 위키스크립트 문서 디렉토리)|(파라미터 1)|(파라미터 2)|...}} 형태로 호출한다.
- 성능 향상을 위해 메모이제이션을 사용한다(이전에 호출한 사용자 위키스크립트 문서의 파싱 결과를 기억)
구현 방향[편집 | 원본 편집]
- 소도구 측 스크립트(프론트엔드)
- 먼저, '위키스크립트' 틀의 파라미터에 삽입된 문서에 '분류:위키스크립트 문서'가 0번째 단락(문서 맨 위부터 '시작' 문단 이전)에 붙어 있는지 확인한다. mw.Api.getCategories(title)로 가져온 배열에 '위키스크립트 문서'가 있는지 확인하면 끝.
- '위키스크립트 오브젝트' 분류와 '위키스크립트 라이브러리' 분류도 확인한다.
- 그 다음index.php?title=(문서 이름)&action=raw§ions=(스크립트 문서 단락 번호)를 사용하여 문서 내용을 가져온다.
- 1번째 문단부터 한 줄씩 파싱하며 스크립트를 실행한다.
- {{특수:위키스크립트 구문을 볼 때마다 API로 인식한다.
- API 발견 시 실행 스택을 쌓아(push)가며 스코프를 구분한다.
- 구조체/클래스는 별도 정의된 JSON 문서를 가져오는 방식으로 구현
- 실제로 게임 스크립트를 실행할 실행 컨텍스트는 JSON 객체로 저장하며, 오브젝트/모듈 별 키를 저장한 "moduleName" 배열을 순회하며 접근
- 단, '렌더링' 단락과 기본 이벤트 단락들은 main과 오브젝트의 것만 저장(렌더링 모듈화 불가)
- 실행 컨텍스트의 자료구조는 다음과 같은 JSON 포맷을 사용하며, 따로 문서 형태로 저장하지 않고 세션에만 존재하도록 함
{ "moduleName": ["main", "두게임/렌더링라이브러리", ...], "module": { "main": ...(메인 문서 raw data), "두게임/렌더링라이브러리": ...(라이브러리 raw data), ... }, "structure": { "한게임/DataStructure.json": { (JSON 객체) }, ... }, "variables_global": { "var1": 0, "var2": "예시", ... }, "current-context": { "rendering": { "main": [...], "한게임/플레이어": [...], ... }, "tick": { "main": [...], "두게임/렌더링라이브러리":[...], "한게임/플레이어": [...], ... }, "event-keyboard": { "main": [...] , "한게임/플레이어": [...], ..., ... }, ... "callback-0": { "main": [...], "한게임/플레이어": [...], ..., }, ... } }
- 오브젝트/라이브러리 로드 시 '시작' 문단은 즉시 실행, '프레임' 문단과 '이벤트' 문단들은 순회 큐에 추가
- 오브젝트는 복수 존재 가능: 키 구분은 뒤에 #(id)를 붙임(예: "한게임/나무#5")
- 먼저, '위키스크립트' 틀의 파라미터에 삽입된 문서에 '분류:위키스크립트 문서'가 0번째 단락(문서 맨 위부터 '시작' 문단 이전)에 붙어 있는지 확인한다. mw.Api.getCategories(title)로 가져온 배열에 '위키스크립트 문서'가 있는지 확인하면 끝.
- PHP 확장 기능 측(백엔드)
- '특수:위키스크립트'라는 빈 문서를 생성한다.
- {{특수:위키스크립트#...}} 구문을 웹 페이지로 직접 볼 경우 해당 구문은 아무 것도 출력하지 않는다.