QueryUT99

External tools, software and sites for creating or editing UT99 resources.
ferapontikov
Posts: 2
Joined: Wed Jan 22, 2025 3:08 pm
Personal rank: Adept

QueryUT99

Post by ferapontikov »

Hello. As the name suggests, this is another tool for obtaining information about a particular server.
I am an old user (programming is my hobby) in UT99 I have been playing for more than twenty years. Maybe. Someone remembers me, I played under the nickname Shvonder, Sword. In general, I often changed nicknames (I can't remember them all).
In recent years I haven't played because there was no opportunity - there were no servers. The ones that were available didn't suit me. Not long ago, it became so boring that I set a goal to find a suitable server and start playing on it, which successfully came true!
I found a server and I really liked it. Probably it existed before, but was unknown to me. Yes. As I was researching, I found out that the server is quite old and has a forum. I visited the forum - tried to register, but it didn't work (probably registration was disabled). I wanted to post this program (QueryUT99). So - I decided to leave it here.

The program is written in C++. The source code is attached. You can build it using VC 6.0 (or higher) or Borland C++ 5.x
This is what the request looks like when the server is empty:
Image
So, when there is a player/players on the server:
Image

If found players - message beep (play sound if found the file "Entered.wav").
Default query server port 7778.
Launch program without parameters: query the server 185.145.201.61, if the players are not played until the player is discovered.
The request interval is one and a half minutes. Will try to play sound if
the sound file ("Entered.wav") is located next to the program.
Exit "Escape", key "Delete" - cls (clear screen system).
QueryUT99.cpp

Code: Select all

/* QueryUT99.cpp
*
* Query server Unreal Tournament 1999.
* If found players - message beep
* (play sound if found the file "Entered.wav").
* Default query server port 7778.
* Launch program without parameters:
* query the server 185.145.201.61
* if the players are not played until the player
* is discovered.
* The request interval is one and a half minutes.
* Will try to play sound if the sound file "Entered.wav"
* is located next to the program.
* Exit "Escape", key "Delete" - cls (clear screen system).
*/

#include "QueryUT99.h"

char* GAppname="QueryUT99 -- v1.1";
WORD wAttributes;

void GeneralErrorMessage(char* svStr) {
	printf("%s\n",svStr);
	system("pause");
	exit(1);
}
void Usage()
{
	puts(" Usage:");
	printf(" QueryUT99 185.145.201.61\n");
	puts(" If no standart port (default 7778)");
	printf(" QueryUT99 127.0.0.1:31337\n");
	system("pause");
	exit(1);
}

char* ParseReply(char* svStr, char* svMatch)
{
	DWORD dwBytes=strlen(svStr);
	if (!dwBytes) return NULL;

	char* pBuffer=(char*)malloc(dwBytes+1);
	if (!pBuffer)
		GeneralErrorMessage("Failed allocate output buffer.");
	pBuffer[dwBytes]=0;
	strcpy(pBuffer,svStr);

	int x=1, nFound=0;
	char* ptr=(char*)strchr(pBuffer,'\\');
	
	while (ptr) {
		*ptr=0;
		if(!x) {
			if (!strcmpi(pBuffer,svMatch))
				nFound=1;
			++x;
		} else {
			if (nFound)
				break;
			x=0;
		}
		strcpy(pBuffer,ptr+1);
		ptr=(char*)strchr(pBuffer,'\\');
	}
	if (!nFound) {
		free(pBuffer);
		return NULL;
	}
	return pBuffer;
}

void ParsePlayers_UT99(char* svStr)
{
	DWORD dwBytes=strlen(svStr);
	if (!dwBytes) return;

	char* pBuffer=(char*)malloc(dwBytes+1);
	if (!pBuffer)
		GeneralErrorMessage("Failed allocate output buffer.");
	pBuffer[dwBytes]=0;
	strcpy(pBuffer,svStr);

	int x=1, nFound=0, nDone=0, nPlayers=0;

	char* ptr=(char*)
	strchr(pBuffer,'\\');

	while (ptr) {
		*ptr=0;
		if (!x) {
			if (strstr(pBuffer,"player_") ||
				strstr(pBuffer,"frags_") ||
				strstr(pBuffer,"ping_")) {
				nFound=1;
				printf("%s: ",pBuffer);
			}
			if (strstr(pBuffer,"mesh_")) {
				nFound=1;
				nDone=1;
				printf("%s: ",pBuffer);
			}
			++x;
		} else {
			if (*pBuffer) {
				if (nFound) {
					nFound=0;
					if (!nDone)
					{
		wAttributes = savecolor();
		setcolor(WHITE);
						printf("%s ",pBuffer);
		restorecolor(wAttributes);
					}
					else
					{
						printf("%s\n",pBuffer);
						nDone=0;
						++nPlayers;
					}
					printf("\n");
				}
			}
			x=0;
		}
		strcpy(pBuffer,ptr+1);
		ptr=(char*)strchr(pBuffer,'\\');
	}
	if (nPlayers>0)
	{
		wAttributes = savecolor();
		setcolor(WHITE);
		printf("--- %d Players\n",nPlayers);
		restorecolor(wAttributes);
	}
}

int ParseStatus_UT99(char* svStr)
{
	int nNumplayers=0;
	while (1) {
		unsigned char c=*svStr;
		if (c>=32 && c<127)
			break;
		*svStr++;
	}

	/* Get "hostname" */
	char* svHostname=ParseReply(svStr,"hostname");
	if (svHostname) {
		printf("hostname: %s\n",svHostname);
		char svBuff[512];
		sprintf(svBuff,"%s %s",GAppname,svHostname);
		SetConsoleTitle(svBuff);
	}

	/* Get "gamever" */
	char* svGamever=ParseReply(svStr,"gamever");
	if (svGamever) {
		printf("gamever: ");
		wAttributes = savecolor();
		setcolor(LIGHTGREEN);
		printf("%s\n",svGamever);
		restorecolor(wAttributes);
	}

	/* Get "minnetver" */
	printf("minnetver: %s\n",ParseReply(svStr,"minnetver"));

	/* Get "hostport" */
	printf("hostport: %s\n",ParseReply(svStr,"hostport"));

	/* Get "mapname" */
	printf("mapname: %s\n",ParseReply(svStr,"mapname"));

	/* Get "maptitle" */
	char* svMaptitle=ParseReply(svStr,"maptitle");
	if (svMaptitle)
		printf("maptitle: %s\n",svMaptitle);

	/* Get "gametype" */
	printf("gametype: %s\n",ParseReply(svStr,"gametype"));

	/* Get "numplayers" */
	char* svNumplayers=ParseReply(svStr,"numplayers");
	if (svNumplayers) {
		printf("numplayers: %s\n",svNumplayers);
		nNumplayers=atoi(svNumplayers);
	}

	/* Get "maxplayers" */
	printf("maxplayers: %s\n",ParseReply(svStr,"maxplayers"));

	/* Get "gamemode" */
	printf("gamemode: %s\n",ParseReply(svStr,"gamemode"));

	/* Get "mutators" */
	printf("mutators: %s\n",ParseReply(svStr,"mutators"));

	/* Get "timelimit" */
	printf("timelimit: %s\n",ParseReply(svStr,"timelimit"));

	/* Get "changelevels" */
	printf("changelevels: %s\n",ParseReply(svStr,"changelevels"));

	/* Get "gamestyle" */
	printf("gamestyle: %s\n",ParseReply(svStr,"gamestyle"));

	/* Get "AdminName" */
	char* svAdminName=ParseReply(svStr,"AdminName");
	if (svAdminName)
		printf("AdminName: %s\n",svAdminName);

	/* Get "AdminEMail" */
	char* AdminEMail=ParseReply(svStr,"AdminEMail");
	if (AdminEMail)
		printf("AdminEMail: %s\n",AdminEMail);

	/* Get "queryid" */
	printf("queryid: %s\n\n",ParseReply(svStr,"queryid"));
	return nNumplayers;
}

int Timeout(SOCKET sock) {
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(sock,&fds);
	struct timeval tv;
	tv.tv_usec=0;
	tv.tv_sec=TIMEOUT;
	int nRet=select(sock+1,&fds,NULL,NULL,&tv);
	if (nRet<0 || !nRet)
		return -1;
	return 0;
}

u_long GetHostAddress(char* svHostname) {
	u_long Addr=inet_addr(svHostname);
	if (INADDR_NONE==Addr) {
		struct hostent* h;
		h=gethostbyname(svHostname);
		if (h) Addr=*(u_long*)h->h_addr;
	}
	return Addr;
}

int CheckIp(char* svStr)
{
	int count=0;
	if (*svStr>='0' && *svStr<='9') {
		int len=strlen(svStr);
		char* svTemp=(char*)malloc(len+1);
		if (!svTemp)
			GeneralErrorMessage("Failed allocate memory.");
		svTemp[len]=0;
		strcpy(svTemp,svStr);

		char *ptr, *s;
		s=svTemp;
		ptr=(char*)strchr(s,'.');
		while (ptr) {
			*ptr=0;
			++count;
			s=ptr+1;
			if (*s>='0' && *s<='9')
				ptr=(char*)strchr(s,'.');
			else {
				count=0;
				break;
			}
		}
		if (3!=count)
			count=0;
		free(svTemp);
	}
	return count;
}

void setcolor(WORD color) {
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);
}

