/**//* * For Yuki, without whom nothing would be much worth doing. * Copyright(c) 2007. BJTU_KAY All rights reserved. * The Program wil only output the running CS 1.5 Servers,(NOT for CS 1.6,CS Source or any higher version) * The Program will modify the file named "favsvrs.dat" which is under the root directory of your CS installation * The Program rewrites this file everytime it runs and updates the server list in this file. * This will save you from the trouble of adding servers by yourself. * And when you start the game and click "Internet Game", the running servers list will be right there,updated. * Any bug report or suggestion, please send email to: linuxyuking@gamil.com * Enjoy~! * * References: * Half-Life Protocol: http://www.int64.org/docs/gamestat-protocols/halflife.html * For the format the file "favsvrs.dat", find one under the root directory of your CS installation * A C# version which inspired my work can be found at: http://www.codeproject.com/csharp/gameserverinfo.asp */ #include<winsock2.h> #include<string.h> #include<stdio.h> #include<process.h> #include<time.h> #pragma comment(lib, "wsock32.lib") #define BufLength 256 //address sturcture typedef struct tagServer...{ char address[16]; int port; }Server,*PServer; //server info structure typedef struct tagGameServerInfo...{ char* serveraddress; char* hostname; char* mapname; int playernum; int maxplayers; int protocolver; char servertype; char serveros; int passworded; }GameServerInfo; //Global variables int threadNum; //the number of running threads int liveServNum; //the number of running CS server found GameServerInfo ResultSet[30];//store the searching results for the purpose of outputing in console int Query(Server* server,char* readbuf); char* ReadNextParam(char* readbuf,int* offset); char* FetchAddress(char* readbuf,int* offset); void ParseDetails(char* readbuf, GameServerInfo* gsinfo); void ThreadProc (void* pserv); void GenServer(PServer pserv,constchar* prefix, int third, int fourth); void SearchLoop(char*prefix, int first, int last,int finished, int total); void DisplayResult(); void DisplayChoice(); void reverse(char* s); char* int2str(int n); void SetColor(unsigned short ForeColor); /**//// Query a specific server ///<param name="server">The address structure of the gameserver</param> ///<param name="readbuf">store the responsed data if succeed</param> int Query(Server* server,char* readbuf) ...{ WSADATA wsaData; SOCKET fd; SOCKADDR_IN ServerAddr; constchar* querystring="details"; // query string int iResult; fd_set readfds; fd_set exceptfds; TIMEVAL timeOut; /**///// Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) ...{ printf("WSAStartup failed with error: %d ", iResult); return1; } // Create a SOCKET for connecting to server fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == INVALID_SOCKET) ...{ printf("socket failed with error: %ld ", WSAGetLastError()); WSACleanup(); return1; } //Fill in the sockaddr_in structure ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons((u_short)server->port); ServerAddr.sin_addr.s_addr = inet_addr(server->address); // Connect to server. iResult = connect( fd, (SOCKADDR*) &ServerAddr, sizeof(ServerAddr) ); if ( iResult == SOCKET_ERROR) ...{ closesocket (fd); //printf("Unable to connect to server: %ld ", WSAGetLastError()); WSACleanup(); return1; } /**///// Send a query iResult = send(fd, querystring, strlen(querystring), 0); if (iResult == SOCKET_ERROR) ...{ printf("send() failed with error: %d ", WSAGetLastError()); closesocket(fd); WSACleanup(); return1; } //printf("Bytes Sent: %d ", iResult); // shutdown the connection since no more data will be sent iResult = shutdown(fd, SD_SEND); if (iResult == SOCKET_ERROR) ...{ printf("shutdown failed with error: %d ", WSAGetLastError()); closesocket(fd); WSACleanup(); return1; } // Receive response data timeOut.tv_sec =1; // receive timeout timeOut.tv_usec =0; FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(fd,&exceptfds); FD_SET(fd,&readfds); iResult = select(fd+1, &readfds, NULL, &exceptfds, &timeOut); if(iResult==0) ...{ //printf("request timeout "); closesocket(fd); WSACleanup(); return1; } if(iResult==SOCKET_ERROR) ...{ printf("recv failed: %d ", WSAGetLastError()); closesocket(fd); WSACleanup(); return1; } if(FD_ISSET(fd,&exceptfds)) ...{ printf("recv failed."); closesocket(fd); WSACleanup(); return1; } iResult = recv(fd, readbuf,BufLength, 0); if ( iResult >0 ) ...{ //printf("Bytes received: %d ", iResult); closesocket(fd); WSACleanup(); return0; } elseif ( iResult ==0 ) ...{ printf("Connection closed "); closesocket(fd); WSACleanup(); return1; } else ...{ //printf("recv failed: %d ", WSAGetLastError()); closesocket(fd); WSACleanup(); return1; } } /**//// distill next string from responsed data ///<param name="readbuf">the responsed data</param> ///<param name="offset">start from where to read</param> char* ReadNextParam(char* readbuf,int* offset) ...{ char temp[30]; char* val; int pos=0; for (; (*offset) < BufLength; (*offset)++) ...{ if (readbuf[*offset] ==0) ...{ (*offset)++; break; } temp[pos++]=readbuf[*offset]; } temp[pos]='/0'; val=(char*)malloc(strlen(temp)+1); strcpy(val,temp); return val; } /**//// distill the first string which looks like "127.0.0.1:27015" from the responsed data /// and then cut down the last part which is the port ":27015" /// so the return value would be the address "127.0.0.1" char* FetchAddress(char* readbuf,int* offset) ...{ char*val,*tmp; int addresslen; tmp=ReadNextParam(readbuf,offset); addresslen=strlen(tmp)-6; val=(char*)malloc(addresslen+1); strncpy(val,tmp,addresslen); val[addresslen]='/0'; free(tmp); return val; } //parse the responsed data /**////<param name="readbuf">the responsed data</param> ///<param name="gsinfo">store details about the CS server</param> void ParseDetails(char* readbuf, GameServerInfo* gsinfo) ...{ int offset=5; gsinfo->serveraddress=FetchAddress(readbuf,&offset); gsinfo->hostname=ReadNextParam(readbuf,&offset); gsinfo->mapname=ReadNextParam(readbuf,&offset); ReadNextParam(readbuf,&offset); ReadNextParam(readbuf,&offset); //translate the rest byte by byte gsinfo->playernum=(int)readbuf[offset++]; gsinfo->maxplayers=(int)readbuf[offset++]; gsinfo->protocolver=(int)readbuf[offset++]; gsinfo->servertype=(char)readbuf[offset++]; gsinfo->serveros=(char)readbuf[offset++]; gsinfo->passworded=(int)readbuf[offset++]; } //reverse a string void reverse(char* s) ...{ int c,i,j; for(i=0,j=strlen(s)-1;i<j;i++,j--) ...{ c=s[i]; s[i]=s[j]; s[j]=c; } } //convert a int to a c-style string char* int2str(int n) ...{ char* val; char temp[5]; int i=0; do...{ temp[i++]=n%10+'0'; }while((n/=10)>0); temp[i]='/0'; reverse(temp); val=(char*)malloc(strlen(temp)+1); strcpy(val,temp); return val; } /**//// generate a Server structure using number ///<param name="pserv">store the generated Server structure</param> ///<param name="prefix">the first and second part of the IP address which is fixed</param> ///<param name="third">the third part of the IP address</param> ///<param name="fourth">the fourth part of the IP address</param> void GenServer(PServer pserv,constchar* prefix, int third, int fourth) ...{ char* thd=int2str(third); char* foh=int2str(fourth); pserv->address[0]='/0'; strcat(pserv->address,prefix); strcat(pserv->address,thd); strcat(pserv->address,"."); strcat(pserv->address,foh); free(thd); free(foh); pserv->port=27015; } // a single thread to process one Server void ThreadProc (void* pserv) ...{ char readbuf[BufLength]=...{0}; GameServerInfo gsi; PServer pserver=(Server*)pserv; // convert if(!Query(pserver,readbuf)) // query succeed ...{ ParseDetails(readbuf,&gsi); if(gsi.protocolver!=47) // only cs1.5 (protocol version for CS1.5 is 46, and CS 1.6 is 47 ) ...{ FILE *serverlist; ResultSet[liveServNum++]=gsi; //add the result to the result set //append a server details at the end of the "favsvrs.dat" file serverlist=fopen("favsvrs.dat","a"); fprintf(serverlist,"%s%s%s%s%s%s%s%d%s%d%s%d%s", " server { " ""address" "",gsi.serveraddress,"" " ""port" "27015" " ""name" "",gsi.hostname,"" " ""map" "",gsi.mapname,"" " ""game" "CounterStrike" " ""dir" "cstrike" " ""url" "" " ""dl" "" " ""maxplayers" "",gsi.maxplayers,"" " ""currentplayers" "",gsi.playernum,"" " ""protocol" "46" " ""favorite" "0" " ""ipx" "0" " ""mod" "0" " ""version" "1" " ""size" "0" " ""svtype" "l" " ""svos" "w" " ""password" "",gsi.passworded,"" " ""secure" "0" " ""svonly" "0" " ""cldll" "1" " ""lan" "0" " ""svping" "0.016388" " ""noresponse" "0" " ""packetloss" "100.000000" " ""status" "13" " ""filtered" "0" " ""fullmax" "0" " ""hlversion" "" " ""proxy" "0" " ""proxytarget" "0" " ""proxyaddress" "" }"); fclose(serverlist); } } free(pserver); //molloced before the thread start, free it now threadNum--; //decrease the number of running threads _endthread(); } //set the color of the output void SetColor(unsigned short ForeColor) ...{ HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hCon,ForeColor); } //output the searching result to the console void DisplayResult() ...{ int i; printf("____________________________________________________________________________ "); for(i=0;i<liveServNum;i++) ...{ printf("[%d] ",i); SetColor(4); if(ResultSet[i].passworded) printf(" * "); else printf(""); SetColor(3); if(strlen(ResultSet[i].hostname)>15) printf("Counter-Strike "); else printf("%-15s",ResultSet[i].hostname); SetColor(5); printf("%-40s",ResultSet[i].mapname); SetColor(6); printf("%d/%d ",ResultSet[i].playernum,ResultSet[i].maxplayers); SetColor(7); } printf("____________________________________________________________________________ "); } void DisplayChoice() ...{ char input[50],command[50]; int choice; printf("请输入想要加入的服务器序号(输入Q或q结束程序):"); do...{ scanf("%s",input); if(input[0]=='q'||input[0]=='Q') return; if(input[0]>'9'||input[0]<'0') ...{ choice=-1; printf("非法输入,请重新选择(输入Q或q结束程序):"); } else ...{ sscanf(input,"%d",&choice); if(choice>=liveServNum) ...{ choice=-1; printf("非法输入,请重新选择(输入Q或q结束程序):"); } } }while(choice<0); sprintf(command,"%s%s%s","cstrike.exe -console -game cstrike +connect ",ResultSet[choice].serveraddress,":27015"); system(command); } /**////<param name="prefix">the first and second part of the IP address which is fixed</param> ///<param name="first">the third part of the begin IP address</param> ///<param name="last">the third part of the end IP address</param> ///<param name="finished">the number of finished IP</param> ///<param name="total">total number of IP </param> void SearchLoop(char*prefix, int first, int last,int finished, int total) ...{ int i,j; int percents; SetColor(4); for(i=first;i<last+1;i++) ...{ for(j=1;j<255;j++) ...{ PServer pserv; pserv=(PServer)malloc(sizeof(Server));//critical. can't use local variables here //because it will be released before the thread using it finish GenServer(pserv,prefix,i,j); _beginthread(ThreadProc,64*1024, (void*)pserv);//start a new thread with the stack size set to 64K threadNum++; //increase the number of running threads if(threadNum>2000) Sleep(3000); //it is said that the max number of threads in Windows is 2000 //if the number of running threads excess 2000, //wait 3 second hoping that some of the running threads will finished } //show the percents completed if(i==first) ...{ printf(""); if(finished>0) printf(""); } else ...{ percents=(i-first+finished)*100/total;//last-1-first printf(""); printf("%2d%%",percents); } } } int main() ...{ char input[50]; int choice=1; int waittimes=0; FILE *ffav; clock_t start,finish; ffav=fopen("favsvrs.dat","w"); fprintf(ffav,"{"); fclose(ffav); SetColor(5); printf("北京交通大学CS局域网搜索程式 (Author:Kay Email:linuxyuking@gmail.com) "); printf("For Yuki, without whom nothing would be much worth doing. "); SetColor(7); printf("____________________________________________________________________________ "); printf( "$ 本程序只适用于CS1.5,请将本程序拷贝到CS1.5根目录下运行。 " "$ 程序会将搜索结果自动导入CS根目录下的'favsvrs.dat'文件, " " 即'进入游戏'->'网上对战'内的服务器列表,免去了您手动添加服务器的麻烦。 " " 您也可以直接通过本程序选择要加入的服务器快速开始游戏。 " "$ 由于本程序搜索过程会占用大量CPU资源,程序运行期间请尽量不要运行其他程序。 " ); printf("____________________________________________________________________________ "); SetColor(2); printf( " [0] 59.64.*.* " "[1] 59.65.*.* " "[2] 219.242.*.* "); SetColor(7); printf("请选择要搜索的IP段(0,1,2):"); do...{ gets(input); if(input[0]>'2'||input[0]<'0') ...{ choice=-1; printf("非法输入,请重新选择(0,1,2):"); } else choice=1; }while(choice<0); switch(input[0]) ...{ case'0': printf("开始搜索,已完成: "); start=clock(); // begin to time SearchLoop("59.64.",0,15,0,15); break; case'1': printf("开始搜索,已完成: "); start=clock(); // begin to time SearchLoop("59.65.",160,191,0,31); break; case'2': printf("开始搜索,已完成: "); start=clock(); // begin to time SearchLoop("219.242.",112,127,0,30); SearchLoop("219.242.",240,255,15,30); } SetColor(7); //wait until all threads finished. printf(" 等待所有线程结束,请稍候"); do ...{ if(waittimes>=3) break; //wait no more than 3 sec in case of the dead loop printf("."); Sleep(1000); waittimes++; }while(threadNum>0); ffav=fopen("favsvrs.dat","a"); fprintf(ffav," }"); fclose(ffav); finish=clock(); printf(" 搜索已完成,耗时: "); SetColor(4); printf("%f",((double)(finish-start))/CLOCKS_PER_SEC); SetColor(7); printf(" 秒."); printf(" 共搜索到 "); SetColor(4); printf("%d",liveServNum); SetColor(7); printf(" 个CS服务器."); if(liveServNum>0) ...{ printf(" 服务器列表已生成. "); DisplayResult(); DisplayChoice(); } else system("pause"); return0; }