넷 코드를 처리하는 방법?


10

넷 코드가 게임 엔진에 "연결"할 수있는 여러 가지 방법을 평가하고 싶습니다. 나는 지금 멀티 플레이어 게임을 디자인하고 있으며, 지금까지 그래픽 루프와 스크립팅을 처리하는 엔진의 나머지 부분과는 별개로 네트워크 소켓을 처리하기 위해 별도의 스레드가 필요하다고 결정했습니다.

네트워크 게임을 완전히 단일 스레드로 만드는 하나의 잠재적 인 방법이 있었는데, 이는 비 차단 소켓을 사용하여 각 프레임을 렌더링 한 후 네트워크를 확인하는 것입니다. 그러나 이는 프레임을 렌더링하는 데 걸리는 시간이 네트워크 지연에 추가되기 때문에 분명히 최적이 아닙니다. 네트워크를 통해 도착하는 메시지는 현재 프레임 렌더링 (및 게임 논리)이 완료 될 때까지 기다려야합니다. 그러나 적어도 이런 식으로 게임 플레이는 여전히 매끄럽게 진행됩니다.

네트워킹을위한 별도의 스레드가 있으면 게임이 네트워크에 완전히 응답 할 수 있습니다. 예를 들어 서버에서 상태 업데이트를 수신하면 즉시 ACK 패킷을 다시 보낼 수 있습니다. 그러나 나는 게임 코드와 네트워크 코드 사이에서 통신하는 가장 좋은 방법에 대해 약간 혼란스러워합니다. 네트워킹 스레드는 수신 된 패킷을 큐로 푸시하고 루프 동안 적절한 시간에 큐에서 게임 스레드를 읽으므로이 최대 1 프레임 지연을 제거하지 못했습니다.

또한 패킷 전송을 처리하는 스레드가 파이프로 내려 오는 패킷을 확인하는 스레드와 분리되도록하려는 것처럼 보입니다. 수신 메시지가 있는지 확인 기능 select이나 비슷한 기능에 대해 생각하고 있습니다 .

내 질문은 최고의 네트워크 응답 성을 위해 게임을 디자인하는 가장 좋은 방법은 무엇입니까? 분명히 클라이언트는 가능한 한 빨리 서버에 사용자 입력을 보내야하므로 게임 루프 내부에서 이벤트 처리 루프 직후에 네트 센드 코드를 가져올 수 있습니다. 이것은 말이됩니까?

답변:


13

응답 성을 무시하십시오. LAN에서 핑은 중요하지 않습니다. 인터넷에서 왕복 60-100ms는 축복입니다. 3K 이상의 스파이크가 발생하지 않도록 지연 신들에게기도하십시오. 이것이 문제가 되려면 소프트웨어가 초당 매우 적은 수의 업데이트로 실행되어야합니다. 초당 25 번의 업데이트를한다면, 패킷을 받아 행동 할 때까지 최대 40ms의 시간이 걸립니다. 그리고 그것은 단일 스레드 사례입니다 ...

유연성과 정확성을 위해 시스템을 설계하십시오. 다음은 네트워크 하위 시스템을 게임 코드에 연결하는 방법에 대한 아이디어입니다. 메시징. 많은 문제에 대한 해결책은 "메시지"일 수 있습니다. 나는 실험 쥐에서 암이 치료되었다고 생각합니다. 메시지를 보내면 자동차 보험료가 200 달러 이상 절약됩니다. 그러나 진지하게, 메시징은 서브 시스템을 게임 코드에 연결하는 동시에 두 개의 독립적 인 서브 시스템을 유지하는 가장 좋은 방법 일 것입니다.

네트워크 서브 시스템과 게임 엔진 간의 통신 및 두 서브 시스템 간의 문제에 메시징을 사용하십시오. 서브 시스템 간 메시징은 std :: list를 사용하여 포인터에 의해 전달 된 데이터 블롭처럼 단순 할 수 있습니다.

네트워크 서브 시스템에 나가는 메시지 큐와 게임 엔진에 대한 참조가 있으면됩니다. 게임은 나가는 대기열로 보내려는 메시지를 덤프하여 자동으로 보내거나 "flushMessages ()"와 같은 함수가 호출 될 때이를 보낼 수 있습니다. 게임 엔진에 하나의 큰 공유 메시지 큐가있는 경우 메시지를 보내야하는 모든 하위 시스템 (논리, ​​AI, 물리, 네트워크 등)은 메인 게임 루프가 모든 메시지를 읽을 수있는 메시지를 모두 덤프 할 수 있습니다. 그리고 그에 따라 행동하십시오.

