강의 잘 보고 있습니다. 하나하나 다 따라해봤는데 전 강의 까지는 문제가 없었지만 이상하게 이번강의 따라해보니 유니티와 서버의 연결은 잘된듯 한데 유저가 2명 이상이면 Disconnect를 호출하더군요 이유를 찾아보니까 OnRecvCompleted에서 첫 if문에 있는 args.BytesTransferred 가 계속 0인데 이는 서버측이든 클라측이든 0으로 되어있었습니다. 혹시 코드가 잘못된지 확인해봐도 모르겠어서 강사님께서 올려주신 코드로 서버와 클라를 연결 시켜봐도 똑같이 BytesTransferred가 0이어서 문제가 생기는데 왜 이러는지 알 수 있을까요?? 기기에 따라 다른가요
public void Remove(Room<T> room) { if (room == Head) { Head = Head.Next; Head.Prev = null; } else if (room == Tail) { Tail = Tail.Prev; Tail.Next = null; } else { room.Prev.Next = room.Next; room.Next.Prev = room.Prev; } Count--; } 제가 구현한 Remove는 이렇습니다. 강의에서 room.Prev 와 room.Next 의 null 유무를 체크하던데, 여기서 이해가 안 되어 질문드립니다. 1, 2, 3, 4, 5의 경우라고 가정해보면 1은 head, 5는 tail, 이를 삭제하려고 할 경우 앞의 두 조건문에서 걸립니다. 나머지의 2, 3, 4 를 가정해보면 이 노드들은 필히 Next 와 Prev가 존재합니다. AddLast를 보더라도 Tail의 next와 newRoom의 Prev를 서로 연결하는 부분이 존재하니 말이죠. 그러니 이를 굳이 조건절에서 검증해야 할 필요가 있나?가 궁금합니다. 또 head, tail 조건에서도 Head.Prev = null, Tail.Next = null 을 하지 않으면 remove의 조건을 정확히 충족하지 못한다고 생각합니다. 어찌됐든 접근할 수 없도록 만드는 게 이론적으로 삭제와도 같으니깐요. null 처리를 안 할 경우 만약 Head.Prev를 하게 되면 삭제했던 노드를 다시 호출할 수 있게 된다고 봅니다.
안녕하세요 루키스님! 강의 잘 듣고 있습니다. 세션과 소켓의 관계에 대해 궁금한게 생겼습니다. 비유로 인해 오해가 생길까 지웠습니다. Connector를 쓰기 전 까지는 더미 클라이언트에 세션을 쓰지 않아 서버와 클라이언트가 서버에서 발급한 세션을 통해 정보를 공유한다고 생각했습니다. 마치 실 전화기 처럼 말이죠. 클라 (소켓) ---------------세션--------------(소켓) 서버 근데, 커넥터라는 개념이 들어오고 다시 흐름을 살펴보니 서버도 서버의 세션을 사용하고, 클라도 클라의 세션을 사용하니 위의 비유와는 잘 맞지 않아 다시 생각해봤는데 이해한 바가 맞는지 봐주셨으면 합니다. 소켓 : 유심칩 세션 : 스마트폰(abstract 를 구현해서 어떻게 행동할지 정의했기 때문에 클라는 클라 전용 스마트폰, 서버는 서버 전용 스마트 폰을 가지고 있다.) 클라 (소켓 + 세션) ------------------- 서버(소켓 + 세션)
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문
public PlayerType GetPlayerType() { return type; } public MonsterType GetMonsterType() { return type; } 해당 메소드는 수업에서 딱히 선언 후 사용 되지 않았기 때문에 생략해도 문제는 없는 걸까요? 혹시 사용 한다면 어떤식으로 사용이 되는걸까요?
안녕하세요 5월부터 시작하는 수업에서 BM (광고, 결제) 시스템과 서버 기본기 구현을 배우게 되는것으로 알고있는데요! 이과정에서 c#과 AWS를 사용하여 직접 서버를 구축하는 방법을 배우는것인지 아니면 파이어베이스나 뒤끝같은 관리형 서비스를 사용하는 방법을 배우는건지 궁금합니다 !
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문
static void Fight(ref Player player, ref Monster monster) { while(true) { //플레이어가 몬스터 공격 monster.hp -= player.attack; if(monster.hp <=0) { Console.WriteLine("승리했습니다."); Console.WriteLine($"남은체력 : {player.hp}"); break; } //몬스터 공격 player.hp -= monster.attack; if (player.hp <= 0) { Console.WriteLine("패배했습니다."); // break; RestartGame(ref player); } } } // 새로 추가한 부분 static void RestartGame(ref Player player) { if( player.hp <= 0) { Console.WriteLine("[3] 게임을 다시 시작하시겠습니까?"); string input = Console.ReadLine(); if(input == "3") { player.hp = 100; EnterGame(ref player); } } } RestartGame함수를 생성해서, Hp가 0이 되었을 때 다시 시작하도록 하는 스크립트를 추가해서 실행 할 때 작동은 되는데, 어색한 부분이나 잘못 사용된 부분이 있을까요?
안녕하세요.. UIManager 의 SetCanvas 에서 public void SetCanvas(GameObject go, bool sort = true, int sortOrder = 0) { Canvas canvas = Util.GetOrAddComponent<Canvas>(go); if (canvas == null) { canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.overrideSorting = true; } "if (canvas == null)" 부분이 "canvas != null" 이 되어야 될 것 같은데 설명 부탁드립니다.
PacketManager에서 MakePacket<T>가 호출되면 T 객체를 생성해서 처리하는데, 혹시 해당 함수가 호출될때마다 객체를 생성하는게 아니라 Dictionary에 Register함수로 등록할 때 객체를 생성해서 Dictionary에 등록해주고 그 객체 하나로만 버퍼를 받아서 처리하면 문제가 발생하나요?
2024/1/18 오브젝트를 풀링을 만들어 보았는데 풀링은 되는데 자식 오브젝트들이 모두 사라집니다 아래 사진은 1번 씬에 배치한 유닛사진이고 그 아래는 1번씬의 유닛을 푸쉬하고 2번씬에 팝한 모습입니다. 같은 씬에서 팝한 다음 푸쉬를 진행했을 때 6마리를 팝하고 푸쉬했는데 6마리 모두 자식오브젝트가 사라진 모습입니다. 바닥 타일도 모두 풀링을 사용하고 있는데 타일은 풀링이 잘됩니다 타일안에 자식오브젝트를 넣어놔도 같이 풀링되는데 유닛 오브젝트만 풀링이 이상하게 되는 이유를 모르겠어요. 디버그 해볼려고 노력해본 내용도 추가합니다 팝후 디버그화면에서 마우스로 하위오브젝트를 모두 비활성화 시키고 삭제하면 하위 오브젝트는 삭제되지않는다 하지만 코드로 할 경우 삭제된다. 팝하고 위치나 로테이션을 변환하지 않아도 하위 오브젝트가 사라진다. 씬 clear를 거치지 않고 수동으로 푸쉬시에도 하위 오브젝트가 사라진다. 푸쉬할때 활성화된 하위 오브젝트만 사라진다. 2024.1.19 해결은 했는데 원인은 모르겠습니다. 제가 사용하는 건 씬에 오브젝트 하나를 만들고 풀링된 오브젝트들을 한 오브젝트의 자식으로 모두 보내고 삭제시 상위 오브젝트에서 자식 오브젝트를 모두 삭제하는 방법을 사용 했었고 강의 내용을 보면서 이 부분을 리스트를 통한 저장 삭제 방식을 하였더니 해결 되었습니다. 해결은 했는데 왜 해결되었는지 궁금하네요
안녕하세요 선생님, 강의 잘들었습니다! 윈도우 빌드로 진행했고, 업데이트가 잘 되는지 확인하려고 아래 과정을 거쳤습니다. AddressableAsset을 추가해 Update a Previous Build로 에셋 빌드하고, play mode > use existing build로 이상 없이 파일 업데이트 하고 잘 진행됨. 하지만 빌드 파일에서는 이 업데이트 내용이 전혀 반영되지 않아 질문 드립니다. hash값도 제대로 바뀐걸 확인했고, 서버에도 파일도 잘 업로드가 되었는데, 빌드 파일에서는 다운받을 데이터를 전혀 찾지 못합니다. 이름을 덮어쓴 파일은 반영이 잘되는데, 새로 에셋을 추가한 부분에 대해서는 전혀 읽어오질 못합니다. use existing build로 플레이한 경우랑 어떤점이 달라서 작동하지 않는지 궁금합니다! (+그리고 Build > Update a Previous Build 선택했을때 팝업창이 뜨는게 아니라 바로 아래의 사진으로 넘어가는데, 이부분은 상관없을까요?)
public void Init(IPEndPoint endPoint, Action<Socket> OnAcceptHandler) { _acceptHandler += OnAcceptHandler; _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _listenSocket.Bind(endPoint); _listenSocket.Listen(10); SocketAsyncEventArgs e = new SocketAsyncEventArgs(); e.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted); RegisterAccept(e); } void RegisterAccept(SocketAsyncEventArgs e) { e.AcceptSocket = null; bool pending = _listenSocket.AcceptAsync(e); if (pending == false) OnAcceptCompleted(null, e); } 이 코드에서 RegisterAccept안에 pending이 false일 때 OnAcceptCompleted를 호출하는 코드를 보여주셨는데 이미 SocketAsyncEventArgs.Completed에 콜백 함수 넣어줘서 자동 처리 되는거 아닌가요?? 실제로 저렇게 함수를 재호출해주는 코드를 빼봐도 정상 작동되는것 같았습니다. 이중으로 함수 호출하시는 이유가 뭔지 궁금합니다!
안녕하세요. 강의 정말 잘 듣고 있습니다. 처음에는 단순 게임 서버를 구현하는 강의라고 생각했는데, 이론으로만 보던 CS, 네트워크 지식을 직접 구현해보면서 하니 기본기 다지는데 정말 도움이 되네요. 그리고 꽤나 자바와 같은 모던 언어를 사용하다보니 깊게 생각하지 않던 부분도 고민할 수 있는 최고의 강의인것 같습니다. 질문으로 돌아가서 비트 마스크를 사용하는 이유가 C# 에서 원자적 연산을 제공하는 Interlocked 클래스 함수의 파라미터 때문인가요? 처음에는 writer, reader를 체크할 수 있는 클래스 변수를 따로 만들면 되지 않을까? 라고 생각했는데 그렇게 되면 if, if 이런 조건문이 계속 들어가서 원자적 연산이 어려워 져서 그런가요?(별도로 락이나 모니터로 잡을 수 있지만 가독성 측면에서도 그렇고, Interlocked가 표준화 되어 있기 때문에 저렇게 사용하나? 추측합니다.) 몇개의 질문을 살펴보니 비트마스크와 Interlocked의 CompareExchange의 조합이 강력하네요. 지금 강의는 언어 레벨에서 지원하는 읽기 쓰기 락 동작 원리를 보여주신 것인데 실제 프로덕션 코드에서 사용하려면 락 타임아웃 같은것도 구현해야하나요? 생각해보면 어떠한 경우에 읽기 락이 많이 쌓이고 해제하지 못한 경우일 때 쓰기락에 대기시간이 길어지니 일정 시간 후 락 해소를 시켜줘야 하나 궁금하네요.