diff --git a/ServerConsoleExample/CodeAnalysis.ruleset b/ServerConsoleExample/CodeAnalysis.ruleset new file mode 100644 index 0000000..845c4a8 --- /dev/null +++ b/ServerConsoleExample/CodeAnalysis.ruleset @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServerConsoleExample/ServerConsole.def b/ServerConsoleExample/ServerConsole.def new file mode 100644 index 0000000..58e9b42 --- /dev/null +++ b/ServerConsoleExample/ServerConsole.def @@ -0,0 +1,4 @@ +LIBRARY + +EXPORTS + ; Explicit exports can go here diff --git a/ServerConsoleExample/ServerConsole.filters b/ServerConsoleExample/ServerConsole.filters new file mode 100644 index 0000000..76165d4 --- /dev/null +++ b/ServerConsoleExample/ServerConsole.filters @@ -0,0 +1,61 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/ServerConsoleExample/ServerConsole.rc b/ServerConsoleExample/ServerConsole.rc new file mode 100644 index 0000000..3ea1469 Binary files /dev/null and b/ServerConsoleExample/ServerConsole.rc differ diff --git a/ServerConsoleExample/ServerConsole.vcxproj b/ServerConsoleExample/ServerConsole.vcxproj new file mode 100644 index 0000000..dc0c63e --- /dev/null +++ b/ServerConsoleExample/ServerConsole.vcxproj @@ -0,0 +1,237 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {8AEE9CFF-0E24-498F-B60A-627A7F4A727D} + MFCDLLProj + ServerInterface + 10.0 + ServerConsole + + + + DynamicLibrary + true + v142 + MultiByte + Dynamic + + + DynamicLibrary + false + v142 + true + MultiByte + Dynamic + + + DynamicLibrary + true + v142 + Unicode + Static + + + DynamicLibrary + false + v142 + true + Unicode + Static + + + + + + + + + + + + + + + + + + + + + true + true + CodeAnalysis.ruleset + + + true + + + false + true + CodeAnalysis.ruleset + + + false + + + + + + Use + Level3 + true + WIN32;_WINDOWS;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DEBUG;_USRDLL;%(PreprocessorDefinitions) + pch.h + ..\..\Sdk\Include + true + stdcpplatest + MultiThreadedDebugDLL + + + Windows + .\ServerConsole.def + Rpcrt4.lib + + + false + _DEBUG;%(PreprocessorDefinitions) + + + 0x0409 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + xcopy /DY "$(TargetPath)" "$(GIANTS_PATH)\$(TargetFileName)*" + + + + + Use + Level3 + true + _WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions) + pch.h + + + Windows + .\ServerInterface.def + + + false + _DEBUG;%(PreprocessorDefinitions) + + + 0x0409 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + true + true + WIN32;_WINDOWS;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + pch.h + ..\..\Sdk\Include + true + stdcpplatest + MultiThreadedDLL + + + Windows + true + true + .\ServerConsole.def + + + false + NDEBUG;%(PreprocessorDefinitions) + + + 0x0409 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + xcopy /DY "$(TargetPath)" "$(GIANTS_PATH)\$(TargetFileName)*" + + + + + Use + Level3 + true + true + true + _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + pch.h + + + Windows + true + true + .\ServerInterface.def + + + false + NDEBUG;%(PreprocessorDefinitions) + + + 0x0409 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServerConsoleExample/ServerConsoleApp.cpp b/ServerConsoleExample/ServerConsoleApp.cpp new file mode 100644 index 0000000..3df4ec7 --- /dev/null +++ b/ServerConsoleExample/ServerConsoleApp.cpp @@ -0,0 +1,56 @@ +#include "pch.h" +#include +#include "ServerConsoleApp.h" +#include "ServerDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + +BEGIN_MESSAGE_MAP(ServerConsoleApp, CWinApp) +END_MESSAGE_MAP() + +ServerConsoleApp ConsoleApp; +BOOL ServerConsoleApp::InitInstance() +{ + INITCOMMONCONTROLSEX InitCtrls; + InitCtrls.dwSize = sizeof(InitCtrls); + InitCtrls.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&InitCtrls); + + CWinApp::InitInstance(); + + return TRUE; +} + +ServerConsoleApp::~ServerConsoleApp() +{ +} + +BOOL ServerConsoleApp::ExitInstance() +{ + return CWinApp::ExitInstance(); +} + +IGameServerConsole* ServerConsoleApp::InitializeDialog(IComponentContainer* container) +{ + // Create the server console window. + // As this is also a Component, Giants will clean up this object automatically once + // it is no longer needed (i.e, there is no need to call delete). + auto* dialog = new ServerDialog(container); + m_pMainWnd = dialog; + return dialog; +} + +__declspec(dllexport) void CreateServerConsole( + int apiVersion, + IComponentContainer* container) +{ + if (apiVersion > 1) + { + throw std::invalid_argument(fmt::format("Unsupported API version {0}", apiVersion).c_str()); + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ConsoleApp.InitializeDialog(container); +} \ No newline at end of file diff --git a/ServerConsoleExample/ServerConsoleApp.h b/ServerConsoleExample/ServerConsoleApp.h new file mode 100644 index 0000000..1386dc1 --- /dev/null +++ b/ServerConsoleExample/ServerConsoleApp.h @@ -0,0 +1,24 @@ +#pragma once + +#ifndef __AFXWIN_H__ + #error "include 'pch.h' before including this file for PCH" +#endif + +#include "resource.h" // main symbols +#include "ServerDialog.h" +#include +#include + +class ServerConsoleApp : public CWinApp +{ +public: + ~ServerConsoleApp(); + +// MFC methods + BOOL InitInstance() override; + BOOL ExitInstance() override; + + IGameServerConsole* InitializeDialog(IComponentContainer* container); + + DECLARE_MESSAGE_MAP() +}; diff --git a/ServerConsoleExample/ServerDialog.cpp b/ServerConsoleExample/ServerDialog.cpp new file mode 100644 index 0000000..c6a78d0 --- /dev/null +++ b/ServerConsoleExample/ServerDialog.cpp @@ -0,0 +1,248 @@ +#include "pch.h" + +#include "ServerConsoleApp.h" +#include "ServerDialog.h" +#include "Utils.h" + +IMPLEMENT_DYNAMIC(ServerDialog, CDialogEx) + +ServerDialog::ServerDialog(IComponentContainer* container, CWnd* parent) + : ComponentBase(container), + CDialogEx(IDD_SERVER, parent) +{ + const auto& pGameServer = m_pContainer->Get(); + + using namespace std::placeholders; + m_playerConnectedEventHandle = pGameServer->Listen(GameServerEventType::PlayerConnected, std::bind(&ServerDialog::HandlePlayerConnected, this, _1)); + m_playerDisconnectedEventHandle = pGameServer->Listen(GameServerEventType::PlayerDisconnected, std::bind(&ServerDialog::HandlePlayerDisconnected, this, _1)); + m_playerChatMessageHandle = pGameServer->Listen(GameServerEventType::ChatMessage, std::bind(&ServerDialog::HandleChatMessage, this, _1)); + m_worldLoadedHandle = pGameServer->Listen(GameServerEventType::WorldLoaded, std::bind(&ServerDialog::HandleWorldLoaded, this, _1)); +} + +ServerDialog::~ServerDialog() +{ + try + { + const auto& pGameServer = m_pContainer->Get(); + + pGameServer->Unlisten(GameServerEventType::PlayerConnected, m_playerConnectedEventHandle); + pGameServer->Unlisten(GameServerEventType::PlayerDisconnected, m_playerDisconnectedEventHandle); + pGameServer->Unlisten(GameServerEventType::ChatMessage, m_playerChatMessageHandle); + } + catch (...) + { + + } +} + +void ServerDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_CONSOLE, ConsoleEditBox); + DDX_Control(pDX, IDC_PLAYERS, PlayersListCtrl); + DDX_Control(pDX, IDC_BAN, ButtonBan); + DDX_Control(pDX, IDC_KICK, ButtonKick); +} + +void __stdcall ServerDialog::TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + ((ServerDialog*)idEvent)->RefreshPlayers(); +} + +void ServerDialog::CloseDialog() +{ + OnCancel(); +} + +void ServerDialog::ShowDialog() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + Create(IDD_SERVER); + ShowWindow(SW_SHOW); +} + +BOOL ServerDialog::OnInitDialog() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + CDialog::OnInitDialog(); + + CreateColumns(); + + using namespace std::placeholders; + SetTimer((UINT_PTR)this, 1000, TimerCallback); + + return TRUE; +} + +void ServerDialog::CreateColumns() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + PlayersListCtrl.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 40); + PlayersListCtrl.InsertColumn(1, _T("Status"), LVCFMT_LEFT, 80); + PlayersListCtrl.InsertColumn(2, _T("Ping"), LVCFMT_LEFT, 40); + PlayersListCtrl.InsertColumn(3, _T("Score"), LVCFMT_LEFT, 80); + PlayersListCtrl.InsertColumn(4, _T("Team"), LVCFMT_LEFT, 80); + + for (int i = 0; i < NumColumns; i++) + { + PlayersListCtrl.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER); + } +} + +void ServerDialog::PostNcDestroy() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + CDialog::PostNcDestroy(); +} + +void ServerDialog::OnOK() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (!UpdateData(TRUE)) + return; + DestroyWindow(); +} + +void ServerDialog::OnCancel() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + KillTimer((UINT_PTR)this); + ShowWindow(SW_HIDE); + DestroyWindow(); +} + +void ServerDialog::RefreshPlayers() +{ + if (!m_hWnd) + { + return; + } + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + const int savedSelection = PlayersListCtrl.GetSelectionMark(); + PlayersListCtrl.DeleteAllItems(); + + + const auto& pTextLookupService = m_pContainer->Get(); + const auto& pGameServer = m_pContainer->Get(); + for (const auto& player : pGameServer->GetPlayers()) + { + if (player.host) + { + continue; // Skip host player + } + + LVITEM item{}; + item.cColumns = NumColumns; + item.mask = LVIF_COLUMNS | LVIF_PARAM; + item.lParam = player.index; + + const int index = PlayersListCtrl.InsertItem(&item); + PlayersListCtrl.SetItemText(index, 0, player.name.c_str()); + PlayersListCtrl.SetItemText(index, 1, pTextLookupService->GetNetPlayerStateName(player.state).c_str()); + PlayersListCtrl.SetItemText(index, 2, fmt::format(_T("{0}"), player.ping).c_str()); + PlayersListCtrl.SetItemText(index, 3, fmt::format(_T("{0}"), player.score).c_str()); + PlayersListCtrl.SetItemText(index, 4, pTextLookupService->GetPlayerTeamName(player.team).c_str()); + } + + PlayersListCtrl.SetSelectionMark(savedSelection); +} + +void ServerDialog::HandlePlayerConnected(const GameServerEvent& event) +{ + RefreshPlayers(); +} + +void ServerDialog::HandlePlayerDisconnected(const GameServerEvent& event) +{ + RefreshPlayers(); +} + +void ServerDialog::HandleChatMessage(const GameServerEvent& event) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + const auto& chatMessageEvent = (const ChatMessageEvent&)event; + + ConsoleEditBox.SetSel(-1, 0); + ConsoleEditBox.ReplaceSel(chatMessageEvent.message.data()); + ConsoleEditBox.SetSel(-1, 0); + ConsoleEditBox.ReplaceSel("\n"); +} + +void ServerDialog::HandleWorldLoaded(const GameServerEvent& event) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + const auto& pGameServer = m_pContainer->Get(); + + NetGameDetails details = pGameServer->GetGameDetails(); + + // TODO: Connect to world state controls +} + +BEGIN_MESSAGE_MAP(ServerDialog, CDialogEx) + ON_BN_CLICKED(IDC_BAN, &ServerDialog::OnBnClickedBan) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_PLAYERS, &ServerDialog::OnItemChanged) + ON_BN_CLICKED(IDC_KICK, &ServerDialog::OnBnClickedKick) +END_MESSAGE_MAP() + + +// ServerDialog message handlers + +void ServerDialog::OnBnClickedBan() +{ + const int selection = PlayersListCtrl.GetSelectionMark(); + if (selection == -1) + { + return; + } + + const PlayerIndex playerIndex = (PlayerIndex)PlayersListCtrl.GetItemData(selection); + if (playerIndex > 0) + { + const auto& pGameServer = m_pContainer->Get(); + pGameServer->BanPlayer(playerIndex); + } +} + +void ServerDialog::OnBnClickedKick() +{ + const int selection = PlayersListCtrl.GetSelectionMark(); + if (selection == -1) + { + return; + } + + const PlayerIndex playerIndex = (PlayerIndex)PlayersListCtrl.GetItemData(selection); + if (playerIndex > 0) + { + const auto& pGameServer = m_pContainer->Get(); + pGameServer->KickPlayer(playerIndex, KickReason::Removed); + } +} + +void ServerDialog::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult) +{ + const LPNMLISTVIEW pNMListView = (const LPNMLISTVIEW)(pNMHDR); + + if (!(pNMListView->uChanged & LVIF_STATE)) + { + return; + } + + if ((pNMListView->uOldState & LVIS_SELECTED) == (pNMListView->uNewState & LVIS_SELECTED)) + { + return; + } + + ButtonKick.EnableWindow(TRUE); + ButtonBan.EnableWindow(TRUE); + + *pResult = 0; +} diff --git a/ServerConsoleExample/ServerDialog.h b/ServerConsoleExample/ServerDialog.h new file mode 100644 index 0000000..2e992c6 --- /dev/null +++ b/ServerConsoleExample/ServerDialog.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include + +// ServerDialog dialog + +class ServerDialog : public CDialogEx, public ComponentBase +{ + DECLARE_DYNAMIC(ServerDialog) + +public: + ~ServerDialog(); + ServerDialog(IComponentContainer* container, CWnd* parent = nullptr); + + void CreateColumns(); + void RefreshPlayers(); + + static void STDMETHODCALLTYPE TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + void STDMETHODCALLTYPE CloseDialog() override; + void STDMETHODCALLTYPE ShowDialog() override; + + void HandlePlayerConnected(const GameServerEvent& event); + void HandlePlayerDisconnected(const GameServerEvent& event); + void HandleChatMessage(const GameServerEvent& event); + void HandleWorldLoaded(const GameServerEvent& event); + + // MFC methods + BOOL OnInitDialog() override; + void PostNcDestroy() override; + void OnOK() override; + void OnCancel() override; + +// Dialog Data +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_SERVER }; +#endif + +private: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() + + CEdit ConsoleEditBox; + CListCtrl PlayersListCtrl; + CButton ButtonBan; + CButton ButtonKick; + + afx_msg void OnBnClickedKick(); + afx_msg void OnBnClickedBan(); + afx_msg void OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult); + + UUID m_playerConnectedEventHandle{}; + UUID m_playerDisconnectedEventHandle{}; + UUID m_playerChatMessageHandle{}; + UUID m_worldLoadedHandle{}; + + const int NumColumns = 5; +}; diff --git a/ServerConsoleExample/Utils.cpp b/ServerConsoleExample/Utils.cpp new file mode 100644 index 0000000..471bbea --- /dev/null +++ b/ServerConsoleExample/Utils.cpp @@ -0,0 +1,12 @@ +#include "pch.h" + +#include + +#include "Utils.h" + +std::wstring to_wstring(const std::string_view& sourceString) +{ + std::wstring_convert> converter; + + return converter.from_bytes(sourceString.data()); +} \ No newline at end of file diff --git a/ServerConsoleExample/Utils.h b/ServerConsoleExample/Utils.h new file mode 100644 index 0000000..cb59597 --- /dev/null +++ b/ServerConsoleExample/Utils.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::wstring to_wstring(const std::string_view& sourceString); diff --git a/ServerConsoleExample/framework.h b/ServerConsoleExample/framework.h new file mode 100644 index 0000000..5175c5a --- /dev/null +++ b/ServerConsoleExample/framework.h @@ -0,0 +1,35 @@ +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +#include "targetver.h" + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +#include // MFC core and standard components +#include // MFC extensions + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC OLE classes +#include // MFC OLE dialog classes +#include // MFC Automation classes +#endif // _AFX_NO_OLE_SUPPORT + +#ifndef _AFX_NO_DB_SUPPORT +#include // MFC ODBC database classes +#endif // _AFX_NO_DB_SUPPORT + +#ifndef _AFX_NO_DAO_SUPPORT +#include // MFC DAO database classes +#endif // _AFX_NO_DAO_SUPPORT + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC support for Internet Explorer 4 Common Controls +#endif +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + diff --git a/ServerConsoleExample/pch.cpp b/ServerConsoleExample/pch.cpp new file mode 100644 index 0000000..64b7eef --- /dev/null +++ b/ServerConsoleExample/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/ServerConsoleExample/pch.h b/ServerConsoleExample/pch.h new file mode 100644 index 0000000..add9e33 --- /dev/null +++ b/ServerConsoleExample/pch.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +// AFX includes: +#include "framework.h" +#include "afxdialogex.h" + +// STL includes: +#include +#include +#include +#include +#include +#include +#include +#include + +// Third party: +#pragma warning(push, 1) +#pragma warning (disable : ALL_CODE_ANALYSIS_WARNINGS) +#include +#pragma warning(pop) \ No newline at end of file diff --git a/ServerConsoleExample/res/ServerConsole.rc2 b/ServerConsoleExample/res/ServerConsole.rc2 new file mode 100644 index 0000000..4c16fbb Binary files /dev/null and b/ServerConsoleExample/res/ServerConsole.rc2 differ diff --git a/ServerConsoleExample/resource.h b/ServerConsoleExample/resource.h new file mode 100644 index 0000000..5e4de71 --- /dev/null +++ b/ServerConsoleExample/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ServerConsole.rc +// +#define IDD_SERVER 132 +#define IDC_GAMETYPE 133 +#define IDC_TEAMS 134 +#define IDC_WORLD 135 +#define IDC_PLAYERS 136 +#define IDC_ROTATIONS 137 +#define IDC_ROTATION 138 +#define IDC_TIMEREMAINING 139 +#define IDC_BAN 140 +#define IDC_KICK 141 +#define IDC_INPUT 142 +#define IDC_CONSOLE 143 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 1000 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 1000 +#endif +#endif diff --git a/ServerConsoleExample/targetver.h b/ServerConsoleExample/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/ServerConsoleExample/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include