Godot 지원
상태: 견적 — 스펙 이전. 경로 3개의 비용 산정이고, 결정 4개에 권고안이 있으며, 확인 항목이 §7입니다
「Godot 지원」은 하나의 작업이 아니라 3개이고, 비용이 서로 20배 이상 차이납니다. Godot의 게임 코드는 GDScript · C# · C++(GDExtension) 중 하나이므로 소비 경로도 셋이고, 그중 둘은 이미 있는 타깃의 재사용이며 하나는 새 언어 타깃 하나를 통째로 추가하는 작업입니다.
| 경로 | 필요한 것 | 추가 코드 (실측 기반 추정) |
|---|---|---|
| C# (Godot .NET) | csharp 타깃 그대로. 바이트 로더를 res://용으로 대입 | 약 215줄 — 어댑터 1개 + 컴파일 게이트용 스텁 |
| C++ (GDExtension) | cpp 타깃 그대로. 로더 대입 | 코어 0줄 — 문서 한 절. 소비 쪽 부담은 큽니다 |
| GDScript | 새 언어 타깃 하나 | 약 5,350줄 — 최근 추가한 언어 1건과 같은 규모 |
권고는 「C# 경로 먼저, GDScript는 §7의 확인 뒤에 결정」입니다. 근거는 §8입니다.
1. 범위 — 바뀌지 않는 것
| 무엇 | 상태 |
|---|---|
| 와이어 형식(v106)·변환기·익스포터 | 무변경. 어느 경로에서도 리더가 바이트를 어디서 받는지만 달라집니다 |
| 기존 골든 | 무변경. C# 경로는 어댑터 파일 하나가 추가되므로 csharp 트리에 파일이 늘고, GDScript는 새 트리가 붙습니다. 기존 파일의 바이트는 움직이지 않습니다 |
| 코어의 등록 코드 | 없습니다. 새 타깃을 만들더라도 [TabbitTarget] 어트리뷰트 스캔이므로 CLI·recipe 스키마·레지스트리에 이름이 들어가지 않습니다 |
| 다른 언어 | 무변경 |
2. 경로 1 — C# (Godot .NET)
2.1 필요한 것
생성되는 C#은 netstandard 2.1 · C# 9이므로 Godot의 .NET에서 그대로 컴파일됩니다. 부족한 것은 3개이고, 셋 다 작습니다.
| 번호 | 무엇 | 왜 |
|---|---|---|
| ① | res:// 바이트 로더 — ReadAllBytesAsync에 대입 | res://의 파일은 export 후 .pck 안에 있으므로 System.IO가 읽지 못합니다. Godot의 FileAccess.GetFileAsBytes가 그 자리입니다 |
| ② | 데이터 파일을 export에 포함시키는 절차 | Godot은 리소스가 아닌 확장자를 기본으로 제외합니다. export 설정의 비리소스 파일 필터에 한 줄이고, 확장자에 제약이 있으면 recipe의 BinaryTableFileExtension으로 흡수됩니다 — 코드 변경 없음 |
| ③ | 문서 1개 — doc/languages/godot.md 또는 csharp.md의 절 | 어느 쪽인지는 §2.2의 결정과 함께 정합니다 |
①의 이음매는 이미 있는 것입니다. csharp-read-bytes.sbn이 「엔진이 필요로 하는 것은 대입으로 도착한다」로 설계되어 있고, csharp-unity-adapter.sbn이 그 대입을 실제로 수행하는 선례입니다.
2.2 결정 1 — 어댑터를 생성할지, 문서로 안내할지
| 안 | 내용 | 비용 |
|---|---|---|
| A. 어댑터 생성 (권고) | Unity와 같은 형태. #if GODOT 안에서 [ModuleInitializer]로 스스로 대입하는 파일 하나를 항상 생성 | 템플릿 약 60줄 + 생성기 5줄 + 스텁 40줄 + 골든 재기록 |
| B. 문서로 안내 | 소비 프로젝트가 5줄을 직접 작성합니다 | 문서 한 절. 코드 0줄 |
A를 권고하는 이유는 소비 쪽 절차가 없어지기 때문입니다. Unity 어댑터가 그 값으로
존재하고, Godot에는 같은 값을 낼 수단이 있습니다 — Godot.NET.Sdk가 정의하는 심볼 안에서
ModuleInitializer가 첫 씬보다 먼저 실행됩니다(심볼 이름과 실행 시점은 §7의 확인
항목입니다).
A는 코어에 엔진 이름을 하나 더 추가합니다. CLAUDE.md의 규칙이 금지하는 것은 프로젝트 이름이고 엔진은 그 대상이 아니지만, 파일 하나로 격리되는지는 같은 기준으로 봅니다. Unity 어댑터가 그 조건을 만족합니다 — 삭제하면 그 파일 하나와 생성기 의 몇 줄이 전부입니다. Godot 어댑터도 같은 형태일 때만 채택합니다.
2.3 게이트
엔진 없이 컴파일까지 검증됩니다. cs-compile-check가
이미 그 구조입니다 — UnityStubs.cs 100줄로 UnityEngine을 대신하고 심볼을 정의해 어댑터
분기를 실제로 컴파일합니다. Godot도 같은 방식이고, 필요한 스텁은 FileAccess 하나입니다.
| 게이트 | 무엇을 판정하는가 | 비용 |
|---|---|---|
| 컴파일 | GODOT 심볼을 정의한 상태에서 어댑터가 컴파일되는가 | 스텁 약 40줄 + 테스트 1개 |
| 골든 | 어댑터가 모든 시나리오의 csharp 트리에 생성되는가 | 재기록만 |
| 실물 엔진 | .pck 안의 파일을 실제로 읽는가 | 두지 않습니다 — §7의 확인으로 대신합니다 |
2.4 합계
| 항목 | 줄 |
|---|---|
| 어댑터 템플릿 | 약 60 |
| 생성기 배선 | 약 5 |
| 컴파일 스텁 | 약 40 |
| 테스트 | 약 30 |
| 문서 | 약 80 |
| 합계 | 약 215줄 + 골든 재기록 |
3. 경로 2 — C++ GDExtension
코어 작업이 없습니다. GDExtension은 소비 프로젝트가 godot-cpp로 빌드하는 공유
라이브러리이고, 그 안에서 cpp 타깃의 생성물은 평범한 C++17입니다. 파일 읽기도
std::ifstream이 그대로 동작합니다 — res://가 필요할 때만 Godot의 FileAccess로
대체하면 되고, 그것은 소비 코드가 정하는 것입니다.
부담은 전부 소비 쪽에 있습니다 — godot-cpp 서브모듈, 플랫폼별 바이너리 빌드,
.gdextension 파일 관리. 이 도구가 줄여 줄 수 있는 부분이 없습니다.
따라서 1차 범위에서 제외하고, 문서에 한 문단으로만 기재합니다.
4. 경로 3 — GDScript 새 타깃
4.1 견적 근거 — 최근 추가한 언어의 실측
새 언어 하나의 비용은 추정이 아니라 저장소에 실측치가 있습니다. Lua는 가장 최근에 추가한 타깃이고 구성이 GDScript와 가장 가깝습니다.
| 구성품 | Lua | Python |
|---|---|---|
| 런타임 리더 | 1,442줄 | 1,399줄 |
| 런타임 업데이터 | 469줄 | 299줄 |
| 생성기 + 뷰 | 1,437 + 463 = 1,900줄 | 1,596 + 561 = 2,157줄 |
| 템플릿 5개 | 606줄 | 539줄 |
LanguageProfile 항목 1개 | 약 50줄 | 약 50줄 |
| 게이트 3개 (툴체인 · 중첩/옵셔널 · 업데이터) | 99 + 313 + 265 = 677줄 | — |
| 컨포먼스 하네스 | 117줄 | — |
| 문서 1개 | 208줄 | 88줄 |
| 합계 | 약 5,470줄 | — |
| (추가) 네이티브 C 모듈 | 663줄 | 해당 없음 |
GDScript도 이 표의 자리를 전부 채웁니다. §4.2·§4.3이 그 표에서 어디가 줄고 어디가 느는지입니다.
4.2 GDScript가 Lua보다 비용이 낮은 곳
| 항목 | Lua | GDScript |
|---|---|---|
| 64비트 정수 | 모드 2개로 분기 — LuaJIT의 FFI cdata와 5.3의 네이티브 정수. 백엔드 파일이 2개 252줄 | 네이티브 int64 하나. 분기가 없습니다 |
| 바이트 접근 | string.unpack 또는 FFI 포인터 | PackedByteArray.decode_* — 엔진이 제공하는 단일 경로 |
| HMAC-SHA-256 | C 모듈로 구현 (순수 구현은 속도 미달) | HMACContext가 코어 제공. 네이티브 속도이고 의존성이 늘지 않습니다 |
| SHA-256 (매니페스트 해시) | C 모듈 | HashingContext가 코어 제공 |
Lua의 네이티브 C 모듈 663줄 중 MAC·해시 부분이 GDScript에서는 불필요합니다. 남는 것은 암호화 하나이고, 그것이 §4.3입니다.
4.3 결정 2 — 봉인(ChaCha20)의 처리
Godot 코어에 ChaCha20이 없습니다. 노출되어 있는 것은 AESContext(ECB · CBC)와
Crypto(RSA · HMAC)이고, 이 형식이 쓰는 12바이트 nonce ChaCha20에
대응하는 함수가 없습니다. v104가 정한 판단 기준은
「그 언어에서 바이트 단위 루프가 수 MB를 감당하는가」이고, GDScript는 감당하지 못하는
쪽입니다.
| 안 | 내용 | 판단 |
|---|---|---|
| A. 1차 범위에서 제외 (권고) | 봉인하지 않은 파일만 읽습니다. MAC 검증은 코어 API로 지원하므로 변조 검출은 그대로 성립합니다 | 봉인이 방어하는 것과 MAC이 방어하는 것이 다르고, 키가 클라이언트 안에 있는 이상 봉인의 값이 제한적이라는 것은 이미 형식 문서에 기재되어 있습니다 |
| B. GDScript 순수 구현 | 약 150줄 | 수 MB에서 속도 문제가 발생합니다. Lua가 C로 간 것과 같은 이유입니다 |
| C. GDExtension 네이티브 모듈 | Lua의 C 모듈에 대응 | 플랫폼별 바이너리를 배포해야 합니다. Lua의 C 소스 한 파일과 부담이 다릅니다 |
A를 권고하되, 이것은 「봉인이 필요한 프로젝트는 GDScript 경로를 쓸 수 없다」는 뜻입니다. 그 프로젝트의 답은 §2의 C# 경로입니다.
4.4 결정 3 — 행의 표현
다른 언어의 리더는 행마다 객체를 생성합니다(Python의 __slots__ 클래스, Lua의 테이블).
GDScript에서 같은 형태는 행마다 RefCounted이고, 행 수가 수십만인 표에서 비용이
드러납니다.
| 안 | 내용 | 비용 |
|---|---|---|
A. RefCounted 클래스 (권고) | 다른 언어와 같은 형태. 생성기가 단순해지고 문서가 언어마다 갈리지 않습니다 | 행 수에 비례한 메모리. 수십만 행에서 확인 필요 |
B. 지연 생성 — 열 단위 PackedByteArray 보관, 접근 시 디코드 | 메모리가 파일 크기에 수렴합니다 | 생성기와 리더가 다른 언어와 다른 형태가 됩니다 |
A를 권고하는 것은 첫 소비자의 규모를 모르기 때문입니다. B는 A로 측정한 뒤에 판단할 항목이고, 측정 없이 먼저 하면 다른 언어와 다른 구조를 근거 없이 떠안게 됩니다.
4.5 결정 4 — 게이트의 실행 방법
Godot 헤드리스 바이너리가 필요합니다. 다른 언어의 툴체인 게이트와 같은 성격이고,
godot --headless로 컨포먼스 하네스를 실행합니다(정확한 실행 형태는 §7의 확인
항목입니다). 바이너리는 약 100 MB이고, 벤더링 또는 CI 내려받기 중 하나를 선택합니다.
| 게이트 | Lua의 대응물 |
|---|---|
| 툴체인 (구문·실행) | LuaToolchain.cs 99줄 |
| 중첩·옵셔널 | 313줄 |
| 업데이터 | 265줄 |
| 컨포먼스 하네스 | 117줄 |