commit 633f20d561b523db271349bc0de1838de83204fa Author: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Sat Jan 25 10:01:58 2020 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4dee3f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.obj +*.sdf +*.suo +*.opensdf +ipch/ +masterlist/ +serverbrowser/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..940ab91 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# servertool diff --git a/serverbrowser.sln b/serverbrowser.sln new file mode 100644 index 0000000..84bba15 --- /dev/null +++ b/serverbrowser.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "serverbrowser", "serverbrowser\serverbrowser.vcxproj", "{4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "servertool", "servertool\servertool.vcxproj", "{FF615871-8561-4F2E-9307-9CEA2E7BE381}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "masterlist", "masterlist\masterlist.vcxproj", "{84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Debug|Win32.ActiveCfg = Release|Win32 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Debug|Win32.Build.0 = Release|Win32 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Debug|x64.ActiveCfg = Debug|x64 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Debug|x64.Build.0 = Debug|x64 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Release|Win32.ActiveCfg = Release|Win32 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Release|Win32.Build.0 = Release|Win32 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Release|x64.ActiveCfg = Release|x64 + {4CAC26DD-5286-43A0-8C9C-60CB1B9613FE}.Release|x64.Build.0 = Release|x64 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Debug|Win32.ActiveCfg = Debug|Win32 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Debug|Win32.Build.0 = Debug|Win32 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Debug|x64.ActiveCfg = Debug|Win32 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Release|Win32.ActiveCfg = Release|Win32 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Release|Win32.Build.0 = Release|Win32 + {FF615871-8561-4F2E-9307-9CEA2E7BE381}.Release|x64.ActiveCfg = Release|x64 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Debug|Win32.ActiveCfg = Debug|Win32 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Debug|Win32.Build.0 = Debug|Win32 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Debug|x64.ActiveCfg = Debug|Win32 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Release|Win32.ActiveCfg = Release|Win32 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Release|Win32.Build.0 = Release|Win32 + {84DA515D-A7E8-4A95-A3A6-B45BEB09FA6E}.Release|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/servertool/icon.ico b/servertool/icon.ico new file mode 100644 index 0000000..21f1887 Binary files /dev/null and b/servertool/icon.ico differ diff --git a/servertool/icon16.ico b/servertool/icon16.ico new file mode 100644 index 0000000..7beff0c Binary files /dev/null and b/servertool/icon16.ico differ diff --git a/servertool/main.c b/servertool/main.c new file mode 100644 index 0000000..646c6e2 --- /dev/null +++ b/servertool/main.c @@ -0,0 +1,51 @@ +#include "servertool.h" +#include "serverlist.h" +#include "masterscan.h" +#include "serverinfo.h" +#include "resource.h" +#include + +APPLICATION_T g_App; + +INT APIENTRY wWinMain(HINSTANCE hInst,HINSTANCE hPrevInst, + LPWSTR lpCmdLine,INT nCmdShow) +{ + WSADATA WSA; + MSG Msg; + + + g_App.hInst = hInst; + g_App.nCmdShow = nCmdShow; + + //Load stuff + + InitCommonControls(); + + g_App.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON1)); + g_App.hIconSm = LoadIcon(hInst,MAKEINTRESOURCE(IDI_ICON2)); + + //Init WSA + + WSAStartup(MAKEWORD(2,2),&WSA); + + //Register windows + + InitServerListWindow(); + InitMasterScanWindow(); + InitServerInfoWindow(); + + //Do something + + CreateServerListWindow(); + //CreateMasterScanPopup(); + + //Main loop + while(GetMessage(&Msg,NULL,0,0)) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + + WSACleanup(); + return (INT)Msg.wParam; +} \ No newline at end of file diff --git a/servertool/master.dat b/servertool/master.dat new file mode 100644 index 0000000..143ae8b --- /dev/null +++ b/servertool/master.dat @@ -0,0 +1 @@ +@4i@'i@Ai@Ai@4i@'i \ No newline at end of file diff --git a/servertool/masterscan.c b/servertool/masterscan.c new file mode 100644 index 0000000..2aeb877 --- /dev/null +++ b/servertool/masterscan.c @@ -0,0 +1,230 @@ +#include "masterscan.h" +#include + +MASTERSCAN_WINDOW_T g_MasterScan; +static INT iWidth = 400; +static INT iHeight = 350; + +static LRESULT CALLBACK WndCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +VOID InitMasterScanWindow() +{ + WNDCLASSEX wndClass; + + g_MasterScan.lpClass = MASTERSCAN_CLASS; + + ZeroMemory(&wndClass,sizeof(WNDCLASSEX)); + + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.lpszClassName = g_MasterScan.lpClass; + wndClass.hInstance = g_App.hInst; + wndClass.lpfnWndProc = WndCallback; + wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW; + wndClass.hIcon = g_App.hIcon; + wndClass.hIconSm = g_App.hIconSm; + + RegisterClassEx(&wndClass); +} + +VOID CreateMasterScanWindow() +{ + g_MasterScan.IsPopup = FALSE; + + g_MasterScan.hWnd = CreateWindow(MASTERSCAN_CLASS,L"Master servers", + WINDOW_STYLE,CW_USEDEFAULT,CW_USEDEFAULT, + iWidth,iHeight,NULL,NULL,g_App.hInst,NULL); + + ShowWindow(g_MasterScan.hWnd,g_App.nCmdShow); + UpdateWindow(g_MasterScan.hWnd); +} + +VOID CreateMasterScanPopup() +{ + //CreateMasterScanWindow(); + //SetWindowPos(g_MasterScan.hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); + g_MasterScan.IsPopup = TRUE; + + g_MasterScan.hWnd = CreateWindowEx(WS_EX_TOPMOST, + MASTERSCAN_CLASS, + L"Master servers [first scan]", + WS_OVERLAPPED|WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT, + iWidth,iHeight,g_MasterScan.hParent,NULL,g_App.hInst,NULL); + + ShowWindow(g_MasterScan.hWnd,g_App.nCmdShow); + UpdateWindow(g_MasterScan.hWnd); +} + +static DWORD WINAPI ScanThread(LPVOID lpArg) +{ + //Do some work + + //Send message to thread + FindMasterServers((MASTERFINDPARAMETERS_T*)lpArg); + + PostMessage(g_MasterScan.hWnd,WM_ENDMASTERSCAN,0,0); + return 0; +} + +static VOID AddServer(struct sockaddr_in* addrs) +{ + LV_ITEM Item; + WCHAR szText[32]; + + ZeroMemory(&Item,sizeof(LV_ITEM)); + Item.iItem = g_MasterScan.MasterNum; + + Item.mask = LVIF_TEXT; + Item.pszText = LPSTR_TEXTCALLBACK; + Item.cchTextMax = 64; + + ListView_InsertItem(g_MasterScan.hList,&Item); + + MultiByteToWideChar(CP_UTF8,0,inet_ntoa(addrs->sin_addr),-1,szText,32); + ListView_SetItemText(g_MasterScan.hList,Item.iItem,0,szText); + + _itow(ntohs(addrs->sin_port),szText,10); + ListView_SetItemText(g_MasterScan.hList,Item.iItem,1,szText); + + g_MasterScan.MasterNum++; + SendMessage(g_MasterScan.hProgressBar,PBM_SETPOS,g_MasterScan.MasterNum,0); +} + +VOID AddMasterServers() +{ + INT i; + INT iSize; + + iSize = g_MasterScan.MasterNum; + g_MasterScan.MasterNum = 0; + for(i = 0; i < iSize; i++) + AddServer(&g_MasterScan.addrs[i]); +} + +VOID DoMasterScan(LPCSTR lpSearch,HWND hParent) +{ + g_MasterScan.hParent = hParent; + CreateMasterScanPopup(); + + SendMessage(g_MasterScan.hProgressBar,PBM_SETRANGE,0,MAKELPARAM(0,6)); + g_MasterScan.MasterNum = 0; + g_MasterScan.ScanParams.addrs = g_MasterScan.addrs; + g_MasterScan.ScanParams.AddServer = AddServer; + //Start thread + g_MasterScan.hScanThread = CreateThread(0,0,ScanThread,&g_MasterScan.ScanParams,0,NULL); + + SetWindowText(g_MasterScan.hLabel,L"Scanning.."); +} + +static VOID AddMaster() +{ + WCHAR szText[64]; + char szAddr[64]; + struct sockaddr_in addr; + + GetWindowText(g_MasterScan.hMasterIpEdit,szText,64); + if(wcslen(szText) == 0) return; + + WideCharToMultiByte(CP_UTF8,0,szText,-1,szAddr,64,NULL,NULL); + addr.sin_addr.s_addr = inet_addr(szAddr); + + GetWindowText(g_MasterScan.hMasterPortEdit,szText,64); + if(wcslen(szText) == 0) return; + addr.sin_port = htons(_wtoi(szText)); + + SetWindowText(g_MasterScan.hMasterIpEdit,L""); + SetWindowText(g_MasterScan.hMasterPortEdit,L""); + + if(g_MasterScan.MasterNum == 32) + { + MessageBox(g_MasterScan.hWnd, + L"Reached limit of master servers!", + L"Error", + MB_ICONERROR); + return; + } + + CopyMemory(&g_MasterScan.addrs[g_MasterScan.MasterNum], + &addr,sizeof(struct sockaddr_in)); + AddServer(&g_MasterScan.addrs[g_MasterScan.MasterNum]); +} + +static HWND CreateListView(LPCREATESTRUCT lpCreate,HWND hParent,INT x,INT y,INT w,INT h) +{ + HWND hList; + LV_COLUMN Column; + + hList = CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,L"",WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_EDITLABELS, + x,y,w,h,hParent,NULL,lpCreate->hInstance,NULL); + + ZeroMemory(&Column,sizeof(LV_COLUMN)); + Column.mask = LVCF_FMT|LVCF_TEXT|LVCF_SUBITEM|LVCF_WIDTH; + + Column.cx = w-(w/4); + Column.iSubItem = 0; + Column.iOrder = 0; + Column.pszText = L"Address"; + ListView_InsertColumn(hList,0,&Column); + + Column.cx = w/4; + Column.iSubItem = 1; + Column.iOrder = 1; + Column.pszText = L"Port"; + ListView_InsertColumn(hList,1,&Column); + + return hList; +} + +static LRESULT CALLBACK WndCallback(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + LPCREATESTRUCT lpCreate; + + switch(Msg) + { + case WM_CREATE: + //Create something + lpCreate = (LPCREATESTRUCT)lParam; + + g_MasterScan.hProgressBar = CreateWindow(PROGRESS_CLASS,L"",WS_CHILD|WS_VISIBLE, + 6,10,380,20,hWnd,NULL,lpCreate->hInstance,NULL); + g_MasterScan.hLabel = CreateWindow(L"STATIC",L"Status",WS_CHILD|WS_VISIBLE, + 6,35,100,20,hWnd,NULL,lpCreate->hInstance,NULL); + g_MasterScan.hList = CreateListView(lpCreate,hWnd,6,50,380,200); + + g_MasterScan.hMasterIpEdit = CreateWindowEx(WS_EX_CLIENTEDGE,L"Edit",L"",WS_CHILD|WS_VISIBLE, + 6,255,300,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_MasterScan.hMasterPortEdit = CreateWindowEx(WS_EX_CLIENTEDGE,L"Edit",L"",WS_CHILD|WS_VISIBLE, + 310,255,76,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_MasterScan.hMasterAddBtn = CreateWindow(L"Button",L"Add master server",WS_CHILD|WS_VISIBLE, + 6,285,380,25,hWnd,(HMENU)IDC_MASTERSCAN_ADDMASTER_BTN,lpCreate->hInstance,NULL); + + if(!g_MasterScan.IsPopup) + AddMasterServers(); + else + { + EnableWindow(g_MasterScan.hMasterIpEdit,FALSE); + EnableWindow(g_MasterScan.hMasterPortEdit,FALSE); + EnableWindow(g_MasterScan.hMasterAddBtn,FALSE); + } + break; + case WM_COMMAND: + //Command + if(LOWORD(wParam) == IDC_MASTERSCAN_ADDMASTER_BTN) + AddMaster(); + break; + case WM_ENDMASTERSCAN: + CloseHandle(g_MasterScan.hScanThread); + SendMessage(g_MasterScan.hParent,WM_ENDMASTERSCAN, + g_MasterScan.MasterNum, + (LPARAM)g_MasterScan.addrs); + SetWindowText(g_MasterScan.hLabel,L"Done"); + DestroyWindow(hWnd); + break; + case WM_CLOSE: + if(g_MasterScan.IsPopup) return TRUE; + break; + case WM_DESTROY: + break; + } + return DefWindowProc(hWnd,Msg,wParam,lParam); +} \ No newline at end of file diff --git a/servertool/masterscan.h b/servertool/masterscan.h new file mode 100644 index 0000000..4d99945 --- /dev/null +++ b/servertool/masterscan.h @@ -0,0 +1,37 @@ +#ifndef __MASTERSCAN_H +#define __MASTERSCAN_H + +#include "servertool.h" + +#define MASTERSCAN_CLASS L"ServerTool_MasterScan" + +#define IDC_MASTERSCAN_ADDMASTER_BTN 134 + +typedef struct { + LPCWSTR lpClass; + HWND hParent; + HWND hWnd; + HWND hProgressBar; + HWND hLabel; + HWND hList; + HWND hMasterIpEdit; + HWND hMasterPortEdit; + HWND hMasterAddBtn; + + struct sockaddr_in addrs[32]; + INT MasterNum; + + BOOL IsPopup; + HANDLE hScanThread; + MASTERFINDPARAMETERS_T ScanParams; +} MASTERSCAN_WINDOW_T; + +VOID InitMasterScanWindow(); +VOID CreateMasterScanWindow(); +VOID CreateMasterScanPopup(); + +VOID DoMasterScan(LPCSTR lpSearch,HWND hParent); + +extern MASTERSCAN_WINDOW_T g_MasterScan; + +#endif \ No newline at end of file diff --git a/servertool/resource.h b/servertool/resource.h new file mode 100644 index 0000000..ef7d41c Binary files /dev/null and b/servertool/resource.h differ diff --git a/servertool/serverinfo.c b/servertool/serverinfo.c new file mode 100644 index 0000000..c595d60 --- /dev/null +++ b/servertool/serverinfo.c @@ -0,0 +1,275 @@ +#include "serverinfo.h" +#include +#include +#include + +SERVERINFO_WINDOW_T g_ServerInfo; + +static LRESULT CALLBACK WndCallback(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam); + +static INT iWidth = 400; +static INT iHeight = 640; + +VOID InitServerInfoWindow() +{ + WNDCLASSEX wndClass; + + g_ServerInfo.lpClass = SERVERINFO_CLASS; + + ZeroMemory(&wndClass,sizeof(WNDCLASSEX)); + + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.lpszClassName = g_ServerInfo.lpClass; + wndClass.hInstance = g_App.hInst; + wndClass.lpfnWndProc = WndCallback; + wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW; + wndClass.hIcon = g_App.hIcon; + wndClass.hIconSm = g_App.hIconSm; + + RegisterClassEx(&wndClass); +} + +VOID CreateServerInfoWindow() +{ + g_ServerInfo.hWnd = CreateWindow(SERVERINFO_CLASS, + L"Server-Info",WINDOW_STYLE, + CW_USEDEFAULT,CW_USEDEFAULT, + iWidth,iHeight,NULL,NULL, + g_App.hInst,NULL); + + ShowWindow(g_ServerInfo.hWnd,g_App.nCmdShow); + UpdateWindow(g_ServerInfo.hWnd); +} + +static VOID AddServer(SERVER_T* Server) +{ + static WCHAR szText[1024]; + + swprintf(szText,1024, + L"Name: %s\r\n" + L"Map: %s\r\n" + L"Game: %s\r\n" + L"Players: %d\r\n" + L"Slots: %d\r\n" + L"Type: %C\r\n" + L"System: %C\r\n" + L"Password: %d\r\n" + L"VAC: %d\r\n", + Server->szName, + Server->szMap, + Server->szGame, + Server->cPlayers, + Server->cMaxPlayers, + Server->cType, + Server->cOS, + Server->cVisibility, + Server->cVAC); + + SetWindowText(g_ServerInfo.hInfoBox,szText); +} + +static VOID AddPlayer(PCHAR pName,UINT Score,FLOAT Duration) +{ + WCHAR szText[64]; + LV_ITEM Item; + INT Idx; + ULONG Time; + + Idx = g_ServerInfo.PlayerNum; + + ZeroMemory(&Item,sizeof(Item)); + Item.mask = LVIF_TEXT; + Item.pszText = LPSTR_TEXTCALLBACK; + Item.cchTextMax = 64; + Item.iItem = Idx; + Item.iSubItem = 0; + ListView_InsertItem(g_ServerInfo.hPlayerList,&Item); + + MultiByteToWideChar(CP_UTF8,0,pName,-1,szText,64); + ListView_SetItemText(g_ServerInfo.hPlayerList,Idx,0,szText); + + swprintf(szText,64,L"%u",Score); + ListView_SetItemText(g_ServerInfo.hPlayerList,Idx,1,szText); + + Time = (ULONG)Duration; + swprintf(szText,64,L"%02u:%02u:%02u",Time/3600,(Time/60)%60,Time%60); + ListView_SetItemText(g_ServerInfo.hPlayerList,Idx,2,szText); + + g_ServerInfo.PlayerNum++; +} + +static VOID AddRule(PCHAR pName,PCHAR pValue) +{ + WCHAR szText[MAX_PATH]; + LV_ITEM Item; + INT Idx; + + Idx = g_ServerInfo.RuleNum; + + ZeroMemory(&Item,sizeof(Item)); + Item.mask = LVIF_TEXT; + Item.pszText = LPSTR_TEXTCALLBACK; + Item.cchTextMax = 64; + Item.iItem = Idx; + Item.iSubItem = 0; + ListView_InsertItem(g_ServerInfo.hRuleList,&Item); + + MultiByteToWideChar(CP_UTF8,0,pName,-1,szText,MAX_PATH); + ListView_SetItemText(g_ServerInfo.hRuleList,Idx,0,szText); + + MultiByteToWideChar(CP_UTF8,0,pValue,-1,szText,MAX_PATH); + ListView_SetItemText(g_ServerInfo.hRuleList,Idx,1,szText); + + g_ServerInfo.RuleNum++; +} + +static DWORD WINAPI RequestThread(LPVOID lpArg) +{ + GetServerInfo((NETADR_T*)lpArg,AddServer,AddPlayer,AddRule); + PostMessage(g_ServerInfo.hWnd,WM_ENDSERVERINFOREQUEST,0,0); + return 0; +} + +static VOID RequestServerInfo() +{ + WCHAR szText[64]; + char szAddr[32]; + PWCHAR pDel; + NETADR_T Addr; + + g_ServerInfo.PlayerNum = 0; + g_ServerInfo.RuleNum = 0; + + SetWindowText(g_ServerInfo.hInfoBox,L""); + ListView_DeleteAllItems(g_ServerInfo.hPlayerList); + ListView_DeleteAllItems(g_ServerInfo.hRuleList); + + if(g_ServerInfo.hRequestThread) + { + TerminateThread(g_ServerInfo.hRequestThread,0); + CloseHandle(g_ServerInfo.hRequestThread); + } + + GetWindowText(g_ServerInfo.hAddressEdit,szText,32); + pDel = wcschr(szText,L':'); + if(!pDel) return; + + *pDel = L'\0'; + WideCharToMultiByte(CP_UTF8,0,szText,-1,szAddr,32,NULL,NULL); + + Addr.sin_addr.s_addr = inet_addr(szAddr); + Addr.sin_port = htons(_wtoi(pDel+1)); + + g_ServerInfo.hRequestThread = CreateThread(NULL,0,RequestThread,&Addr,0,NULL); + + SetWindowText(g_ServerInfo.hRequestBtn,L"Wait.."); +} + +VOID EndServerInfoRequest() +{ + SetWindowText(g_ServerInfo.hRequestBtn,L"Request"); + CloseHandle(g_ServerInfo.hRequestThread); + g_ServerInfo.hRequestThread = NULL; +} + +static HWND CreatePlayerList(LPCREATESTRUCT lpCreate,HWND hParent,INT x,INT y) +{ + HWND hList; + LV_COLUMN Column; + + hList = CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,L"", + WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_EDITLABELS,x,y,380,150, + hParent,NULL,lpCreate->hInstance,NULL); + + ZeroMemory(&Column,sizeof(Column)); + + Column.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; + + Column.iOrder = 0; + Column.iSubItem = 0; + Column.cx = 190; + Column.pszText = L"Name"; + ListView_InsertColumn(hList,0,&Column); + + Column.iOrder = 1; + Column.iSubItem = 1; + Column.cx = 95; + Column.pszText = L"Score"; + ListView_InsertColumn(hList,1,&Column); + + Column.iOrder = 2; + Column.iSubItem = 2; + Column.cx = 90; + Column.pszText = L"Time"; + ListView_InsertColumn(hList,2,&Column); + + return hList; +} + +static HWND CreateRulesList(LPCREATESTRUCT lpCreate,HWND hParent,INT x,INT y) +{ + HWND hList; + LV_COLUMN Column; + + hList = CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,L"", + WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_EDITLABELS,x,y,380,170, + hParent,NULL,lpCreate->hInstance,NULL); + + ZeroMemory(&Column,sizeof(Column)); + + Column.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; + + Column.iOrder = 0; + Column.iSubItem = 0; + Column.cx = 140; + Column.pszText = L"Name"; + ListView_InsertColumn(hList,0,&Column); + + Column.iOrder = 1; + Column.iSubItem = 1; + Column.cx = 235; + Column.pszText = L"Value"; + ListView_InsertColumn(hList,1,&Column); + + return hList; +} + +static LRESULT CALLBACK WndCallback(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) +{ + LPCREATESTRUCT lpCreate; + + switch(Msg) + { + case WM_CREATE: + lpCreate = (LPCREATESTRUCT)lParam; + + g_ServerInfo.hAddressEdit = CreateWindowEx(WS_EX_CLIENTEDGE,L"Edit",L"Address:Port", + WS_CHILD|WS_VISIBLE,6,10,280,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_ServerInfo.hRequestBtn = CreateWindow(L"Button",L"Request",WS_CHILD|WS_VISIBLE, + 295,10,92,25,hWnd,(HMENU)IDC_SERVERINFO_REQUEST_BTN,lpCreate->hInstance,NULL); + g_ServerInfo.hInfoBox = CreateWindowEx(WS_EX_CLIENTEDGE,L"Edit",L"", + WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE|ES_AUTOVSCROLL|ES_LEFT, + 6,40,380,200,hWnd,NULL,lpCreate->hInstance,NULL); + + CreateWindow(L"STATIC",L"Players",WS_CHILD|WS_VISIBLE, + 6,245,100,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_ServerInfo.hPlayerList = CreatePlayerList(lpCreate,hWnd,6,275); + + CreateWindow(L"STATIC",L"Server's ConVar (FCVAR_NOTIFY)", + WS_CHILD|WS_VISIBLE,6,405,380,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_ServerInfo.hRuleList = CreateRulesList(lpCreate,hWnd,6,435); + + g_ServerInfo.PlayerNum = 0; + g_ServerInfo.RuleNum = 0; + break; + case WM_COMMAND: + if(LOWORD(wParam) == IDC_SERVERINFO_REQUEST_BTN) + RequestServerInfo(); + break; + case WM_ENDSERVERINFOREQUEST: + EndServerInfoRequest(); + break; + } + return DefWindowProc(hWnd,Msg,wParam,lParam); +} \ No newline at end of file diff --git a/servertool/serverinfo.h b/servertool/serverinfo.h new file mode 100644 index 0000000..76f53ac --- /dev/null +++ b/servertool/serverinfo.h @@ -0,0 +1,31 @@ +#ifndef __SERVERINFO_H +#define __SERVERINFO_H + +#include "servertool.h" + +#define SERVERINFO_CLASS L"ServerTool_ServerInfo" + +#define IDC_SERVERINFO_REQUEST_BTN 135 + +typedef struct { + LPCWSTR lpClass; + HWND hWnd; + + HWND hAddressEdit; + HWND hRequestBtn; + HWND hInfoBox; + HWND hPlayerList; + HWND hRuleList; + + INT PlayerNum; + INT RuleNum; + + HANDLE hRequestThread; +} SERVERINFO_WINDOW_T; + +VOID InitServerInfoWindow(); +VOID CreateServerInfoWindow(); + +extern SERVERINFO_WINDOW_T g_ServerInfo; + +#endif \ No newline at end of file diff --git a/servertool/serverlist.c b/servertool/serverlist.c new file mode 100644 index 0000000..c7c32ae --- /dev/null +++ b/servertool/serverlist.c @@ -0,0 +1,611 @@ +#include "serverlist.h" +#include "masterscan.h" +#include "serverinfo.h" +#include +#include +#include + +SERVERLIST_WINDOW_T g_ServerList; +static INT iWidth = 1072; +static INT iHeight = 600; + +static LRESULT CALLBACK WndCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +VOID InitServerListWindow() +{ + WNDCLASSEX wndClass; + + g_ServerList.lpClass = SERVERLIST_CLASS; + + ZeroMemory(&wndClass,sizeof(WNDCLASSEX)); + + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.lpszClassName = g_ServerList.lpClass; + wndClass.hInstance = g_App.hInst; + wndClass.lpfnWndProc = WndCallback; + wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW; + wndClass.hIcon = g_App.hIcon; + wndClass.hIconSm = g_App.hIconSm; + + RegisterClassEx(&wndClass); +} + +VOID CreateServerListWindow() +{ + g_ServerList.hWnd = CreateWindow(SERVERLIST_CLASS,L"ServerList", + WINDOW_STYLE,CW_USEDEFAULT,CW_USEDEFAULT, + iWidth,iHeight,NULL,NULL,g_App.hInst,NULL); + + ShowWindow(g_ServerList.hWnd,g_App.nCmdShow); + UpdateWindow(g_ServerList.hWnd); +} + +static VOID StartMasterScan(HWND hParent) +{ + DoMasterScan("\\appid\\320",hParent); +} + +static VOID LoadMasterServers(HWND hParent) +{ + HANDLE hFile; + LARGE_INTEGER size; + NETADR_T Addr; + DWORD dwRead; + INT i; + + hFile = CreateFile(L"master.dat",GENERIC_READ,FILE_SHARE_READ, + NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + StartMasterScan(hParent); + return; + } + + GetFileSizeEx(hFile,&size); + g_ServerList.MasterNum = (INT)(size.QuadPart/sizeof(NETADR_T)); + if(g_ServerList.MasterNum > 32) + g_ServerList.MasterNum = 32; + g_MasterScan.MasterNum = g_ServerList.MasterNum; + for(i = 0; i < g_ServerList.MasterNum; i++) + { + ReadFile(hFile,&Addr,sizeof(NETADR_T),&dwRead,NULL); + g_ServerList.Masters[i].Conn.addr.sin_family = AF_INET; + g_ServerList.Masters[i].Conn.addr.sin_addr = Addr.sin_addr; + g_ServerList.Masters[i].Conn.addr.sin_port = Addr.sin_port; + g_MasterScan.addrs[i].sin_family = AF_INET; + g_MasterScan.addrs[i].sin_addr = Addr.sin_addr; + g_MasterScan.addrs[i].sin_port = Addr.sin_port; + } + CloseHandle(hFile); + + g_ServerList.Params.Masters = g_ServerList.Masters; + g_ServerList.Params.MasterNum = g_ServerList.MasterNum; + g_MasterScan.ScanParams.addrs = g_MasterScan.addrs; +} + +static VOID SaveMasters() +{ + HANDLE hFile; + NETADR_T Addr; + DWORD dwWrote; + INT i; + + hFile = CreateFile(L"master.dat",GENERIC_ALL,0,NULL, + CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + for(i = 0; i < g_ServerList.MasterNum; i++) + { + Addr.sin_addr = g_ServerList.Masters[i].Conn.addr.sin_addr; + Addr.sin_port = g_ServerList.Masters[i].Conn.addr.sin_port; + WriteFile(hFile,&Addr,sizeof(NETADR_T),&dwWrote,NULL); + } + CloseHandle(hFile); +} + +static VOID EndMasterScan(WPARAM wParam,LPARAM lParam) +{ + INT i; + struct sockaddr_in* addrs; + + if(wParam < MAX_MASTER_NUM) //We need 3 master servers at least + { + Sleep(5000); + StartMasterScan(g_ServerList.hWnd); + } + else + { + g_ServerList.MasterNum = (INT)wParam; + addrs = (struct sockaddr_in*)lParam; + + for(i = 0; i < g_ServerList.MasterNum; i++) + { + CopyMemory(&g_ServerList.Masters[i].Conn.addr, + &addrs[i],sizeof(struct sockaddr_in)); + } + + //Save masters to file + SaveMasters(); + } +} + +static DWORD WINAPI RequestThread(LPVOID lpArg) +{ + MASTERREQUESTPARAMETERS_T* Params; + + Params = (MASTERREQUESTPARAMETERS_T*)lpArg; + + DoMasterServers(Params->Masters,Params->MasterNum,Params->szSearch); + Params->Servers = GetUniqueServers(Params->Masters,Params->MasterNum,&Params->ServerNum); + + SendMessage(Params->hParent,WM_ENDMASTERREQUEST,0,(LPARAM)Params); + return 0; +} + +VOID MakeMasterServersRequest() +{ + WCHAR szRequest[MAX_PATH]; + + if(g_ServerList.Params.ServerList) + { + free(g_ServerList.Params.ServerList); + g_ServerList.Params.ServerList = NULL; + } + + if(g_ServerList.hScanThread) + { + TerminateThread(g_ServerList.hScanThread,0); + CloseHandle(g_ServerList.hScanThread); + g_ServerList.hScanThread = NULL; + } + + ListView_DeleteAllItems(g_ServerList.hServerList); + SendMessage(g_ServerList.hProgressBar,PBM_SETPOS,0,0); + + ZeroMemory(&g_ServerList.Params,sizeof(g_ServerList.Params)); + + GetWindowText(g_ServerList.hRequestEdit,szRequest,MAX_PATH); + WideCharToMultiByte(CP_UTF8,0,szRequest,-1, + g_ServerList.Params.szSearch,MAX_PATH,NULL,NULL); + + g_ServerList.Params.hParent = g_ServerList.hWnd; + g_ServerList.Params.Masters = g_ServerList.Masters; + g_ServerList.Params.MasterNum = g_ServerList.MasterNum; + + g_ServerList.hScanThread = CreateThread(0,0,RequestThread, + &g_ServerList.Params,0,NULL); + + SetWindowText(g_ServerList.hLabel,L"Requests to masters.."); + SetWindowText(g_ServerList.hRequestBtn,L"Stop"); +} + +static VOID AddServer(SERVER_T* Server) +{ + LV_ITEM Item; + WCHAR szText[64]; + char szAddr[64]; + LPCWSTR pText; + INT Idx; + + ZeroMemory(&Item,sizeof(LV_ITEM)); + Idx = g_ServerList.ServerIdx; + + Item.mask = LVIF_TEXT; + Item.pszText = LPSTR_TEXTCALLBACK; + Item.iItem = Idx; + + ListView_InsertItem(g_ServerList.hServerList,&Item); + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,0,Server->szName); + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,1,Server->szMap); + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,2,Server->szGame); + + _itow(Server->cPlayers,szText,10); + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,3,szText); + + _itow(Server->cMaxPlayers,szText,10); + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,4,szText); + + switch(Server->cType) + { + case 'd': pText = L"Dedicated"; break; + case 'l': pText = L"Non-Dedic."; break; + case 'p': pText = L"SourceTV"; break; + default: + swprintf(szText,64,L"<%02X>",Server->cType&0xFF); + pText = szText; + } + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,5,(LPWSTR)pText); + + switch(Server->cOS) + { + case 'l': pText = L"Linux"; break; + case 'w': pText = L"Windows"; break; + case 'm': pText = L"Mac OS"; break; + default: + swprintf(szText,64,L"<%02X>",Server->cOS&0xFF); + pText = szText; + } + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,6,(LPWSTR)pText); + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,7, + Server->cVisibility ? L"Yes" : L"No"); + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,8, + Server->cVAC ? L"Yes" : L"No"); + + sprintf(szAddr,"%s:%d",inet_ntoa(Server->Addr.sin_addr), + ntohs(Server->Addr.sin_port)); + MultiByteToWideChar(CP_UTF8,0,szAddr,-1,szText,64); + + ListView_SetItemText(g_ServerList.hServerList,Item.iItem,9,szText); + + SendMessage(g_ServerList.hProgressBar,PBM_SETPOS,Idx,0); + g_ServerList.ServerIdx++; +} + +typedef struct { + UINT Num; + UINT Size; + UINT WaitTime; +} BLOCKPARAMS_T; + +static BLOCKPARAMS_T s_BlockSize[] = { + {300,32,2}, + {512,128,3}, + {1024,256,4}, + {2048,512,5}, + {4096,1024,7}, + {-1,2048,10}, +}; + +static BLOCKPARAMS_T* GetBlockParams(UINT Num) +{ + INT i; + + i = 0; + while(Num > s_BlockSize[i].Num){i++;} + return &s_BlockSize[i]; +} + +static DWORD WINAPI QueryThread(LPVOID lpArg) +{ + MASTERREQUESTPARAMETERS_T* Params; + BLOCKPARAMS_T* Block; + NETADR_T* AddrPtr; + SERVER_T* ServerPtr; + INT iNum,iRemaind; + INT i; + + Params = (MASTERREQUESTPARAMETERS_T*)lpArg; + Block = GetBlockParams(Params->ServerNum); + Params->ServerList = (SERVER_T*)calloc(Params->ServerNum,sizeof(SERVER_T)); + ServerPtr = Params->ServerList; + + g_ServerList.ServerIdx = 0; + iNum = Params->ServerNum/Block->Size; + iRemaind = Params->ServerNum%Block->Size; + + AddrPtr = Params->Servers; + for(i = 0; i < iNum; i++) + { + QueryServers(AddrPtr,ServerPtr,Block->Size,AddServer,Block->WaitTime); + AddrPtr += Block->Size; + ServerPtr += Block->Size; + } + + if(iRemaind) + QueryServers(AddrPtr,ServerPtr,iRemaind,AddServer,Block->WaitTime); + + PostMessage(g_ServerList.hWnd,WM_ENDQUERYSERVERS,0,0); + return 0; +} + +VOID EndMasterRequest() +{ + WCHAR szText[64]; + + CloseHandle(g_ServerList.hScanThread); + + swprintf(szText,64,L"Found %d servers. Request..",g_ServerList.Params.ServerNum); + SetWindowText(g_ServerList.hLabel,szText); + + SendMessage(g_ServerList.hProgressBar,PBM_SETRANGE,0,MAKELPARAM(0,g_ServerList.Params.ServerNum)); + + //Query all servers + g_ServerList.hScanThread = CreateThread(0,0,QueryThread,&g_ServerList.Params,0,NULL); +} + +VOID EndQueryServers() +{ + SetWindowText(g_ServerList.hLabel,L"Done"); + + free(g_ServerList.Params.Servers); + g_ServerList.hScanThread = NULL; + + //ZeroMemory(&g_ServerList.Params,sizeof(g_ServerList.Params)); + SendMessage(g_ServerList.hProgressBar,PBM_SETPOS,0,0); + SetWindowText(g_ServerList.hRequestBtn,L"Execute"); +} + +VOID StopAnyRequests() +{ + TerminateThread(g_ServerList.hScanThread,0); + EndQueryServers(); +} + +typedef struct { + INT iWidth; + LPWSTR lpName; +} COLUMNHDR_T; + +static COLUMNHDR_T Columns[] = { + {300,L"Name"}, + {100,L"Map"}, + {100,L"Game"}, + {65,L"Players"}, + {65,L"Slots"}, + {95,L"Type"}, + {80,L"System"}, + {60,L"Password"}, + {40,L"VAC"}, + {150,L"Address"}, +}; + +VOID CreateBar(LPCREATESTRUCT lpCreate,HWND hParent) +{ + HMENU hMenuBar,hMenu; + + hMenuBar = CreateMenu(); + + hMenu = CreateMenu(); + AppendMenu(hMenu,MF_STRING,IDC_FILE_SAVEAS,L"Save ss.."); + + AppendMenu(hMenuBar,MF_POPUP,(UINT_PTR)hMenu,L"File"); + + hMenu = CreateMenu(); + AppendMenu(hMenu,MF_STRING,IDC_SERVERS_MASTERS,L"Master servers"); + AppendMenu(hMenu,MF_STRING,IDC_SERVERS_QUERY,L"Server-Info"); + + AppendMenu(hMenuBar,MF_POPUP,(UINT_PTR)hMenu,L"Servers"); + + hMenu = CreateMenu(); + AppendMenu(hMenu,MF_STRING,IDC_HELP_HELP,L"Help"); + AppendMenu(hMenu,MF_SEPARATOR,0,NULL); + AppendMenu(hMenu,MF_STRING,IDC_HELP_ABOUT,L"About program"); + + AppendMenu(hMenuBar,MF_POPUP,(UINT_PTR)hMenu,L"Help"); + + SetMenu(hParent,hMenuBar); +} + +VOID DialogHelp() +{ + MessageBox(g_ServerList.hWnd, + L"You must make request like it described here:" + L">>> https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#Filter\n" + L"\n" + L"Example for Garry's Mod:\n\\appid\\4000\n\n" + L"\\appid\\%SteamAppId !!!%\\\n\n", + + L"Help", + MB_ICONINFORMATION); +} + +VOID DialogAbout() +{ + MessageBox(g_ServerList.hWnd, + L"https://github.com/nikolaytihonov/servertool\n" + L"\thttps://steamcommunity.com/id/dominqnta\n" + L"\tDiscord: ISurface030#1470" + L"\n\nAuthor: nikolaytihonov", + + L"About", + MB_ICONINFORMATION); +} + +#define FILE_1STLINE L"Name\tMap\tGame\tPlayers\tSlots\tType\tSystem\tPassword\tVAC\tAddress\r\n" + +VOID SaveServers() +{ + HANDLE hFile; + static WCHAR szLine[512]; + static WCHAR szPath[MAX_PATH]; + static WCHAR szText[64]; + static CHAR sz8Line[1024]; + char szAddr[32]; + PWCHAR pText; + OPENFILENAME ofn; + DWORD dwWrote; + INT i; + + ZeroMemory(&ofn,sizeof(OPENFILENAME)); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_ServerList.hWnd; + ofn.lpstrFile = szPath; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrDefExt = L".TXT"; + + if(!GetSaveFileName(&ofn)) + { + MessageBox(g_ServerList.hWnd,L"Can't save file!",L"Error",MB_ICONERROR); + return; + } + + hFile = CreateFile(szPath,GENERIC_ALL,0,NULL, + CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + MessageBox(g_ServerList.hWnd,L"Can't save file!",L"Error",MB_ICONERROR); + return; + } + + WideCharToMultiByte(CP_UTF8,0,FILE_1STLINE,-1,sz8Line,sizeof(sz8Line),NULL,NULL); + WriteFile(hFile,sz8Line,(DWORD)strlen(sz8Line)*sizeof(CHAR),&dwWrote,NULL); + + for(i = 0; i < g_ServerList.Params.ServerNum; i++) + { + SERVER_T* Server; + + //swprintf(szLine,L"%s\t%s\t%s%d\t%d\t%s\t%s\t%s + Server = &g_ServerList.Params.ServerList[i]; + if(!Server->IsValid) continue; + + wcsncpy(szLine,Server->szName,512); + wcsncat(szLine,L" ",512); + + wcsncat(szLine,Server->szMap,512); + wcsncat(szLine,L" ",512); + + wcsncat(szLine,Server->szGame,512); + wcsncat(szLine,L" ",512); + + _itow(Server->cPlayers,szText,10); + + wcsncat(szLine,szText,512); + wcsncat(szLine,L" ",512); + + _itow(Server->cMaxPlayers,szText,10); + + wcsncat(szLine,szText,512); + wcsncat(szLine,L" ",512); + + switch(Server->cType) + { + case 'd': pText = L"Dedicated"; break; + case 'l': pText = L"Non-Dedic."; break; + case 'p': pText = L"SourceTV"; break; + default: + swprintf(szText,64,L"<%02X>",Server->cType&0xFF); + pText = szText; + } + + wcsncat(szLine,pText,512); + wcsncat(szLine,L" ",512); + + switch(Server->cOS) + { + case 'l': pText = L"Linux"; break; + case 'w': pText = L"Windows"; break; + case 'm': pText = L"Mac OS"; break; + default: + swprintf(szText,64,L"<%02X>",Server->cOS&0xFF); + pText = szText; + } + + wcsncat(szLine,pText,512); + wcsncat(szLine,L" ",512); + + wcsncat(szLine,Server->cVisibility ? L"Yes" : L"No",512); + wcsncat(szLine,L" ",512); + + wcsncat(szLine,Server->cVAC ? L"Yes" : L"No",512); + wcsncat(szLine,L" ",512); + + sprintf(szAddr,"%s:%d",inet_ntoa(Server->Addr.sin_addr), + ntohs(Server->Addr.sin_port)); + MultiByteToWideChar(CP_UTF8,0,szAddr,-1,szText,64); + + wcsncat(szLine,szText,512); + wcsncat(szLine,L"\r\n",512); + + WideCharToMultiByte(CP_UTF8,0,szLine,-1,sz8Line,sizeof(sz8Line),NULL,NULL); + WriteFile(hFile,sz8Line,(DWORD)strlen(sz8Line)*sizeof(CHAR),&dwWrote,NULL); + } + CloseHandle(hFile); +} + +static HWND CreateListView(LPCREATESTRUCT lpCreate,HWND hParent,INT x,INT y,INT w,INT h) +{ + HWND hList; + LV_COLUMN Column; + INT i; + + hList = CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,L"",WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_EDITLABELS, + x,y,w,h,hParent,NULL,lpCreate->hInstance,NULL); + + ZeroMemory(&Column,sizeof(LV_COLUMN)); + Column.mask = LVCF_FMT|LVCF_TEXT|LVCF_SUBITEM|LVCF_WIDTH; + + for(i = 0; i < sizeof(Columns)/sizeof(COLUMNHDR_T); i++) + { + Column.cx = Columns[i].iWidth; + Column.pszText = Columns[i].lpName; + Column.iSubItem = i; + Column.iOrder = i; + ListView_InsertColumn(hList,i,&Column); + } + + return hList; +} + +static LRESULT CALLBACK WndCallback(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + LPCREATESTRUCT lpCreate; + + switch(Msg) + { + case WM_CREATE: + //Create something + lpCreate = (LPCREATESTRUCT)lParam; + + g_ServerList.hProgressBar = CreateWindow(PROGRESS_CLASS,L"",WS_CHILD|WS_VISIBLE, + 6,10,1055,20,hWnd,NULL,lpCreate->hInstance,NULL); + g_ServerList.hRequestEdit = CreateWindowEx(WS_EX_CLIENTEDGE,L"Edit",L"\\appid\\4000", + WS_CHILD|WS_VISIBLE,6,35,400,25,hWnd,NULL,lpCreate->hInstance,NULL); + g_ServerList.hRequestBtn = CreateWindow(L"Button",L"Request",WS_CHILD|WS_VISIBLE, + 415,35,95,25,hWnd,(HMENU)IDC_REQUEST_BTN,lpCreate->hInstance,NULL); + g_ServerList.hLabel = CreateWindow(L"STATIC",L"Status",WS_CHILD|WS_VISIBLE, + 525,35,200,30,hWnd,NULL,lpCreate->hInstance,NULL); + + CreateBar(lpCreate,hWnd); + + g_ServerList.hServerList = CreateListView(lpCreate,hWnd,6,70,1055,480); + g_ServerList.hScanThread = NULL; + g_ServerList.Params.Servers = NULL; + ZeroMemory(&g_ServerList.Params,sizeof(g_ServerList.Params)); + + //Find master servers + //StartMasterScan(hWnd); + LoadMasterServers(hWnd); + break; + case WM_ENDMASTERSCAN: + EndMasterScan(wParam,lParam); + break; + case WM_ENDMASTERREQUEST: + EndMasterRequest(); + break; + case WM_ENDQUERYSERVERS: + EndQueryServers(); + break; + case WM_COMMAND: + if(LOWORD(wParam) == IDC_REQUEST_BTN) + { + if(g_ServerList.hScanThread != 0) + StopAnyRequests(); + else MakeMasterServersRequest(); + } + else + { + switch(LOWORD(wParam)) + { + case IDC_FILE_SAVEAS: SaveServers(); break; + case IDC_SERVERS_MASTERS: + CreateMasterScanWindow(); + break; + case IDC_SERVERS_QUERY: + CreateServerInfoWindow(); + break; + case IDC_HELP_HELP: DialogHelp(); break; + case IDC_HELP_ABOUT: DialogAbout(); break; + } + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + } + return DefWindowProc(hWnd,Msg,wParam,lParam); +} \ No newline at end of file diff --git a/servertool/serverlist.h b/servertool/serverlist.h new file mode 100644 index 0000000..110e7f5 --- /dev/null +++ b/servertool/serverlist.h @@ -0,0 +1,39 @@ +#ifndef __SERVERLIST_H +#define __SERVERLIST_H + +#include "servertool.h" + +#define SERVERLIST_CLASS L"ServerTool_ServerList" + +#define IDC_REQUEST_BTN 128 +#define IDC_FILE_SAVEAS 129 +#define IDC_SERVERS_MASTERS 130 +#define IDC_SERVERS_QUERY 131 +#define IDC_HELP_HELP 132 +#define IDC_HELP_ABOUT 133 + +typedef struct { + LPCWSTR lpClass; + HWND hWnd; + HWND hProgressBar; + HWND hRequestEdit; + HWND hRequestBtn; + HWND hLabel; + HWND hServerList; + + INT ServerIdx; + + MASTER_T Masters[32]; + INT MasterNum; + + + MASTERREQUESTPARAMETERS_T Params; + HANDLE hScanThread; +} SERVERLIST_WINDOW_T; + +VOID InitServerListWindow(); +VOID CreateServerListWindow(); + +extern SERVERLIST_WINDOW_T g_ServerList; + +#endif \ No newline at end of file diff --git a/servertool/servertool.aps b/servertool/servertool.aps new file mode 100644 index 0000000..5ae7ee0 Binary files /dev/null and b/servertool/servertool.aps differ diff --git a/servertool/servertool.c b/servertool/servertool.c new file mode 100644 index 0000000..43b42df --- /dev/null +++ b/servertool/servertool.c @@ -0,0 +1,786 @@ +#include "servertool.h" +#include +#include +#include + +typedef struct { + LONG changed; + INT stage; + BOOL IsResending; +} CONNSTATUS_T; + +static ULONG GetTimeNow() +{ + return (LONG)time(NULL); +} + +VOID FormatRequest(LPVOID lpBuf,NETADR_T* pAddr,LPSTR lpSearch,PSIZE_T pszLen) +{ + PBYTE pCur; + SIZE_T szLen; + PCHAR pChar; + CHAR szFormat[32]; + + pCur = (PBYTE)lpBuf; + *pCur++ = 0x31; + *pCur++ = 0xFF; + + sprintf(szFormat,"%s:%d",inet_ntoa(pAddr->sin_addr), + ntohs(pAddr->sin_port)); + pChar = szFormat; + szLen = strlen(pChar)+1; + CopyMemory(pCur,pChar,szLen); + pCur += szLen; + + pChar = lpSearch; + szLen = strlen(pChar)+1; + CopyMemory(pCur,pChar,szLen); + pCur += szLen; + + *pszLen = (SIZE_T)((char*)pCur-(char*)lpBuf); +} + +VOID MakeNonblocking(SOCKET s) +{ + unsigned long arg = 1; + ioctlsocket(s, FIONBIO, &arg); +} + +BOOL FindMasterServers(MASTERFINDPARAMETERS_T* pParams) +{ + struct hostent* host; + NETADR_T addr; + + CONNECTION_T m_conn[32]; + INT m_num; + INT stages[32]; + INT rel[32]; + INT changed,tries; + + WSAPOLLFD polls[32]; + INT p_num; + + static BYTE szPacket[1240]; + SIZE_T szLen; + INT i; + + host = gethostbyname("hl2master.steampowered.com"); + if(!host) return FALSE; + + addr.sin_addr.s_addr = 0; + addr.sin_port = htons(0); + + FormatRequest(szPacket,&addr,"\\app\\4020",&szLen); + + i = 0; + m_num = 0; + while(host->h_addr_list[i] != 0) + { + INT j; + + for(j = 27010; j <= 27015; j++) + { + m_conn[m_num].s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + + m_conn[m_num].addr.sin_family = AF_INET; + m_conn[m_num].addr.sin_addr = *(IN_ADDR*)host->h_addr_list[i]; + m_conn[m_num].addr.sin_port = htons(j); + m_num++; + } + + i++; + } + + ZeroMemory(stages,sizeof(stages)); + + tries = 0; + do { + p_num = 0; + for(i = 0; i < m_num; i++) + { + if(stages[i] == 2) continue; + polls[p_num].fd = m_conn[i].s; + if(stages[i] == 0) + polls[p_num].events = POLLOUT; + else polls[p_num].events = POLLIN; + rel[p_num] = i; + p_num++; + } + + changed = 0; + + if(p_num == 0) break; + if(WSAPoll(polls,p_num,500) > 0) //5 second timeout + { + for(i = 0; i < p_num; i++) + { + CONNECTION_T* pConn; + + pConn = &m_conn[rel[i]]; + if(polls[i].revents & POLLOUT) + { + sendto(pConn->s,(const char*)szPacket,(INT)szLen,0, + (const struct sockaddr*)&pConn->addr,sizeof(struct sockaddr_in)); + stages[rel[i]]++; + //printf("sent request to %s:%d\n",inet_ntoa(pConn->addr.sin_addr), + // ntohs(pConn->addr.sin_port)); + changed = 1; + } + else if(polls[i].revents & POLLIN) + { + INT FromLen; + + FromLen = sizeof(struct sockaddr_in); + recvfrom(pConn->s,(char*)szPacket,sizeof(szPacket),0, + (struct sockaddr*)&pConn->addr,&FromLen); + stages[rel[i]]++; + + //Close + closesocket(pConn->s); + + //Add to list + CopyMemory(pParams->addrs,&pConn->addr,sizeof(struct sockaddr_in)); + pParams->addrs++; + + pParams->AddServer(&pConn->addr); + + //printf("recv response from %s:%d\n",inet_ntoa(pConn->addr.sin_addr), + // ntohs(pConn->addr.sin_port)); + changed = 1; + } + //unnecessary for UDP + else if(polls[i].revents & (POLLHUP|POLLERR)) + { + //Error + + //Close + closesocket(pConn->s); + stages[rel[i]] = 2; + + //printf("error %s:%d\n",inet_ntoa(pConn->addr.sin_addr), + // ntohs(pConn->addr.sin_port)); + } + } + } + + //printf("changed %d tries %d\n",changed,tries); + if(changed == 0) + { + if(tries++ == 2) + { + for(i = 0; i < m_num; i++) + { + if(stages[i] == 2) continue; + stages[i] = 2; + closesocket(m_conn[i].s); + } + } + } else tries = 0; + } while(p_num > 0); + + return TRUE; +} + +INT DoMasterServers(MASTER_T* Masters,INT MasterNum,LPCSTR lpSearch) +{ + static BYTE szPacket[2048]; + CONNSTATUS_T status[32]; + WSAPOLLFD polls[32]; + SIZE_T szLen; + INT rel[32]; + INT i,p_num,CurTime,Delta; + INT servers; + PBYTE pPtr; + + for(i = 0; i < MasterNum; i++) + { + Masters[i].Conn.s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + Masters[i].ServerNum = 0; + Masters[i].PacketNum = 0; + MakeNonblocking(Masters[i].Conn.s); + status[i].stage = 0; + status[i].changed = GetTimeNow(); + } + + servers = 0; + do { + p_num = 0; + for(i = 0; i < MasterNum; i++) + { + if(status[i].stage == 2) + continue; + polls[p_num].fd = Masters[i].Conn.s; + if(status[i].stage == 0) + polls[p_num].events = POLLOUT; + else polls[p_num].events = POLLIN; + rel[p_num] = i; + p_num++; + } + + if(p_num == 0) break; + + if(WSAPoll(polls,p_num,500)) + { + for(i = 0; i < p_num; i++) + { + MASTER_T* Master; + + Master = &Masters[rel[i]]; + if(polls[i].revents & POLLOUT) + { + NETADR_T last; + if(Master->ServerNum == 0) + { + last.sin_addr.s_addr = 0; + last.sin_port = 0; + } + else last = Master->Servers[Master->ServerNum-1]; + FormatRequest(szPacket,&last,(LPSTR)lpSearch,&szLen); + + sendto(Master->Conn.s,(const char*)szPacket,(INT)szLen,0, + (const struct sockaddr*)&Master->Conn.addr, + sizeof(struct sockaddr_in)); + status[rel[i]].stage = 1; //Request stage + status[rel[i]].changed = GetTimeNow(); + } + else if(polls[i].revents & POLLIN) + { + INT FromLen; + INT SvNum; + + FromLen = sizeof(struct sockaddr_in); + szLen = recvfrom(Master->Conn.s,(char*)szPacket,sizeof(szPacket), + 0,(struct sockaddr*)&Master->Conn.addr,&FromLen); + //Now we need to process data + pPtr = szPacket; + if(Master->PacketNum == 0) + { + //First packet have header which we want to skip + szLen -= 6; + MoveMemory(szPacket,szPacket+6,szLen); + } + + SvNum = (INT)(szLen/6); + //Check for ending mark + if(((NETADR_T*)szPacket+SvNum-1)->sin_addr.s_addr == 0) + { + //printf("got last packet\n"); + SvNum--; //We don't want to copy ending mark + //status[rel[i]].stage = 2; + status[rel[i]].stage = 2; + if(Master->Conn.s) + { + closesocket(Master->Conn.s); + Master->Conn.s = 0; + } + continue; + } else status[rel[i]].stage = 0; + if(SvNum == 0) continue; + + //Alocate space for server data and copy server + if(Master->ServerNum == 0) + Master->Servers = (NETADR_T*)malloc(SvNum*6); + else + { + Master->Servers = (NETADR_T*)realloc( + Master->Servers,(Master->ServerNum*6)+SvNum*6); + } + + CopyMemory(Master->Servers+Master->ServerNum,szPacket,SvNum*6); + Master->ServerNum += SvNum; + + //printf("got %d servers from %s:%d\n",SvNum,inet_ntoa(pMaster->Conn.addr.sin_addr), + // ntohs(pMaster->Conn.addr.sin_port)); + servers += SvNum; + status[rel[i]].changed = GetTimeNow(); + } + else if(polls[i].revents & (POLLHUP|POLLERR)) + { + status[rel[i]].stage = 2; + if(Master->Conn.s) + { + closesocket(Master->Conn.s); + Master->Conn.s = 0; + } + + //printf("POLLHUP|POLLERR\n"); + } + } + } + CurTime = GetTimeNow(); + for(i = 0; i < MasterNum; i++) + { + CONNSTATUS_T* pStat; + + pStat = &status[i]; + Delta = CurTime - pStat->changed; + if(Delta > 8) + { + //Get the fuck out! + pStat->stage = 2; + if(Masters[i].Conn.s) + { + closesocket(Masters[i].Conn.s); + Masters[i].Conn.s = 0; + } + } + } + } while(p_num != 0); + + return servers; +} + +VOID MasterFree(MASTER_T* Master) +{ + if(Master->ServerNum) + free(Master->Servers); +} + +BOOL IsServerAlreadyInList(NETADR_T* Servers,INT ServerNum,NETADR_T* Target) +{ + INT i; + + for(i = 0; i < ServerNum; i++) + { + if(!memcmp(Target,&Servers[i],sizeof(NETADR_T))) + return TRUE; + } + return FALSE; +} + +NETADR_T* GetUniqueServers(MASTER_T* pMasters,INT MasterNum,INT* pServerNum) +{ + INT i,j; + INT SvNum; + NETADR_T* pServers; + + SvNum = 0; + pServers = NULL; + for(i = 0; i < MasterNum; i++) + { + for(j = 0; j < pMasters[i].ServerNum; j++) + { + if(!IsServerAlreadyInList(pServers,SvNum,&pMasters[i].Servers[j])) + { + if(SvNum == 0) + { + pServers = (NETADR_T*)malloc(sizeof(NETADR_T)); + } + else + { + pServers = (NETADR_T*)realloc(pServers,sizeof(NETADR_T)*(SvNum+1)); + } + CopyMemory(&pServers[SvNum++],&pMasters[i].Servers[j],sizeof(NETADR_T)); + } + } + } + + *pServerNum = SvNum; + return pServers; +} + +VOID ParsePacket(PBYTE pPacket,SERVER_T* Server) +{ + PBYTE pCur; + INT iNum; + + pCur = pPacket; + + if(*(PINT)pCur != -1) + { + wcscpy(Server->szName,L"!!!!"); + return; + } + + pCur += 5; + pCur++; + MultiByteToWideChar(CP_UTF8,0,(LPCSTR)pCur,-1,Server->szName,MAX_PATH); + pCur += strlen((const char*)pCur)+1; + + MultiByteToWideChar(CP_UTF8,0,(LPCSTR)pCur,-1,Server->szMap,64); + pCur += strlen((const char*)pCur)+1; + + pCur += strlen((const char*)pCur)+1; //Skip folder + + MultiByteToWideChar(CP_UTF8,0,(LPCSTR)pCur,-1,Server->szGame,64); + pCur += strlen((const char*)pCur)+1; + + pCur += 2; //Skip ID + + Server->cPlayers = *pCur++; + Server->cMaxPlayers = *pCur++; + iNum = *pCur++; + Server->cPlayers += iNum; + Server->cType = *pCur++; + Server->cOS = *pCur++; + Server->cVisibility = *pCur++; + Server->cVAC = *pCur++; +} + +static BYTE szSourceQuery[] = "\xFF\xFF\xFF\xFFTSource Engine Query"; + +VOID QueryServers(NETADR_T* Addrs,SERVER_T* Servers,INT ServerNum, + AddServer_t AddServer,INT WaitTime) +{ + static WSAPOLLFD polls[SERVER_BLOCK]; + static CONNECTION_T m_conn[SERVER_BLOCK]; + static CONNSTATUS_T status[SERVER_BLOCK]; + static INT rel[SERVER_BLOCK]; + BYTE szPacket[1240]; + INT i,p_num; + LONG CurTime,Delta; + + ZeroMemory(status,sizeof(status)); + ZeroMemory(rel,sizeof(rel)); + + for(i = 0; i < ServerNum; i++) + { + m_conn[i].s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + MakeNonblocking(m_conn[i].s); + m_conn[i].addr.sin_family = AF_INET; + m_conn[i].addr.sin_addr = Addrs[i].sin_addr; + m_conn[i].addr.sin_port = Addrs[i].sin_port; + status[i].stage = 0; + status[i].changed = GetTimeNow(); + } + + do { + p_num = 0; + for(i = 0; i < ServerNum; i++) + { + if(status[i].stage == 2) continue; + polls[p_num].fd = m_conn[i].s; + if(status[i].stage == 0) + polls[p_num].events = POLLOUT; + else polls[p_num].events = POLLIN; + rel[p_num] = i; + p_num++; + } + + if(p_num == 0) break; + + //We need to be precisous, + //because everything based on timings + if(WSAPoll(polls,p_num,1000)) + { + for(i = 0; i < p_num; i++) + { + CONNECTION_T* pConn; + CONNSTATUS_T* pStat; + + pConn = &m_conn[rel[i]]; + pStat = &status[rel[i]]; + if(polls[i].revents & POLLOUT) + { + //Send request + sendto(pConn->s,(const char*)szSourceQuery,25, + 0,(const struct sockaddr*)&pConn->addr,sizeof(struct sockaddr_in)); + pStat->stage = 1; + //pStat->changed = GetTimeNow(); + } + else if(polls[i].revents & POLLIN) + { + INT FromLen; + + FromLen = sizeof(struct sockaddr_in); + recvfrom(pConn->s,(char*)szPacket,sizeof(szPacket),0, + (struct sockaddr*)&pConn->addr,&FromLen); + //Parse packet + ZeroMemory(Servers,sizeof(SERVER_T)); + Servers->Addr.sin_addr = pConn->addr.sin_addr; + Servers->Addr.sin_port = pConn->addr.sin_port; + + ParsePacket(szPacket,Servers); + Servers->IsValid = TRUE; + AddServer(Servers); + Servers++; + + closesocket(pConn->s); + pConn->s = 0; + pStat->stage = 2; + pStat->changed = GetTimeNow(); + } + else if(polls[i].revents & (POLLHUP|POLLERR)) + { + closesocket(pConn->s); + pConn->s = 0; + pStat->stage = 2; + } + } + } + CurTime = GetTimeNow(); + for(i = 0; i < ServerNum; i++) + { + if(status[i].stage == 2) continue; + + Delta = CurTime - status[i].changed; + if(Delta >= WaitTime) + { + //2 seconds no activity - get the fuck out! + status[i].stage = 2; + if(m_conn[i].s) + { + closesocket(m_conn[i].s); + m_conn[i].s = 0; + } + } + } + } while(p_num != 0); +} + +static BOOL RecvTimeOut(CONNECTION_T* pConn,PBYTE pPacket,INT Len,INT TimeOut) +{ + INT FromLen; + FromLen = sizeof(struct sockaddr_in); + recvfrom(pConn->s,(char*)pPacket,Len,0, + (struct sockaddr*)&pConn->addr,&FromLen); + return TRUE; +} + +static VOID ParseRules(PBYTE pMem,AddRule_t AddRule) +{ + PBYTE pPtr; + SHORT NumRules; + INT Len; + + pPtr = pMem; + pPtr += 5; + + NumRules = *(PSHORT)pPtr; + pPtr += 2; + + while(NumRules--) + { + Len = (INT)strlen((const char*)pPtr)+1; + AddRule((PCHAR)pPtr,(PCHAR)pPtr+Len); + pPtr += Len; + pPtr += strlen((const char*)pPtr)+1; + } +} + +static BOOL GetServerRules(CONNECTION_T* pConn,AddRule_t AddRule) +{ + static BYTE szPacket[2048]; + WSAPOLLFD poll; + PBYTE pMem; + PBYTE pPtr; + INT Len; + INT Stage; + UINT Challenge; + BOOL IsMultipacket; + INT Total,Current; + SHORT SwitchSize; + + //Stage 0 - send request with challenge + //Stage 1 - if challenge is -1, request again with challenge + // otherwise, start parsing multipackets + //Stage 2 - final + + Total = 0; + Current = 0; + SwitchSize = 0; + + pMem = NULL; + + Challenge = (UINT)-1; + Stage = 0; + do { + poll.fd = pConn->s; + if(Stage == 0) + poll.events = POLLOUT; + else poll.events = POLLIN; + + if(WSAPoll(&poll,1,3000)) + { + if(poll.revents & POLLOUT) + { + pPtr = szPacket; + *(PUINT)pPtr = (UINT)-1; + pPtr += 4; + + *pPtr++ = 0x56; + + *(PUINT)pPtr = (UINT)Challenge; + pPtr += 4; + + sendto(pConn->s,(const char*)szPacket,9,0, + (const struct sockaddr*)&pConn->addr,sizeof(struct sockaddr_in)); + Stage = 1; + } + else if(poll.revents & POLLIN) + { + INT FromLen; + + pPtr = szPacket; + + FromLen = sizeof(struct sockaddr_in); + Len = recvfrom(pConn->s,(char*)szPacket,sizeof(szPacket),0, + (struct sockaddr*)&pConn->addr,&FromLen); + + if(*(PUINT)pPtr == -1) + { + if(Challenge == -1) + { + pPtr += 5; + Challenge = *(PUINT)pPtr; + Stage = 0; //Request again + } + else //If it regular packet + { + IsMultipacket = FALSE; + pMem = (PBYTE)malloc(Len); + CopyMemory(pMem,szPacket,Len); + + Stage = 2; + } + } + else if(*(PUINT)pPtr == -2) //Multipacket + { + INT HdrSize; + + IsMultipacket = TRUE; + pPtr += 4; + if(*(PUINT)pPtr & 0x80000000) + { + //Compressed??! FUCK YOU! + return FALSE; + } + pPtr += 4; + if(!Total) Total = *pPtr; + pPtr++; + Current = *pPtr++; + + if(!SwitchSize) //Avoid exploit + SwitchSize = *(PSHORT)pPtr; + pPtr += 2; + + HdrSize = (INT)(pPtr-szPacket); + Len -= HdrSize; + + //Remove multipacket header + MoveMemory(szPacket,szPacket+HdrSize,Len); + + if(Current == 0 || !pMem) //If first + { + //Allocate memory + pMem = (PBYTE)calloc(Total,SwitchSize); + } + + //Copy memory at given index (current) + //Because UDP is unordered + if(Current < Total) + CopyMemory(pMem+SwitchSize*Current,szPacket,SwitchSize); + if(Current + 1 == Total) //Last packet + Stage = 2; + else Stage = 1; + } + } + } + else return FALSE; //Time out! + } while(Stage != 2); + + ParseRules(pMem,AddRule); + free(pMem); + return TRUE; +} + +BOOL GetServerInfo(NETADR_T* Addr,AddServer_t AddServer, + AddPlayer_t AddPlayer,AddRule_t AddRule) +{ + static BYTE szPacket[1240]; + CONNECTION_T m_conn; + PBYTE pPtr; + SERVER_T Server; + UINT Challenge; + + m_conn.s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + m_conn.addr.sin_family = AF_INET; + m_conn.addr.sin_addr = Addr->sin_addr; + m_conn.addr.sin_port = Addr->sin_port; + + //MakeNonblocking(m_conn.s); + + //Request info + + sendto(m_conn.s,(const char*)szSourceQuery,sizeof(szSourceQuery),0, + (const struct sockaddr*)&m_conn.addr,sizeof(struct sockaddr_in)); + if(RecvTimeOut(&m_conn,szPacket,sizeof(szPacket),3000)) + { + ParsePacket(szPacket,&Server); + AddServer(&Server); + } + else + { + closesocket(m_conn.s); + return FALSE; + } + + //Request players + Challenge = (UINT)-1; + + pPtr = szPacket; + *(PUINT)pPtr = (UINT)-1; + pPtr += 4; + *pPtr++ = 0x55; + *(PUINT)pPtr = Challenge; + pPtr += 4; + + //Got challenge + sendto(m_conn.s,(const char*)szPacket,9,0, + (const struct sockaddr*)&m_conn.addr,sizeof(struct sockaddr_in)); + if(RecvTimeOut(&m_conn,szPacket,sizeof(szPacket),3000)) + { + Challenge = *(PUINT)(szPacket+5); + } + else + { + closesocket(m_conn.s); + return FALSE; + } + + pPtr = szPacket; + *(PUINT)pPtr = (UINT)-1; + pPtr += 4; + *pPtr++ = 0x55; + *(PUINT)pPtr = Challenge; + pPtr += 4; + + sendto(m_conn.s,(const char*)szPacket,9,0, + (const struct sockaddr*)&m_conn.addr,sizeof(struct sockaddr_in)); + if(RecvTimeOut(&m_conn,szPacket,sizeof(szPacket),3000)) + { + //Parse players + BYTE PlayerNum; + PCHAR Name; + UINT Score; + FLOAT Duration; + INT i; + + pPtr = szPacket; + pPtr += 5; + PlayerNum = *pPtr++; + + for(i = 0; i < PlayerNum; i++) + { + pPtr++; + Name = (PCHAR)pPtr; + pPtr += strlen((const char*)pPtr)+1; + + Score = *(PUINT)pPtr; + pPtr += 4; + + Duration = *(PFLOAT)pPtr; + pPtr += 4; + + AddPlayer(Name,Score,Duration); + } + } + + MakeNonblocking(m_conn.s); + + //Parse rules + GetServerRules(&m_conn,AddRule); + closesocket(m_conn.s); + + return TRUE; +} \ No newline at end of file diff --git a/servertool/servertool.h b/servertool/servertool.h new file mode 100644 index 0000000..73ba0ec --- /dev/null +++ b/servertool/servertool.h @@ -0,0 +1,103 @@ +#ifndef __SERVERTOOL_H +#define __SERVERTOOL_H + +#include + +#define WINDOW_STYLE (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX) +#define POPUP_STYLE (WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX) + +#define WM_ENDMASTERSCAN (WM_USER+1) +//WPARAM - MasterNum +//LPARAM - Addrs +#define WM_ENDMASTERREQUEST (WM_USER+2) +//LPARAM - Param ptr +#define WM_ENDQUERYSERVERS (WM_USER+3) +//LPARAM - Server num +#define WM_ENDSERVERINFOREQUEST (WM_USER+4) + +#define SERVER_BLOCK 2048 + +#define MAX_MASTER_NUM 6 + +typedef struct { + HINSTANCE hInst; + INT nCmdShow; + HICON hIcon; + HICON hIconSm; +} APPLICATION_T; + +typedef struct { + SOCKET s; + struct sockaddr_in addr; +} CONNECTION_T; + +#pragma pack(push,1) +typedef struct { + IN_ADDR sin_addr; + SHORT sin_port; +} NETADR_T; +#pragma pack(pop) + +typedef struct { + CONNECTION_T Conn; + NETADR_T* Servers; + INT ServerNum; + INT PacketNum; +} MASTER_T; + +typedef struct { + LPCSTR lpszSearch; + struct sockaddr_in* addrs; + VOID (*AddServer)(struct sockaddr_in*); +} MASTERFINDPARAMETERS_T; + +typedef struct { + WCHAR szName[MAX_PATH]; + WCHAR szMap[64]; + WCHAR szGame[64]; + BYTE cPlayers; + BYTE cMaxPlayers; + BYTE cOS; + BYTE cType; + BYTE cVisibility; + BYTE cVAC; + BOOL IsValid; + NETADR_T Addr; +} SERVER_T; + +typedef struct { + IN char szSearch[MAX_PATH]; + IN MASTER_T* Masters; + IN INT MasterNum; + IN HWND hParent; + OUT NETADR_T* Servers; + OUT INT ServerNum; + SERVER_T* ServerList; +} MASTERREQUESTPARAMETERS_T; + +typedef struct { + WCHAR szName[32]; + INT Score; + FLOAT Duration; +} PLAYER_T; + +typedef VOID (*AddServer_t)(SERVER_T*); +typedef VOID (*AddPlayer_t)(PCHAR,UINT,FLOAT); +typedef VOID (*AddRule_t)(PCHAR,PCHAR); + +VOID FormatRequest(LPVOID lpBuf,NETADR_T* pAddr,LPSTR lpSearch,PSIZE_T pszLen); +VOID MakeNonblocking(SOCKET s); +BOOL FindMasterServers(MASTERFINDPARAMETERS_T* pParams); +INT DoMasterServers(MASTER_T* pMasters,INT MasterNum,LPCSTR lpSearch); +VOID MasterFree(MASTER_T* Master); +BOOL IsServerAlreadyInList(NETADR_T* Servers,INT ServerNum,NETADR_T* Target); +NETADR_T* GetUniqueServers(MASTER_T* pMasters,INT MasterNum,INT* pServerNum); +VOID ParsePacket(PBYTE pPacket,SERVER_T* Server); +VOID QueryServers(NETADR_T* Addrs,SERVER_T* Servers,INT ServerNum, + AddServer_t AddServer,INT WaitTime); +BOOL GetServerInfo(NETADR_T* Addr,AddServer_t AddServer, + AddPlayer_t AddPlayer,AddRule_t AddRule); + +extern APPLICATION_T g_App; + +#endif \ No newline at end of file diff --git a/servertool/servertool.rc b/servertool/servertool.rc new file mode 100644 index 0000000..7682401 Binary files /dev/null and b/servertool/servertool.rc differ diff --git a/servertool/servertool.vcxproj b/servertool/servertool.vcxproj new file mode 100644 index 0000000..7679c5f --- /dev/null +++ b/servertool/servertool.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {FF615871-8561-4F2E-9307-9CEA2E7BE381} + servertool + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + + + true + + + + + Level3 + Disabled + + + true + + + + + Level3 + MaxSpeed + true + true + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + WS2_32.lib;Comctl32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/servertool/servertool.vcxproj.filters b/servertool/servertool.vcxproj.filters new file mode 100644 index 0000000..0f61fcd --- /dev/null +++ b/servertool/servertool.vcxproj.filters @@ -0,0 +1,64 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;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 + + + + + Файлы исходного кода + + + Файлы исходного кода + + + Файлы исходного кода + + + Файлы исходного кода + + + Файлы исходного кода + + + + + Заголовочные файлы + + + Заголовочные файлы + + + Заголовочные файлы + + + Заголовочные файлы + + + Заголовочные файлы + + + + + Файлы ресурсов + + + Файлы ресурсов + + + + + Файлы ресурсов + + + \ No newline at end of file diff --git a/servertool/servertool.vcxproj.user b/servertool/servertool.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/servertool/servertool.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/servertool/x64/Release/CL.read.1.tlog b/servertool/x64/Release/CL.read.1.tlog new file mode 100644 index 0000000..02b4334 Binary files /dev/null and b/servertool/x64/Release/CL.read.1.tlog differ diff --git a/servertool/x64/Release/CL.write.1.tlog b/servertool/x64/Release/CL.write.1.tlog new file mode 100644 index 0000000..4fa38ea Binary files /dev/null and b/servertool/x64/Release/CL.write.1.tlog differ diff --git a/servertool/x64/Release/cl.command.1.tlog b/servertool/x64/Release/cl.command.1.tlog new file mode 100644 index 0000000..30add9d Binary files /dev/null and b/servertool/x64/Release/cl.command.1.tlog differ diff --git a/servertool/x64/Release/link-cvtres.read.1.tlog b/servertool/x64/Release/link-cvtres.read.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/servertool/x64/Release/link-cvtres.read.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/servertool/x64/Release/link-cvtres.write.1.tlog b/servertool/x64/Release/link-cvtres.write.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/servertool/x64/Release/link-cvtres.write.1.tlog @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/servertool/x64/Release/link.command.1.tlog b/servertool/x64/Release/link.command.1.tlog new file mode 100644 index 0000000..a2eac1a Binary files /dev/null and b/servertool/x64/Release/link.command.1.tlog differ diff --git a/servertool/x64/Release/link.read.1.tlog b/servertool/x64/Release/link.read.1.tlog new file mode 100644 index 0000000..c0b5a78 Binary files /dev/null and b/servertool/x64/Release/link.read.1.tlog differ diff --git a/servertool/x64/Release/link.write.1.tlog b/servertool/x64/Release/link.write.1.tlog new file mode 100644 index 0000000..7e5df22 Binary files /dev/null and b/servertool/x64/Release/link.write.1.tlog differ diff --git a/servertool/x64/Release/mt.command.1.tlog b/servertool/x64/Release/mt.command.1.tlog new file mode 100644 index 0000000..059f145 Binary files /dev/null and b/servertool/x64/Release/mt.command.1.tlog differ diff --git a/servertool/x64/Release/mt.read.1.tlog b/servertool/x64/Release/mt.read.1.tlog new file mode 100644 index 0000000..c2e942d Binary files /dev/null and b/servertool/x64/Release/mt.read.1.tlog differ diff --git a/servertool/x64/Release/mt.write.1.tlog b/servertool/x64/Release/mt.write.1.tlog new file mode 100644 index 0000000..09a55d6 Binary files /dev/null and b/servertool/x64/Release/mt.write.1.tlog differ diff --git a/servertool/x64/Release/rc.command.1.tlog b/servertool/x64/Release/rc.command.1.tlog new file mode 100644 index 0000000..a98dc11 Binary files /dev/null and b/servertool/x64/Release/rc.command.1.tlog differ diff --git a/servertool/x64/Release/rc.read.1.tlog b/servertool/x64/Release/rc.read.1.tlog new file mode 100644 index 0000000..4b755db Binary files /dev/null and b/servertool/x64/Release/rc.read.1.tlog differ diff --git a/servertool/x64/Release/rc.write.1.tlog b/servertool/x64/Release/rc.write.1.tlog new file mode 100644 index 0000000..ea991f9 Binary files /dev/null and b/servertool/x64/Release/rc.write.1.tlog differ diff --git a/servertool/x64/Release/servertool.Build.CppClean.log b/servertool/x64/Release/servertool.Build.CppClean.log new file mode 100644 index 0000000..c3184dd --- /dev/null +++ b/servertool/x64/Release/servertool.Build.CppClean.log @@ -0,0 +1,20 @@ +D:\serverbrowser\servertool\x64\Release\cl.command.1.tlog +D:\serverbrowser\servertool\x64\Release\CL.read.1.tlog +D:\serverbrowser\servertool\x64\Release\CL.write.1.tlog +D:\serverbrowser\servertool\x64\Release\link.command.1.tlog +D:\serverbrowser\servertool\x64\Release\link.read.1.tlog +D:\serverbrowser\servertool\x64\Release\link.write.1.tlog +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\MAIN.OBJ +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\MASTERSCAN.OBJ +D:\serverbrowser\servertool\x64\Release\mt.command.1.tlog +D:\serverbrowser\servertool\x64\Release\mt.read.1.tlog +D:\serverbrowser\servertool\x64\Release\mt.write.1.tlog +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\SERVERINFO.OBJ +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\SERVERLIST.OBJ +D:\serverbrowser\servertool\x64\Release\servertool.exe.intermediate.manifest +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\SERVERTOOL.OBJ +D:\serverbrowser\servertool\x64\Release\servertool.vcxprojResolveAssemblyReference.cache +D:\serverbrowser\servertool\x64\Release\servertool.write.1.tlog +D:\SERVERBROWSER\SERVERTOOL\X64\RELEASE\VC100.PDB +D:\SERVERBROWSER\X64\RELEASE\SERVERTOOL.EXE +D:\serverbrowser\x64\Release\servertool.pdb diff --git a/servertool/x64/Release/servertool.exe.intermediate.manifest b/servertool/x64/Release/servertool.exe.intermediate.manifest new file mode 100644 index 0000000..1c06b61 --- /dev/null +++ b/servertool/x64/Release/servertool.exe.intermediate.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/servertool/x64/Release/servertool.lastbuildstate b/servertool/x64/Release/servertool.lastbuildstate new file mode 100644 index 0000000..fde1e1c --- /dev/null +++ b/servertool/x64/Release/servertool.lastbuildstate @@ -0,0 +1,2 @@ +#v4.0:v100:false +Release|x64|D:\serverbrowser\| diff --git a/servertool/x64/Release/servertool.log b/servertool/x64/Release/servertool.log new file mode 100644 index 0000000..f322bde --- /dev/null +++ b/servertool/x64/Release/servertool.log @@ -0,0 +1,33 @@ +Построение начато 25.01.2020 9:51:48. + 1>Проект "D:\serverbrowser\servertool\servertool.vcxproj" в узле 2 (целевые объекты build). + 1>InitializeBuildStatus: + Создание "x64\Release\servertool.unsuccessfulbuild", так как было задано "AlwaysCreate". + ClCompile: + C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\CL.exe /c /Zi /nologo /W3 /WX- /O2 /Oi /GL /D _CRT_SECURE_NO_WARNINGS /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fo"x64\Release\\" /Fd"x64\Release\vc100.pdb" /Gd /TC /errorReport:prompt main.c masterscan.c serverinfo.c serverlist.c servertool.c + main.c + masterscan.c + serverinfo.c + serverlist.c + servertool.c + ResourceCompile: + Для всех выходных данных обновления не требуется. + Link: + C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:PROMPT /OUT:"D:\serverbrowser\x64\Release\servertool.exe" /NOLOGO WS2_32.lib Comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /ManifestFile:"x64\Release\servertool.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"D:\serverbrowser\x64\Release\servertool.pdb" /OPT:REF /OPT:ICF /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"D:\serverbrowser\x64\Release\servertool.lib" /MACHINE:X64 x64\Release\servertool.res + x64\Release\main.obj + x64\Release\masterscan.obj + x64\Release\serverinfo.obj + x64\Release\serverlist.obj + x64\Release\servertool.obj + Создание кода + Создание кода завершено + servertool.vcxproj -> D:\serverbrowser\x64\Release\servertool.exe + Manifest: + C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\mt.exe /nologo /verbose /outputresource:"D:\serverbrowser\x64\Release\servertool.exe;#1" /manifest x64\Release\servertool.exe.intermediate.manifest + FinalizeBuildStatus: + Файл "x64\Release\servertool.unsuccessfulbuild" удаляется. + Обращение к "x64\Release\servertool.lastbuildstate". + 1>Построение проекта "D:\serverbrowser\servertool\servertool.vcxproj" завершено (целевые объекты build). + +Построение успешно завершено. + +Затраченное время: 00:00:04.88 diff --git a/servertool/x64/Release/servertool.res b/servertool/x64/Release/servertool.res new file mode 100644 index 0000000..a188f03 Binary files /dev/null and b/servertool/x64/Release/servertool.res differ diff --git a/servertool/x64/Release/servertool.vcxprojResolveAssemblyReference.cache b/servertool/x64/Release/servertool.vcxprojResolveAssemblyReference.cache new file mode 100644 index 0000000..3f01681 Binary files /dev/null and b/servertool/x64/Release/servertool.vcxprojResolveAssemblyReference.cache differ diff --git a/servertool/x64/Release/servertool.write.1.tlog b/servertool/x64/Release/servertool.write.1.tlog new file mode 100644 index 0000000..e69de29 diff --git a/servertool/x64/Release/vc100.pdb b/servertool/x64/Release/vc100.pdb new file mode 100644 index 0000000..5256d38 Binary files /dev/null and b/servertool/x64/Release/vc100.pdb differ diff --git a/x64/Release/master.dat b/x64/Release/master.dat new file mode 100644 index 0000000..a45b179 --- /dev/null +++ b/x64/Release/master.dat @@ -0,0 +1 @@ +@4i@'i@4i@'i@Ai@Ai \ No newline at end of file diff --git a/x64/Release/servertool.exe b/x64/Release/servertool.exe new file mode 100644 index 0000000..c540796 Binary files /dev/null and b/x64/Release/servertool.exe differ diff --git a/x64/Release/servertool.pdb b/x64/Release/servertool.pdb new file mode 100644 index 0000000..c6e5b52 Binary files /dev/null and b/x64/Release/servertool.pdb differ