Tuesday 31 January 2017

Lets add buttons!

In my previous article (https://winter-in-hnh.blogspot.ru/2017/01/extending-haven-and-hearth-client.html) we extended Haven and Hearth client of our choice (Amber) with AutoKin functionality. We used GameUI::globtype() method to bind our AutoKin functionality to Control+K combination. This time we are going to try adding a menu button under Xtensions menu.




Placing icons into the menu

We'll start by adding a new icon into the menu - it's really easy to do, and requires addition of just one line. So lets have a look at ManuGrid.java class and it's implementation of attach() method.
    protected void attach(UI ui) {
        super.attach(ui);
        Glob glob = ui.sess.glob;
        synchronized (glob.paginae) {
            Collection<Pagina> p = glob.paginae;
            p.add(glob.paginafor(Resource.local().load("paginae/amber/coal11")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/coal12")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/branchoven")));
           // p.add(glob.paginafor(Resource.local().load("paginae/amber/steel")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/autosurvey")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/torch")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/clover")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/timers")));
            p.add(glob.paginafor(Resource.local().load("paginae/amber/autokin")));
        }
    }
Now we should have our icon in the menu, or maybe not? Lets build, run and have a look...

Crash and Burn!

Quiet expectidly client crashes with below exception:
java.lang.RuntimeException: Delayed error in resource paginae/amber/autokin (v-1), from local res source
 at haven.Resource$Pool$Queued.get(Resource.java:352)
 at haven.Resource$Pool$Queued.get(Resource.java:321)
 at haven.Glob$Pagina.res(Glob.java:150)
 at haven.Glob$Pagina.act(Glob.java:154)
 at haven.MenuGrid.cons(MenuGrid.java:93)
 at haven.MenuGrid.updlayout(MenuGrid.java:149)
 at haven.MenuGrid.tick(MenuGrid.java:433)
 at haven.Widget.tick(Widget.java:651)
 at haven.Widget.tick(Widget.java:651)
 at haven.GameUI.tick(GameUI.java:677)
 at haven.Widget.tick(Widget.java:651)
 at haven.UI.tick(UI.java:132)
 at haven.HavenPanel.run(HavenPanel.java:580)
 at java.lang.Thread.run(Unknown Source)
Caused by: haven.Resource$LoadException: Load error in resource paginae/amber/autokin(v-1), from local res source
 at haven.Resource$Pool.handle(Resource.java:409)
 at haven.Resource$Pool.access$1100(Resource.java:298)
 at haven.Resource$Pool$Loader.run(Resource.java:550)
 ... 1 more
 Suppressed: haven.Resource$LoadException: Load error in resource paginae/amber/autokin(v-1), from filesystem res source (res)
  ... 4 more
 Caused by: java.io.FileNotFoundException: res\paginae\amber\autokin.res (The system cannot find the path specified)
  at java.io.FileInputStream.open0(Native Method)
  at java.io.FileInputStream.open(Unknown Source)
  at java.io.FileInputStream.<init>(Unknown Source)
  at haven.Resource$FileSource.get(Resource.java:190)
  at haven.Resource$Pool.handle(Resource.java:393)
  ... 3 more
Caused by: java.io.FileNotFoundException: Could not find resource locally: paginae/amber/autokin
 at haven.Resource$JarSource.get(Resource.java:202)
 at haven.Resource$Pool.handle(Resource.java:393)
 ... 3 more
I guess I will post more on exception debugging in Haven and Hearth client at some point later, but now what is immediately evident from this exception is that it wasn't able to find a resource associated with something in MenuGrid and it's definitely related to our change since we have "autokin" all over the place. Solution to our problem is right in front of us:
Caused by: java.io.FileNotFoundException: res\paginae\amber\autokin.res (The system cannot find the path specified)
Which corresponds to our code looking like this:
p.add(glob.paginafor(Resource.local().load("paginae/amber/autokin")));
So we have instructed a menu item to be added to the MenuGrid using autokin icon, which is translated to local path "res\paginae\amber\autokin.res". By the looks of it path is relative to current directory - directory from where the client was started. But where do we get a RES file?

Lets mine some buttons

