Look behind you, a Three-Headed Monkey! Musings on 1s and 0s

4Mar/160

Creating and monitoring a Glassfish Cluster

In this post I will detail how to create a basic Glassfish cluster and how to monitor it using JMX. First, download the latest release of Glassfish and extract the archive to a folder of your choice. Then start the asadmin utility from the glassfish4\bin directory. Unless otherwise noted, all of the commands that follow will be executed using this tool.

In order to manage the cluster, we first need to set up a domain administration server (DAS). Delete the default domain:

> delete-domain domain1

Afterwards, create a new domain for the das (replace afqa with a name of your domain):

> create-domain afqa-das
Enter admin user name> admin
Enter the admin password> *****
Enter the admin password again> *****

In general, it is good practice to enable the secure admin feature for your new domain:

> start-domain afqa-das
> enable-secure-admin
Enter admin password for user "admin"> *****
> restart-domain afqa-das
> login
Enter admin user name> admin
Enter admin password> *****

With the DAS running, we can create the cluster and set the properties and options that will enable us to remotely monitor the instances via JMX:

> create-cluster afqa-cluster
> create-node-config --nodehost node01
> create-system-properties --target afqa-cluster-config JMX_REMOTE_PORT=60600
> create-jvm-options --target afqa-cluster-config -Dcom.sun.management.jmxremote.port=${JMX_REMOTE_PORT}
> create-jvm-options --target afqa-cluster-config -Dcom.sun.management.jmxremote.authenticate=false
> create-jvm-options --target afqa-cluster-config -Dcom.sun.management.jmxremote.ssl=false

Now, create a couple of instances:

> create-instance --cluster afqa-cluster --node node01 --systemproperties JMX_REMOTE_PORT=60600 node01-1
> create-instance --cluster afqa-cluster --node node01 --systemproperties JMX_REMOTE_PORT=60601 node01-2

Note the HTTP_LISTENER_PORT and JMX_REMOTE_PORT for each instance - we will need these to connect to the instances.

Start up the cluster and verify that we can connect to the instances via HTTP and JMX:

> start-cluster afqa-cluster
> get-health afqa-cluster
node01-1 started since Fir Mar 04 13:45:12 PST 2016
node01-2 started since Fir Mar 04 13:45:11 PST 2016

At this point you should be able to navigate to each instance in a browser:
glassfish-instance

Finally, verify that you can connect to your instances via JMX by using JVisualVM and providing the service URL:

service:jmx:rmi:///jndi/rmi://HOSTNAME:JMX_REMOTE_PORT/jmxrmi

Filed under: Java, Technology No Comments
19Feb/160

Implementing a Java Agent (Part 3)

The previous posts in this series described how to create a Java agent and how to dynamically attach it to a running JVM. Once the agent is running, you may want to interact with it, for example to collect data from it or to execute specific methods on demand. Today's post will show how to do just that using the Java Management Extensions (JMX). As before, you can download the complete source code as a Maven project.

We'll start by creating an MBean interface that will encapsulate the management operations of the agent. In this case, the only supported operation will be to get the time when the agent was loaded:

public interface AgentManagementMBean {
    // Name used to access MBean
    String JMX_BEAN = "com.afqa123:type=AgentManagement";
    // Returns the timestamp when the agent was started.
    long getStartTime();
}

The actual implementation of the interface is fairly straightforward:

public class AgentManagement implements AgentManagementMBean {
    // Store time when agent was first loaded
    private static final long START_TIME = System.currentTimeMillis();
    @Override
    public long getStartTime() {
        return START_TIME;
    }    
}

Next, we'll have to modify the agent class to register the new interface with the MBean server. The code to do that will have to be executed from both the premain and agentmain methods, so we'll create a new method for that purpose:

public class MyAgent {
    // Instance of AgentManagement MBean
    private static AgentManagement mgmtBean;

    private static void registerMBean() {
        if (mgmtBean == null) {
            mgmtBean = new AgentManagement();
            // Register MBean with MBean Server
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            try {
                ObjectName name = new ObjectName(AgentManagementMBean.JMX_BEAN);
                if (mbs.isRegistered(name)) {
                    mbs.unregisterMBean(name);
                }
                mbs.registerMBean(mgmtBean, name);
            } catch (JMException ex) {
                ex.printStackTrace();
            }
        }
    }

Additionally, we'll update the existing methods to call this new method during startup:

