00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package com.realtime.crossfire.jxclient.metaserver;
00023
00024 import com.realtime.crossfire.jxclient.util.NumberParser;
00025 import java.io.IOException;
00026 import java.util.regex.Pattern;
00027 import org.jetbrains.annotations.NotNull;
00028 import org.jetbrains.annotations.Nullable;
00029
00034 public class MetaserverEntryParser {
00035
00039 @NotNull
00040 private static final String UNKNOWN_VERSION = "?";
00041
00045 @NotNull
00046 private static final String DEFAULT_BASE = "not specified";
00047
00051 @NotNull
00052 private static final Pattern FIELD_SEPARATOR_PATTERN = Pattern.compile("\\|");
00053
00057 private boolean inSection = false;
00058
00062 private int updateSeconds = 0;
00063
00067 @Nullable
00068 private String hostname = null;
00069
00073 private int players = 0;
00074
00078 @NotNull
00079 private String version = UNKNOWN_VERSION;
00080
00085 @NotNull
00086 private String comment = "";
00087
00091 private long bytesIn = 0;
00092
00096 private long bytesOut = 0;
00097
00101 private int uptimeSeconds = 0;
00102
00106 @NotNull
00107 private String archBase = DEFAULT_BASE;
00108
00112 @NotNull
00113 private String mapBase = DEFAULT_BASE;
00114
00118 @NotNull
00119 private String codeBase = DEFAULT_BASE;
00120
00124 public MetaserverEntryParser() {
00125 clear();
00126 }
00127
00133 @Nullable
00134 public static MetaserverEntry parseEntry(@NotNull final CharSequence entry) {
00135 final String[] entries = FIELD_SEPARATOR_PATTERN.split(entry, -1);
00136 if (entries.length != 11) {
00137 return null;
00138 }
00139
00140 final int updateSeconds;
00141 final String hostname;
00142 final int players;
00143 final String version;
00144 final String comment;
00145 final long bytesIn;
00146 final long bytesOut;
00147 final int uptimeSeconds;
00148 final String archBase;
00149 final String mapBase;
00150 final String codeBase;
00151 try {
00152 updateSeconds = Integer.parseInt(entries[0]);
00153 hostname = entries[1];
00154 players = Integer.parseInt(entries[2]);
00155 version = entries[3];
00156 comment = entries[4];
00157 bytesIn = Long.parseLong(entries[5]);
00158 bytesOut = Long.parseLong(entries[6]);
00159 uptimeSeconds = Integer.parseInt(entries[7]);
00160 archBase = entries[8];
00161 codeBase = entries[9];
00162 mapBase = entries[10];
00163 } catch (final NumberFormatException ignored) {
00164 return null;
00165 }
00166
00167 return new MetaserverEntry(updateSeconds, hostname, players, version, comment, bytesIn, bytesOut, uptimeSeconds, archBase, codeBase, mapBase);
00168 }
00169
00177 @Nullable
00178 public MetaserverEntry parseLine(@NotNull final String line) throws IOException {
00179 if (inSection) {
00180 if (line.equals("END_SERVER_DATA")) {
00181 @Nullable final MetaserverEntry metaserverEntry;
00182 if (hostname == null) {
00183 System.err.println("Warning: metaserver response missing hostname field, skipping");
00184 metaserverEntry = null;
00185 } else {
00186 metaserverEntry = new MetaserverEntry(updateSeconds, hostname, players, version, comment, bytesIn, bytesOut, uptimeSeconds, archBase, mapBase, codeBase);
00187 }
00188 clear();
00189 inSection = false;
00190 return metaserverEntry;
00191 } else {
00192 final String[] tmp = line.split("=", 2);
00193 if (tmp.length == 2) {
00194 final String key = tmp[0];
00195 final String value = tmp[1];
00196 if (key.equals("hostname")) {
00197 hostname = value;
00198 } else if (key.equals("port")) {
00199 } else if (key.equals("html_comment")) {
00200 comment = value;
00201 } else if (key.equals("text_comment")) {
00202 if (comment.length() == 0) {
00203 comment = value;
00204 }
00205 } else if (key.equals("archbase")) {
00206 archBase = value;
00207 } else if (key.equals("mapbase")) {
00208 mapBase = value;
00209 } else if (key.equals("codebase")) {
00210 codeBase = value;
00211 } else if (key.equals("num_players")) {
00212 players = NumberParser.parseInt(value, 0);
00213 } else if (key.equals("in_bytes")) {
00214 bytesIn = NumberParser.parseLong(value, 0);
00215 } else if (key.equals("out_bytes")) {
00216 bytesOut = NumberParser.parseLong(value, 0);
00217 } else if (key.equals("uptime")) {
00218 uptimeSeconds = NumberParser.parseInt(value, 0);
00219 } else if (key.equals("version")) {
00220 version = value;
00221 } else if (key.equals("sc_version")) {
00222 } else if (key.equals("cs_version")) {
00223 } else if (key.equals("last_update")) {
00224 final long now = (System.currentTimeMillis()+500)/1000;
00225 final long uptime = NumberParser.parseLong(value, now);
00226 updateSeconds = Math.max((int)((uptime-now)/1000), 0);
00227 } else if (key.equals("flags")) {
00228 } else {
00229 System.err.println("Ignoring unknown key: "+key);
00230 }
00231 } else {
00232 throw new IOException("syntax error: "+line);
00233 }
00234 }
00235 } else {
00236 if (line.equals("START_SERVER_DATA")) {
00237 inSection = true;
00238 } else {
00239 throw new IOException("syntax error: "+line);
00240 }
00241 }
00242
00243 return null;
00244 }
00245
00250 private void clear() {
00251 updateSeconds = 0;
00252 hostname = null;
00253 players = 0;
00254 version = UNKNOWN_VERSION;
00255 comment = "";
00256 bytesIn = 0;
00257 bytesOut = 0;
00258 uptimeSeconds = 0;
00259 archBase = DEFAULT_BASE;
00260 mapBase = DEFAULT_BASE;
00261 codeBase = DEFAULT_BASE;
00262 }
00263
00270 @NotNull
00271 public static String format(@NotNull final MetaserverEntry entry) {
00272 return entry.getUpdateSeconds()+"|"+replace(entry.getHostname())+"|"+entry.getPlayers()+"|"+replace(entry.getVersion())+"|"+replace(entry.getComment())+"|"+entry.getBytesIn()+"|"+entry.getBytesOut()+"|"+entry.getUptimeSeconds()+"|"+replace(entry.getArchBase())+"|"+replace(entry.getCodeBase())+"|"+replace(entry.getMapBase());
00273 }
00274
00280 @NotNull
00281 private static String replace(@NotNull final String str) {
00282 return str.replaceAll("[\\|\r\n]", " ");
00283 }
00284
00285 }