There are several utilities that allow RES file creation to name a few:
I had some poor experience with Boshaw's Hafen Layer Utility and ended-up using his LayerUtil the Java version - it worked really well and that is what we will use for this post. It can be compiled using the same approach as described in this post - https://winter-in-hnh.blogspot.ru/2017/01/haven-and-hearth-client.html.
But before we get into that, lets start by simply duplicating one of existing RES files. In your "build" folder locate "amber-res.jar" and open it with your favourite Zip archiver - I'll be using 7Zip:
Lets extract "amber.res" and rename it to "autokin.res" placing it under "res\paginae\amber\" directory relative to our "build". Like this:
This should solve the problem of non-existent file and we are still sure that our code does the right thing - so no changes needed and thus no recompilation required, lets start the client... again.

Expect the unexpected

This time client starts without any problem, we get in the game and get a new button:
Not exactly what I would expect, but kina makes sense - RES file is not just an image, but rather a compiled resource. This means we need our own resource file that we would be able to use for our purpose. So lets decode one file as a template. Since we want our icon to be "inside Xtensions" lets pick one of the resources for menu items placed there - so go ahead and unpack another RES file. I will be using coal12.res. 
Ok, so now you should have your "template" extracted and LayerUtil downloaded and built somewhere. I created a project for it in the same workspace, so it is placed next to my amber project directory:
Now lets decode coal12.res and finally have a look at what is inside:

Running this you should end up having a "dout" folder with several files and folders in it looking like the following:
First of all lets change the image associated with the resource, I'll use something like:
Put it into "image" folder as "image_0.png". In "pagina" folder we have "pagina_0.data" which contains description for our menu item, so lets change it to "Auto add Kin's from secrets.txt file." here is how it looks like in my Notepad++:
Now the most challanging part - modifying the "action"; in "action" folder modify "action_0.data" file to look like the following:

So I changed the name to be "Winter AutoKin", ad length to be "2" - used to be "3", and set ad[1] to "autokin". Great, now lets recompile our resource.
Now we have our recompiled coal12.res - lets rename it to "autokin.res" and replace our previous version in "res\paginae\amber\", and lets start the client:
So we have successfully created our button with name, description, image and action associated with it. The part missing now is binding the button to functionality - so lets go ahead and back to code.

The binding of the button

To bind the button we need to add it's action into MenuGrid's use() method, there are two use() methods for MenuGrid class - we need the public one that takes array of String's as an argument:
    public void use(String[] ad) {
There is a big IF block describing all known actions and their arguments, we will add our action at the bottom of the list:
        } else if (ad[1].equals("clover")) {
            Thread t = new Thread(new FeedClover(gui), "FeedClover");
            t.start();
        } else if (ad[1].equals("autokin")){
         Thread t = new Thread(new AutoKin(gui), "AutoKin");
         t.start();
        }
    }

As you can see this is pretty much the same as in https://winter-in-hnh.blogspot.ru/2017/01/extending-haven-and-hearth-client.html where we did exactly the same for a key chord - create a new thread with AutoKin class as an argument and start the thread.
Now, lets rebuild and start the client hopefully for the last time in this post.

...and it works!

Recap

Honestly speaking it feels like adding a button in Haven and Hearth client is a bit overcomplicated, resource compilation in particular is not as straightforward and clear, but I guess developers had a reason to do it so. Looking back at the post this is what we've done to make it all happen:
  1. Prepare resource file by decoding an existing pagina resource as a template, modifying "image", "pagina" and "action" content, encoding it into a new file and placing it into appropriate local directory
  2. Load the resource file created from within MenuGrid.java class attach() method 
  3. Modify MenuGrid.java class use() method to utilize your ad's defined in "action" in step 1
Now we are able to create menu items and bind functionality to them! Good luck!

Monday 30 January 2017

Extending Haven and Hearth Client

Hey All, in my previous post - https://winter-in-hnh.blogspot.ru/2017/01/haven-and-hearth-client.html we explored Amber - an advanced Haven and Hearth client by romovs; we learned to fork it on GitHub, clone it to a local repository and compile it in Eclipse IDE, so now it's time for a change, or actually to make a change.

What's the plan?

This particular amendment will allow us to simplify adding Kin's into our Kin list - while adding somebody via hearth secret is not hard and is easily manageble for a solo player, or a player with a couple of friends it gets really challanging once you have over 100 hearth secrets which would be the case for a bigger village or even kingdom. So what are we going to do? We are going to create an AutoKin class which sole purpose would be to open a text file, read hearth secrets from it and tell the client to add Kin's with respective secrets.

Setting everything up