void gotoxy(SHORT x, SHORT y) {
	COORD coord;
	coord.X=x;
	coord.Y=y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}

WORD savecolor(void)
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	WORD wAttributes = LIGHTGRAY;
	if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
		wAttributes = csbi.wAttributes;
	return wAttributes;
}

void restorecolor(WORD wAttributes) {
	/* Set the attribute to the original. */
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
		wAttributes);
}

char* TimeCalc(int nTotal) {
	static char svBuff[64];
	int minutes=nTotal / 60;
	int seconds=nTotal % 60;
	int hours=minutes / 60;
	minutes=minutes % 60;
	if (!hours) {
		if (!minutes)
			sprintf(svBuff,"%d secs",seconds);
		else
			sprintf(svBuff,"%02d:%02d",minutes,seconds);
	}
	else
		sprintf(svBuff,"%2d:%02d:%02d",hours,minutes,seconds);
	return svBuff;
}

char* appBaseDir() {
	static char svBuff[MAX_PATH];
	if (!svBuff[0]) {
		GetModuleFileName(GetModuleHandle(NULL),svBuff,MAX_PATH);
		int i;
		for (i=lstrlen(svBuff)-1;i>0;--i) {
			if ('\\'==svBuff[i-1] || '/'==svBuff[i-1])
			break;
		}
		svBuff[i]=0;
	}
	return svBuff;
}

