251102

lililllilillll·2025년 11월 2일

개발 일지

목록 보기
343/350

✅ 한 것들


  • R&D :: 멀티 게임
  • 게임 서버 프로그래밍 교과서


🛠️ R&D :: 멀티 게임


Fishnet

Fundamentals

fishnet 쓰는 네트워크 게임은
싱글 플레이 게임을 여러 기계에서 돌리는 것과 동일
(또는 여러 게임 인스턴스를 같은 기계에서 실행)

Synced Variables : 변수 동기화
Remote Procedure Calls : 메서드 동기화
Broadcasts : 아무곳에나 communicate

NetworkObject 붙인 게임 오브젝트에 ID 할당됨
NetworkBehaviour 상속받은 컴포넌트에 ID 할당됨

Spawnable Prefabs : 런타임에 프리팹 스폰하려면 프리팹 적어놔야 하는 SO

Networking Models

  • 데디 서버 : 클라들이 서버랑 연결
  • 호스트(리슨 서버) : 클라들이 서버 돌리는 클라 하나에 연결
  • P2P : 클라들끼리 서로 소통. (security 낮고 sync 어렵다, fishnet은 지원 안 함)
  • relay server : 리슨 서버 매개하는 서버에 연결 (fallback용. thrid-party 연결 지원함)

host migration? : 아직 없음. 향후 개발 예정.

기능 구현

우클 이동 동기화 (ing)

Multiplayer Play Mode의 추가 인스턴스에서 Player Input 입력 안 먹는 문제

  • 새 프로젝트 만들어서 확인했더니 재현 x
  • fishnet import에 NetworkManager prefab까지 가져와도 재현 x
  • Player 오브젝트에 Player Input이 붙어있고, Input Test와 Input Manager를 위한 오브젝트에도 Player Input 붙어있었음
    • Player 오브젝트를 끄고 실행하면 문제 사라짐
    • 근데 왜? → 내일 마저 원인 분석


📖 게임 서버 프로그래밍 교과서


1.7 교착 상태

교착 상태 : 두 스레드가 서로를 기다리는 상황

서버에서 교착 상태 되면 발생하는 현상

  • CPU 사용량이 현저히 낮거나 0%
  • 클라가 서버를 이용 불가

CRITICAL_SECTION 사용 방법

  • window에서만 가능, include windows.h 해야 함.
  • InitializeCriticalSectionEx() : 임계 영역 생성
  • DeleteCriticalSection() : 임계 영역 제거
  • EnterCriticalSection() : 임계 영역 잠금
  • LeaveCriticalSection() : 임계 영역 잠금 해제
// CRITICAL_SECTION wrapper
// enter, leave 어쩌구를 lock, unlock으로 바꾼다
class CriticalSection
{
	CRITICAL_SECTION m_critSec;
	public:
	CriticalSection();
	~CriticalSection();

	void Lock();
	void Unlock();
};

// CriticalSection wrapper
// lock_guard처럼 만들기 위해, 소멸자에 unlock() 호출
class CriticalSectionLock
{
	CriticalSection* m_pCritSec;
	public:
	CriticalSectionLock(CriticalSection& critSec);
	~CriticalSectionLock();
};

CriticalSection::CriticalSection() { InitializeCriticalSectionEx(&m_critSec, 0, 0); }
CriticalSection::~CriticalSection() { DeleteCriticalSection(&m_critSec); }

void CriticalSection::Lock() { EnterCriticalSection(&m_critSec); }
void CriticalSection::Unlock() { LeaveCriticalSection(&m_critSec); }

CriticalSectionLock::CriticalSectionLock(CriticalSection& critSec)
{
	m_pCritSec = &critSec;
	m_pCritSec->Lock();
}

CriticalSectionLock::~CriticalSectionLock() { m_pCritSec->Unlock(); }

편하게 쓰기 위해 만든 wrapper 클래스

int a;
CriticalSection a_mutex;
int b;
CriticalSection b_mutex;

int main()
{
	// t1 스레드를 시작한다
	thread t1([]()
	{
		while (1)
		{
			CriticalSectionLock lock(a_mutex);
			a++;
			CriticalSectionLock lock2(b_mutex);
			b++;
			cout << "t1 done.\n";
		}
	});

	// t1 스레드를 시작한다
	thread t2([]()
	{
		while (1)
		{
			CriticalSectionLock lock(b_mutex);
			b++;
			CriticalSectionLock lock2(a_mutex);
			a++;
			cout << "t2 done. \n";
		}
	});

	// 무한 반복 돌렸으니 여기까진 안 옴
	t1.join();
	t2.join();
}

인위적으로 교착 상태 일으키는 코드

  • 디버그 일시정지 후 디버그 > 창 > 스레드에서 콜 스택 확인 가능.
  • mutex의 OwningThread를 확인하고 10진수로 바꿔보면 스레드의 ID

1.8 잠금 순서의 규칙

뮤텍스 여러 개 사용할 때 교착 상태 예방하려면,
잠금 순서를 그래프로 그려두어야 한다.
잠금을 할 때 그래프 보면서 순서 맞는지 확인.

이미 A → B → C 순서대로 잠가놨는데,
다른 곳에서 C → B 순서로 잠그면 교착 상태 일어난다.

재귀 뮤텍스 : lock() 중첩 가능. 여러 번 잠그면 여러 번 풀어야 함.
이미 잠근 후엔 순서 다르게 추가로 잠가도 상관 없음

1.9 병렬성과 시리얼 병목

병렬성(parallelism) : 여러 CPU가 각 스레드 연산 실행하여 동시 처리량 올림
시리얼 병목(serial bottleneck) : 병렬 실행되게 만들었는데 한 CPU만 연산 수행
암달의 법칙 : 연산 50%를 아무리 최적화해도 최대 2배 개선

Concurrency Visualize : 멀티스레드 프로그램 병목 있는지 분석 및 시각화 가능한 Visual Studio Extension

잠금 후에 디스크 읽는 코드 같은 거 넣으면 성능 떡락
그때만이라도 락을 풀던가 해라.

1.10 싱글 스레드 게임 서버

싱글 스레드 서버

  • 멀티 스레드 어려우니까 그냥 싱글 스레드로 하기도 한다
  • CPU 개수만큼 프로세스를 띄운다.
  • 디스크 로딩 시 시리얼 병목. 해결 위해 비동기 함수나 코루틴 사용.
  • 부득이한 경우 아니면 방 개수만큼 스레드나 프로세스 띄우는 건 피하기.
    • 컨텍스트 스위칭이 마구 일어나기 때문에, 실제 처리 동접자 수 떡락
profile
너 정말 **핵심**을 찔렀어

0개의 댓글