// うんころりんこ〜♪
#include "stdafx.h"
#include <winsock2.h>
#include <list>
//------------------------------------------------------------------------
// マスターサーバアドレス
#define MASTER_SERVER_ADDRESS "207.173.177.11"
//#define MASTER_SERVER_ADDRESS "68.142.72.250"
//#define MASTER_SERVER_ADDRESS "69.28.151.162"
// マスターサーバポート
#define MASTER_SERVER_PORT 27011
// リージョンコード
#define FILTERING_REGION_CODE 0x04
// フィルタ
#define SERVER_FILTER \
"\\type\\d" \
"\\secure\\1" \
"\\gamedir\\hl2mp" \
/* "\\map\\dm_lockdown"*/ \
/* "\\linux\\1"*/ \
"\\empty\\1" \
/* "\\full\\1" */ \
/* "\\proxy\\1"*/ \
""
//------------------------------------------------------------------------
// 最大パケットサイズ
#define MAX_OF_PACKET_SIZE 1400
// 最大パケット数
#define MAX_OF_PACKET_COUNT 15
// 最大パケット番号
#define MAX_OF_PACKET_NUMBER (MAX_OF_PACKET_COUNT - 1)
// 受信バッファサイズ
#define RBUFFER_SIZE (MAX_OF_PACKET_SIZE * MAX_OF_PACKET_COUNT)
// 非分割データ識別コード
#define SINGLE_PACKET_CODE 0xFFFFFFFF
// 分割データ識別コード
#define DEVIDED_PACKET_CODE 0xFFFFFFFE
// タイムアウト時間
#define RECEIVE_TIMEOUT 2000
// 通信クラス
class CA2SCommunicator {
private:
// データバッファ
char *m_DataBuffer;
// 接続してる?
bool m_Connected;
// ソケット
SOCKET m_Socket;
// データサイズ
int m_DataSize;
// データインデクス
int m_DataIndex;
public:
// コンストラクタ
CA2SCommunicator() {
// つながてない
m_Connected = false;
// データねえよ
m_DataSize = 0;
// 予想されるデータ長だけ領域用意しちゃえ
m_DataBuffer = new char[RBUFFER_SIZE];
}
// デストラクタ
~CA2SCommunicator() {
if (m_Connected) {
// つながってんなら切断
Disconnect();
}
delete m_DataBuffer;
}
// サーバに接続
bool Connect(const char* hostAddress, int port);
// 切断
void Disconnect() {
closesocket(m_Socket);
WSACleanup();
m_Connected = false;
}
// つながってる?
bool HasConnected() {
return m_Connected;
}
// データあんの?
bool IsAvailableData() {
return (m_DataSize > 0);
}
// 送信
bool SendData(const char *buffer, int size, bool setHeader = true);
// 受信
bool ReceiveData();
private:
// データコピー
bool DataCopy(void *target, int size) {
if ((m_DataSize - m_DataIndex) < size) {
// もう取り出せない
return false;
}
memcpy(target, &m_DataBuffer[m_DataIndex], size);
m_DataIndex += size;
return true;
}
public:
// データ取り出し
template<class T>
bool GetData(T *data) {
return DataCopy(data, sizeof(T));
}
// string取り出し
bool GetString(char **data) {
static char tempChar[255];
wchar_t tempWchar[255];
memset(tempChar, 0, sizeof(tempChar));
memset(tempWchar, 0, sizeof(tempWchar));
if ((m_DataSize - (m_DataIndex + strlen(&m_DataBuffer[m_DataIndex]) + 1)) < 0) {
// もう取り出せない
return false;
}
// Unicode変換
MultiByteToWideChar(
CP_UTF8,
0,
&m_DataBuffer[m_DataIndex],
(signed)strlen(&m_DataBuffer[m_DataIndex]),
tempWchar,
sizeof(tempWchar)
);
// ANSI変換
WideCharToMultiByte(
CP_ACP,
WC_NO_BEST_FIT_CHARS,
tempWchar,
(signed)wcslen(tempWchar),
tempChar,
sizeof(tempChar),
NULL,
NULL
);
*data = tempChar;
m_DataIndex += (signed)(strlen(&m_DataBuffer[m_DataIndex]) + 1);
return true;
}
// データ表示
template<class T>
bool ShowData(char *caption, T *data) {
T tempData;
bool available = DataCopy(&tempData, sizeof(tempData));
if (available) {
printf(caption, tempData);
printf("\n");
}
if (data != NULL) {
*data = tempData;
}
return available;
}
// string表示
bool ShowString(char *caption, char **data) {
char* tempData;
bool available = GetString(&tempData);
if (available) {
printf(caption, tempData);
printf("\n");
}
if (data != NULL) {
*data = tempData;
}
return available;
}
};
// サーバに接続
bool CA2SCommunicator::Connect(const char* hostAddress, int port) {
if (m_Connected) {
printf("Connect:もう繋がってんだろバカ!\n");
}
// みなくていいけどWinSockのバージョン確認してみんよ
WORD wVersionRequested = MAKEWORD( 2, 2 );
WSADATA wsaData;
int err = WSAStartup( wVersionRequested, &wsaData );
if(err != 0){
printf("Connect:WSAStartupだめじゃん\n");;
return false;
}
// Socketくれよ
m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_Socket == INVALID_SOCKET ){
printf("Connect:socketだめじゃん\n");
return false;
}
// 受信タイムアウト時間設定
long timeout = RECEIVE_TIMEOUT;
setsockopt(m_Socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
// つなげれ
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.s_addr = inet_addr(hostAddress);
sockAddr.sin_port = htons(port);
err = connect(m_Socket, (const sockaddr *)&sockAddr, sizeof(sockAddr));
if(err == SOCKET_ERROR ){
printf("Connect:connectだめじゃん\n");
WSACleanup();
return false;
}
// (・∀・)つながたよ〜
m_Connected = true;
return true;
}
// 送信
bool CA2SCommunicator::SendData(const char *buffer, int size, bool setHeader) {
if (!m_Connected) {
printf("SendData:繋がってねえよボケ!\n");
}
int offset = 0;
long packetCode = SINGLE_PACKET_CODE;
char tempBuffer[MAX_OF_PACKET_SIZE];
memset(tempBuffer, 0, sizeof(tempBuffer));
// 送信バッファ作成
if (setHeader) {
// ヘッダ挿入の指示がある場合のみ
memcpy(tempBuffer, &packetCode, sizeof(packetCode));
offset += sizeof(packetCode);
}
memcpy(&tempBuffer[offset], buffer, size);
int err = send(m_Socket, tempBuffer, size + offset, 0);
if(err == SOCKET_ERROR ){
printf("SendData:sendだめじゃん\n");
return false;
}
return true;
}
// 受信
bool CA2SCommunicator::ReceiveData() {
if (!m_Connected) {
printf("ReceiveData:繋がってねえよボケ!\n");
}
// サイズ初期化
m_DataSize = 0;
// バッファ初期化
memset(m_DataBuffer, 0, RBUFFER_SIZE);
// 1つ目のパケット受信
long packetCode;
char tempBuffer[MAX_OF_PACKET_SIZE];
int dataSize = recv(m_Socket, tempBuffer, sizeof(tempBuffer), 0);
if (dataSize < (signed)sizeof(packetCode)) {
// 最低限4バイトないとだめ
printf("ReceiveData:受信データないよ\n");
return false;
}
// 先頭のコードくれや
memcpy(&packetCode, tempBuffer, sizeof(packetCode));
if (packetCode == SINGLE_PACKET_CODE) {
// 非分割データ
m_DataSize = dataSize - sizeof(packetCode);
memcpy(m_DataBuffer, &tempBuffer[sizeof(packetCode)], m_DataSize);
// データインデックス初期化
m_DataIndex = 0;
return true;
} else if (packetCode != DEVIDED_PACKET_CODE) {
// コードが腐ってる
printf("ReceiveData:分割コード腐ってる:%d\n", packetCode);
return false;
} else if (dataSize < (sizeof(long) * 2 + sizeof(char))) {
// 分割ヘッダは5バイトないとだめ
printf("ReceiveData:分割ヘッダサイズ不足\n");
return false;
}
// 第一分割データの情報コピー
long requestID = 0;
long totalPacket;
long currentPacket;
// 残りのパケットのデータを取る
for (int i = 0; i < MAX_OF_PACKET_COUNT; i++) {
if (i > 0) {
// 1番以降のパケットを受信(0番はもう取ってある)
dataSize = recv(m_Socket, tempBuffer, sizeof(tempBuffer), 0);
}
int offset = 0;
// 分割コード
memcpy(&packetCode, tempBuffer, sizeof(packetCode));
offset += sizeof(packetCode);
// 要求ID
long oldRequestID = requestID;
memcpy(&requestID, &tempBuffer[offset], sizeof(requestID));
offset += sizeof(requestID);
// 全パケット数
totalPacket = (tempBuffer[offset] & 0x0F);
// 現在のパケット番号
currentPacket = (tempBuffer[offset] & 0xF0) >> 4;
offset += sizeof(char);
if (currentPacket != i) {
printf("ReceiveData:パケット番号が狂ってる\n");
return false;
} else if (totalPacket < 2 || totalPacket <= currentPacket) {
printf("ReceiveData:パケット個数が狂ってる\n");
return false;
} else if ( i > 0 && oldRequestID != requestID) {
printf("ReceiveData:要求IDの異なるパケットが割り込んできた\n");
}
memcpy(&m_DataBuffer[m_DataSize], &tempBuffer[offset], (dataSize - offset));
m_DataSize += (dataSize - offset);
}
// データインデックス初期化
m_DataIndex = 0;
return true;
}
//------------------------------------------------------------------------
// MASTER SERVER QUERY
#define MASTER_SERVER_QUERY { \
'1', FILTERING_REGION_CODE, "0.0.0.0:0\0" SERVER_FILTER \
}
#define MASTER_SERVER_QUERY_RES00 0x66
#define MASTER_SERVER_QUERY_RES01 0x0A
// A2S_INFO
#define A2S_INFO { \
'T',"Source Engine Query" \
}
#define A2S_INFO_RES 'I'
//------------------------------------------------------------------------
// グローバル君
CA2SCommunicator g_Com;
//------------------------------------------------------------------------
// サーバIP
typedef struct {
unsigned char ip[4];
int port;
} GSERVER_IP;
//------------------------------------------------------------------------
// A2S_INFO
bool GetServerInfo() {
bool aborted = false;
// 送信
char request[] = A2S_INFO;
aborted = !g_Com.SendData(request, sizeof(request));
if (!aborted) {
// 受信
aborted = !g_Com.ReceiveData();
}
if (!aborted && g_Com.IsAvailableData()) {
// レスポンスコード
char responseCode = '\0';
aborted = !g_Com.GetData(&responseCode);
aborted = (aborted || responseCode != A2S_INFO_RES);
if (!aborted) {
int errCount = 0;
// サーバ情報を取り出し、表示
char tempChar;
short tempShort;
char *tempString;
errCount += !g_Com.ShowData("[バージョン] %02X", &tempChar);
errCount += !g_Com.ShowString("[サーバ名] %s", NULL);
errCount += !g_Com.ShowString("[マップ名] %s", NULL);
// 空読み
g_Com.GetString(&tempString); // ゲームディレクトリ
g_Com.GetString(&tempString); // ゲームの説明
g_Com.GetData(&tempShort); // AppID
// 人数
unsigned char player = 0;
unsigned char maxPlayer = 0;
errCount += !g_Com.GetData((char*)&player); // プレイヤー数
errCount += !g_Com.GetData((char*)&maxPlayer); // プレイヤー数
printf("[プレイヤー数] %d / %d\n", player, maxPlayer);
aborted = (errCount > 0);
}
}
return !aborted;
}
// めーいーんー
int _tmain(int argc, _TCHAR* argv[])
{
std::list<GSERVER_IP> serverIPList;
bool aborted = false;
//-------------------------------------
// マスターサーバ
//-------------------------------------
// 接続
aborted = !g_Com.Connect(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT);
if (!aborted) {
// 送信
char request[] = MASTER_SERVER_QUERY;
aborted = !g_Com.SendData(request, sizeof(request), false);
}
if (!aborted) {
// 受信
aborted = !g_Com.ReceiveData();
}
if (!aborted && g_Com.IsAvailableData()) {
// IP取込み
char tempChar;
long tempLong;
unsigned short tempUShort;
// レスポンスコード00
aborted = !g_Com.GetData(&tempChar);
aborted = (aborted || tempChar != MASTER_SERVER_QUERY_RES00);
// レスポンスコード01
aborted = !g_Com.GetData(&tempChar);
aborted = (aborted || tempChar != MASTER_SERVER_QUERY_RES01);
while(!aborted && g_Com.GetData(&tempLong)) {
//ip が全部0の場合は終了(データ終端)
if (tempLong == 0) {
break;
}
GSERVER_IP ip;
for (int i = 0; i < sizeof(ip.ip); i++) {
memcpy(&ip.ip[i], &tempLong, sizeof(char));
tempLong >>= 8;
}
if (g_Com.GetData((short*)&tempUShort)) {
// ポートだけはビッグエンディアン
ip.port = tempUShort & 0xFF;
ip.port <<= 8;
tempUShort >>= 8;
ip.port |= (tempUShort & 0xFF);
serverIPList.insert(serverIPList.end(), ip);
} else {
aborted = true;
}
}
}
// 切断
g_Com.Disconnect();
int index = 0;
for (std::list<GSERVER_IP>::iterator i = serverIPList.begin();
i != serverIPList.end() && !aborted; ++i, index++) {
char addressString[16];
GSERVER_IP ip = *i;
printf("サーバ %04d ------------------------------------------\n", index);
// アドレス
sprintf(addressString, "%d.%d.%d.%d", ip.ip[0], ip.ip[1], ip.ip[2], ip.ip[3]);
printf("ADDR=%s:%d\n", addressString, ip.port);
// 接続
aborted = !g_Com.Connect(addressString, ip.port);
if (!aborted) {
// 失敗しても続行させる
GetServerInfo();
}
// 切断
g_Com.Disconnect();
}
if (aborted) {
printf("エラーが発生しています。\n");
}
return 0;
}
|