Diablo3 Farming + Python

I haven’t played that much Diablo 3 recently, but since the last patch I’ve done a little bit of Demonic Essence farming in Warrior’s Rest. To get an idea of how efficient it is, I wrote a really hack’ish Python script to track some stats and used Tim Golden’s example code for catching global key presses in Windows. The script allowed me to press Shift+F1 or Shift+F2 while inside Diablo 3 to record a time stamp and whether or not I got an essence in the previous run. Note that the code is pretty horrible, but it was a 20 minute project and I only needed something quick and dirty.

Code

import os
import sys
import ctypes
from ctypes import wintypes
import win32con
import time
import datetime
import winsound

byref = ctypes.byref
user32 = ctypes.windll.user32

HOTKEYS = {
    1: (win32con.VK_F3, win32con.MOD_WIN),
    2: (win32con.VK_F4, win32con.MOD_WIN),
    3: (win32con.VK_F1, win32con.MOD_SHIFT),
    4: (win32con.VK_F2, win32con.MOD_SHIFT)
}


def handle_win_f3():
    os.startfile(os.environ['TEMP'])


def handle_win_f4():
    user32.PostQuitMessage(0)

################

LAST_TIMESTAMP = False
RUNS = 0
ESSENCES = 0


def handle_shift_f1():
    record_run(True)


def handle_shift_f2():
    record_run(False)


def record_run(got_essence):
    global LAST_TIMESTAMP
    global RUNS
    global ESSENCES

    RUNS += 1
    if got_essence:
        ESSENCES += 1

    timestamp = int(time.time())
    date_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")

    drop_rate = 0
    if ESSENCES > 0:
        drop_rate = (ESSENCES / float(RUNS)) * 100

    run_length = 0
    if LAST_TIMESTAMP is not False:
        run_length = timestamp - LAST_TIMESTAMP

    if got_essence:
        print 'DROP - %d - %s - %d/%d - %0.2f%% - %d' % (timestamp, date_str, ESSENCES, RUNS, drop_rate, run_length)
    else:
        print 'NULL - %d - %s - %d/%d - %0.2f%% - %d' % (timestamp, date_str, ESSENCES, RUNS, drop_rate, run_length)

    winsound.PlaySound('CallWaiting.wav', winsound.SND_FILENAME)

    LAST_TIMESTAMP = timestamp


################


HOTKEY_ACTIONS = {
    1: handle_win_f3,
    2: handle_win_f4,
    3: handle_shift_f1,
    4: handle_shift_f2
}


# RegisterHotKey takes:
#  Window handle for WM_HOTKEY messages (None = this thread)
#  arbitrary id unique within the thread
#  modifiers (MOD_SHIFT, MOD_ALT, MOD_CONTROL, MOD_WIN)
#  VK code (either ord ('x') or one of win32con.VK_*)
for id, (vk, modifiers) in HOTKEYS.items ():
    print "Registering id", id, "for key", vk
    if not user32.RegisterHotKey (None, id, modifiers, vk):
        print "Unable to register id", id


# Home-grown Windows message loop: does
#  just enough to handle the WM_HOTKEY
#  messages and pass everything else along.
try:
    msg = wintypes.MSG()
    while user32.GetMessageA(byref(msg), None, 0, 0) != 0:
        if msg.message == win32con.WM_HOTKEY:
            action_to_take = HOTKEY_ACTIONS.get(msg.wParam)
            if action_to_take:
                action_to_take()

        user32.TranslateMessage(byref(msg))
        user32.DispatchMessageA(byref(msg))

finally:
    for id in HOTKEYS.keys():
        user32.UnregisterHotKey(None, id)

Runs

