콘텐츠로 이동

엔진 어댑터

← E2E 클라이언트 | 목차 | 다음: 패키징 →


엔진 어댑터는 core connector를 private 구현으로 소유하고, 각 엔진의 타입과 thread 규칙에 맞는 표면만 노출한다. core의 result_t<T>, connector_t 같은 타입은 어댑터 public header에 드러나지 않는다. Main thread 전달, packet 이름별 callback 등록, 호출별 요청 완료는 C++ 계약 §7을 따른다.


Unreal Engine

배포

.uplugin 형식으로 배포한다. Plugins/ZLinkStreamConnector/ 아래에 배치한다.

Plugins/
└── ZLinkStreamConnector/
    ├── ZLinkStreamConnector.uplugin
    └── Source/
        ├── ZLinkStreamConnector/      ← Runtime module
        └── ZLinkStreamConnectorTests/ ← Automation Test module

기본 사용법

Engine Lobby 샘플의 Actor는 connector를 만들고 서버 push인 ChatNotify의 delegate를 등록한 뒤 연결한다.

Unreal/Source/EngineLobby/Private/EngineLobbyClientActor.cpp
void AZLinkClientActor::BeginPlay ()
{
    Super::BeginPlay ();
    Connector = NewObject<UZLinkStreamConnector> (this);
    PacketHandle =
      Connector->On (PacketName (engine_lobby::packet::chat_notify),
                     [this] (const FZLinkStreamPacket &Packet) { HandlePacket (Packet); });
    SetStatus (TEXT ("Engine Lobby: connecting"));
    Connector->Connect (Endpoint);
    if (!Connector->IsConnected ()) {
        SetStatus (TEXT ("Engine Lobby: connection failed"), FColor::Red);
        return;
    }
    SendPing ();
}

Tick에서 connector를 pump하면 delegate가 Game Thread에서 실행된다.

Unreal/Source/EngineLobby/Private/EngineLobbyClientActor.cpp
void AZLinkClientActor::Tick (float DeltaSeconds)
{
    Super::Tick (DeltaSeconds);
    if (Connector != nullptr) {
        Connector->Tick (DeltaSeconds);
    }
}

Blueprint에서 사용

Connect, Close, SendJson, RequestJson, On, Dispatch는 모두 BlueprintCallable이다. On(PacketName, Delegate)는 해제 handle을 반환하고, RequestJson(..., OnCompleted)는 해당 요청의 결과를 delegate로 전달한다.

PIE 종료, map unload, game instance shutdown에서 Close()가 자동 호출된다.

Automation Test 실행

UnrealEditor-Cmd MyProject.uproject \
  -ExecCmds="Automation RunTests ZLink.StreamConnector; Quit" \
  -unattended -NullRHI

Godot 4

배포

GDExtension source package로 배포한다. Godot project의 addons/zlink_stream_connector/ 아래에 배치한다.

.gdextension 파일이 빌드된 shared library를 등록한다.

기본 사용법 (C++)

Engine Lobby 샘플의 node는 ChatNotify의 callback을 등록한 뒤 연결한다.

Godot/cpp/src/engine_lobby_node.cpp
void EngineLobbyNode::_ready ()
{
    status_ = get_node<Label> ("Status");
    chat_subscription_ =
      connector_.on (chat_notify, [this] (const auto &packet) { handle_packet (packet); });
    const CharString endpoint = endpoint_.utf8 ();
    connector_.connect (std::string (endpoint.get_data (), endpoint.length ()));
    if (connector_.state () != zlink::godot_stream_connector::connection_state_t::connected) {
        fail ("connection failed");
        return;
    }
    connector_.request_json (ping_req, encode_field ("sentAtUnixMs", "1000"), 5.0,
                             [this] (const auto &result) { handle_ping_response (result); });
    set_process (true);
}

Godot main thread의 _process에서 connector를 pump한다.

Godot/cpp/src/engine_lobby_node.cpp
void EngineLobbyNode::_process (double)
{
    if (!failed_) {
        connector_.dispatch ();
    }
}

Axmol Engine

배포

CMake source package로 배포한다. Axmol project CMakeLists.txt에 포함하거나 vcpkg/Conan으로 core를 설치한다.

add_subdirectory(third_party/zlink_axmol_connector)
target_link_libraries(${APP_NAME} PRIVATE zlink_axmol_connector)

기본 사용법

Engine Lobby 샘플의 Scene은 ChatNotify의 callback을 등록한 뒤 연결한다.

Axmol/Source/EngineLobbyScene.cpp
chat_subscription_ = connector_.on (engine_lobby::packet::chat_notify,
                                    [this] (const auto &packet) { handle_packet (packet); });
connector_.connect (endpoint);
if (connector_.state () != zlink::axmol_stream_connector::connection_state_t::connected) {
    set_status ("Engine Lobby: connection failed");
    return true;
}
send_ping ();

Axmol scheduler update에서 connector를 pump한다.

Axmol/Source/EngineLobbyScene.cpp
schedule ([this] (float) { connector_.dispatch (); }, "engine-lobby-pump");

공통 엔진 어댑터 API 대응표

동작 Unreal Godot Axmol
연결 Connect(Endpoint) connect(endpoint) connect(endpoint)
종료 Close() close() close()
단방향 송신 SendJson(Name, Json) send_json(name, json) send_json(name, json)
요청/응답 RequestJson(Name, Json, Timeout, OnCompleted) request_json(name, json, timeout, callback) request_json(name, json, timeout, callback)
push 등록 On(PacketName, Delegate) → handle on(packet_name, callback) → handle on(packet_name, callback) → handle
dispatch Dispatch() (Tick에서 호출) dispatch() (프레임에서 호출) dispatch() (프레임에서 호출)
상태 변경 OnConnectionStateChanged delegate on_connection_state_changed callback on_connection_state_changed callback