상위 레벨 네트워킹 개념들

다음의 정보는 어떠한 종류의 게임 네트워크 활동과 관계 있는 개념들의 모음이며, 유니티에 국한되어 있는 것이 아닙니다. 이들 개념을 유니티 네트워킹을 사용하기 전에 이해하는 것은 매우 도움이 됩니다.

네트워킹은 무엇인가?

네트워킹의 기본 개념은 다수의 컴퓨터들 사이에 통신입니다. 통신을 위해서는 clientserver가 필요합니다. 서버는 모두를 위한 헌신적인 호스트입니다. 또한 플레이어가 게임을 실행시키는 것은 다른 플레이어들을 위해서는 서버의 역할을 합니다. 서버가 설치되고 클라이언트가 접속하면, 두 컴퓨터는 다중 네트워크 게임을 위해서 필요한 데이터를 교환합니다.

네트워크 게임을 만드는 것은 세부사항에 많은 주의를 요구합니다. 유니티에서도, 대부분의 액션은 쉽게 디자인되고 생성되지만, 네트워크는 여전히 매우 복잡합니다. 우리는 유니티에서 네트워킹을 튼튼하며 유연하게 만들기로 했었습니다. 이는 게임 작성자로서는 다른 사람들과 달리 많은 선택을 해야 합니다. 이러한 선택들이 어떻게 게임을 빌드할지에 큰 영향을 줄 것입니다. 그리고 가능한 한 쉽게 만들어야 합니다. 이는 이러한 개념들을 배우고, 가능한 한 많이 계획하며, 장기간의 영향에 대해 명심해야 합니다.

네트워킹 접근들

네트워크 게임을 구성하는 데는 두 개의 공통적이고 증명된 접근법들이 있습니다. 이들 접근법들은 Authoritative ServerNon-Authoritative Server로 나뉩니다. 두 접근법들 모두 서버와 접속하는 클라이언트들, 그리고 이들 사이의 정보의 전달에 의존합니다. 추가로, 그들은 사용자들 사이에 더 많은 프라이버시를 제공합니다. 왜냐하면 클라이언트들은 실제로 자신들끼리 직접 접속하거나 다른 클라이언트들에게 자신의 아이피 주소를 드러내지 않습니다.

권한 보유 서버 (Authoritative Server)

권한 보유 서버 접근법은 서버가 모든 월드 시뮬레이션을 하며, 게임법칙들의 적용을 하고, 클라이언트 플레이어의 인풋을 프로세싱 합니다. 각각의 클라이언트는 그들의 인풋(키를 누르거나 수행할 함수들을 요구)을 서버에게 보내고 서버로부터 계속해서 게임의 현재 상태를 수신합니다. 클라이언트는 자신 스스로 게임 로직의 변화를 수행하지 않습니다. 대신 서버에게 자신이 원하는 것을 말하며, 서버는 내부 로직으로 클라이언트에게 무슨 일이 있었는지를 알립니다.

기본적으로, 플레이어가 원하는 것과 실제로 발생하는 것은 분리된 층이 있습니다. 이는 서버가 모든 사람이 원하는 것이 무엇인지 듣게 하고, 게임법칙(게임마다 다름)의 로직을 적용하게 합니다. 그리고, 각 클라이언트에게 무엇을 해야 하는지 알립니다. 이를 “플레이어는 서버에게 그들이 원하는 것이 무엇인지 말한다 → 서버는 무엇이 발생하는지 알아낸다 → 서버는 클라이언트 플레이어들에게 어떻게 업데이트 하는지 알린다”.

이러한 접근을 하였을 때의 이점은 클라이언트들이 속이는 것을 더욱 어렵게 만듭니다. 예를 들어, 그들은 서버에게 (즉 모든 클라이언트들에게) “나는 너의 캐릭터를 사살했다” 라고 말하는 기능이 없습니다. 왜냐하면 통신의 한계를 두기 때문입니다. 그들은 서버에게 “나는 무기를 발사했다” 라고 서버에게 말할 수 있고, 서버는 사살이 이루어졌는지 알아냅니다.

또 다른 권한 보유 서버의 예는 물리게임의 다음과 같습니다. "플레이어 클라이언트는 인풋을 서버에게 보냅니다 –> 서버는 물리 시뮬레이션을 수행합니다. 그리고 모든 충돌과 충돌의 결과를 시뮬레이션 합니다–> 서버는 플레이어 클라이언트에게 오브젝트 위치들의 업데이트를 전송합니다". 이는 각 플레이어 클라이언트가 완전한 물리 시뮬레이션을 가동하는 것처럼 보이지만, 그렇지 않습니다. 그들은 단지 서버에 의해 수행되는 물리 시뮬레이션의 결과를 받을 뿐입니다.