But before we get to coding there is one additional step we need to perform and that is telling Eclipse IDE that we are actually dealing with a Java project. Previously (in https://winter-in-hnh.blogspot.ru/2017/01/haven-and-hearth-client.html) we created a generic project to store our source code, and then only used Eclipse IDE to check out source code from Git and told it to execute an Ant build using build.xml supplied by the project. Since now we are getting into more advanced stuff we need to make sure Eclipse IDE has an idea of what we are working with. To do so right-click your project name in Project Explorer tab and select Properties. Then click "Project Facets" and select "Java" to instruct Eclipse IDE that we are going to be using Java in our project.
Now we are all set to code.

Adding AutoKin

In our Project Explorer tab - lets pick a nice place for where we will put our AutoKin class, to me the most obvious place is with other automation tasks:

So right-click "haven.automation" folder and select "New > Class":

Now select the name of your class and check the package where it will be added, as well as pick interfaces it is going to implement:
It's worth noting that Runnable is a standard Java class (https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) that needs to implement a single method - run().

Once finished you should see your newly added AutoKin.java in Project Explorer window under "haven.automation" package in the source tree:
As you can see Eclipse IDE also prepopolated our class based on information we provide, so it "implements Runnable" and has stub run() method.


Coding AutoKin

Now that we successfully added a new class lets populate it with some actual functionality.

package haven.automation;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import haven.GameUI;

public class AutoKin implements Runnable {
  // private attribute pointing to GameUI
  private GameUI gui;
  // AutoKin constructor
  public AutoKin(GameUI gui) {
    this.gui = gui;
  }
  @Override
  public void run() {
    gui.syslog.append("AutoAdding Kin's:", Color.WHITE);
    Path file = Paths.get("secrets.txt");
    Charset charset = Charset.forName("UTF-8");
    try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
      String line = null;
      while ((line = reader.readLine()) != null) {
        gui.syslog.append(line,Color.BLUE);
        gui.wdgmsg(gui.buddies,"bypwd",line);
      }
    } catch (IOException x) {
      gui.syslog.append("Exception: "+x.toString(),Color.RED);
    }
  }
}

Now, this is pretty much self explanatory - very basic stuff. We create a Path pointing to "secrets.txt" file in current directory (directory from where the client was started) and try to read it as if it was "UTF-8" encoded line by line. Every line is interpreted as a hearth secret, so we try to add a kin (buddy) using their secret (bypwd). This is good time to rebuild the client and see if we broke anything with our addition. I hope you remember how to execute the Ant build - so right-click on "build.xml", and then select "Run As > Ant Build" and there it goes - remember to save your changes! In Console tab you should be able to see your project building:
Buildfile: C:\workspace\amber\build.xml
build-env:
hafen-client:
    [javac] Compiling 1 source file to C:\workspace\amber\build\classes
buildinfo:
lib-classes:
jar:
      [jar] Updating jar: C:\workspace\amber\build\hafen.jar
res-jar:
l10n-jar:
jars:
deftgt:
BUILD SUCCESSFUL
Total time: 4 seconds

Great, so our functionality compiled, but how do we trigger it?

Lets run() the Runnable

For now we are not going to get into intricacies of menu's, windows, widgets and buttons, but rather go the easy way and bind our functionality to a hotkey. To do so, we will modify globtype() method in GameUI.java class. GameUI.class is located under package "haven" and particular place within globtype() we are interested in is the following:
        } else if (!ev.isShiftDown() && ev.getKeyCode() == KeyEvent.VK_Q) {
            Thread t = new Thread(new PickForageable(this), "PickForageable");
            t.start();
            return true;
        } else if (ev.isControlDown() && ev.getKeyCode() == KeyEvent.VK_K){
         // AutoKin
         Thread t = new Thread(new AutoKin(this), "AutoKin");
         t.start();
         return true;
        }
        return (super.globtype(key, ev));
As you can see this method takes two arguments - character representing key pressed and a KeyEvent class, we are using the latter since we need slightly more information than what is the key character. Above you can see us adding another clause binding execution of AutoKin in a new thread to Control+K chord.

Auto-adding-Kins