C:\Python27\python.exe D:/Dropbox/Code/py-farm-companion/companion.py
Registering id 1 for key 114
Registering id 2 for key 115
Registering id 3 for key 112
Registering id 4 for key 113
DROP - 1363409462 - 2013-03-16 15:51 - 1/1 - 100.00% - 0
NULL - 1363409526 - 2013-03-16 15:52 - 1/2 - 50.00% - 64
DROP - 1363409594 - 2013-03-16 15:53 - 2/3 - 66.67% - 68
NULL - 1363409660 - 2013-03-16 15:54 - 2/4 - 50.00% - 66
NULL - 1363409742 - 2013-03-16 15:55 - 2/5 - 40.00% - 82
NULL - 1363409808 - 2013-03-16 15:56 - 2/6 - 33.33% - 66
DROP - 1363409870 - 2013-03-16 15:57 - 3/7 - 42.86% - 62
NULL - 1363409923 - 2013-03-16 15:58 - 3/8 - 37.50% - 53
NULL - 1363409980 - 2013-03-16 15:59 - 3/9 - 33.33% - 57
NULL - 1363410035 - 2013-03-16 16:00 - 3/10 - 30.00% - 55
DROP - 1363410098 - 2013-03-16 16:01 - 4/11 - 36.36% - 63
DROP - 1363410158 - 2013-03-16 16:02 - 5/12 - 41.67% - 60
DROP - 1363410216 - 2013-03-16 16:03 - 6/13 - 46.15% - 58
NULL - 1363410277 - 2013-03-16 16:04 - 6/14 - 42.86% - 61
DROP - 1363410340 - 2013-03-16 16:05 - 7/15 - 46.67% - 63
NULL - 1363410456 - 2013-03-16 16:07 - 7/16 - 43.75% - 116
NULL - 1363410515 - 2013-03-16 16:08 - 7/17 - 41.18% - 59
NULL - 1363410578 - 2013-03-16 16:09 - 7/18 - 38.89% - 63
DROP - 1363410656 - 2013-03-16 16:10 - 8/19 - 42.11% - 78
DROP - 1363410768 - 2013-03-16 16:12 - 9/20 - 45.00% - 112
DROP - 1363410837 - 2013-03-16 16:13 - 10/21 - 47.62% - 69
DROP - 1363410899 - 2013-03-16 16:14 - 11/22 - 50.00% - 62
NULL - 1363410953 - 2013-03-16 16:15 - 11/23 - 47.83% - 54
NULL - 1363411016 - 2013-03-16 16:16 - 11/24 - 45.83% - 63
DROP - 1363411076 - 2013-03-16 16:17 - 12/25 - 48.00% - 60
NULL - 1363411168 - 2013-03-16 16:19 - 12/26 - 46.15% - 92
NULL - 1363411244 - 2013-03-16 16:20 - 12/27 - 44.44% - 76
NULL - 1363411306 - 2013-03-16 16:21 - 12/28 - 42.86% - 62
DROP - 1363411375 - 2013-03-16 16:22 - 13/29 - 44.83% - 69
NULL - 1363411454 - 2013-03-16 16:24 - 13/30 - 43.33% - 79
NULL - 1363411523 - 2013-03-16 16:25 - 13/31 - 41.94% - 69
NULL - 1363411582 - 2013-03-16 16:26 - 13/32 - 40.62% - 59
DROP - 1363411645 - 2013-03-16 16:27 - 14/33 - 42.42% - 63
NULL - 1363411699 - 2013-03-16 16:28 - 14/34 - 41.18% - 54
NULL - 1363411764 - 2013-03-16 16:29 - 14/35 - 40.00% - 65
NULL - 1363411821 - 2013-03-16 16:30 - 14/36 - 38.89% - 57
DROP - 1363411879 - 2013-03-16 16:31 - 15/37 - 40.54% - 58
DROP - 1363411942 - 2013-03-16 16:32 - 16/38 - 42.11% - 63
DROP - 1363412001 - 2013-03-16 16:33 - 17/39 - 43.59% - 59
NULL - 1363412067 - 2013-03-16 16:34 - 17/40 - 42.50% - 66
DROP - 1363412145 - 2013-03-16 16:35 - 18/41 - 43.90% - 78
DROP - 1363412223 - 2013-03-16 16:37 - 19/42 - 45.24% - 78
NULL - 1363412293 - 2013-03-16 16:38 - 19/43 - 44.19% - 70
NULL - 1363412360 - 2013-03-16 16:39 - 19/44 - 43.18% - 67
DROP - 1363412422 - 2013-03-16 16:40 - 20/45 - 44.44% - 62
NULL - 1363412485 - 2013-03-16 16:41 - 20/46 - 43.48% - 63
DROP - 1363412548 - 2013-03-16 16:42 - 21/47 - 44.68% - 63
DROP - 1363412611 - 2013-03-16 16:43 - 22/48 - 45.83% - 63
NULL - 1363412681 - 2013-03-16 16:44 - 22/49 - 44.90% - 70
DROP - 1363412752 - 2013-03-16 16:45 - 23/50 - 46.00% - 71
NULL - 1363412815 - 2013-03-16 16:46 - 23/51 - 45.10% - 63
NULL - 1363412880 - 2013-03-16 16:48 - 23/52 - 44.23% - 65
DROP - 1363412953 - 2013-03-16 16:49 - 24/53 - 45.28% - 73
DROP - 1363413020 - 2013-03-16 16:50 - 25/54 - 46.30% - 67
DROP - 1363413090 - 2013-03-16 16:51 - 26/55 - 47.27% - 70
NULL - 1363413156 - 2013-03-16 16:52 - 26/56 - 46.43% - 66
NULL - 1363413209 - 2013-03-16 16:53 - 26/57 - 45.61% - 53
DROP - 1363413274 - 2013-03-16 16:54 - 27/58 - 46.55% - 65
NULL - 1363413349 - 2013-03-16 16:55 - 27/59 - 45.76% - 75
NULL - 1363413417 - 2013-03-16 16:56 - 27/60 - 45.00% - 68
DROP - 1363413479 - 2013-03-16 16:57 - 28/61 - 45.90% - 62
DROP - 1363413548 - 2013-03-16 16:59 - 29/62 - 46.77% - 69
NULL - 1363413608 - 2013-03-16 17:00 - 29/63 - 46.03% - 60
NULL - 1363413669 - 2013-03-16 17:01 - 29/64 - 45.31% - 61
NULL - 1363413735 - 2013-03-16 17:02 - 29/65 - 44.62% - 66
DROP - 1363413801 - 2013-03-16 17:03 - 30/66 - 45.45% - 66