권한 서버를 사용해서는 로컬 움직임은 서버가 반응하기 전까지는 아무런 효과가 없습니다. 이는 플레이어에게 매우 부자연스러워 보입니다. 그러므로 go forward 버튼을 누르면, 각 방향(서버, 클라이언트)이 50ms 시간이 걸린다면 총 100ms동안 아무 일도 일어나지 않을 것입니다. 이를 극복하려면, Client Side Prediction이 사용됩니다. 본 가이드는 클라이언트 쪽 예측에 대해서는 자세하게 설명하지 않습니다. 왜냐하면, 이는 매우 전문화된 기술이기 때문입니다. 핵심적으로, 각각의 클라이언트가 동작해야 된다고 믿는 방식으로 클라이언트를 동작하게 하는 것입니다. 그리고 서버로부터 수정을 받는데 오직 연산이 부정확 할 때만 입니다. 서버는 클라이언트가 잘못 예측하면 클라이언트의 액션을 오버라이드 할 수 있습니다. 클라이언트 쪽 예측에 대해 더 궁금하시면, Google “Client Side Prediction”.

서버를 권한 보유화 시키면 서버에 많은 프로세싱 부하가 필요합니다. 서버가 각종 다른 프로세스들과 명령어들을 액션 게임에서32다른 플레이어들로부터 받는다고 상상해 보십시오. 서버는 모든 유저 인풋을 프로세싱 해야하며, 클라이언트들 사이의 충돌을 해결하며 무엇이 적법하고 적법하지 않는지 결정할 필요가 있습니다.

비 권한 보유 서버 (Non-Authoritative Server)

비 권한 서버는 각 유저 인풋의 결과물을 컨트롤 하지 않습니다. 클라이언트들 자신이 유저 인풋을 프로세스하며, 게임 로직을 지역적으로 프로세스 합니다. 그리고 서버로 결정된 액션의 결과를 보냅니다. 서버는 월드스테이트의 모든 액션들을 동기화 합니다. 이는 디자인 관점에서 구현하기 쉽습니다. 왜냐하면, 서버는 단지 클라이언트들의 메시지들을 전달할 뿐이지 클라이언트가 하는 것을 넘어선 어떠한 추가적인 프로세싱을 하지 않습니다.

그러므로, 어떠한 종류의 prediction 방법들이 필요 없으며, 클라이언트들이 모든 종류의 물리와 이벤트를 자신 스스로 다루며, 서버에게는 무엇이 일어났는지 메시지를 릴레이 할 뿐입니다. 그들은 그들의 오브젝트들의 owners이고, 그들의 오브젝트의 지역적 수정을 네트워크를 통해 보내는 게 허락되는 에이전트 들입니다.

네트워크 통신 방법들(Methods of Network Communication)

이제 우리는 네트워크 게임의 기본 구조를 살펴보았습니다. 우리는 클라이언트와 서버가 더 낮은 레벨에서 어떤 식으로 얘기하는지 알아볼 것입니다.

두 가지 관계 있는 방법들이 있습니다: Remote Procedure CallsState Synchronization입니다. 어떤 특정한 게임에서 두 방식을 어떤 지점에서 모두 사용하는 것은 흔합니다.

Remote Procedure Calls

RPCs라기도 하고 네트워크를 통해 다른 머쉰(machine)들의 함수를 호출하는데 사용됩니다. 이는 플레이어의 지역 게임 인스턴스 또한 포함합니다. 클라이언트들은 서버로 RPC를 보낼수 있고, 서버는 RPC들을 하나 또는 다수의 클라이언트들에게 전송할 수 있습니다. 더 보편적으로, 잘 발생하지 않는 액션들을 위해 사용됩니다. 예를 들어, 클라이언트가 문을 열기 위해 스위치를 켰다면, RPC를 통해 서버로 문이 열렸다고 말할 수 있습니다. 서버는 다른 RPC로 모든 클라이언트들에게 같은 문을 열라는 로컬 함수를 호출 할 수 있습니다. 그들은 개개의 사건들을 다루고 수행합니다.

상태 동기화 State Synchronization