필요하지는 않지만 다른 스레드에서 소켓을 실행하는 것이 좋습니다. 이 디자인의 유일한 문제점은 일반적으로 비동기 적이며 (패킷이 전송되는시기를 정확히 알지 못함) 디버그하기가 어려워지고 타이밍 관련 문제가 임의로 나타나거나 사라질 수 있다는 것입니다. 그래도 올바르게 수행하면 문제가되지 않습니다.

높은 수준에서 게임 엔진 자체와 별도의 네트워킹을 말합니다. 게임 엔진은 소켓이나 버퍼는 신경 쓰지 않고 이벤트는 신경 쓰입니다. 이벤트는 "플레이어 X가 총을 쏴" "게임 T에서 폭발이 일어났다"와 같은 것들입니다. 이것들은 게임 엔진에 의해 직접 해석 될 수 있습니다. 스크립트, 클라이언트 작업, AI 플레이어 등에서 생성 된 위치는 중요하지 않습니다.

네트워크 서브 시스템을 이벤트를 전송 / 수신하는 수단으로 취급하는 경우 소켓에서 recv ()를 호출하는 것보다 많은 장점이 있습니다.

예를 들어 50 개의 작은 메시지 (길이 1-32 바이트)를 가져 와서 네트워크 서브 시스템이 하나의 큰 패킷으로 패키지하여 전송하도록하여 대역폭을 최적화 할 수 있습니다. 그것이 큰 일이라면 보내기 전에 압축 할 수 있습니다. 다른 한편으로, 코드는 게임 엔진이 읽을 수 있도록 큰 패킷을 50 개의 개별 이벤트로 다시 압축 해제 / 포장 풀 수 있습니다. 이것은 모두 투명하게 일어날 수 있습니다.

다른 멋진 기능으로는 단일 클라이언트 게임 모드가 있습니다. 단일 클라이언트 게임 모드에는 순수한 클라이언트 + 공유 메모리 공간의 메시징을 통해 통신하는 동일한 컴퓨터에서 실행되는 순수한 서버가 있습니다. 그런 다음 싱글 플레이어 게임이 제대로 작동하면 원격 클라이언트 (예 : 진정한 멀티 플레이어)도 작동합니다. 또한 단일 플레이어 게임이 올바르게 보이거나 완전히 잘못되어 클라이언트에 필요한 데이터를 미리 고려해야합니다. 멀티 플레이어 게임에서 믹스 앤 매치, 서버 운영 및 클라이언트가 되십시오.


간단한 std :: list 또는 일부를 사용하여 메시지를 전달한다고 언급했습니다. 이것은 StackOverflow의 주제 일 수 있지만 스레드가 모두 동일한 주소 공간을 공유한다는 것은 사실이며 여러 스레드가 동시에 대기열에 속한 메모리와 나사 조이는 것을 방지하는 한 괜찮습니까? 나는 힙처럼 큐에 대한 데이터를 평소처럼 할당하고 그 안에 뮤텍스를 사용할 수 있습니까?
Steven Lu

예, 맞습니다. std :: list에 대한 모든 호출을 보호하는 뮤텍스입니다.
PatrickB

답장을 보내 주셔서 감사합니다! 지금까지 스레딩 루틴을 많이 발전시켜 왔습니다. 내 자신의 게임 엔진을 가지고있는 것은 정말 좋은 느낌입니다!
Steven Lu

4
그 느낌은 사라질 것입니다. 당신이 얻는 큰 놋쇠는 당신과 함께 있습니다.
ChrisE

@StevenLu 다소 [늦게] 늦었지만 스레드를 메모리와 동시에 조이는 것을 방지 하는 방법과 수행 방법 및 효율성에 따라 매우 어려울 수 있음 을 지적하고 싶습니다. 오늘이 작업을 수행 한 경우, 수많은 오픈 소스 동시 큐 구현 중 하나를 지적하므로 복잡한 휠을 다시 만들 필요가 없습니다.
기금 모니카의 소송

4

네트워크 소켓을 처리하기 위해 별도의 스레드가 있어야합니다.

아뇨.

네트워크 게임을 완전히 단일 스레드로 만드는 하나의 잠재적 인 방법이 있었는데, 이는 비 차단 소켓을 사용하여 각 프레임을 렌더링 한 후 네트워크를 확인하는 것입니다. 그러나 이것은 프레임을 렌더링하는 데 걸리는 시간이 네트워크 지연에 추가되므로 명확하지 않습니다.

