andy malakov's software blog

Saturday, June 13, 2009

Writing game bot in Java

Can Java be used to write a simple MMORPG game bots?

Easily. Java has a class java.awt.Robot that is intended for writing automated tests and provides just what we need.

First, lets create a bot that will keep an eye on your character's health status. The basic idea is very simple - when health status is running low, drink a healing potion. Repeat. This simple script covers your back and releases your mind from some routine actions. You will concentrate on attacking your opponent ;-)

import java.awt.Color;
import java.awt.Robot;
import java.awt.Toolkit;
import static java.awt.event.KeyEvent.*;

private Toolkit toolkit = Toolkit.getDefaultToolkit();
private Robot robot = new Robot();


We need two type of things:

  1. Inspect some area of the screen that shows your character health's status (usually appears as some sort of progress bar). We just need to know when some pixel inside the bar is getting black:
    boolean isBlack (int x, int y) {
    Color c = robot.getPixelColor (x, y);
    return c.getRed() < 16 && c.getGreen() < 16 && c.getBlue() < 16;
    }

  2. Simulating keyboard action is also easy (we need to press a hotkey for your healing potions)
    void key (int keyCode, long duration) throws InterruptedException {
    robot.keyPress(keyCode);
    Thread.sleep(duration);
    robot.keyRelease(keyCode);
    }

Now the program itself is trivial:
while (true) {
if (isBlack (90, 760))
key (VK_F1, 500);

Thread.sleep (50);
}


Here I assume that your healing potions are assigned to hotkey F1 (KeyEvent.VK_F1) and coordinates {90, 760} show your health status and become black when health is below 25% (your can use Paint or some other program to analyze game's screen shot).


Similar approach can be used to automate other tedious MMORPG tasks (mining gold, fishing, or gaining experience).


P.S. Take a look at this guy's screen (like a Boeing cockpit).



DISCLAIMER:Check your game's license before using this technique. It may be prohibited as something that gives unfair advantage over other players.

Wednesday, December 24, 2008

My favorite features in Scala



  • Tupples in function results

    def f(a: int, b: int) : (int, int) = (a/b, a%b)


  • type Nothing and Null (like NONE and ANY in Eiffel)
  • Object definitions
    object Singleton { }

  • Parameterless methods, that can be overridden by fields and vice versa.

    class Circle(radius:double) {
    def diameter = 2*radius
    }
    ...
    new Circle(5).diameter


  • Subtyping of generics (so-called co-variant subtyping). I miss this feature in Java:

    def Iterator[+E] ...
    now Iterator[String] will assign to Iterator[AnyObj].


  • Pattern-matching expressions? Need to try it.


Some of these I was missing since I programmed in Eiffel back in 1995.

Features I am not so fond of:

  • Case classes (a-la subtyped enums?)

Tuesday, September 30, 2008

Mindstorms are back

Online game like this will be excellent for software job interview.
Cover voor Light Bot

Friday, September 12, 2008

Obfuscated code

Situation: Third-party client API library periodically fails with nasty exception:


Caused by: cli.System.AccessViolationException: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
at cli.TalTrade.Toolkit.DataBlock.ReadData(Unknown Source)
at cli.TalTrade.Toolkit.RawData.GetDataAsBlock(Unknown Source)
... 34 more


After verifying that input parameters are legitimate I decided to take a look at the failing code. My trusty Reflector exposed the following:



private unsafe void ReadData(void modopt(IsConstModifier)* pData)
{
// This item is obfuscated and can not be translated.
...
if (*(((int*) (&gtal_handle + 4)))[4] < 6)
goto Label_0038;
ushort num5 = *((ushort*) (((*(((int*) (&gtal_handle + 4))) + *(((int*) (&gtal_handle + 4)))[6]) - 8) + 6));
... 100 more lines like these ...
}



That code is supposed to read data block from memory queue and possibly format it as string. What kind of secret know-how can it be?

Now instead of figuring out the problem by ourselves we are going to send this error to the vendor. What a waste of time on both sides.

Thursday, August 14, 2008

Motorola RAZR repair

Replaced keyboard in my cell phone. This is how it looked like at the worst moment.

Tuesday, August 5, 2008

Update on "direct vs. encapsulated field access times"

Update to earlier post about performance of direct field versus method-encapsulated access. A friend of mine pointed to strange decrease in method access performance right before it is getting compiled to native code. So I went back and I enhanced output of the benchmark:


static void benchmark () throws Exception {
FieldAccessor fv = new FieldAccessor ();
MethodAssessor mv = new MethodAssessor ();

System.err.println ("\tTotal, Method, Field, Ratio");
for (int n=1, total = 0; n < 10000000; n=2*n) {
final long t0 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((mv.getField() & 0x101) != 0)
mv.setField(mv.getField()+1);
else
mv.setField(mv.getField()+2);
}
final long t1 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((fv.field & 0x101) != 0)
fv.field = (fv.field+1);
else
fv.field = (fv.field+2);
}
final long t2 = System.nanoTime();

// dispay stats
final double mvCycleTime = (double)(t1 - t0) / n;
final double fvCycleTime = (double)(t2 - t1) / n;
final double ratio = 100.0*fvCycleTime/mvCycleTime;
total +=n;
System.err.printf("% 12d, %.3f, %.3f, %.2f%% %c\n",
total, mvCycleTime, fvCycleTime, ratio,
(fv.field == mv.getField() ? ' ' : '!'));

Thread.sleep(500);
}
}

This code produces the following output (with -XX:+PrintCompilation):

