View Javadoc

1   package net.sourceforge.queried.gametypes;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   
6   import net.sourceforge.queried.PlayerInfo;
7   import net.sourceforge.queried.QueriEd;
8   import net.sourceforge.queried.ScoreComparator;
9   import net.sourceforge.queried.ServerInfo;
10  import net.sourceforge.queried.Util;
11  
12  /**
13   * http://unreal.student.utwente.nl/UT2003-queryspec.html
14   *
15   * @author Jonas Berlin <xkr47@outerspace.dyndns.org>
16   */
17  public class UT2ServerInfo {
18  
19  	/**
20  	 * Read an integer and advance offset.
21  	 *
22  	 * @param str the full response string
23  	 * @param offsetHolder the offset holder
24  	 * @return the integer
25  	 */
26  	private static int getInt(String str, int[] offsetHolder) {
27  		int v =
28  			((str.charAt(offsetHolder[0] + 0) & 255) << 0) |
29  			((str.charAt(offsetHolder[0] + 1) & 255) << 8) |
30  			((str.charAt(offsetHolder[0] + 2) & 255) << 16) |
31  			((str.charAt(offsetHolder[0] + 3) & 255) << 24);
32  
33  		offsetHolder[0] += 4;
34  		return v;
35  	}
36  
37  	/**
38  	 * Read a string and advance offset. Any color codes are stripped.
39  	 *
40  	 * @param str the full response string
41  	 * @param offsetHolder the offset holder
42  	 * @return the string
43  	 */
44  	private static String getString(String src, int[] offsetHolder) {
45  		int len = src.charAt(offsetHolder[0]++);
46  		if(len == 0 || len == 128) {
47  			return "";
48  
49  		} else if(len > 0 && len <= 127) {
50  			String result = src.substring(offsetHolder[0], offsetHolder[0] + len - 1);
51  			offsetHolder[0] += len;
52  			return result;
53  
54  		} else if(len > 128 && len <= 255) {
55  			len -= 128;
56  			StringBuffer sb = new StringBuffer();
57  			for(int i=0; i<len-1; i++) {
58  				char ch = src.charAt(offsetHolder[0] + i*2);
59  				if(ch == '^' && src.charAt(offsetHolder[0] + i*2 + 2) == '#') {
60  					// color - skip it
61  					i += 2;
62  				} else {
63  					sb.append(ch);
64  				}
65  			}
66  			offsetHolder[0] += len * 2;
67  			return sb.toString();
68  
69  		} else {
70  			throw new RuntimeException("Bad data in response");
71  		}
72  	}
73   
74  	public static ServerInfo getDetails(int localPort, String ipStr, int port, int infoType, int queryType,
75          int gameType) {
76          
77  		if(queryType != QueriEd.QUERY_UT2S) {
78  			// fall back to gamespy etc
79  			return UTServerInfo.getDetails(localPort, ipStr, port, infoType, queryType, gameType);
80  		}
81  
82          String queryResult = 
83              Util.getInfo(localPort, ipStr, port, "\u0078\u0000\u0000\u0000\u0000", infoType, queryType, gameType);
84  
85  		if(queryResult == null || queryResult.length() == 0) return null;
86  
87  		ServerInfo serverInfo = new ServerInfo();
88  		try {
89  			// verify we got what we wanted
90  			int[] offsetHolder = { 0 };
91  			if(queryResult.charAt(0) == 128) offsetHolder[0] = 4;
92  			if(queryResult.charAt(offsetHolder[0]) != 0) return null;
93  			offsetHolder[0]++;
94  
95              serverInfo.setIp(ipStr);
96  
97  			getInt(queryResult, offsetHolder); // ignore server id
98  			getString(queryResult, offsetHolder); // ignore server ip (because it's not reliable)
99  			serverInfo.setPort(Integer.toString(getInt(queryResult, offsetHolder))); // ignore game port
100 			getInt(queryResult, offsetHolder); // ignore query port
101 			serverInfo.setName(getString(queryResult, offsetHolder)); // server name
102 			serverInfo.setMap(getString(queryResult, offsetHolder)); // map
103 			serverInfo.setGame(getString(queryResult, offsetHolder)); // game type
104 			serverInfo.setPlayerCount(Integer.toString(getInt(queryResult, offsetHolder))); // currently playing
105 			serverInfo.setMaxPlayers(Integer.toString(getInt(queryResult, offsetHolder))); // max players
106 			getInt(queryResult, offsetHolder); // ignore ping
107             serverInfo.setFullResponse(queryResult);
108 
109 		} catch(StringIndexOutOfBoundsException e) {
110 			return null;
111 		}
112 
113 		// attempt to get game version also.. it's available through
114 		// the game info query
115 
116         queryResult = 
117             Util.getInfo(localPort, ipStr, port, "\u0078\u0000\u0000\u0000\u0001", infoType, queryType, gameType);
118 
119 		if(queryResult == null || queryResult.length() == 0) return serverInfo;
120 
121 		try {
122 			// verify we got what we wanted
123 			int[] offsetHolder = { 0 };
124 			if(queryResult.charAt(0) == 128) offsetHolder[0] = 4;
125 			if(queryResult.charAt(offsetHolder[0]) == 1) {
126 				offsetHolder[0]++;
127 
128 				// loop through the key-value pairs
129 				while(offsetHolder[0] < queryResult.length()) {
130 					String key = getString(queryResult, offsetHolder);
131 					String value = getString(queryResult, offsetHolder);
132 					if(key.equals("ServerVersion")) {
133 						serverInfo.setGameVersion(value);
134 						break;
135 					}
136 				}
137 			}
138 
139 		} catch(StringIndexOutOfBoundsException e) {
140 			// on failure, jost continue without server version..
141 		}
142 
143 		return serverInfo;
144 	}
145 
146     public static ArrayList getPlayers(int localPort, String ipStr, int port, int infoType, int queryType,
147         int gameType) {
148             
149 		if(queryType != QueriEd.QUERY_UT2S) {
150 			// fall back to gamespy etc
151 			return UTServerInfo.getPlayers(localPort, ipStr, port, infoType, queryType, gameType);
152 		}
153 
154         String queryResult = 
155             Util.getInfo(localPort, ipStr, port, "\u0078\u0000\u0000\u0000\u0002", infoType, queryType, gameType);
156 
157 		if(queryResult == null || queryResult.length() == 0) return null;
158 
159         ArrayList playerInfo = new ArrayList();
160 		try {
161 			// verify we got what we wanted
162 			int[] offsetHolder = { 0 };
163 			if(queryResult.charAt(0) == 128) offsetHolder[0] = 4;
164 			if(queryResult.charAt(offsetHolder[0]) != 2) return null;
165 			offsetHolder[0]++;
166 
167 			// loop through the players
168 			while(offsetHolder[0] < queryResult.length()) {
169                 PlayerInfo player = new PlayerInfo();
170 
171 				getInt(queryResult, offsetHolder); // ignore player id
172 				player.setName(getString(queryResult, offsetHolder)); // player name
173 				getInt(queryResult, offsetHolder); // ignore player ping
174 				player.setScore(getInt(queryResult, offsetHolder)); // player score
175 				getInt(queryResult, offsetHolder); // ignore player stats id
176 				
177 				playerInfo.add(player);
178 			}
179 		} catch(StringIndexOutOfBoundsException e) {
180 			return null;
181 		}
182 
183 		Collections.sort(playerInfo, new ScoreComparator());
184 		return playerInfo;
185     }
186 }