/*
* Entry point.
*/
void main(int argc, char* argv[])
{
	int nPortDest=7778; /* Default query server port. */
	char svBuff[1024];
	char svHostname[256];
	svBuff[0]=svHostname[0]=0;

	SetConsoleTitle(GAppname);
	if (argc<2)
		strcpy(svHostname,"185.145.201.61");
	else {
		for (int x=1;x<argc;++x) {
			if (!svBuff[0])
			{
				if (0==strcmpi(argv[x],"-?") ||
					0==strcmpi(argv[x],"-HELP"))
				{
					Usage();
				}
				strcpy(svBuff,argv[x]);
				continue;
			}
		} /* End "for" */

		if (strstr(svBuff,":"))
		{
			char* svTemp=svBuff;
			char* ptr=strchr(svTemp,':');
			while (ptr) {
				*ptr=0;
				if (*svTemp)
					strcpy(svHostname,svTemp);
				svTemp=ptr+1;
				ptr=strchr(svTemp,':');
			} /* End "while" */

			nPortDest=atoi(svTemp);
			if (7777==nPortDest || 0==nPortDest)
				nPortDest=7778;
		}
		else
			strcpy(svHostname,svBuff);

		if (nPortDest>0xFFFF) /* 65535 */
		{
			sprintf(svBuff,
				"Failed the port number \"%d\".\n"
				"Maximum the port number 65535.",
				nPortDest);
			GeneralErrorMessage(svBuff);	
		}

		if (!svHostname[0]) {
			printf("Failed. Missing hostname.\n");
			Usage();
		}
		if (!CheckIp(svHostname)) {
			printf("Failed hostname.\n");
			Usage();
		}
	}

	/* Level #1 */
	WSADATA wsadata;
	if (WSAStartup(MAKEWORD(2,2),&wsadata))
		GeneralErrorMessage("Error initializing winsock.");
AGAIN:
	/* Resolving host. */
	struct sockaddr_in ToAddr;
	ToAddr.sin_family=AF_INET;
	ToAddr.sin_port=htons((WORD)nPortDest);
	ToAddr.sin_addr.s_addr=GetHostAddress(svHostname);

	if (INADDR_NONE==ToAddr.sin_addr.s_addr) {
		sprintf(svBuff,"Failed resolving hostname \"%s\"",svHostname);
		GeneralErrorMessage(svBuff);
	}
	SOCKET sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if (INVALID_SOCKET==sock)
		GeneralErrorMessage("Failed create socket.");

	time_t curtime=time(NULL);
	char* svTime=ctime(&curtime);
	if ('\n'==svTime[strlen(svTime)-1])
		svTime[strlen(svTime)-1]=0;
	printf("---[ %s port: %d, %s ]---\r\n",inet_ntoa(ToAddr.sin_addr),nPortDest,svTime);

	/* Current "\\status\\"
	* or: "\\basic\\", "\\info\\", "\\players\\"
	*/
	strcpy(svBuff,"\\status\\");
	int nLen=strlen(svBuff);

	int nRet=sendto(sock,svBuff,nLen,0,(struct sockaddr*)&ToAddr,sizeof(ToAddr));
	if (nRet<0) {
		closesocket(sock);
		GeneralErrorMessage("Failed SendTo.");
	}
	if (Timeout(sock)<0) {
		closesocket(sock);
		GeneralErrorMessage("Failed socket timeout, no reply received.");
	}
	nRet=recvfrom(sock,svBuff,1024,0,NULL,NULL);
	if (nRet<0) {
		closesocket(sock);
		GeneralErrorMessage("Failed receive data.");
	}
	svBuff[nRet]=0;

	int nNumplayers=ParseStatus_UT99(svBuff);
	if (nNumplayers)
	{
		ParsePlayers_UT99(svBuff);
		closesocket(sock);
		WSACleanup();

		/* Play sound if found "Entered.wav". */
		char svFilename[MAX_PATH];
		wsprintf(svFilename,"%Entered.wav",appBaseDir());
		WIN32_FIND_DATA FindFile;
		HANDLE handle=FindFirstFile(svFilename,&FindFile);
		if (INVALID_HANDLE_VALUE!=handle) {
			FindClose(handle);
			if (!PlaySound(svFilename,NULL,SND_FILENAME|SND_NODEFAULT|SND_NOWAIT|SND_ASYNC))
				printf("Unable to play sound.\n");
		}

		system("pause");
		exit(1);
	}
	else
	{
		wAttributes = savecolor();
		setcolor(LIGHTRED);
		printf("--- NO players.\n");
		restorecolor(wAttributes);
	}
	puts("");
	MSG msg;
	nRet=3000;
	while (nRet)
	{
		/* Press key "Escape"? */
		SHORT ptr=GetAsyncKeyState(VK_ESCAPE);
		if (ptr)
			exit(1);

		/* Press key "Delete"? */
		ptr=GetAsyncKeyState(VK_DELETE);
		if (ptr)
			system("cls");

		Sleep(20);
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		--nRet;
		//printf("\rNext launch query left %d", nRet);
		printf("\rNext launch query left %s",TimeCalc(nRet));
	}
	puts("");
	__asm jmp AGAIN
}
QueryUT99.h

