если кого интересует из программистов, как написать самопальную интеграцию аплета пати покера с чем-то своим, например Poker Tracker 3, я делал так:
- изменял локальный jre/lib/rt.jar - для этого взял сановские сорсы, изменил их, скомпилял и переписал в архив поверху существующих классов в rt.jar. мы меняем не ориганальное софтваре PartyPoker (кстати все обработанное обфускатором и закриптованное во всех местах - так что на него смотреть совершенно бесполезно, траффик снифить тоже), а свою локальную среду - это ничего не нарушает.
- засунул одну единственную заглушку в один метод в AWT чтобы снять данные из памяти интересуещие в момент добавления патипокер объектов на отрисовку в панели AWT.
- критично только снять эти текстовые данные и тут же сбросить их на локальный сокет, где их примет и обработает по-нормальному другой свой написанный джава тул. протормозка в этой заглушке - это зависание всех отрисовок.
- этот другой джава тул слушающий порт берет ту же хэндхистори, и записывает ее (с изменениями! убейте - не понимаю, почему текстовые данные в аплете не совсем совместимы с тем, что пишет толстый клиент на экспорте на файловую систему - там буквально пару изменений, но их надо делать) на локальную фаловую систему, откуда уже Poker Tracker 3 импортит их в базу.
вот как примерно выглядит заглушка в java/awt/Container.java
public class Container extends Component {
// кэш, чтобы одну и ту же хистори личшний раз не обрабатывать - потенциальный мемори лик на большой нагрузке
protected static Set gameshistory = Collections.synchronizedSet(new HashSet());
...
// вот AWTшный метод куда мы засовываем свою заглушку
protected void addImpl(Component comp, Object constraints, int index)
{
synchronized (getTreeLock()) {
try
{
String clazzName = comp.getClass().getName();
if (clazzName != null &&
(clazzName.indexOf("com.partypoker") >= 0 || clazzName.indexOf("com.pg") >= 0))
{
Set types = new HashSet();
types.add(clazzName);
Container.dump("", comp, types);
}
}
catch (IllegalStateException e)
{
}
catch (Throwable e)
{
e.printStackTrace();
}
...
}
// рукурсивно дампаем интересуещие нас данные в новых методах ниже
public static void dump(String prefixPar, Object obj, Set types)
{
Field[] fields = obj.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
try
{
fields[i].setAccessible(true);
if (fields[i].isEnumConstant() || Modifier.isStatic(fields[i].getModifiers()))
{
continue;
}
String prefix = prefixPar + i;
String clazzNameDeclared = fields[i].getDeclaringClass().getName();
if (clazzNameDeclared == null ||
(clazzNameDeclared.indexOf("com.partypoker") < 0 && clazzNameDeclared.indexOf("com.pg") < 0))
{
continue;
}
Object val = null;
try
{
val = fields[i].get(obj);
}
catch (NullPointerException e)
{
val = null;
}
String valstr = val == null ? null : val.toString();
if (valstr != null && valstr.contains("ваш_ник_в_руме") && !valstr.equals("ваш_ник_в_руме") && !valstr.contains("Good luck ваш_ник_в_руме !"))
{
// detect hand history
if (valstr.contains("** Hand History for"))
{
// b:9911111111
// ** Hand History for Game 9911111111 **
// $2 USD NL Texas Hold'em - Wednesday, December 18, 20:20:20 ET 2010
// Table Table 177111 (Real Money)
// Seat 1 is the button...
recordHandHistory(valstr);
}
}
// каждую эту развилку можно по своему логировать, если надо снять дамп памяти выборочный
if (valstr == null)
{
}
else if (types.contains(val.getClass().getName()))
{
continue; // избегаем бесконечной рекрусии
}
else if (val.getClass().isPrimitive()
|| (val instanceof Byte)
|| (val instanceof Short)
|| (val instanceof Integer)
|| (val instanceof Long)
|| (val instanceof Float)
|| (val instanceof Double)
|| (val instanceof Boolean)
|| (val instanceof Character))
{
}
else if (val instanceof CharSequence)
{
}
else if (val.getClass().isArray())
{
}
else if (val.getClass().isEnum())
{
}
else if (val.getClass().getName().startsWith("java"))
{
}
else
{
// избегаем слишком глубокой рекурсии
if (prefix.length() > 15)
{
return;
}
Set types2 = new HashSet();
types2.addAll(types);
types2.add(val.getClass().getName());
// рекурсия для сложного объекта
dump(prefix + ".", val, types2);
}
}
catch (IllegalStateException e)
{ // можно изнутрей кинуть, если мы что-то нашли и хотим принудительно на этом уровне прекратить рекурсию
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public static void recordHandHistory(String history)
{
//9911111111
//** Hand History for Game 9911111111 **
//$2 USD NL Texas Hold'em - Wednesday, December 18, 20:20:20 ET 2010
//Table Table 177111 (Real Money)
//Seat 1 is the button
try
{
BufferedReader rd = new BufferedReader(new StringReader(history));
String line = null;
String previousline = null;
String lastgameid = null;
java.util.List playersingame = new ArrayList();
while ((line = rd.readLine()) != null)
{
if (line.contains("** Hand History for"))
{
lastgameid = previousline;
playersingame.clear();
}
if (line.startsWith("Seat ") && line.contains(":") && line.contains("("))
{
// Seat 1: someuser ( $1.82 USD )
String playeringame = line.substring(line.indexOf(":") + 1, line.indexOf("(")).trim();
playersingame.add(playeringame);
}
previousline = line;
}
rd.close();
if (!gameshistory.contains(lastgameid))
{
gameshistory.add(lastgameid);
// засылаем всю хэндхистори
sendData("history#" + history);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < playersingame.size(); i++)
{
if (i > 0)
{
sb.append('|');
}
sb.append((String) playersingame.get(i));
}
// засылаем отдельно ники всех, кто сидит за столом (для других моих приложений)
sendData("table#" + sb.toString());
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
// скидываем инфу на порт, который слушает другое наше приложение
protected static void sendData(String request)
{
try
{
Socket sender = new Socket("127.0.0.1", 1501);
PrintStream out = null;
try
{
out = new PrintStream(sender.getOutputStream());
out.println(request);
out.flush();
}
finally
{
if (out != null)
{
out.close();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
эта вся красота отрабатывает, если мы кликнем по ссылке в наше аплете чтобы показать hand history попап - т.е. попрежнему мануально запуск дампа происходит.
к вопросу, что надо поменять, чтобы эта строковая хэндхистори была сохранена на файловой системе и могла быть потом импортирована в Poker Tracker 3 (содержит баги эта имплементация, некоторые руки будут проскипаны, меньше 5% плохих обычно).
protected void processHandHistory(String args)
{
//9911111111
//** Hand History for Game 11111111 **
//$2 USD NL Texas Hold'em - Wednesday, December 18, 20:20:20 ET 2010
//Table Table 177111 (Real Money)
//Seat 1 is the button
List content = new ArrayList();
boolean isTourny = false;
String fileName = null;
try
{
BufferedReader rd = new BufferedReader(new StringReader(args));
String line = null;
String previousline = null;
String lastgameid = null;
while ((line = rd.readLine()) != null)
{
if (line.contains("***** Hand History for"))
{
lastgameid = previousline;
content.add((char) 0x00 + "Game #"+ lastgameid + " starts.");
content.add("");
previousline = "#Game No : " + lastgameid;
}
else if (line.startsWith("Trny:"))
{
isTourny = true;
}
else if (line.startsWith("Table "))
{
fileName = line.substring(6);
}
if (previousline != null && previousline.contains("Trny:") && previousline.contains("Level:"))
{
content.add(previousline + " " + line);
previousline = null;
}
else
{
if (previousline != null)
{
content.add(previousline);
}
previousline = line;
}
}
rd.close();
if (previousline != null)
{
content.add(previousline);
}
}
catch (IOException e)
{
e.printStackTrace();
}
if (fileName != null)
{
fileName = "/media/Ubuntu/Games/Poker/PartyHandHistory/" + fileName;
log("saving hand history to file: " + fileName);
PrintWriter out = null;
try
{
out = new PrintWriter(new FileWriter(fileName));
for (int i = 0; i < content.size(); i++)
{
out.print((String) content.get(i));
out.print("\r\n");
}
out.flush();
}
catch (IOException e)
{
e.printStackTrace();
log("Failed to write hand history: " + args);
}
finally
{
if (out != null)
{
out.close();
}
}
}
}
вот такой велосипед, на случай, если все же ничего стандартного нет и апплет расширятся не планирует.