// うんころりんこ〜♪
#include "stdafx.h"
#include <winsock2.h>
//------------------------------------------------------------------------
// サーバアドレス
#define SERVER_ADDRESS "127.0.0.1"
// サーバポート
#define SERVER_PORT 27015
//------------------------------------------------------------------------
// 最大パケットサイズ
#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;
}
//------------------------------------------------------------------------
// A2S_SERVERQUERY_GETCHALLENGE
#define A2S_SERVERQUERY_GETCHALLENGE { \
'W' \
}
#define A2S_SERVERQUERY_GETCHALLENGE_RES 'A'
// A2S_INFO
#define A2S_INFO { \
'T',"Source Engine Query" \
}
#define A2S_INFO_RES 'I'
// A2S_PLAYER
#define A2S_PLAYER { \
'U', 0x00, 0x00, 0x00, 0x00 \
}
#define A2S_PLAYER_RES 'D'
// A2S_RULES
#define A2S_RULES { \
'V', 0x00, 0x00, 0x00, 0x00 \
}
#define A2S_RULES_RES 'E'
//------------------------------------------------------------------------
// グローバル君
CA2SCommunicator g_Com;
//------------------------------------------------------------------------
// A2S_SERVERQUERY_GETCHALLENGE
bool GetChallengeNumber(long &challengeNumber) {
bool aborted = false;
// 送信
char request[] = A2S_SERVERQUERY_GETCHALLENGE;
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_SERVERQUERY_GETCHALLENGE_RES);
if (!aborted) {
// 問い合わせ番号取得
aborted = !g_Com.ShowData("[問い合わせ番号] %08X", &challengeNumber);
// g_Com.GetData(&challengeNumber);
}
}
return !aborted;
}
// 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;
errCount += !g_Com.ShowData("[バージョン] %02X", &tempChar);
errCount += !g_Com.ShowString("[サーバ名] %s", NULL);
errCount += !g_Com.ShowString("[マップ名] %s", NULL);
errCount += !g_Com.ShowString("[ゲームディレクトリ] %s", NULL);
errCount += !g_Com.ShowString("[ゲームの説明] %s", NULL);
errCount += !g_Com.ShowData("[AppID] %d", &tempShort);
errCount += !g_Com.ShowData("[プレイヤー数] %d", &tempChar);
errCount += !g_Com.ShowData("[最大プレイヤー数] %d", &tempChar);
errCount += !g_Com.ShowData("[BOT数] %d", &tempChar);
errCount += !g_Com.ShowData("[サーバ稼動モード識別] %c", &tempChar);
errCount += !g_Com.ShowData("[OS識別] %c", &tempChar);
errCount += !g_Com.ShowData("[パスワード要求] %02X", &tempChar);
errCount += !g_Com.ShowData("[セキュリティー適用] %02X", &tempChar);
errCount += !g_Com.ShowString("[ゲームバージョン] %s", NULL);
aborted = (errCount > 0);
}
}
return !aborted;
}
// A2S_PLAYER
bool GetPlayer(long challengeNumber) {
bool aborted = false;
// 送信
char request[] = A2S_PLAYER;
memcpy(&request[sizeof(char)], &challengeNumber, sizeof(challengeNumber));
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_PLAYER_RES);
if (!aborted) {
char playerCount = 0;
int errCount = 0;
// サーバ情報を取り出し、表示
char tempChar;
long tempLong;
float tempFloat;
errCount += !g_Com.ShowData("[プレイヤー数] %d", &playerCount);
for (int i = 0; i < playerCount && errCount == 0; i++) {
errCount += !g_Com.ShowData(" [プレイヤー番号] %d", &tempChar);
errCount += !g_Com.ShowString(" [プレイヤー名] %s", NULL);
errCount += !g_Com.ShowData(" [フラッグ数] %d", &tempLong);
errCount += !g_Com.ShowData(" [接続時間] %.1f", &tempFloat);
}
aborted = (errCount > 0);
}
}
return !aborted;
}
// A2S_RULES
bool GetRules(long challengeNumber) {
bool aborted = false;
// 送信
char request[] = A2S_RULES;
memcpy(&request[sizeof(char)], &challengeNumber, sizeof(challengeNumber));
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_RULES_RES);
if (!aborted) {
short ruleCount = 0;
int errCount = 0;
// サーバ情報を取り出し、表示
errCount += !g_Com.ShowData("[ルール数] %d", &ruleCount);
for (int i = 0; i < ruleCount && errCount == 0; i++) {
errCount += !g_Com.ShowString(" [ルール名称] %s", NULL);
errCount += !g_Com.ShowString(" [ルール設定値] %s", NULL);
}
aborted = (errCount > 0);
}
}
return !aborted;
}
// めーいーんー
int _tmain(int argc, _TCHAR* argv[])
{
// 問い合わせ番号
long challengeNumber;
// 接続
bool aborted = !g_Com.Connect(SERVER_ADDRESS, SERVER_PORT);
//-------------------------------------
// A2S_SERVERQUERY_GETCHALLENGE
//-------------------------------------
if (!aborted) {
printf("《《A2S_SERVERQUERY_GETCHALLENGE》》\n");
aborted = !GetChallengeNumber(challengeNumber);
}
//-------------------------------------
// A2S_INFO
//-------------------------------------
if (!aborted) {
printf("《《A2S_INFO》》\n");
aborted = !GetServerInfo();
}
//-------------------------------------
// A2S_PLAYER
//-------------------------------------
if (!aborted) {
printf("《《A2S_PLAYER》》\n");
aborted = !GetPlayer(challengeNumber);
}
//-------------------------------------
// A2S_RULES
//-------------------------------------
if (!aborted) {
printf("《《A2S_RULES》》\n");
aborted = !GetRules(challengeNumber);
}
// 切断
g_Com.Disconnect();
if (aborted) {
printf("エラーが発生しています。\n");
}
return 0;
}
|