Code: Select all

/* QueryUT99.h */

#pragma once

#ifdef _MSC_VER
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"winmm.lib")
#endif

#include <windows.h>
#include <time.h>
#include <stdio.h>

#ifndef _MSC_VER
#pragma option -w-aus
#pragma option -w-par
#endif

#define	TIMEOUT 3

enum COLORS {
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};

void setcolor(WORD color);
void gotoxy(SHORT x, SHORT y);
WORD savecolor(void);
void restorecolor(WORD wAttributes);
You do not have the required permissions to view the files attached to this post.
Last edited by ferapontikov on Thu Jan 23, 2025 12:15 pm, edited 1 time in total.
User avatar
UTPe
Masterful
Posts: 605
Joined: Sun Jul 12, 2009 7:10 pm
Personal rank: Dude
Location: Trieste, Italy

Re: QueryUT99

Post by UTPe »

hello,
this is interesting but imho it would be nice to add a GUI (or a kind of) to read better server data.
I'm not a programmer so I don't know how much time this work could take.

cheers,
Pietro
Personal map database: http://www.ut99maps.net

"These are the days that we will return to one day in the future only in memories." (The Midnight)
ferapontikov
Posts: 2
Joined: Wed Jan 22, 2025 3:08 pm
Personal rank: Adept

Re: QueryUT99

Post by ferapontikov »

UTPe,
Personally, I'm unlikely to do this. It's just not very interesting anymore. Besides, there are already many such programs, for example UT99Query (it's here on this site).
I posted this program because I couldn't do it where I wanted. I thought - will it be useful to someone?
I don't know. Although, if you're really interested in it - I could come up with something over time? :)
User avatar
UTPe
Masterful
Posts: 605
Joined: Sun Jul 12, 2009 7:10 pm
Personal rank: Dude
Location: Trieste, Italy

Re: QueryUT99

Post by UTPe »

I know several server query programs have been published in the past (for years I've been using UTSpy) and I guess many users still use ut99 internal server browser to check traffic on their favourite online servers.
maybe, to do something new and also useful, it could be interesting to build a new server query showing the best features seen in other programs. just my 2cents.
Personal map database: http://www.ut99maps.net

"These are the days that we will return to one day in the future only in memories." (The Midnight)