diff --git a/src/Server/GameInstance.cpp b/src/Server/GameInstance.cpp index d16e4e5..3cad1a7 100644 --- a/src/Server/GameInstance.cpp +++ b/src/Server/GameInstance.cpp @@ -71,10 +71,6 @@ void GameInstance::SetVelocity(double speed, double dir) { vspeed = lengthdir_y(speed, dir); } -RenderInstance& GameInstance::GetRenderInstance() { - return my_renders; -} - void GameInstance::SetBoundBox(const RECT& mask) { CopyRect(&box, &mask); } diff --git a/src/Server/GameInstance.h b/src/Server/GameInstance.h index 46071e8..7cf636e 100644 --- a/src/Server/GameInstance.h +++ b/src/Server/GameInstance.h @@ -20,7 +20,6 @@ class GameInstance { virtual const char* GetIdentifier() const; RenderInstance& AssignRenderingInfo(double angle); - RenderInstance& GetRenderInstance(); void SetBoundBox(const RECT& mask); int GetBoundLT() const; diff --git a/src/Server/Main.cpp b/src/Server/Main.cpp index 96ded37..bdb38a1 100644 --- a/src/Server/Main.cpp +++ b/src/Server/Main.cpp @@ -1,46 +1,34 @@ -#include "stdafx.h" +#include "stdafx.h" #include "Framework.h" #include "CommonDatas.h" -#include "ServerFramework.h" #include "Main.h" +#include "ServerFramework.h" -RenderInstance rendering_infos_last[RENDER_INST_COUNT]; +// 스레드 프로세스 +DWORD WINAPI ConnectProcess(LPVOID arg); +DWORD WINAPI GameProcess(LPVOID arg); -int main() { - WSADATA wsadata; - if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata)) { - ErrorAbort("WSAStartup()"); - return false; - } +ServerFramework f{}; - my_socket = socket(AF_INET, SOCK_STREAM, 0); - if (INVALID_SOCKET == my_socket) { - ErrorAbort("socket()"); - return false; - } +int main() { + f.Initialize(); - BOOL option = TRUE; - if (SOCKET_ERROR == setsockopt(my_socket, SOL_SOCKET, SO_REUSEADDR - , reinterpret_cast(&option), sizeof(option))) { - ErrorAbort("setsockopt()"); - return false; - } + CreateThread(NULL, 0, ConnectProcess, nullptr, 0, NULL); - ZeroMemory(&my_address, sizeof(my_address)); - my_address.sin_family = AF_INET; - my_address.sin_addr.s_addr = htonl(INADDR_ANY); - my_address.sin_port = htons(COMMON_PORT); + Sleep(8000); + f.CreatePlayer(); + f.SetGameProcess(); - if (SOCKET_ERROR == bind(my_socket, reinterpret_cast(&my_address), my_address_size)) { - ErrorAbort("bind()"); - return false; + // 클라이언트 연결 + while (true) + { } - if (SOCKET_ERROR == listen(my_socket, CLIENT_NUMBER_MAX + 1)) { - ErrorAbort("listen()"); - return false; - } +CCharacter::CCharacter() : GameInstance() { + SetRenderType(RENDER_TYPES::CHARACTER); + SetBoundBox(RECT{ -6, -6, 6, 6 }); +} AtomicPrintLn("서버 시작"); @@ -96,23 +84,25 @@ int main() { DWORD WINAPI ConnectProcess(LPVOID arg) { while (true) { + SOCKET listen_socket = f.GetListenSocket(); SOCKET client_socket; SOCKADDR_IN client_address; int my_addr_size = sizeof(client_address); - SetEvent(event_accept); + f.SetConnectProcess(); - client_socket = accept(my_socket, reinterpret_cast(&client_address), &my_addr_size); + client_socket = accept(listen_socket, reinterpret_cast(&client_address), &my_addr_size); if (INVALID_SOCKET == client_socket) { ErrorDisplay("connect()"); continue; } + BOOL option = FALSE; setsockopt(my_socket, IPPROTO_TCP, TCP_NODELAY , reinterpret_cast(&option), sizeof(option)); - auto client = new ClientSession(client_socket, NULL, players_number++); + auto client = new ClientSession(client_socket, NULL, f.GetPlayerNumber()); auto th = CreateThread(NULL, 0, GameProcess, (LPVOID)(client), 0, NULL); if (NULL == th) { @@ -121,11 +111,11 @@ DWORD WINAPI ConnectProcess(LPVOID arg) { } CloseHandle(th); - players.push_back(client); + f.AddPlayer(client); - AtomicPrintLn("클라이언트 접속: ", client_socket, ", 수: ", players_number); + AtomicPrintLn("클라이언트 접속: ", client_socket, ", 수: ", f.GetPlayerNumber()); - WaitForSingleObject(event_accept, INFINITE); + WaitForSingleObject(f.GetAcceptEvent(), INFINITE); } return 0; @@ -136,7 +126,7 @@ DWORD WINAPI GameProcess(LPVOID arg) { SOCKET client_socket = client->my_socket; while (true) { - WaitForSingleObject(event_game_communicate, INFINITE); + WaitForSingleObject(f.GetGameProcessEvent(), INFINITE); PACKETS header; ZeroMemory(&header, HEADER_SIZE); @@ -222,14 +212,14 @@ DWORD WINAPI GameProcess(LPVOID arg) { // 3. 게임 처리 // 4. 렌더링 정보 작성 - BakeRenderingInfos(); + f.CreateRenderingInfos(); // 5. 렌더링 정보 전송 - SendRenderingInfos(client_socket); + f.SendRenderingInfos(client_socket); // 6. 대기 Sleep(FRAME_TIME); - SetEvent(event_game_communicate); + f.SetGameProcess(); } return 0; diff --git a/src/Server/Main.h b/src/Server/Main.h index d5c39a4..c2f23c8 100644 --- a/src/Server/Main.h +++ b/src/Server/Main.h @@ -1,8 +1,9 @@ -#pragma once +#pragma once #include "stdafx.h" #include "CommonDatas.h" #include "GameInstance.h" +CRITICAL_SECTION client_permission, print_permission; class CCharacter : public GameInstance { public: @@ -43,116 +44,6 @@ class ClientSession { ~ClientSession(); }; -/* 소켓 */ -SOCKET my_socket; // 서버 소켓 -SOCKADDR_IN my_address; // 서버 주소 -int my_address_size = sizeof(my_address); - -/* 다중 스레드 정보 */ -HANDLE event_accept; // 클라이언트 수용 신호 -HANDLE event_game_communicate; // 게임 처리 신호 -HANDLE event_quit; // 종료 신호 -CRITICAL_SECTION permission_client, permission_; - -/* 플레이어 관련 속성 */ -vector players; // 플레이어 목록 -int player_process_index; // 현재 처리 중인 플레이어의 순번 [0~client_number) -int players_number; // 지금 접속한 플레이어의 수 -int player_number_last; // 마지막에 추가된 플레이어의 번호 -int player_captain; // 방장 플레이어 -int player_winner; // 승리한 플레이어 - /* 게임 관련 속성 */ -vector instances; // 인스턴스 목록 normal_distribution<> random_distrubution; // 서버의 무작위 분포 범위 default_random_engine randomizer; - -bool game_started; -int** PLAYER_SPAWN_PLACES; // 플레이어가 맨 처음에 생성될 위치의 배열 -const int SPAWN_DISTANCE = 300; // 플레이어 생성 위치를 정할 때 사용하는 거리 값 - -/* 스레드 선언 */ -DWORD WINAPI ConnectProcess(LPVOID arg); // 다중, 수신 스레드 -DWORD WINAPI GameProcess(LPVOID arg); // 단일, 송신 스레드 - -void Initialize(); -void Startup(); -void Ready(); -void Cleanup(); - -void ClientConnect(); -void ClientDisconnect(int player_index); - -// 정해둔 스폰 지점에 플레이어 캐릭터들을 생성한다. -void CreatePlayerCharacters(); - -void ProceedContinuation(); // 게임 진행 확인 -bool CheckClientNumber(); // 접속한 클라이언트 수 확인 -bool ValidateSocketMessage(int socket_state); // 받은 소켓 메시지 검증 -void BakeRenderingInfos(); // 렌더링 정보 만들기 -void SendRenderingInfos(SOCKET my_socket); // 렌더링 정보 보내기 - -inline DWORD WINAPI AwaitClientAcceptEvent() { - AtomicPrintLn("AwaitClientAcceptEvent()"); - return WaitForSingleObject(event_accept, INFINITE); -} - -inline DWORD WINAPI AwaitReceiveEvent() { - AtomicPrintLn("AwaitReceiveEvent()"); - return WaitForSingleObject(event_game_communicate, INFINITE); -} - -// 지정한 위치에 인스턴스를 생성한다. -template -_GameClass* Instantiate(double x = 0.0, double y = 0.0) { - auto result = new _GameClass(); - result->x = x; - result->y = y; - - instances.push_back(result); - - return result; -} - -// 지정한 인스턴스를 삭제한다. -template -void Kill(_GameClass* target) { - auto loc = find_if(instances.begin(), instances.end(), [target] (const auto& lhs) { - return (lhs == target); - }); - - if (loc != instances.end()) { - target->OnDestroy(); - instances.erase(loc); - } -} - -// 두 게임 인스턴스의 충돌을 검사한다. -template -inline _GameClass2* CheckCollision(_GameClass1* self, _GameClass2* other) { - if (self && other && self != other) { - if (self->IsCollideWith(other)) - return other; - } - return nullptr; -} - -// 어떤 게임 인스턴스에게 충돌하는 인스턴스를, 식별자 fid를 기반으로 찾아낸다. -template -_GameClassTarget* SeekCollision(_GameClassSelf* self, const char* fid) { - if (self && !instances.empty()) { - auto CopyList = vector(instances); - - auto it = std::find_if(CopyList.begin(), CopyList.end(), [&] (GameInstance* inst) { - auto iid = inst->GetIdentifier(); - auto id_check = strcmp(iid, fid); - - return (0 == id_check); - }); - - if (it != CopyList.end()) { - return dynamic_cast<_GameClassTarget*>(*it); - } - } - return nullptr; -} diff --git a/src/Server/ServerFramework.cpp b/src/Server/ServerFramework.cpp index 1d1ab0c..156ff31 100644 --- a/src/Server/ServerFramework.cpp +++ b/src/Server/ServerFramework.cpp @@ -1,14 +1,60 @@ #include "stdafx.h" #include "CommonDatas.h" #include "ServerFramework.h" +#include "Main.h" -int ServerFramework::Initialize() +ServerFramework::ServerFramework() : s_port(15000), players(CLIENT_NUMBER_MAX), instances(40), +player_process_index(0), player_number_last(0), player_captain(0), player_winner(0), +rendering_infos_last(RENDER_INST_COUNT), game_started(false) +{ + PLAYER_SPAWN_PLACES = new int* [CLIENT_NUMBER_MAX]; + + double dir_increment = (360.0 / CLIENT_NUMBER_MAX); + for (int i = 0; i < CLIENT_NUMBER_MAX; ++i) { + double dir = dir_increment * i; + int cx = static_cast(WORLD_W * 0.5 + lengthdir_x(SPAWN_DISTANCE, dir)); + int cy = static_cast(WORLD_W * 0.5 + lengthdir_y(SPAWN_DISTANCE, dir)); + + PLAYER_SPAWN_PLACES[i] = new int[2]{ cx, cy }; + } +} + +ServerFramework::~ServerFramework() +{ + CloseHandle(event_accept); + CloseHandle(event_game_process); + closesocket(s_socket); + + for (auto& p : players) + { + delete p; + } + + for (auto& i : instances) + { + delete i; + } + + for (int i = 0; i < CLIENT_NUMBER_MAX; ++i) + { + delete PLAYER_SPAWN_PLACES[i]; + } + + delete[] PLAYER_SPAWN_PLACES; + players.shrink_to_fit(); + instances.shrink_to_fit(); + + WSACleanup(); +} + +bool ServerFramework::Initialize() { WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != NOERROR) { - return -1; + ErrorAbort("WSAStartup()"); + return false; } s_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -16,10 +62,19 @@ int ServerFramework::Initialize() if (s_socket == INVALID_SOCKET) { ErrorAbort("socket()"); + return false; } - ZeroMemory(&s_address, sizeof(s_address)); + bool option = true; + if (setsockopt(s_socket, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) + { + ErrorAbort("setsockopt()"); + return false; + } + + ZeroMemory(&s_address, sizeof(s_address)); s_address.sin_family = AF_INET; s_address.sin_addr.s_addr = htonl(INADDR_ANY); s_address.sin_port = htons(s_port); @@ -27,34 +82,115 @@ int ServerFramework::Initialize() if (bind(s_socket, reinterpret_cast(&s_address), sizeof(s_address)) == SOCKET_ERROR) { ErrorAbort("bind()"); + return false; } if (listen(s_socket, SOMAXCONN) == SOCKET_ERROR) { ErrorAbort("listen()"); + return false; } + + AtomicPrintLn("서버 시작"); + + event_accept = CreateEvent(NULL, FALSE, TRUE, NULL); + event_game_process = CreateEvent(NULL, FALSE, FALSE, NULL); } -bool ServerFramework::Connect() +void ServerFramework::CreatePlayer() { - cl_socket = accept(s_socket, reinterpret_cast(&cl_addr), &cl_addr_len); + auto sz = players.size(); + for (int i = 0; i < sz; ++i) { + int places[2] = { 80, 80 };//PLAYER_SPAWN_PLACES[i]; + auto character = Instantiate(places[0], places[1]); - if (cl_socket == INVALID_SOCKET) - { - ErrorDisplay("accept()"); - return false; + players[i]->player_character = character; + players[i]->player_character->owner = players[i]->player_index; + //SendData(player->my_socket, PACKETS::SERVER_GAME_START); } +} - return true; +void ServerFramework::CreateRenderingInfos() +{ + if (!instances.empty()) { + AtomicPrintLn("렌더링 정보 생성\n크기: ", instances.size()); + if (!rendering_infos_last.empty()) { + ZeroMemory(rendering_infos_last.data(), rendering_infos_last.size()); + } + + auto CopyList = instances; + + // 플레이어 개체를 맨 위로 + std::partition(CopyList.begin(), CopyList.end(), [&](GameInstance* inst) { + return (strcmp(inst->GetIdentifier(), "Player") == 0); + }); + + int index = 0; + for (auto it = CopyList.begin(); it != CopyList.end(); ++it) { + auto render_infos = (*it)->my_renders; + + // 인스턴스가 살아있는 경우에만 렌더링 메세지 전송 + if (!(*it)->dead) { + auto dest = (rendering_infos_last.data() + index); + auto src = &render_infos; + + memcpy(dest, src, sizeof(RenderInstance)); + index++; + } + } + } + else if (!rendering_infos_last.empty()) { + ZeroMemory(rendering_infos_last.data(), rendering_infos_last.size()); + } } -void ServerFramework::Disconnect() +void ServerFramework::SendRenderingInfos(SOCKET client_socket) { - closesocket(cl_socket); + auto renderings = reinterpret_cast(rendering_infos_last.data()); + auto render_size = sizeof(rendering_infos_last); + + SendData(client_socket, SERVER_RENDER_INFO, renderings, render_size); } -void ServerFramework::Close() +template +_GameClass* ServerFramework::Instantiate(double x, double y) { - closesocket(s_socket); - WSACleanup(); + auto result = new _GameClass(); + result->x = x; + result->y = y; + + instances.push_back(result); + + return result; +} + +template +void Kill(_GameClass* target) { + auto loc = find_if(instances.begin(), instances.end(), [target](const auto& lhs) { + return (lhs == target); + }); + + if (loc != instances.end()) { + target->OnDestroy(); + instances.erase(loc); + } } + +template +_GameClassTarget* SeekCollision(_GameClassSelf* self, const char* fid) { + if (self && !instances.empty()) { + auto CopyList = vector(instances); + + auto it = std::find_if(CopyList.begin(), CopyList.end(), [&](GameInstance* inst) { + auto iid = inst->GetIdentifier(); + auto id_check = strcmp(iid, fid); + + return (0 == id_check); + }); + + if (it != CopyList.end()) { + return dynamic_cast<_GameClassTarget*>(*it); + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/Server/ServerFramework.h b/src/Server/ServerFramework.h index d9b4aac..51888b8 100644 --- a/src/Server/ServerFramework.h +++ b/src/Server/ServerFramework.h @@ -4,28 +4,73 @@ class ServerFramework { public: - ServerFramework() : s_port(15000), cl_addr_len(sizeof(cl_addr)) {} + ServerFramework(); + ~ServerFramework(); public: - int Initialize(); // 서버 초기화 - bool Connect(); // 클라이언트 연결 - void Disconnect(); // 클라이언트 연결 해제 - void Close(); // 서버 종료 + bool Initialize(); // 서버 초기화 public: - void RecieveKeyInput(); + void CreatePlayer(); // 플레이어 생성 + void AddPlayer(ClientSession* player) { players.push_back(player); } // 플레이어 추가 + void CreateRenderingInfos(); // 렌더링 정보 생성 + void SendRenderingInfos(SOCKET client_socket); // 렌더링 정보 전송 public: - SOCKET GetServerSocket() { return s_socket; } - sockaddr_in GetClientAddr() { return cl_addr; } + void ProceedContinuation(); // 게임 진행 확인 + void ValidateSocketMessage(int socket_state); // 받은 ㅅ켓 메시지 검증 +public: + // 지정한 위치에 인스턴스를 생성한다. + template + _GameClass* Instantiate(double x = 0.0, double y = 0.0); + + // 지정한 인스턴스를 삭제한다. + template + void Kill(_GameClass* target); + + // 두 게임 인스턴스의 충돌을 검사한다. + template + inline _GameClass2* CheckCollision(_GameClass1* self, _GameClass2* other); + + // 어떤 게임 인스턴스에게 충돌하는 인스턴스를, 식별자 fid를 기반으로 찾아낸다. + template + _GameClassTarget* SeekCollision(_GameClassSelf* self, const char* fid); +public: + void SetConnectProcess() { SetEvent(event_accept); } // accept 이벤트 활성화 + void SetGameProcess() { SetEvent(event_game_process); } // 게임 프로세스 이벤트 활성화 +public: + SOCKET GetListenSocket() { return s_socket; } + HANDLE GetAcceptEvent() { return event_accept; } + HANDLE GetGameProcessEvent() { return event_game_process; } + int GetPlayerNumber() { return players.size(); } +public: + friend DWORD WINAPI ConnectProcess(LPVOID arg); + friend DWORD WINAPI GameProcess(LPVOID arg); private: - SOCKET s_socket; // 서버 소켓 - sockaddr_in s_address; // 서버 주소 - u_short s_port; // 서버 포트번호 + SOCKET s_socket; // 서버 소켓 + sockaddr_in s_address; // 서버 주소 + u_short s_port; // 서버 포트번호 +private: + HANDLE event_accept; // 클라이언트 수용 이벤트 + HANDLE event_game_process; // 게임 처리 이벤트 private: - SOCKET cl_socket; // 클라이언트 소켓 - sockaddr_in cl_addr; // 클라이언트 주소 - int cl_addr_len; // 클라이언트 주소 길이 + vector players; // 플레이어 목록 + int player_process_index; // 현재 처리 중인 플레이어의 순번 [0~client_number) + int player_number_last; // 마지막에 추가된 플레이어의 번호 + int player_captain; // 방장 플레이어 + int player_winner; // 승리 플레이어 private: - std::vector key_input; + vector instances; // 인스턴스 목록 + vector rendering_infos_last; // 렌더링 정보 +private: + bool game_started; }; -#endif \ No newline at end of file +#endif + +template +inline _GameClass2* CheckCollision(_GameClass1* self, _GameClass2* other) { + if (self && other && self != other) { + if (self->IsCollideWith(other)) + return other; + } + return nullptr; +} \ No newline at end of file