DL: [URL]http://udhq.falconpunch.net/downloads/mods/UQS.zip[/URL]
From the README:
//********************************************************//
Unreal Query Suite (UQS)
by Pravin "Pcube" Prabhu
Ver: 1.0
Released: 8/22/10
NOTE:
To make use of this package, you need at least JRE v6.21. Download the latest JRE from http://java.com/en/download/index.jsp.
//********************************************************//
Abstract:
The Unreal Query Suite is a simple means for communicating with Unreal 1 servers.
//********************************************************//
For Non-Developers:
To run the UQS, simply execute run.bat. You may modify run.bat and change the commandline options if you wish. The available flags are:
-x - Turns debug mode on.
-o - Starts the UQS and immediately connects to the given Locator string.
By default, the UQS's bat file will immediately connect to the Unreal 1 master server and establish connections to every server individually. You can interact with the servers manually from the console.
For a description of available commands, type "help" in the console. You can get information about each individual command by typing "help ".
The interface allows you to send queries to individual servers and monitor replies. To check the connection table, type "conns". You will see that each connection is specified by a name, an address, and a CID (connection ID). To broadcast a query to all servers (like "\status\"), you can use the "broad " command (i.e. "broad \status\"). To send a query to an individual server, you can use the "send " command (i.e. "send 3 \info\").
As per the Unreal 1 UDP query protocol, the following queries are what "should" work with most servers. Note, however, that many servers won't reply to these queries for security reasons!
General Info:
\basic\ - Returns name of server, min ver, and ver of server
\info\ - Returns general info about the server
\status\ - Returns info about the server, players, map name, etc.
\players\ - Returns info about all of the players in the server
\rules\ - Reutrns info about the game's rules, mutators
Level Properties:
\level_property\property_name - Returns info about the property asked for
Game Properties: (usually blocked)
\game_property\property_name - Returns info about the property asked for
Player Properties: (usually blocked)
\player_property\property_name - Returns info about the property asked for
Misc:
\echo\ - Returns "echo" or "reply"
\secure\key - Returns the validation of the input variable, key
A note about 227 servers:
227 servers will smartly return information with the property requesting queries, in that, if you request a variable that is defined in a superclass of a class (i.e., you want to look at the game property "numplayers", which is defined in GameInfo), it will succeed. In subsequent versions of Unreal, the query will fail, since "numplayers" was not defined in the class being used (i.e. coopgame, jcoopz, xcoop, etc.). That being said, however, 227 servers automatically block most game and player property queries, anyway.
//********************************************************//
For Developers:
The main purpose of this package is to provide a framework for developers, so you can interface with Unreal 1 servers to gather information and communicate with them in a useful way. The concept revolves around what I call "Strategies". Internally, the QueryServer object (your main interface to the outside world) goes through a series of steps.
1.) The QueryServer communicates with the Unreal 1 master server, and gets the list of Unreal 1 servers.
2.) The QueryServer assigns each Unreal 1 server an individual connection.
2.a) Each Connection tries to determine the Unreal 1 server's GameVersion, GameType, and Name.
2.b) If the above times out after 30 seconds, then the connection is aborted.
2.c) If not, go to step 3.
3.) Each Connection that has successfully gotten its GameVersion, GameType, and Name, will ask the Settings class to determine what Strategy to implement as a back end to the Connection.
4.) After attaching a Strategy to the Connection, behavior is open-ended. That is, at this point, how the Unreal 1 server is transacted with is entirely user-defined.
There are two ways to use the UQS. If you are a developer, you can subclass the UnrealServerQueryStrategy class with your own implementation, overriding the stub methods. The methods you can override are:
//==================================//
/** Receive - Called when the remote server has sent a message.
* @param Message - The message received from the remote server.
*/
public abstract void Receive(String Message);
/** Begin - Called when the connection to the remote server has been completely established.
*/
public abstract void Begin();
/** End - Called when the connection to the remote server has been terminated.
*/
public abstract void End();
/** Timer - Called once, or periodically, depending on the initial call to QueryServer.SetTimer(). See QueryServer.SetTimer for more information.
*/
public abstract void Timer();
//==================================//
By providing definitions to the above methods in your subclass, you can fully specify the behavior of your Strategy under different scnearios. A simple implementation could, for example, talk to a server, ask it for the level property "Computer Name", and then log that to some file. This would be achieved by overriding Begin to call "Send("\\level_property\\computername");", and then listening for a reply with Receive.
The best way to get familiar with implementing your own Strategies is to first look at the example class: GenericUnrealServerQueryStrategy.
//==================================//
/* GenericUnrealServerQueryStrategy:
* This class serves as an example of how an UnrealServerQueryStrategy should be implemented.
*
* Points to note:
* - You MUST define a constructor similar to GenericUnrealServerQueryStrategy's. It must
* take in a DP_UnrealserverTransactor and call super, passing it in. Failure to do so
* renders this class useless and highly unstable.
*
* - Begin is called immediately after a connection is established, and the stream is usable.
*
* - End is called when a connection has been terminated. This is only called when the user
* manually closes the connection. There is no automatic timeout for the underlying connection.
* A good strategy to assess timeout, would be to use a recurring timer event to check for
* timeouts.
*
* - Receive is called whenever the underlying connection receives input data. Received data
* is guaranteed to end in /final/ as per the Unreal protocol's requirement. (i.e. you will
* receive a full packet every time, and do not have to do any internal buffering). You can
* easily automatically parse the query for a properties list if you use the static method
* ParseProperties in DP_UnrealServerTransactor.
*
* - Send is implicitly defined, here. You may use send to send a string or raw byte array. See
* superclass for declaration.
*
* - Timer is overridable, but is useless unless you use QueryServer.SetTimer(this,#,recurring)
* first.
*
* - Cancel the Timer event associated with this object when the connection closes. If Timer
* executes even after the underlying socket has closed, there is a high chance of unstable
* behavior.
*/
package UQS;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.TimerTask;
import UQS.Event.EEventType;
/** GenericUnrealServerQueryStrategy
* A generic strategy that can communicate with any Unreal server.
*
* The goal of this class is to provide a logic to talking with an Unreal 1 server. Internally,
* one may devise a simple AI that can make decisions based on what it learns about the server. Ultimately,
* the class is to dump the data it has gathered for post-processing elsewhere.
*/
public class GenericUnrealServerQueryStrategy extends UnrealServerQueryStrategy
{
//==================================================
// Internal variables
private TimerTask Timer;
private final Queue EventHistory; // List of events that this server has gone through
//==================================================
// Constructors
public GenericUnrealServerQueryStrategy(DP_UnrealServerTransactor Parent,UnrealServerInfo NewInfo)
{
super(Parent,NewInfo);
EventHistory = new LinkedList();
}
//==================================================
// Methods
/** Begin - Called when a connection has been successfully established.
*/
public void Begin()
{
EventHistory.add(new Event(EEventType.ET_ServerLaunched));
Timer = QueryServer.SetTimer(this,30,true);
}
/** End - Called when a connection has been terminated.
*/
public void End()
{
Timer.cancel();
}
/** Receive - Called when a full Unreal packet has been received. Packet is guaranteed to end in \final\ as per the Unreal query protocol.
* @param Message - Received message from remote server.
*/
public void Receive(String Message)
{
Console.Log(ServerInfo.GetHostName()+": "+Message);
// Recognize these property change events.
ArrayList PropList = DP_UnrealServerTransactor.ParseProperties(Message);
for( Property Prop : PropList )
{
// Log changes
if( ServerInfo.UpdateProperty(Prop) )
{
Console.Debug("Property change: "+Prop);
EventHistory.add(new Event(EEventType.ET_PropertyChanged, Prop));
}
}
}
/** Timer - Event that is called one-shot or recurringly after a call to QueryServer.SetTimer(this,#,recurring)
*/
public void Timer()
{
// (Insert latent code here)
// e.g. Send("\\info\\");
}
}
//==================================//
As you can see, there are several useful functions which you can take advantage of. The most useful functions at your disposal are probably ParseProperties (in DP_UnrealServerTransactor) and SetTimer (in QueryServer). Respectively, ParseProperties provides a simple way to extract all of the Properties of a given Unreal 1 query string, and SetTimer must be called in order to initialize Timer events.
Event objects are also at your disposal. An Event object is simply an object that records an event. You can create an Event object with one of three EventTypes - ServerLaunched, PropertyChanged, and ServerClosed. As in the above example, it may prove useful to create an Event queue, so you can keep track of how information about a server changes over time.
Each Strategy also has a ServerInfo associated with it. The ServerInfo is what holds all of the Properties of the server. To add to the ServerInfo object, call UpdateProperty. UpdateProperty will return True if the update was successful (the information is new, i.e. the variable is newly seen or has changed from a previous value), and False if the update was unncessary (nothing changed). This way, you can determine what Properties have recently changed, and assign Events to them, accordingly.
That's about it, but, make note that the package can be easily abstracted to interface with non-Unreal entities. (All that you would have to do is make your own DatagramProcessor subclass, and add an entry in the lookup function in Settings to interface with a different protocol) The design is pretty modular, so you could technically build your own infrastructure to communicate with an entirely different class of servers, or, communicate in a completely different way... just some food for thought.
//********************************************************//
Thanks:
Special thanks to...
Gigu (my fuzzle) for helping me debug parts of the UQS
Casey for helping me debs and properly interface with the Unreal Master Server
Jackrabbit for helping me debs random bugs with the UQS
No thanks at all to...
Dante for being useless