Summary

In the end I ran Warrior’s Rest on MP9 66 times with my

Barbarian. The average run time was 67 seconds (timed from pressing resume to existing back to the lobby screen). I got 30 essences (45%) which is quite a bit higher than the expected drop rate (35%). Overall I got 1 essences every 2:24, which doesn’t really sound that great. I think I need to tweak my build and items (I really want Earthquake, but I don’t want to give up the healing from Rend). Maybe I’ll time some Vault of the Assassin runs next time.

Posted on

Christmas 2012

Edit: 2022-04-10 - New Gallery link

I swear I just don’t like people or something.

Posted on

Mouth of the Powlett River

From Christmas 2012.

Edit: 2022-04-10 - New Gallery link

Posted on

Portsea

Edit: 2022-04-10 - New Gallery link

Posted on

DCUO: Healing Weapon Skills

Just for my own reference:

Type Restoration Crit Chance Crit Multiplier
Hand Blasters 3 1 12
One Handed 3 14
Dual Pistol 45 1
Shield 45 1
Martial Arts 45 2
Dual Wield 47
Two Handed 4 2
Brawling 3 3 2
Rifle 3 2
Staff 14
Bow 3 12

Posted on

Firewall Fun: pf, tables, and state

I hate firewalls. Hate them. If you are using pf and wish to block a specific ip address using a simple table and a block quick rule it turns out just adding an ip to the table isn’t enough… pf wont actually block a new ip if it already has state information about it, instead it continues to let all traffic through. The solution is to remove all the state rules for the ip too:

pfctl -t blacklist -T add 123.123.123.123
pfctl -k 123.123.123.123

Posted on

LoL + Firewall Problems

Since upgrading my router to a Billion BiPAC 7800N I’ve been having trouble joining a new game in LoL; it sometimes takes up to 3 retries just to get to the loading screen. After connecting all is well but it’s a little annoying having to wait around every game. After doing some searching this seems to be a common problem with this router. Luckily there is a easy fix (even if it makes little sense). In the router configuration under Advanced -> Configuration -> QoS add a new QoS item.

  • Application: LoL
  • Direction: LAN to WAN
  • Protocol: TCP
  • Priority: High
  • Internal Port: 5000 ~ 5500
  • External Port: 5000 ~ 5500 Leave the all the other questions default and click
  • Add

With any luck, LoL will be back to normal.

Posted on

League of Legends Login Screen

Riot make some pretty nice login screens for their game League of Legends. They are frequently changing as they make a new one for each new champion released. The latest champion, Vi, has one the best login screens so far in my opinion. You can of course view it by downloading Lead of Legends, but I’ve also made a copy available here (may take a minute to load).

Posted on

DCUO: CR Calculator

Slot Item Level Mod Bonus

Posted on

Reseting Saved Password in Eudora

I was having trouble updating the password used for SMTP in Eudora 7. I couldn’t find a way to enter a new password, or get the client to forget the currently saved password. (Maybe be an issue with Gmail server sending a generic error message, rather than telling the client they have the wrong username/password?)

To manually reset the saved password in Eudora 7, close Eduora and find the Eduora.ini file located in:

C:\Documents and Settings\<user account>\Application Data\Qualcomm\Eudora\

Change the following lines from:

SavePassword=1
SavePasswordText=<randomtext>

To:

SavePassword=0
SavePasswordText=

Next time Eduora tries to send mail it will ask for a new password.

On a side note: Eduora apparently store passwords in plain text in an ini file… (Base64 doesn’t count).

Posted on