    public static void premain(String args, Instrumentation inst) {
        System.out.println("MyAgent start: " + args);
        registerMBean();
    }
    
    public static void agentmain(String args, Instrumentation inst) {
        System.out.println("MyAgent main: " + args);
        registerMBean();
    }    

Now, switch over to the controller application we created last time. As you may remember, we used it to attach to the JVM and then to load our Java agent. We will extend that class to also load the JMX agent. Once that is done, we can get the JMX service URL:

        String agentJar = args[1];
        String jmxUrl = null;
        try {
            VirtualMachine vm = VirtualMachine.attach(targetJvm);
            // Load our agent
            vm.loadAgent(agentJar, null);
            // Load the JMX agent
            vm.loadAgent(System.getProperty("java.home", "") + "/lib/management-agent.jar", 
                    "com.sun.management.jmxremote");
            // The agent properties contain the JMX URL
            Properties props = vm.getAgentProperties();
            jmxUrl = props.getProperty("com.sun.management.jmxremote.localConnectorAddress");
            vm.detach();

I should note that if you are using Java 8 or newer, you can use a newly introduced method to load the JMX agent instead of having to provide the path to the management agent jar.

With the service URL, we can then connect to the JMX agent and create an instance of our management bean. Finally, we call the method we defined earlier, which will be executed by the agent running on the target JVM:

            // Connect to JMX agent
            JMXServiceURL url = new JMXServiceURL(jmxUrl);
            try (JMXConnector jmxc = JMXConnectorFactory.connect(url)) {
                MBeanServerConnection connection = jmxc.getMBeanServerConnection();
                // Get reference to our agent's management bean
                ObjectName name = new ObjectName(AgentManagementMBean.JMX_BEAN);
                AgentManagementMBean bean = JMX.newMBeanProxy(connection, name, AgentManagementMBean.class);
                // Invoke agent method
                Date start = new Date(bean.getStartTime());
                System.out.println("Agent has been running since: " + start);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

After you build the project, copy the JARs to a single directory, and as before start the sample application:

> java -jar MyApplication-0.0.1-SNAPSHOT.jar

Finally, run the controller again:

> java.exe -cp "%JAVA_HOME%/lib/tools.jar;MyController-0.0.1-SNAPSHOT.jar;MyAgent-0.0.1-SNAPSHOT.jar" com.afqa123.example.MyController MyApplication-0.0.1-SNAPSHOT.jar MyAgent-0.0.1-SNAPSHOT.jar 
Agent has been running since: Fri Feb 19 15:28:17 PST 2016

You'll notice that if you execute the controller multiple times the start date will not change, because the agent jar is only loaded once.

Filed under: Java, Programming No Comments
17Feb/160

Implementing a Java Agent (Part 2)

In the first part of this series, I demonstrated how to create a Java agent and attach it to an application during startup. It is also possible to dynamically attach your agent to a running program, as long as you have permissions to access the JVM. The updated source code is available for download.

The first change that we will have to make is to add a new method called "agentmain" to our Agent class that will get executed when we dynamically attach to the JVM:

public class MyAgent {
    // Same as before
    public static void premain(String args, Instrumentation inst) {
        System.out.println("MyAgent start: " + args);
    }
    // New method
    public static void agentmain(String args, Instrumentation inst) {
        System.out.println("MyAgent main: " + args);
    }
}

Note that the arguments which are getting passed to this new method are the same as to the premain method before. Also, I should say that when the agent is loaded dynamically, the premain method will no longer get called - so make sure that any initialization needed by your agent is handled for both use cases.

Next, we need to add the new method to the agent's JAR manifest by editing its pom-file:

  …
  <configuration>
    <archive>
	  <manifestEntries>
	    <Premain-Class>com.afqa123.example.MyAgent</Premain-Class>
	    <Agent-Class>com.afqa123.example.MyAgent</Agent-Class>
	  </manifestEntries>
	</archive>
  </configuration>
  …

In the project download, you'll find a new module called MyController. We will use this class to connect to the target JVM and load the agent. Using the VirtualMachine class, we can request a list of all running JVMs which you can access:

public class MyController {
    public static void main(String[] args) {
        // Name of the Java executable to attach to is passed as 1st argument
        // from the command line
        String targetName = args[0];
        // Find the target JVM by name
        VirtualMachineDescriptor targetJvm = null;
        for (VirtualMachineDescriptor descriptor : VirtualMachine.list()) {
            if (descriptor.displayName().startsWith(targetName)) {
                targetJvm = descriptor;
                break;
            }
        }

Once we have a reference to the JVM, we can attach to it and load the agent-jar:


        // Location of the agent-jar is passed as 2nd argument from the 
        // command line
        String agentJar = args[1];
        try {
            VirtualMachine vm = VirtualMachine.attach(targetJvm);
            vm.loadAgent(agentJar, null);
            vm.detach();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

After you build the modules, copy the JAR files for all of them to a single directory of your choice. Then, start the test application from the command line:

> java -jar MyApplication-0.0.1-SNAPSHOT.jar

Now, the moment you've all been waiting for: open up a new command line window, and execute the controller application to attach and load the agent:

> java -cp "%JAVA_HOME%/lib/tools.jar;MyController-0.0.1-SNAPSHOT.jar" \
com.afqa123.example.MyController MyApplication-0.0.1-SNAPSHOT.jar \
MyAgent-0.0.1-SNAPSHOT.jar

If you switch over to the first command line window, you should see output from the agent similar to this:

…
Tick
Tock
MyAgent main: null
Tick
Tock

In the next post I will show you how to control the agent by exposing methods using JMX.

Filed under: Java, Programming No Comments
15Feb/160

Implementing a Java Agent (Part 1)

This is the first in a series of posts describing how to implement a Java agent. Java agents execute in the same Java Virtual Machine (JVM) as regular Java application, and provide insight into them as well as the ability to modify their behavior at run-time. All of the source code is available for download in the form of a Maven project.

Before we start with the actual implementation of the agent, let's create an simple application that we will attach our agent to:

public class MyApplication {
    public static void main(String[] args) {
        long counter = 0l;
        while (!Thread.interrupted()) {
            try {
                System.out.println(counter++ % 2 == 0 ? "Tick" : "Tock");
                Thread.sleep(1000l);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

The main method of the application loops until the application is interrupted, printing "Tick" and "Tock", respectively.

Next we will create a simple agent by creating a class which implements the premain method, as per the Java documentation. Similar to the main method in a regular application, this method will be called during creation of the JVM when our agent is loaded.

public class MyAgent {
    public static void premain(String args, Instrumentation inst) {
        System.out.println("MyAgent start: " + args);
    }    
}

The agent will be contained inside its own JAR file, and in order for the JVM to correctly load the MyAgent class, we need to specify the classname in the JAR's manifest. While this can be done in a number of ways, I'll only describe the approach using the Maven jar-plugin. To set the required manifest entry, include the following configuration in the pom-file for the agent module:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.6</version>
      <configuration>
        <archive>
          <manifestEntries>
            <Premain-Class>com.afqa123.example.MyAgent</Premain-Class>
          </manifestEntries>
        </archive>
      </configuration>
    </plugin>
  </plugins>
</build>

Similarly, modify the pom-file for the application module to set the Main-class manifest entry:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.6</version>
      <configuration>
        <archive>
	  <manifestEntries>
            <Main-class>com.afqa123.example.MyApplication</Main-class>
	  </manifestEntries>
        </archive>
      </configuration>
    </plugin>
  </plugins>    
</build>

After building the project, copy both JAR files to a directory of your choice. To run the application without the agent, execute the following:

java -jar MyApplication-0.0.1-SNAPSHOT.jar

Your output should match the expected message once per second:

Tick
Tock
...

Press Ctrl+C to stop the program. To start the agent when the JVM loads, run the following command:

java -javaagent:MyAgent-0.0.1-SNAPSHOT.jar -jar MyApplication-0.0.1-SNAPSHOT.jar

This time, you will see the agent startup message before the application enters the main loop:

MyAgent start: null
Tick
Tock
...

And that's all! Check out the full sample project. In the next article in this series, I will demonstrate how to dynamically load a Java agent into a running JVM.

Filed under: Java, Programming No Comments
7Mar/1516

Deciphering Blade Runner

I have been doing some research into voxel rendering techniques as of lately. While the whole approach has fallen to the wayside a bit with the advent of 3D accelerators, I still find it to be a uniquely elegant approach to modeling and rendering objects in software. As part of my research, I thought it would be interesting to look at how some of these techniques were used in commercial games, back in the days of yore, during the 90s.

One of the games that used voxels extensively was Blade Runner, a game I have previously written about. Westwood studios, the game's developer, was doing a lot of work with voxels back then, Command & Conquer - Tiberian Sun being another prominent example.

As a first step, I wanted to see if I could get find the voxel model data in the game's resource files. Like many other Westwood games, Blade Runner uses an in-house format (MIX), which acts as a container for multiple resource files. Looking at the executable at run-time, I saw that the game always first attempts to load a file from disk, and then, only if it cannot find it, get it from one of the MIX files. I'm not sure yet how it decides what MIX file to load the data from; that may just be hard-coded in the executable.

As said before, the MIX format was used by most Westwood games of that period, and has over the years been documented extensively by fans and modders of the games. In essence, each MIX file starts with a header that contains entries for each file in the container. Each file entry consists of a file ID, the offset in the MIX, as well as the file size. The ID is can be computed as a hash of the filename using the following algorithm:

// File is up to 8 characters + 3 characters for the extension
function computeHash(filename)
    filename = toUppercase(filename)
    filename = pad filename with 0x0 so length is a multiple of 4
    hash = 0
    for each group of 4 bytes
      hash = rotateLeft(hash) + group
    loop
    return hash

So far so simple - however, that means that by just looking at the MIX file, there is no way to figure out what each container entry was called, or for that matter what type of file it represents. Looking through the resources contained in the game folder, I was nonetheless able to extract some of the original file names. This in turn let me to figure out some of the data types and formats.

Here is a preliminary list of file formats used by Blade Runner:

  1. VQA files - these are videos in a proprietary format, which has been documented extensively. However, when I tried to load some of the videos that come with Blade Runner, I didn't have much luck. The game may be using a slightly different version of the file format than the documented cases.
  2. SHP files - these contain one or more sprites. The SHP format has been used and documented all the way back to the original Command & Conquer, but as with the VQA files, the format used by Blade Runner is slightly different: instead of the more complex compression used in earlier versions, in this iteration the file starts off with a counter of images. Immediately following, each image starts with a header that gives the width and height of the image, as well as the length of the data so that one can find the start of the next image header in the file. The image data itself is uncompressed rows of 16-bit High-Color values.
  3. SET files - as far as I can tell, these files contain information on the sets / locations seen in the Game. The file contains multiple series of records, the first of which seems to be the list of objects in that location.
  4. DAT files - these are in fact containers themselves, albeit more primitive than the MIX format. The files start with a header which contains the number of files, as well as the offset of each of them. I have not made much progress understanding the records themselves yet, unfortunately. It should be noted that the game installation directory contains a number of DAT files outside of the MIX files - presumably because of size restrictions of what could be contained in a MIX container.
  5. GAMEINFO.DAT - despite having a .DAT ending, this file is actually just a collection of resource names. For example, it contains the names of all sets, as well as audio files in the game.
  6. TRE files - this format is very similar to DAT files, however they miss the initial magic number that identifies a DAT. As far as I can tell, they always contain just string resources. For example, the labels used by the UI are contained in one of the files.

Additionally, there are some other formats contained in the MIX files which I haven't yet looked into further. Among these are the following:

  1. AUD - audio files
  2. TLK - speech files
  3. FON - fonts

As I was exploring these formats, I created a command line tool which let me browse the contents of the MIX files and load data from individual entries inside the container. The source of the project is available via my Github repository. It's lacking quite a bit of polish at this time, so that it probably will only prove useful for people willing to dig into the code and make changes as needed; but hey, maybe someone else will be able to contribute some more information on the remaining formats.

That being said, so far I did not find the voxel models that prompted me to start this whole endeavor. At this point, I suspect that the data is stored somewhere in the COREANIM.DAT container, since that is being read by the executable at start-up. Unfortunately, I don't have any information on the actual file format yet. I'm hoping to post more as I find more time to dig into the data.

5Jan/1511

13th Age Character Sheet Generator

I have been taking part in a 13th Age campaign for the past 6 months or so. It is a really cool system that I would highly recommend anyone interested in Pen & Paper RPGs to check out.

As the story of our party has been developing, it appears that we have come to the end of the line for my trusty, if slightly dim-witted, Half-Orc rogue. While he may yet live to see another day, for the time being he will have to leave the party and make room for someone more morally aligned with the other adventurers. So, I decided to get out my players' handbook and roll up a new character. While that process isn't terribly complicated, I felt it would be nice to automate some of the more tedious computations involved in filling out a character sheet.

A quick search revealed that while there was already an Excel spreadsheet that did a lot of what I wanted, it was lacking a nice UI that would let you print a character directly from the web. So, I figured it was time for me to implement just that!

Here we are now, a few days later, and I've got a hosted version up, as well as the source code available on my Github repository.

At this point, it is possible to print a character sheet directly from the page. Also, you can save the character to a file and later load it back up to modify it. In addition, I have got the following things implemented:
- All classes and races from the core book as well as the 13 true ways expansion
- Ability scores
- Defense values
- Attack values for standard attacks
- Hit points and recoveries
- Racial Power and Class Features

I know there is still some work left to be done: specifically a lot of feats and abilities that change character stats are not yet implemented. I'll be testing the system for my own characters in the next few months and try to fill in the missing pieces. As usual, comments and suggestions for improvement are very much encouraged.

Filed under: Games, JavaScript, RPGs 11 Comments
9Dec/132

Drools Netbeans Plugin

Having become fed up with Eclipse, I've found myself using Netbeans for my day-to-day work more and more in recent weeks. As a replacement IDE for Java development I'm pretty happy with it, though the one thing I found lacking was support for the Drools rule engine. While you can certainly create rule files in a text editor, the lack of basic features like syntax coloring can make this a tiresome experience. So, I decided to do something about this, and jump right into creating a Netbeans Plugin to support Drools development. You can find the result in my Git repository, and soon hopefully also on the Netbeans Plugins Portal.

drools-plugin

So far, the plugin provides syntax highlighting, code folding, and bracket matching for DRL files. I'm hoping to eventually include error detection as well as auto-complete and code suggestions.

Filed under: Drools, Programming 2 Comments
2Dec/130

For King and Country

I recently felt the urge to take some time off from the long-term game project I had been working on for most of this year, so I decided it would be fun to have a little weekend hackathon focusing on a small project. After the recent game-jams I had participated in as part of a group, I wanted to work through this one on my own, without any real constraints with regard to the game format or theme. So, I set out to spend the majority of my free time last week, all in all about 4 days, making a game.

I have been a fan of the Total War series for years now, and it just happens that my all-time favorite entry, Medieval, is no longer playable on most modern machines. I remember the game had a reputation for being buggy back when it came out a decade ago, and time has certainly not improved this. I'm sure there are ways to make it cooperate via wine or something similar, but needless to say, it won't just run on its own any more. Thus, I figured it was time to recreate the game experience.

In the week leading up to this, I had done some prep work; for example, I drew up a map of Europe to be used as the "board" of the game. Early on, I made the decision to go for a pixel / 8bit look, since that would allow me to crank out usable, if not final, art assets without loosing too much development time.

I also decided to use the Crafty.js framework, which had served me more or less well on prior occasions. For the uninitiated reader, Crafty is a component-based JavaScript framework for use with HTML5 / canvas, and I've found it to be pretty quick to pick up despite the lack of documentation. In most cases, their mailing list provided answers to any problems I encountered.

As is usually the case with projects like this, time is the enemy. With every day that I was working, plenty of new ideas came to me, but at the same time, I began to realize how much less I was going to be able to implement given the constraints I had. In the end, the biggest missing pieces were the lack of AI, which I hadn't realistically expected to implement, and the severely underdeveloped combat mechanics and in-game economy. Still, overall I enjoyed the experience, and felt that I was able to accomplish a fairly basic, but still playable game. If you care to check it out, it is available online and should run fine in both Firefox and Chrome.

Some observations and problems I encountered during the hackathon:

Scaling
Since I was going for the 8-bit look, initially I targeted the classic VGA resolution of 320x200. I was hoping to just be able to scale up the whole game by a factor of 4, thus ending up with 1280x800 to be displayed on a modern screen. The results of this, however, looked pretty terrible. The default scaling in Crafty (which depends on the browsers' scaling) uses interpolation, which introduces artifacts into pixel assets and also muddles the colors. Since there is no way to turn this off, I ended up having to scale all images by hand in GIMP. This was obviously not an ideal solution, as it not only increases the time effort, but also the file size and memory use at run-time.

Rendering to Canvas
After some experimenting, I was able to create components to render directly to the canvas. One use for this, is to replace a specific color in a entity on screen. This is, of course, one of the older tricks in the book, allowing you to reuse sprites with different color sets. The following component replaces all pixels that are of "src" color with "dest":

Crafty.c('FactionColor', {
    src: [255, 0, 255], // color to replace
    dest: [0, 0, 0], // color to insert 

    init: function() {
        this.bind('Draw', function(e) {
  	    // get image data for this entity
            var img = e.ctx.getImageData(this.x, this.y, this.w, this.h);
            // replace each pixel that matches src with dest
	    for (var i = 0; i < img.data.length; i += 4) {
	        if (img.data[i] == this.src[0] && 
                    img.data[i + 1] == this.src[1] && 
                    img.data[i + 2] == this.src[2]) {
		    img.data[i] = this.dest[0];
		    img.data[i+1] = this.dest[1];
		    img.data[i+2] = this.dest[2];
		}
	    }
            // write back image data
	    e.ctx.putImageData(img, this.x, this.y);			
	});
    }
});

Rendering Text
Another problems I ran into was font rendering: for some reason, I couldn't get them to render properly as canvas elements in Firefox, so I had to create them as part of the DOM. The following component displays a text over an entity, optionally only showing it on mouse-over:

Crafty.c('TextOverlay', {
    _text: null,

    init: function() {
        this.requires('Mouse');
	this.color = this.color || '#ffffff';
        this.fontFamily = this.fontFamily || 'Century,System';
        this.fontSize = this.fontSize || '24px';
        this.fontWeight = this.fontWeight || 'bold';

        this._text = Crafty.e("2D, DOM, Text")
                .attr({ x: this.x, y: this.y, w: this.w, h: this.h, 
                        z: this.z, visible: true })
		.textColor(this.color)
                .textFont({ family: this.fontFamily, size: this.fontSize, 
                            weight: this.fontWeight })
                .unselectable();
	this.attach(this._text);
	
        this.bind('MouseOver', function() {
	    if (this.hover) {
		this._text.visible = true;
	    }
        });
	this.bind('MouseOut', function() {
	    if (this.hover) {
		this._text.visible = false;				
	    }
	});
	return this;
    },
	
    text: function(value) {
	this._text.text(value);
	this._text.visible = !this.hover;
	return this;
    }
});
6Aug/1311

Replacing the Ubuntu network driver

(Update 10/15/2016: There is now an updated version of the driver package available on Github. Some of the changes described in the following post are no longer necessary)

I've been running a Shuttle mini PC for the past few years as a low-power Linux server / NAS. All was peachy, until I started noticing that the upload speed over the network kept dropping while uploading large files recently. My initial thought was that this had something to do with my router, but the problem persisted after replacing it.

It finally dawned on me that this was an issue on the server when I noticed that I was also no longer to use wake-on-lan to power on the server, which had worked in the past. Spoiler alert: upgrading Ubuntu to the latest LTS release (12.04) apparently deployed a new network driver which isn't quite working with the on-board ethernet controller.

Checking the network device showed me the kernel module (sky2) which was causing the problems:

> lspci -k | grep -A5 Ethernet
02:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8056 PCI-E Gigabit Ethernet Controller (rev 12)
	Subsystem: Holco Enterprise Co, Ltd/Shuttle Computer Device 3140
	Kernel driver in use: sky2
	Kernel modules: sky2

A quick google search revealed that there was a proprietary driver for the Marvell chip. It is available from here. Select "Linux 2.6 - Fedora" as the plaform, and download the Kernel 2.6.x Linux Driver package.

After downloading the package, unpack the archive and change the install script to use bash. For some reason, the installer wouldn't run using the original /bin/sh. Finally start the installer:

> tar xjf install_v10.93.3.3.tar.bz2
> cd DriverInstall/
> sed '1s/.*/#!\/bin\/bash/' install.sh > install.sh
> ./install.sh

The installer found the existing driver and gave me the option to remove it, however that did not work for me. After the install is done, it is thus necessary to blacklist the old driver so it won't get loaded again:

> echo 'blacklist sky2' > /etc/modprobe.d/blacklist-sky2.conf'
> update-initramfs -k all -u
update-initramfs: Generating /boot/initrd.img-3.2.0-40-generic

Upon rebooting, I checked the network adapter again:

> lspci -k | grep -A5 Ethernet
02:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8056 PCI-E Gigabit Ethernet Controller (rev 12)
	Subsystem: Holco Enterprise Co, Ltd/Shuttle Computer Device 3140
	Kernel driver in use: sk98lin
	Kernel modules: sk98lin, sky2

Finally, I enabled wake-on-lan using ethtool:

> ethtool -s eth0 wol g
> ethtool eth0 | grep Wake-on
	Supports Wake-on: pg
	Wake-on: g
Filed under: Linux, OSS, Technology 11 Comments
3May/130

Lessons from the Jam

Another April, another Game Jam: in fact, it was time for Ludum Dare 26. After the mixed experience that was this year's Global Game Jam, I was curious to see if we were able to apply any of the lessons learned.

The theme of the Jam was "Minimalism", which to me personally isn't so much a theme as it is a design principle. Then again, it feels like our group usually uses the theme as a jumping-off point to come up with ideas, but doesn't adhere to it too closely beyond that. It might be worth while to try to come up with an idea for a game next time before applying the theme to it, and thus avoid being too constrained initially.

Unlike last time around, we picked a game mechanic (Platformer) that all team members knew and understood early on. Changing details of a game later is easy, as long as everyone is aware of how the game is meant to play. Also, we were lucky to have a team member create artwork for us, which I think ultimately made the presentation much more impressive. I mean, come on, look at that guy:

 

On the technical side, we decided to use Gamemaker this time around after having used Crafty.js for the past two jams. Crafty wasn't bad, but had some serious issues with sound and cross-browser performance, so we hoped to get a little more mileage out of the arguably more advanced Gamemaker. I had done some testing with it prior to the jam, and felt pretty comfortable that we could implement a 2D platformer.

After using it for a weekend-long project, I've come to the conclusion that I wouldn't want to use it in the future. Like any tool there is a learning curve, which with Gamemaker is pleasantly gradual. You can either write code in a proprietary language (GML), or use a more graphic approach dragging actions and events onto game objects. However, I found that for most advanced goals, the graphical approach was too limited. Also mixing the two approaches can get confusing fast, since code can be hiding behind numerous event handlers. On multiple occasions I was reminded of coding in Visual Basic 6, which is not an experience I had hoped to never go back to.

Additionally, there were a lot of minor annoyances, such as inconsistent naming schemes and a fairly primitive language - call me picky, but automatic string conversion for primitive data types should be pretty standard for a programming language in 2013. And having multiple, different IDs per object per instance in both the Gamemaker editor, and then at runtime did make things way more complex than they need to be in my opinion.

As of right now, it looks like we (just like everyone else these days?) are gravitating towards Unity for our next jam, although I do feel that I want to give Construct 2 another look after hearing some good things about it.

At this point, I haven't spent a lot of time looking at games other people made, but I will try to get around to that this weekend. If you'd like to check out the fruit of our labor, Molbert is available from the Ludum Dare page.

Filed under: Programming No Comments