[7월 18일]
[MOD 강좌 Chapter 7] 네트워크의 이해
○ MOD - 네트워킹을 지원하고, 멀티 플레이를 지원하는 게임이다.
○ 네트워크 - 다양한 통신 모델 : 서버 - 클라이언트 구조로 짜고 있다.
○ 클라이언트 : 접속된 각각의 유저
○ 서버 : 클라이언트의 요청을 받는 서버
* 각 클라이언트는 모두 서버와 연결되어 있다.
* 서버-클라이언트 관계는 위의 사진과 같이 1:n의 관계이다.
Component - Property, Function
- 하나의 Entity를 생성하게 되면, 클라이언트에 하나, 서버에 하나가 생긴다.
- 서버에 있는 Entity와 클라이언트에 있는 Entity는 겉보기에는 완전히 동일하지만, 내부적으로는 차이가 있다.
- 각 Entity의 Property 값들은 서로 연관되어 있지 않다. 하지만, 이는 바람직한 상황이 아니다.
- 따라서, 클라이언트-서버 모델에서는 어느 한 쪽에서 값이 바뀐다면 다른 한 쪽의 값도 바뀔 수 있도록 맞추는 행위를 하는데, 이것을 동기화라고 한다.
- 전통적인 방법에서는 하나 하나 동기화 처리를 해주어야 하지만, 그런 번거로움을 최소화하기 위하여 MOD에서는 기본적으로 Property 동기화 옵션을 설정할 수 있게 해주었다.
- Score 변수는 기본적으로 동기화가 된다.
- Sync를 클릭하면 None으로 변경되며 동기화가 되지 않음을 의미한다.
- 동기화를 하게 되면 어떻게 되는가?
→ 서버에서 값을 변경하면 내가 별 다른 행위를 하지 않아도 클라이언트에서도 값이 동일하게 변경된다.
- 내부적으로는 메시지를 쏘고 패킷을 쏘고 하는 과정이 있다고는 하지만 지금은 중요하지 않다고 한다.
- 몇몇 자료형에 대해서는 동기화가 불가능하다.
- 동기화에는 방향이 존재한다. 서버는 한 개지만, 클라이언트는 여러 개가 될 수 있다. 모든 클라이언트에 대해서 변경하고자 하는 값을 변경하고자 할 때 서버에서 값을 변경하면 된다. 하지만, 모든 클라이언트에서 동일하게 적용되는 것이 아니라 각 클라이언트마다 다르게 적용되어야 하는 값들에 대해서는 하나의 클라이언트에서 값이 바뀐다고 하더라도 모든 클라이언트에서 값이 바뀌면 안 된다. 따라서, 클라이언트에서 값을 변경했을 때 동기화가 발생하면 안 된다. 그러므로, 모든 클라이언트에 대해서 값을 변경하고 싶을 때 서버에서 값을 변경해준다면 자동으로 모든 클라이언트에 대해서 동기화가 되어야 함을 알 수 있다.
- "동기화는 단방향으로 진행되며, 서버를 기준으로 값이 바뀐다."
- 다행히, 우리가 사용하는 대부분의 Logic은 서버 기준으로 돌아간다. (onBeginPlay : server only)
○ 실행제어
- 서버가 있고, 서버에서 모든 로직의 흐름을 관리한다.
- 중간중간 변경되는 사항에 대해서는 각각의 클라이언트에 전파를 하게 된다.
- 일반적으로, 서버가 바뀌는 상황에 따라서 클라이언트가 대응한다.
○ OnsyncProperty
- 서버에서 변경된 Property가 있다면, 클라이언트에서는 이 바뀐 상황을 알 필요가 있다.
- 이 떄 무엇인가 효과를 주기 위해서는 이 값이 바뀌는 타이밍을 알아야 하는데, 타이밍을 알기 어렵다.
- 동기화는 서버에서 클라이언트 방향으로 단방향 동기화가 진행되므로, OnsyncProperty 함수를 활용하여 변경된 property 값을 받아준다.
- 대입이 끝난 이후에 들어오는 형식이므로, 따로 대입을 해줄 필요는 없다.
void OnSyncProperty (string name, any value) :
{
if name == "score" then
log(value)
end
}
- Property가 진행될 때 불리는 함수이며, 무엇인가 처리하고 싶다면 여기에서 처리하면 된다.
○ Function
- 서버에서 로직을 작성할 때 함수를 호출하는 경우가 있을 것이다.
- 서버에서 호출한다면 서버에서 실행됐다고 볼 수 있고, 클라이언트에서 실행됐다고 볼 수 있다.
- 메소드는 부르는 위치에 따라서 동작하는 공간이 다르다.
- Function에 Option이 없을 땐 호출한 곳에서 실행되는데, 메소드 입장에서는 자신이 어디에서 호출될 지 알 수 없으므로 사용하기에 다소 불안한 측면이 있다.
- 우클릭 - Method Setting : 메소드의 속성을 정의할 수 있다. 이것을 공간이라 하고, 공간과 실행제어라는 용어를 앞으로 쓰게 될 것이다.
함수 | 설명 |
ClientOnly | 클라이언트에서만 호출할 수 있는 함수. 서버에서 호출하면 작동하지 않는다. |
ServerOnly | 서버에서만 불릴 수 있는 수 있는 함수. 클라이언트에서 호출하면 작동하지 않는다. 대부분의 Logic은 Server 기준으로 짜게 된다. 주로 Server 위주의 행위를 많이 하게 될 것이다. |
Client, Server | Client : 클라이언트에서 돌아가야 한다. Q. ClientOnly와의 차이점? A. 혹시 이 함수가 Server에서 호출됐다면 자동으로 Client 쪽으로 패킷을 보내 통신하여 Client가 함수를 실행하게 한다. Server : 혹시 이 함수가 Client에서 불렸다고 하더라도 Server 쪽에 보내서 실행하게 한다. Client에서는 Server 함수를 마주치게 된다면 Server에 함수 실행을 요청하고, 자신이 해야 할 명령들을 수행한다. Client용 함수를 Server에서 호출하게 된다면 각각의 Client에게 해당 함수를 실행하라고 명령한 후, Server 또한 자신의 일을 수행한다. 함수 - Packat - Parameter - 필요한 데이터들 입력 : 각각의 Client들에게 수행되기 때문에 Broadcasting 된다. 이 일련의 과정을 실행 제어란 이름으로 한 번에 보내게 되므로 따로 이러한 것들을 별도로 제어할 필요없다. |
Multicast | Server와 Client 양쪽 모두에게서 실행해야 하는 경우 Multicast를 사용하게 된다. |
- 기본적인 Logic은 전부 Server에서 돈다. Event Function 같은 경우에는 기본적으로 다 Server에서만 돌아가게 되어있다.
- Client에서만 돌아가게 할 수 있지만, Server에서 돌아가는 것이 기본이다.
- Client와 Server 모두에서 돌아가게 할 수도 있다. 하지만 흐름이 양쪽으로 진행되면 혼란스러워지기 때문에 비추천한다.
- 단순화하는 측면에서 Server로 하는 것이 좋다.
함수 | 설명 |
ClientOnly() | * 클라이언트 / 공간 행동 : 클라이언트에서 실행 |
Client() | * 서버 / 공간 행동 : 클라이언트에서 실행 |
ServerOnly() | * 서버 / 공간 행동 : 서버에서 실행 |
Server() | * 클라이언트 / 공간 행동 : 서버에서 실행 |
Multicast() | * 서버 / 공간 행동 : 서버에서 실행 |
- self:ClientOnly() : 서버 공간에서 클라이언트 전용 함수를 호출할 수 없다.
- self:ServerOnly() : 같은 서버이므로 실행이 될 것이다.
- 실행 결과
- self:Server() : 실행을 어디에서 하든 서버에서 실행될 것이다. 서버에서 불리면 서버에서 불리고, 클라이언트에서 불려도 서버에서 불리게 될 것이다.
- self:Client() : 클라이언트엣 실행될 것이다. Client이므로 여기에서 실행되지 않고 Client 족으로 전송해서 Client 쪽에서 Client가 찍힐 것이다.
- 실행 결과
- Client가 찍히지 않았다. 이 이슈는 밑에서 다루겠다. 타이밍 이슈가 있다.
- 실행 결과
- BEGIN PLAY → SERVER ONLY → CLIENT
- 실행 결과
- SERVER, END, CLIENT 순으로 찍힐 것이다.
- 게임이 시작되면 우선 서버에서 메시지를 뿌려준다.
- 그 후, 클라이언트에게 클라이언트 메시지를 뿌려달라고 요청함과 동시에 바로 다음 문장인 END를 출력한다.
- 그래서 SERVER가 첫 번째, END가 두 번째가 되고, 서버가 CLIENT에 보낸 메시지가 네트워크를 타고 이동하고, 그 메시지가 CLIENT에 도착하면, 메시지를 받은 순간에 CLIENT를 찍을 것이다. 따라서 CLIENT가 세 번째가 된다.
- 메시지가 이동하는 데 얼마나 걸리는 지 정확한 타이밍은 우리가 알 수 없기 때문에, SERVER와 END 이후 CLIENT가 출력된다는 순서를 파악하고, 잘 분리해서 생각해주면 된다.
- 실행 결과
- SERVER가 먼저 찍혔다. CLIENT에게 메시지를 보낼 때 CLIENT 메시지를 찍는 것이 아니다.
- CLIENT에게 요청을 한 후, MULTICAST를 찍는다.
- MULTICAST 본인은 실행을 하고, 이 뒤에 CLIENT 쪽으로 다시 보내는 것이다. MULTICAST라고.
- 그리고 다시 밑으로 내려가면 END가 실행이 될 것이다.
- 그리고 언젠가 CLIENT에서 메시지를 받으면 CLIENT를 출력할 것이다. CLIENT가 먼저 왔기 때문에 CLIENT 메시지를 먼저 출력한 것이다. 이 순서는 보장 된다.
- 그리고 그 다음에 MULTICAST가 왔기 때문에 MULTICAST가 오게 되는 것이다.
핵심 : 서버에서 메인 플로우가 지나간다. 서버가 흐름을 관장하고 클라이언트는 거기에서 받아온 것들을 표시해준다.
대부분 서버에서 로직을 짜지만, 클라이언트에서는 처리를 하는 게 거의 없다고 말했던 적이 있다. 하지만 그 예외적인 것이 몇 개가 더 있다.
서버와 클라이언트가 있는데 서버에 Entity가 생성됐다고 가정해보자. 그렇다면 일반적인 경우에는 모든 Client에 Entity가 생성되는 게 맞다. 하지만, 하나의 클라이언트에서만 생성하고 싶은 Entity가 있을 수 있다. 대표적인 예시가 UI로, 스탯창, 아이템창과 같이 각 클라이언트마다 다르게 관리되어야 하는 것들이다. 따라서 UI의 경우 클라이언트에서 관리한다. 몬스터와 부딪혀 체력이 깎이는 경우와 같은 입력 또한 Local(또는 Client)를 따라가야 한다. 즉, 나를 중심으로 움직여야 한다. 따라서 이러한 녀석들은 서버에 없다.
추가로 클라이언트를 사용하는 경우로는 이펙트를 관리하는 경우인데, 이펙트 또한 Entity이다. 서버에서 이펙트 Entity를 만들면 각 클라이언트에 전부 생성이 될 것이지만, 가끔 특정 Client에서만 연출을 하고 싶을 때가 있다. 이런 경우엔 Local, 즉 Clien에서 생성하고 제어하는 것이 필요할 것이다. Local Entity를 사용한다는 뜻이 Client를 제어한다는 뜻이다. 이는 최적화와도 연관 있는데, 연출용 이펙트를 수도 없이 많이 생성하면 이펙트 Entity를 동기화하는 비용, 네트워크 비용 등의 비용이 굉장히 클 것이다. 최적화를 할 때, 정밀한 제어를 하기 위해서 Client Logic을 따로 사용한다 정도로 알고 있도록 하자.
실행 제어 얘기를 조금 더 해보자면, 매개변수를 사용할 수 있고, 매개변수를 작성하면 알아서 처리가 된다.
- 실행 결과
실제 Client에서 실행이 된 것임에도 불구하고 서버에서 이런 것을 쳤는데 이런 argument를 따라서 전달되는 것이다.
아까 wait(2)를 쓰기 전까지는 나오지 않았었다. 그 이유가 무엇일까? 클라이언트 서버를 만들 때 주의를 해야 하는데, 기본적으로 서버가 구동이 되고 클라이언트가 열린다. Entity들이 서버에 생성이 된 다음에, 클라이언트에도 생성이 된다. 근데 생성이 된 뒤에 보내는데, 아까 생성 도중에 이 시점에서 클라이언트에 패킷을 쏘면 어떻게 될까? 얘를 보내는데 받을 애가 없을 것이다. 아직 생성이 되기 전이니까. BeginPlay 때 생성을 하긴 하는데, BeginPlay에서 실행이 되는데, self.client라고 작성했다. 클라이언트에서는 이걸 받았는데 아직 이 entity가 생성이 안 되어 있다. 안 되어있을 수도 있고, 되어 있을 수도 있다. 그래서 알 수가 없다. 그렇기 때문에 이런 타이밍 이슈가 있을 수 있다 라는 걸 알고 계시면 되겠다.
회고 : Property - 실행 제어에 대해 알아보았다. 익숙해지기 전에는 Server 위주로 작성하는 것이 편리할 것으로 보인다.
*해당 게시글은 Supporters Hackathon 참여자에게 제공되는 MOD 학습 강의와 기본 학습 교안의 내용을 기반으로 작성되었음을 알립니다.
'Coding > 멋쟁이사자처럼' 카테고리의 다른 글
[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 3주차 회고 (9. 컴포넌트의 활용 1) (0) | 2022.07.23 |
---|---|
[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 3주차 회고 (8. Event와 컴포넌트 확장) (0) | 2022.07.22 |
[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 2주차 회고 (6. 스크립트의 이해) (0) | 2022.07.19 |
[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 2주차 회고 (5. 자주 사용하는 컴포넌트) (0) | 2022.07.14 |
[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 2주차 회고 (4. 지형과 레이어의 이해) (0) | 2022.07.13 |