PrivateServerToolKit
2026.08 – 2026.09 · 개인
C++ · C# · Code Generation
C++/C# 패킷 코드 생성과 서비스 연결을 자동화한 서버 개발 도구
주요 내용
- Packet Tool: JSON 스키마에서 C++/C# 패킷 타입과 codec 생성
- Service Host: 입력 검증부터 응답 Encode까지 이어지는 서비스 처리 파이프라인
- 공통 결과 계약: TkResult로 결과를, TkDiagnostic으로 상세 원인을 전달
- Execution 레이어: Work Lane과 Worker Pool로 스케줄링 절차 공통화
구조
공통 스키마에서 생성된 코드는 Service Host까지 전달되고, 모든 모듈이 공통 결과 계약인 TkResult, TkDiagnostic을 사용합니다.
설계 결정
-
① 패킷 정의는 스키마 하나로
- 문제
- 패킷마다 C++ struct와 C# record, 필드 배치, Encode/Decode 코드를 추가해야 함
- 선택
- JSON 스키마에서 공통 Descriptor를 만들고 언어별 코드를 생성, 구조는 Protobuf를 참고하되 기존 바이트 규칙은 유지
- 트레이드오프
- 생성기는
-
② 생성 이후의 연결도 공통으로
- 문제
- 코드를 생성한 뒤에도 패킷마다 검증과 handler 연결 작업이 반복됨
- 선택
- Decode·입력 검증 → middleware → handler → 응답 Encode를 하나의 파이프라인으로 두고, 패킷별 연결은 binding으로 선언
- 트레이드오프
- 요청마다 Service Job을 할당하고, 제출에 성공한 Job은 쓰는 쪽이 실행하고 정리해야 함
-
③ 결과는 공통 계약으로
- 문제
- 계층을 새로 만들 때마다 전용 결과 타입을 다시 정의함
- 선택
- 제어 흐름용 TkResult와 상세 원인을 담는 TkDiagnostic(callback)을 분리하고, 공통 계층은 header-only의 작은 계약만 소유
- 트레이드오프
- 상세 원인을 받으려면 쓰는 쪽이 callback과 diagnostic 수명을 관리해야 함
-
④ I/O 계층은 I/O 작업에만 집중한다
- 문제
- PrivateServer에서는 IOCP Worker가 패킷 헤더만 검증해 세션 Mailbox에 넣을 때, 세부 처리를 맡을 Actor Worker의 실행 예약까지 함께 처리해야 했음
- 선택
- Akka 메일박스를 참고해 실행 예약을 Mailbox(Work Lane)가 관리하도록 Execution 레이어로 분리, IOCP Worker는 게시만 하고 실행은 Worker Pool이 lane 단위로 처리(같은 lane은 직렬, 다른 lane은 병렬)
- 트레이드오프
- Mailbox는 작업이 가리키는 대상을 소유하지 않기 때문에, 그 대상의 라이프사이클 관리는 사용자가 담당해야 함
사용 예시
패킷 정의 — MovementInput
C++과 C#에서 따로 작성하던 바이트 배치를 스키마 한 곳으로 옮기고, 스키마를 바탕으로 패킷 코드를 생성합니다.
V1WireCodec.WriteVersion(output);
V1WireCodec.WriteU32(ControlledEntityGeneration, 2, output);
V1WireCodec.WriteU32(TargetServerTick, 6, output);
V1WireCodec.WriteI16(MoveX, 10, output);
V1WireCodec.WriteI16(MoveY, 12, output); {
"packet": {
"name": "MovementInput",
"id": 257,
"fields": [
{ "name": "controlledEntityGeneration", "type": "uint32" },
{ "name": "targetServerTick", "type": "uint32" },
{ "name": "moveX", "type": "int16" },
{ "name": "moveY", "type": "int16" }
]
}
} 서비스 연결 — TimeSync
handler에는 요청을 받아 응답을 채우는 코드만 남고, 패킷과 handler의 연결은 binding으로 선언합니다.
TkResult OnTimeSync(
const TkServiceContext&,
const WorldTimeSyncRequest& request,
WorldTimeSyncResponse* outResponse) noexcept
{
outResponse->probeSequence = request.probeSequence;
outResponse->serverTick = currentServerTick;
return TK_SUCCESS;
} constexpr auto timeServiceBindings = pstk::service::MakeBindings(
pstk::service::BindRequestResponse<
TimeService, WorldTimeSyncRequest,
WorldTimeSyncResponse, &TimeService::OnTimeSync>());
pstk::service::RegisterService(
host, timeService, executor, middlewares, timeServiceBindings); 관련 글
-
[C++] Execution 레이어: Work Lane으로 스케줄링 공통화
PrivateServer의 Actor 스케줄링 경험과 Akka의 메일박스 구조를 바탕으로 Execution 레이어를 구성한 과정을 정리합니다.
-
[C++] 결과 반환과 상세 진단을 위한 공통 파이프라인
PrivateServer에서 새로운 계층마다 결과 타입을 반복해 작성했던 경험을 돌아보고, Vulkan의 VkResult를 참고해 결과 반환과 상세 진단을 TkResult와 TkDiagnostic으로 분리한 공통 계층을 설계하고 Packet Tool에 적용한 과정을 정리합니다.
-
[C++] Service Host: 패킷과 서비스 로직을 연결하는 공통 파이프라인
업데이트:Packet Tool을 이용해 패킷 생성을 자동화해도 실제 서비스에 반영하기 위해 필요한 분기 처리, 검증, 패킷 전달과 응답의 과정을 Service Host의 공통 파이프라인으로 모은 과정을 정리합니다.
-
[C++] Packet Tool: JSON 스키마 기반 패킷 코드 자동화
패킷을 추가할 때마다 새로운 코드를 매번 작성하던 기존 구조에서, Protobuf의 코드 생성 구조를 참고한 JSON 기반 Packet Tool의 도입 과정을 살펴봅니다.