반드시 중요하지는 않습니다. 로직은 언제 업데이트됩니까? 아직 아무 것도 할 수 없다면 네트워크에서 데이터를 가져 오는 것은 별 의미가 없습니다. 마찬가지로 아직 아무 말도하지 않으면 반응이 거의 없습니다.

예를 들어 서버에서 상태 업데이트를 수신하면 즉시 ACK 패킷을 보낼 수 있습니다.

게임의 속도가 너무 빨라 다음 프레임이 렌더링 될 때까지 기다리는 것이 상당히 지연되는 경우 별도의 ACK 패킷을 보낼 필요가없는 충분한 데이터를 보내 게됩니다. 일반 데이터 안에 ACK 값만 포함하면됩니다. 필요한 경우 페이로드.

대부분의 네트워크 게임의 경우 다음과 같은 게임 루프를 갖는 것이 가능합니다.

while 1:
    read_network_messages()
    read_local_input()
    update_world()
    send_network_updates()
    render_world()

업데이트와 렌더링을 분리 할 것을 권장합니다. 그러나 권장 사항은 있지만 특정 요구 사항이없는 한 그 밖의 모든 내용은 단순하게 유지 될 수 있습니다. 정확히 어떤 종류의 게임을 만들고 있습니까?


7
렌더링에서 업데이트를 분리하는 것은 강력히 권장 할뿐만 아니라 똥 엔진이 아닌 경우에 필요합니다.
AttackingHobo

대부분의 사람들은 엔진을 만들지 않으며 아마도 대부분의 게임이 여전히 두 가지를 분리하지 않습니다. 경과 시간 값을 업데이트 기능에 전달하기위한 정착은 대부분의 경우 허용 가능합니다.
Kylotan

2

그러나 이는 프레임을 렌더링하는 데 걸리는 시간이 네트워크 지연에 추가되기 때문에 분명히 최적이 아닙니다. 네트워크를 통해 도착하는 메시지는 현재 프레임 렌더링 (및 게임 논리)이 완료 될 때까지 기다려야합니다.

전혀 사실이 아닙니다. 수신자가 현재 프레임을 렌더링하는 동안 메시지는 네트워크 통해 전달됩니다 . 네트워크 지연은 클라이언트 측의 전체 프레임 수에 고정됩니다. 예.하지만 고객에게 FPS가 너무 적어서 이것이 큰 문제라면 더 큰 문제가 있습니다.


0

네트워크 통신은 일괄 처리해야합니다. 각 게임 틱에 전송되는 단일 패킷을 위해 노력해야합니다 (프레임이 렌더링 될 때 실제로는 독립적이어야 함).

게임 엔티티는 네트워크 서브 시스템 (NSS)과 통신합니다. NSS는 메시지, ACK 등을 일괄 처리하고 최적 크기의 UDP 패킷 (일반적으로 ~ 1500 바이트)을 전송합니다. NSS는 단일 UDP 패킷 만 보내면서 패킷, 채널, 우선 순위, 재전송 등을 에뮬레이션합니다.

게임 튜토리얼 에서 gaffer를 읽 거나 Glenn Fiedler의 많은 아이디어를 구현 하는 ENet 을 사용하십시오 .

또는 게임에 트 위치 반응이 필요하지 않은 경우 TCP를 사용할 수 있습니다. 그런 다음 모든 배치, 재전송 및 ACK 문제가 사라집니다. 그러나 여전히 NSS가 대역폭과 채널을 관리하기를 원할 것입니다.


0

"응답 성"을 완전히 무시하지 마십시오. 이미 지연된 패킷에 다른 40ms 대기 시간을 추가하면 얻을 수있는 이점이 거의 없습니다. 60fps에서 몇 개의 프레임을 추가하는 경우 위치 처리가 지연되어 다른 두 개의 프레임이 업데이트됩니다. 패킷을 신속하게 수용하고 신속하게 처리하여 시뮬레이션의 정확성을 높이는 것이 좋습니다.

화면에 보이는 것을 나타내는 데 필요한 최소 상태 정보를 생각하여 대역폭을 최적화하는 데 큰 성공을 거두었습니다. 그런 다음 각 데이터 비트를보고 모델을 선택하십시오. 위치 정보는 시간이 지남에 따라 델타 값으로 표현 될 수 있습니다. 이를 위해 자체 통계 모델을 사용하고 디버깅에 시간을 소비하거나 라이브러리를 사용하여 도움을 줄 수 있습니다. 이 라이브러리의 부동 소수점 모델을 사용하는 것을 선호합니다. DataBlock_Predict_Float 게임 장면 그래프에 사용되는 대역폭을 정말 쉽게 최적화 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.