With all changes done and saved, we recompile our build and start the client. Once logged in and in game lets press Control+K for the fun of it:
Yay! Our code picked up and tried opening secrets.txt, which was not there and thus failed with NoSuchFileException. Now let's create "secrets.txt", which will only consist of single line "A9XylWM6" and put it into our "build" directory - and now we are all set to press Control+K again.
Ok, now this is better - we see the hearth secret we are trying to add, but we see that nobody has it - this happens because "l" and "I" look alike, so we just had the wrong hearth secret - lets fix it in the file. This time no red line appears and "Kith and Kin" window pops-up showing our new added Kin(s).

Achievement Unlocked

Here is what we have achieved:
  1. Set our project to be Java based
  2. Developed AutoKin functionality - reading hearth secrets from file and adding kins by their hearth secret
  3. Added a hotkey to invoke our AutoKin functionality
  4. Compiled updated Haven and Hearth client



Sunday 29 January 2017

Haven and Hearth Client

To start doing arcane magic in Haven and Hearth one needs to learn to checkout, change and compile Haven and Hearth client. One of the most advanced clients for Haven and Hearth is developed by romovs and is called Amber, and that's what we would be using in our journey.

First things first

To begin with we need to ensure that we have all basic things that would be needed in future:
  • Java Development Kit (JDK) - make sure to have latest JDK installed, you can download it from http://www.oracle.com/technetwork/java/javase/downloads/index.html 
  • GIT client - GIT is version control system. Since we will be using GitHub to get the source code, we need a client. You can still do without GIT client and download ZIPed snapshots but you won't be able to synchronize your changes to GitHub then. You can get GIT client from https://git-scm.com/downloads
  • Integrated Development Environment (IDE) - we will be using Eclipse IDE, there are multiple other options IDEA, NetBeans to name a few. There is no particular reason why we are going to be using Eclipse, approach would be the same whatever you decide to pick. Eclipse can be downloaded here - https://eclipse.org/downloads/eclipse-packages/
Now we should be ready to get the source...

Use the source

To begin with lets fork Amber on GitHub, you can do so by clicking "Fork" button in the upper right corner of Amber repository page - https://github.com/romovs/amber:
This should create a copy of Amber repository for yourself and you should see something like this on repository page created for your copy:
Congratulations now you have your very own copy of Amber hosted on GitHub! Now lets download it and start using it. Time to start our IDE - after configuring workspace (directory where IDE will store your project files) you get to Welcome screen, where we will select "Check out projects from Git":
This presents us with a choice to use an existing local repository or to clone a URI, since we don't yet have any local repositories second options is what we are after:
You will be presented with the following form to specify Location of your repository and credentials to be used to access it. Once you fill in URI - Host and Repository path will be filled in automatically, authenitcation credentials are ones you use to access your GitHub account:
Next screen allows to select branches we want to check out - we are only interested in "master" branch, so that's what we select:
Now we need to specify where to store the copy localy and which branch is to be used:
Note that we checked "Clone submodules" - this is not really needed currently for Amber, but you never know what will change and perhaps at some point Amber might have external dependencies. Now Eclipse will clone the repository and place a local copy whereever you pointed it to. This gets us to next step - creating a project to work with checked out sources. Since source have no project files we will be importing them as "general project":
Next screen will allow you to set the project name and you are done. Now you have your GitHub repository cloned to your local PC and stored under directory that you specified and "Project Explorer" tab should be looking like the following:
Amber is built using Apache Ant, luckily enough it is integrated into Eclipse IDE and there is no need to download it separately and "build.xml" is configuration file defining build targets (like Makefile for those of you who are more familiar with "make". So we right-click "build.xml" and select "Run As -> Ant Build":
Follow Console tab for any erros, a couple of tasks need to be performed and the line we are looking for is "BUILD SUCCESSFUL":
Yay! Now lets go to workspace and see what we have there:

Note the "build" folder - that is where all the compiled classes went, but before that lets copy "run.bat" from "etc" folder - it contains startup parameters for the client. So here is how the final state of "build" folder should look like from inside:
 
Now lets start "run.bat" and behold our glorious creation.

The Deed is Done

So to recap of what we have done:
  1. We setup our development environment comprised of JDK, Eclipse IDE and Git setup
  2. We forked Amber official repository on GitHub creating a copy for ourselves
  3. We cloned our new repository to a local copy to work on it
  4. We built Amber client using build.xml supplied with the project
  5. We copied run.bat supplied with the project to build directory created during build process
  6. We executed run.bat to get client running

This should get us rolling and set a path for us to expore Artifice and Arcana of Haven and Hearth world.