상태 동기화는 끊임없이 변하는 데이터를 공유할 때 사용합니다. 가장 좋은 예는 액션 게임에서의 플레이어의 위치입니다. 플레이어는 항상 움직이고, 달리고, 점프 등을 합니다. 모든 다른 네트워크의 플레이어들은, 심지어 플레이어를 지역적으로 컨트롤하지 않는 플레이어 조차도, 그가 누구이고 무엇을 하고 있는지를 알아야 합니다. 끊임없이 이 플레이어의 위치를 릴레이 해서, 게임은 위치를 다른 플레이어들에게 정확하게 표현합니다.

이런 데이터는 정기적으로 종종 네트워크를 통해 보내집니다. 이런 데이터가 시간에 민감하기 때문에, 한 머쉰에서 다른 머쉰으로 인터넷 튜브를 타고 여행하는 시간이 필요로 합니다. 그러므로, 보내어지는 데이터 량을 줄이는 게 무엇보다 중요합니다. 간단한 말로, 상태 동기화는 많은 대역폭을 필요로 하고, 가능한 한 적은 대역폭을 사용하도록 하여야 합니다.

서버들과 크라이언트들을 함께 접속

모든 요소를 고려했을 때 서버들과 클라이언트들을 함께 접속하는 것은 매우 복잡합니다. 머쉰들은 그들 자신의 사설 아이피나 공인 아이피를 가집니다. 그리고 그들은 로컬또는 외부의 방화벽을 가져 접근을 막을 수 있습니다. 유니티 네트워킹은 이런 모든 상황을 다루기 위한 방법의 제공을 노력하지만, 특별한 마법은 없습니다.

사설 주소들은 아이피 주소들인데 인터넷으로부터 직접적인 접근이 되지 않습니다. 그들은 또한 NAT 주소들이라고 불리는데, 인터넷을 접속하는 방식 때문에 그렇게 불립니다 (NAT: Network Address Translation). 이를 간단한 용어로 설명하면, 사설 주소는 로컬 라우터를 거쳐 주소를 공인 주소로 바꿉니다. 이러한 방식으로, 많은 사설 주소들이 하나의 공인 아이피를 통해 인터넷으로 통신합니다. 이는 인터넷에서 유저가 사설 주소로 접속을 맺고자 하는 것을 제외하고는 괜찮습니다. 유저는 사설 주소를 다루는 라우터의 공인 주소로 가서 사설주소와 접속하려면NAT punchthrough라는 개념을 사용해서 접속합니다. NAT punchthrough을 사용해서 일반적인 서버 (facilitator라 불림)는 공인 주소가 사설 주소로 통신할 수 있도록 중계합니다. 사설 주소는 처음 facilitator를 접속해서 로컬 라우터에 펀치를 뚫습니다. Facilitator는 이제 공인 아이피 주소와 사설 아이피 주소가 사용하는 포트를 볼 수 있습니다. 이 정보를 사용하여, 어떠한 인터넷의 머쉰도 직접적으로 이전에는 닿을 수 없는 사설 주소를 연결 할 수 있게 됩니다. 이는 기초를 다루는 간단한 설명입니다.

공인 주소는 더 간단합니다. 주요 위험은 접속이 내부 또는 외부 방화벽에 막히는 것입니다. 내부 방화벽은 방화벽이 머쉰위에서 로컬로 수행되는 것을 일컫습니다. 이 경우, 사용자는 포트를 열거나, 게임 서버를 접속할 수 있도록 부탁 받을 수 있습니다. 외부의 방화벽은 방화벽이 네트워크의 라우터에서 수행되거나 이와 비슷한 사용자의 컨트롤을 벗어난 곳에서 수행되는 것으로 이해될 수 있습니다. 이 경우 방화벽에 구멍을 내는NAT punchthrough 방법의 사용을 시도하는 것을 제외하고는 접속에 특별히 할 일은 없습니다. 테스팅 하는 동안 이는 작동했지만 얼마나 성공적인지를 알리는 연구는 없습니다.

