✅C# DummyClient 수동 조작 버전 Flow
* 기본 Flow완성
🔄C# DummyClient 수동 조작으로 Character 생성 까지 만들어보기
* 로그인까지 완료
✅C++ Client수신 > DB > GameClient 캐싱 > Client송신 Flow 제작 및 테스트
➕C# DummyClient Command Reflection / Attribute C#문법 사용하여 함수매핑자동화
➕DummyClient 로직 수정
➕C++ PacketManager -> Packet생성자 매핑 지저분해서 함수제작
➕C++ PacketManager -> Packet핸들러 매핑 지저분해서 함수제작
일단 DummyClient는 어느정도 기본 플로우는 만들어뒀다
그래도 User 캐싱부분은 추가로 제작해야함.
기존 로직을 Flow구성 하면서 수정했더니 오래걸렸다.
문제가 생긴부분은 C++인데 Flow대로 구성해보니까
비효율 적인 부분이 너무많이 보였다
DB에서 Request(프로시저 1개)로 생각했는데
결과에 따라 추가적으로 DB를 작업해서 보내는 경우 현재로는
Client-> DBRequest(데이터 +client) -> DB에서 데이터를 받아 ClientSend인데
IOCP작업을 DB가 하는것도 이상하고 추가 DB를 해야할경우 DB가 Send를
여러번 해야할수도 있는 점이 이상하다고 느꼈다. 구조를 갈아 엎어야 할것 같은느낌이다.
뿐만아니라 packetmanager에서 Handler_패킷 부분에서
DBRequest를 할경우 현재 구조에서는 DBRequest 종류당
include Request_##.h를 해야한다는점이 당황스러웠다.
정말 이구조가 맞는가 싶었다..
아무래도 구조를 한번 다시 제대로 생각해서 수정이 필요할것같다..
class A : public enable_shared_from_this<A>
virtual void Func()
{
FA(shared_from_this());
}
class B : public A , public enable_shared_from_this<B>
void Func() override
{
FB(enable_shared_from_this<B>::shared_from_this());
}
void FA(shared_ptr<A> a)
void FB(shared_ptr<B> b)
int main()
{
shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a = b;
a->Func();
return 0;
}
C++에서 shared_ptr관련해서도 한가지 알게된사실
이경우에는 raw ptr의 경우에는 자연스럽게 되던겄이지만
Shared_ptr에서는 _Throw_bad_weak_ptr가 나오기때문에 사용하지 못한다
디버깅을 해본결과 virtual로 들어간 FB실행전 확인결과
B객체의 weak_ptr이 상속받은 A의 weak_ptr과 B의 weak_ptr 모두 empty이기때문에 Throw가 걸린다.
왜 안들어가나 확인해보니까 memory.h에서
template <class _Yty>
struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
: is_convertible<remove_cv_t<_Yty>*, typename _Yty::_Esft_type*>::type {
// is_convertible is necessary to verify unambiguous inheritance
};
B*는 enable_shared_from_this<B>*로 암시적으로 변환이 불가능하다
B*는 enable_shared_from_this<B>* 가 될수도있고 enable_shared_from_this<A>*가 될수도있다보니까
컴파일단에서 저상태의경우 _Wptr이 초기화가 되지 않는다.
즉 Shared_ptr은 상속받더라도 하나의 클래스에서만 해야한다.
이 방식으로 하기위해선
class B : public A
{
public:
std::shared_ptr<B> GetSelf() {
return std::static_pointer_cast<B>(shared_from_this());
}
void Func() override
{
FB(GetSelf());
}
};
위와 같이
std::shared_ptr<B> GetSelf() { return std::static_pointer_cast<B>(shared_from_this()); }
처럼 헬퍼 함수를 제작하여 넘겨주면 정상적으로 관리된다.
또한
shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a = b;
생명주기도 확인해보았는데 b,a 둘중 어느게 먼저 사라지더라도
본래의 make_shared<B>()는 정상적으로 사라지지 않고 존재한다
즉 B로생성해서 A로 캐스팅하여 관리하더라도 B가 지워지지 않고,
B로 쓰고있던 어느곳에서 B의 shared_ptr이 사라지더라도 B를 받은 A가 존재한다면 사라지지 않는다
일단 연결은 확인 했다
C#에서 놀라웠던건 Command Reflection / Attribute 문법이었다
Attribute를 이용해 함수에 Attribute지정을 해주고
클래스의 함수를 전부 불러와서 Attribute확인, 매개변수확인해서 Dictionary로 관리하여
명령어를 제작했다.
private static readonly Dictionary<string, CommandEntry> _table;
//미리 정의한 CommandEntry 로 생성해주기
_table = new Dictionary<string, CommandEntry>();
//Command class내부의 모든 static 함수 가져온다 (Client class의 함수는 안가져옴)
var methods = typeof(Command).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
//
foreach (var method in methods)
{
//메소드의 Attribute값 있는지 확인,
//선언한 CommandAttribute(string name, string description)가 붙어있는지 확인
var attr = method.GetCustomAttribute<CommandAttribute>();
if (attr == null)
continue;
//파라미터 체크: Client 하나만 받는 함수로 걸러내기
//메소드의 파라미터를 가져온다
//파라미터가 1개가 아니면, 1개의 파리미터 타입이 Client가 아니면 캐싱 걸러내기
var parameters = method.GetParameters();
if (parameters.Length != 1 || parameters[0].ParameterType != typeof(Client))
continue;
//CommandEntry의 변수 2개 만들어주기 (Action / string -> attribute의 discription )
//attribute 는 [Attribute명(내부변수1, 내부변수2....)
//여기서 CommandAttribute 내부에 Name , Description으로 만들어줬기때문에 값을 가져올수 있음.
//변수의 순서와 [Attribute(1,2)] 전방선언과 맵핑
var action = (Action<Client>)Delegate.CreateDelegate(typeof(Action<Client>), method);
_table[attr.Name] = new CommandEntry { Handler = action, Description = attr.Description };
}
[CommandAttribute("exit", "프로그램을 종료합니다.")]
private static void HandleExit(Client client)
{
Console.WriteLine("프로그램을 종료합니다.");
Environment.Exit(0);
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class CommandAttribute : Attribute //sealed 이후 상속 불가 C++ final
{
public string Name { get; }
public string Description { get; }
public CommandAttribute(string name, string description)
{
Name = name.ToLower();
Description = description;
}
}
public class CommandEntry
{
public Action<Client> Handler;
public string Description;
}
'학습 노트 > 개인학습' 카테고리의 다른 글
250611 수요일 @ 45일차 (0) | 2025.06.12 |
---|---|
250609 월요일 @ 43일차 (0) | 2025.06.10 |
250607 토요일 @ 41일차 (0) | 2025.06.08 |
250606 금요일 @ 40일차 (ODBC MSSQL 연결) (0) | 2025.06.07 |
250605 목요일 @ 38일차 (0) | 2025.06.06 |
댓글