Total, Method, Field, Ratio
1, 5866.000, 1759.000, 29.99%
3, 5463.000, 405.500, 7.42%
7, 1925.500, 217.750, 11.31%
15, 1166.000, 156.250, 13.40%
31, 648.313, 123.938, 19.12%
63, 440.438, 110.250, 25.03%
127, 401.922, 103.547, 25.76%
255, 270.688, 106.156, 39.22%
511, 243.715, 95.785, 39.30%
1023, 225.525, 94.367, 41.84%
2047, 231.510, 142.017, 61.34%
4095, 232.009, 97.745, 42.13%
2 tmp.TestFieldVsMethod$MethodAssessor::getField (5 bytes)
8191, 227.914, 97.593, 42.82%
3 tmp.TestFieldVsMethod$MethodAssessor::setField (6 bytes)
16383, 238.234, 98.486, 41.34%
1% tmp.TestFieldVsMethod::benchmark @ 44 (277 bytes)
32767, 13.628, 1.675, 12.29%
65535, 1.621, 1.666, 102.78%
131071, 1.611, 1.658, 102.88%
262143, 1.602, 1.655, 103.31%
524287, 1.599, 1.653, 103.41%
1048575, 1.897, 1.923, 101.40%
4 java.lang.String::charAt (33 bytes)
2097151, 1.606, 1.652, 102.88%
4194303, 1.595, 1.652, 103.52%
8388607, 1.595, 1.652, 103.53%
16777215, 1.595, 1.655, 103.76%


Notice how ratio dives down at around 30,000 mark:



What that probably means is that field access code is compiled faster:



Without knowing much about hotspot inner workings, I expected to see a sharper decline.
I run this test on server version of Java 6 (1.6.0_10-beta-b25, 64 bit).

Java NIO with large data files

We have a tool that sequentially reads financial data from massive multi-gigabyte files. It performs a lot of bytes-to-long, bytes-to-double, and utf-string conversions that were implemented in optimized fashion on top of BufferedInputStream.

It turns out that replacing that code with NIO's MappedByteBuffer processes 2 Gb file twice faster, while memory consumption remains the same.


Update: I have been warned and it turned out to be true: Reading 20 Gb file shows reverse results. Now NIO version is 8x slower than "old" stream-based approach. In this case I implemented paging because Java NIO cannot map file regions larger than 2Gb at once (Integer.MAX_VALUE).

On Windows XP 8K is a sweet spot for BufferedInputStream buffer size. Allocating larger buffer actually reduces performance significantly. May be size of mapped region has similar optimum.

Direct field vs. Method access times

In my opinion there is no justification for using 'friendly' direct access to mutable class fields rather than getters and setters properties. I heard the following two arguments:

1. It makes performance-critical code run faster.



False. There is no benefit of direct access in frequently executed code. Chart below compares access time of direct field access to getters and setters.



As you can see, after 20,000 calls there is no difference in performance. This threshold is controlled by -XX:CompileThreshold JVM argument. Core of the test:


final long t0 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((mv.getField() & 0x101) != 0)
mv.setField(mv.getField()+1);
else
mv.setField(mv.getField()+2);
}
final long t1 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((fv.field & 0x101) != 0)
fv.field = (fv.field+1);
else
fv.field = (fv.field+2);
}
final long t2 = System.nanoTime();



2. Direct field access reduces bureaucracy.


Read: I am too lazy to write getters and setters. Well, Eclipse and Netbeans have "Encapsulate Field" and "Generate getters and Setters" commands. And BTW, what none of free IDEs have is "Find where modified" search for class fields.

Having said that, I have no problems with public final fields :-)

Thursday, July 31, 2008

A bug in Sun's 64-bit hotspot compiler

A colleague of mine found a scary bug in Sun's hotspot compiler (64 bit version). It is reproducible on several computers in our office (CPUs: 2x AMD Opteron, Intel E8400; OS: Windows XP Professional 64-bit). Here is the code:

public class FailingTest {

private final java.io.RandomAccessFile raf;
private long sequence;

public FailingTest() throws Exception {
java.io.File f = new java.io.File("test.dat");
f.delete();
raf = new java.io.RandomAccessFile(f, "rw");
f.deleteOnExit();
}

public void test(long timestamp) throws Exception {
// This condition never happens but is required to
// reproduce this bug, so is unused timestamp argument
if (timestamp < 0)
System.exit(0);

final long valueToStore = sequence++;
raf.seek(0);
raf.writeLong(valueToStore);
raf.seek(0);
final long valueRead = raf.readLong();
if (valueRead != valueToStore)
System.err.println("Error: Read value " +
valueRead + " != " + valueToStore);
}

public static void main (String [] args) throws Exception {
FailingTest t = new FailingTest();
for (int ii = 0; ii < 1000000; ii++)
t.test(ii);
}
}

On my machine this test fails with the following output (I run it with -server -XX:CompileThreshold=100):

Error: Read value 4826 != 4825

The test fails every time with different numbers, with any version of 64-bit Sun JDK 1.6 we tried. This problem is NOT reproducible under 32-bit version and NOT reproducible on BEA's JRockit 1.6 (64 bit). Just verified that problem remains on the latest JVM available: build 1.6.0_10-beta-b25, Java HotSpot(TM) 64-Bit Server VM (build 11.0-b12, mixed mode).

Monday, July 21, 2008

Debug mode penalty in .NET and Java

Java users quite often distribute their code compiled in debug mode. This improves error diagnostics while performance penalty for having debug information in class files is negligible. Java doesn't have compile-time optimization since version 1.3 (for 8 years by now).

That is why I was amazed to see that C# Debug version of my test was 14.5 times slower compared to Release (compiled as /optimize+ /checked- /debug-). I could have imagined such difference for C++, but not for C# that also has just-in-time (JIT) optimization at run-time.