위에서 언급한 접속 이슈들은 서버들과 클라이언트들에 다르게 영향을 미칩니다. 클라이언트들은 서버에 단지 접속이 필요하기 때문에 더 간단하고, 그러한 접속은 외부 트래픽을 관여시킵니다. 클라이언트가 공인 주소를 가지면, 이는 항상 작동합니다. 외부로 나가는 트래픽은 회사 네트워크 (사용이 종종 제한됩니다)를 제외하고는 좀처럼 차단 되지 않습니다. 클라이언트가 사설 주소를 가진다면, 모든 서버를 접속할 수 있습니다. 예외는 사설 주소를 가진 서버들로, 그들은 NAT punchthrough를 할 수 없습니다 (나중에 좀더 자세히 살표봅니다). 서버들은 더 복잡한데, 그들은 모르는 소스들로부터의 들어오는 접속들을 수락 가능하게 해야 할 필요가 있습니다. 공인 주소를 사용하여, 서버는 게임포트를 인터넷에 열게 할 필요가 있으며, 방화벽에 막히지 않아야 할 필요가 있습니다. 포트가 열리지 않고 인터넷으로 접근 불가능하면, 서버는 어떠한 클라이언트들 로부터의 접속을 수락할 수 없습니다. 그러므로 사용이 불가능합니다. 서버가 사설 주소가 있다면, NAT punchthrough를 할 수 있어야 합니다. 그렇지 않으면, 접속을 할 수 있는 것이 없게 됩니다. NAT punchthrough를 하지 않는 클라이언트들은 서버로 접속할 수 없습니다.

유니티는 이런 다른 접속 환경에서 테스트하는 툴을 제공합니다. 모든 것이 해결되면, 서버들과 클라이언트를 동시에 접속하고 싶을 것입니다. 여기에는 두 가지 방식이 있습니다. 클라이언트는 DNS 이름과 서버의 IP주소, (마스터 서버로부터의 도움으로) 커넥션을 알아야 하는 직접 접속이 있습니다. 마스터 서버는 서버들로 하여금 그들의 존재를 클라이언트들에게 알리도록 합니다. 클라이언트들은 이 경우 서버에 대해서 미리 알아야 할 필요가 없습니다.

네트워크 대역폭 최소화

다수의 클라이언트를 거친 상태 동기화를 작업할 때는, 오브젝트들이 동기화 되어 보이도록 모든 세부사항을 동기화 할 필요가 없습니다. 예를 들어, 캐릭터 아바타를 동기화 할 때는, 클라이언트 사이의 위치와 회전만 보내면 됩니다. 비록 캐릭터 자체는 더 복잡하고 더 깊은 Transform 계층 구조를 가지지만, 전체 계층 구조에 관한 데이터는 공유될 필요가 없습니다.

게임내의 많은 데이터는 정적으로 보이지만, 클라이언트들은 처음에 보내거나 동기화 할 필요가 없습니다. 가끔 또는 한번의 RPC 호출이 많은 기능들이 작동하게 하는데 충분합니다. 알고 있는 데이터를 이용하는 것은 게임의 설치와 클라이언트가 가능한 스스로 작업하게 하는데 존재하게 됩니다. 예를 들어, 질감이나 메쉬와 같은 asset들은 모든 설치에 존재하고 변화 하지 않습니다. 그러므로, 그들은 동기화될 필요가 없습니다. 이는 간단한 예제이지만, 어떤 데이터가 전적으로 하나의 클라이언트에서 다른 클라이언트로 공유하는데 중요한지 생각하게 합니다. 이는 공유해야 할 유일한 데이터입니다. 만약 전에 네트워크 게임을 만들어 본적이 없다면 이와 같은 작업은 쉽지 않습니다. 이 사항을 고려해보십시오. 모든 클라이언트들을 특정레벨로 부르고 그들 자신의 네트워크 된 요소들을 자동으로 추가할 때 레벨 이름을 사용한 하나의 RPC 호출을 사용할 수 있습니다. 게임을 구조화하고 각각의 클라이언트를 가능한 자급자족하게 만드는 것이 대역폭을 줄이는 결과를 만들 것 입니다.

다중 게임 성능

물리적 위치와 서버의 성능은 게임의 실행성에 크게 영향을 미칩니다. 서버로부터 대륙단위로 떨어진 클라이언트들은 큰 랙 (lag)을 경험할 수도 있습니다. 이는 대개 본질적이며 특별한 방법이 없습니다. 아무리 서버의 하드웨어가 강력해도, 서버로부터 이동하는 거리는 게임에서 항상 요소로 작용합니다. 서버와 클라이언트들이 같은 대륙에 위치하는 것이 가장 최상의 경우 입니다.

추가 자료들

추가로 네트워킹의 개념을 배우기 위한 도움이 되는 링크들을 모았습니다. 아래의 자료들을 가능한 많이 읽기를 권유합니다.

역링크