Monday, December 24, 2012

Original Generic Reporting Intranet Release

Although it has only been about a month since the last release one feature that was really missing was "back-end security". Of course the support for SSL encryption of the traffic between the web server and the client is a given; however the web server must communicate with the authentication service and any server that actual host reports that are available via this tool-set.

Hence the latest release (available from here) - version 0.9.0 - adds RSA Encryption support for those communications. This is entirely optional but is strongly recommended. However it is fairly easy to set up; but once enabled all servers that provide reports must make use of it.

The actual protocol in use attempts to be quite lightweight - so only the initial exchange to generate a random key using RSA encryption - once key has been securely exchanged the remainder of the communication will make use symmetric compression for improved performance.

Perl provides two basic modules for providing RSA encryption:

Crypt::RSA

Crypt::OpenSSL::RSA

OGRI can make use of either - however whichever is used must be used consistently across all machines! This is simply because the format and content of the public/private key files are not inter-changeable.

Tuesday, December 11, 2012

New TruePackager2 Release

It's taken a good while to release this significant update to the packaging suite; but hopefully it will be worth it. This software package consists of over 35,000 lines of Perl code and around 200 pages of documentation; hence the time between releases!

As normal there are a whole host of fixes but some notable additional functionality too:

  • The "tp2 list --namespaces --all" functionality will list the capabilities the current user has in the namespaces listed.

  • A new "allow_list" capability has been added. This allows the system administrator to restrict UNIX permissions for a namespace, but still allows users access to view the content if they are allowed.

  • The "tp2 show_ns" has been extended to show additional details, including namespace package signing requirements and also lists known DSA signatures for the namespace.

  • The functionality offered by the TP2 daemon has been enhanced and so the install with "--diffs" works via the daemon and current repository settings are passed across for installs to.

If you've not come across TP2 now might be a time to look; it has been written to provide robust, audited package installation facilities aimed at corporations when require cross-environment deployments. It offers some unique features including "preview installs/removes", a complete audit log; flexible dependency handling; automatic file system growth; access control lists; flexible package signature enforcements; package bundles; atomic installs, etc.



Monday, November 5, 2012

Ogri Source Refreshed

Introduction


After quite a bit of refinement the latest version of Ogri has been pushed up the google projects server (link below). This is classed as only version 0.0.4 but the code in reality is probably really 1.0.4 but will only truly be numbered in that manner once a couple of kinks are finally ironed out.

This is the first release where some of the portal access management can be delegated to "owners" of the portal by site administrators. Next comes the concept of "voters" for portals allowing the democratic decisioning (if you want it) for defining whether access to certain portals for requesting users should be granted or not.

Documentation

This release ahs been delayed whilst I complete the user guide and supplements the installation guide - both available on the Google Code downloads page. As always I've veered towards simplicity but welcome any feedback.

User Management

The tool can be quite easily integrated into "Single sign-on" services and also shows how a interface to LDAP-based group access can be performed too.

Currently site administrators can also manage users alerting to ensure they get emailed when certain new reports come into existence.

What Next?

I'll probably get the "portal voting" functionality in place soon and then call that version 1.0.0. Along the way it will probably pick up a few more preferences and administration functions.

When will this appear? Possibly note until I've done some minor updates of some of my other projects:

  • cm2 - improvements to mass code check-ins, change-set generation/application improvements, optional relaxation of the check in/out permissions.
  • truecl - work through some of the enhancements currently logged. This is a huge project (think 100,000 lines of code for a single person), of great complexity and I've enjoyed a break from the brain-ache, but time has come to jump back into the code and push this forward again!

Links

Ogri - the "Original Generic Reporting Intranet" can be found here.

Monday, October 22, 2012

Original GRI Source Uploaded

Before spending a fair proportion of my coding time working on the Javascript/Web-2/AJAX interface known as "GRI 2" I had the original as a rudimentary series of pages linked together with a bit of Perl.

It was fairly successful as far as it went; a basic web-based reporting portal, but didn't offer the glitz and interactivity that some seemed to crave. Hence I put it aside and worked on a different front-end whilst keeping the server side as compatible as possible (with extensions to facilitate the asynchronous nature of the requests driven by AJAX).

That is ongoing (not a small task). However I came to realise that the simplicity of the original code was a strength not to be over-looked. Yes the interface is basic; but the requirements for viewing the reporting application are very modest.

With that taken into account I've taken the code from my local virtual environments, and have begun the process of improving portability, adding some more features, and also improving GUI-based administration and posted to Google code.

The install guide has been published too - a user guide is in the works. The code is usable as it stands so if you need a web-based report portal with good security, distributed report management and low system requirements take a look!

http://code.google.com/p/old-generic-reporting-intranet/

Monday, August 13, 2012

Skulker Update Out

Skulker is pretty mature software these days; not much goes wrong and in the environments I run it on a daily basis it quietly goes about its work; handing 1,000,000 files a month and saving 6Tb of disk space in one organization I work in.

However recently I was made to consider a requirement for auditing that required logs to be integrity checked and since Skulker manages the aspect on compressing logs and removing them once old enough it only seemed sensible to extend Skulker to provide the integrity checking facilities too.

Hence for the last few weeks my spare evening time has been spent adding these features and some of the others that had been collecting in the issues log. The coding, testing and documentation changes are now complete and the updates can be found on the relevant Google code page here.

I'll probably write a separate post to describe a typical use of the new checksumming functionality soon; suffice to say it attempts to remain true to my mantra of keeping all things as simple as possible.

Technically the only really interesting thing about the new checksumming functionality is that the two new features (chksumadd and chksum) both support multi-threading. Given that checksum calculation can requires a full sequential read of the file it made sense to provide this facility. Of course whether this makes a difference depends on your Perl implementation, the size and number of files covered (larger files make better sense for threading) and of course the IO and CPU facilities of the machine on which you are running it.

Saturday, June 30, 2012

TrueCL Update finally out

Ok ... so it is about 6 months late, but hopefully worth waiting for. This release has over 100 fixes and enhancements as can be seen from the following link:

0.9.5 Fixes/Enhancements

Still not really ready for a 1.0.0 release as such ... but getting pretty close on Linux. The 2nd-class architecture supported of Solaris, AIX, HP-UX and BSD are a bit further behind unfortunately, though they should still work.

This is a big project; with around a 300 page manual updated and 100k code lines across multiple operating systems is a lot of work; but I'm pleased with the results and are greatly looking forward to a 1.0.0 release later in 2012.

The latest packages and documentation can be found in the download tab on the projects source code page:

TrueCL Source Code

Friday, March 30, 2012

Another TP2 Release

Although only a few days back a release of tp2 was made available, an update has followed. This improves the format of the "tp2 verify" package verification output ... but the main change is support for two new namespace ACL's:

ALLOW_PREVIEW_INSTALLERS - given a list of names these users are able to perform "tp2 install --preview ..." commands to perform preview installs.

ALLOW_PREVIEW_REMOVERS - specifies a list of users that are able to perform "tp2 remove --preview ..." to perform preview remove commands.

Note that membership of the "ALLOW_INSTALLERS" assumes membership of "ALLOW_PREVIEW_INSTALLERS - the same being true for the remove ACL too.

Tuesday, March 20, 2012

True Packager 2 Release 1.3.3 Features - Part 2

Passing Environment Variables

When you are running as the owner of a namespace when you attempt to install or remove a package the software will run the relevant programs natively; and hence have access to the current environment.

However if you are running as a user that is authorised (but does not own) the namespace in question, all activity is spawned via the daemon process. This has the following impact:

  • The actual process doing all the work is initiated by the daemon; the process in the terminal is simply relaying the information it receives in this instance.
  • Since the process installing the package is doing so via the daemon it is no access to the environment variables that the caller has available currently.
However from 1.3.3 onwards this problem can be overcome by making use of an additional optional section in the package configuration file, namely:

  <env>
    ENV_VAR1
    ENV_VAR2 
  </env>

The "env" element allows you to list a series of environment variables that the caller and the spawned install/remove session will pass between them. When such is part of the package those variables will be communicated from the calling shell and made available to the four following scripts:

preinstall
postinstall
preremove
postremove

Getting User Responses
This is still in the early stages of development but the idea is that the package should be able to configure a series of queries which the user can respond to and these responses are then made available to the two installation scripts; "preinstall" and "postinstall".

Again this is achieved via a new section in the package configuration file, namely the optional "response_info" element. This will have a separate "q" element for each query that the user is asked. Consider the following example:

  <response_info>
    <q text="Please enter your age" default="5" 
       type="integer" range="1 99" label="get_age"/>
    <q text="Please enter your email address" default="%USERNAME%@%HOSTNAME%" 
       type="text" label="get_email" pattern="^\S+@\S+" 
       hint="Please enter valid email address format"/>
  </response_info>

In this case two questions will be asked. The utility will attempt to perform validation and range checking for numbers, and for text queries it can optionally include a "pattern" attribute to ensure the response entered matches the pattern in question.

The "default" attribute is mandatory - this will allow installations to take default values if the user responses are not available. This is designed for headless installs; even though that is not yet available.

In such cases the preinstall and postinstall scripts have these values entered as part of the environment - these are in variables TP2_RESPONSE_label. Hence in the above example:

TP2_RESPONSE_get_age
TP2_RESPONSE_get_email

For more information see the available documentation on the google code downloads page: true packager 2 downloads.

Javascript simple Game writing - main loops

Basic Main Loop
Firstly the demo site is here: main_loop_demo_1

Here the main-character is animated using the following code:

setInterval( function() {game_loop(maze1,ctx2,my_pacman);},50);

The "game_loop" function being:

function game_loop(maze,ctx,pacman) {
move_pacman(maze,pacman);
draw_sprites(maze,ctx,pacman);
}

So every 50ms the game_loop function is called to move the pac-man object and then re-draw it. This is equivalent to 20 frames-per-second - the ideal is 50.

Of course you could simply change the "50" in the "setInterval" command to 1000/50 to "20", but some things should be considered:

  • How to we know whether the client browser can cope with this animation rate?
  • Should our game take all resources it can or take account of the load on the client browser?

My approach is to always take account of the end-user environment - and so if attempting 50 frames per second puts stress on the client system we should not do so. Hence a better approach is to try for 50 frames and reduce if not possible.

An Aside - Closures
Like most things, it took me a while to get to grips with closures. Put simply, they allow references to variables and functions to "hang around" after a calling function has returned. Consider the following code:

function set_up_x() {
var x=20;
  return function next_x() {
    return x++;
  };
}

If you have Node.js installed (very useful) you can run the above interactively and calling "y()" see the returned value increase:

$ node

> function set_up_x() {
... var x=20;
...   return function next_x() {
...     return x++;
...   };
... }




> y=set_up_x(44);
[Function: next_x]
> y();
20
> y();
21
> y();
22




So in the above example the function "set_up_x" has the variable "x" defined locally - and this is visible to the anonymous function (as returned by the function call). The result is the "x" remains visible to the anonymous function ... hence the function gains a "static" variable - most useful!

Notice that we must call "set_up_x" and use the return value (the function) to actually perform the work!

More Advanced Main Loop
We can use the concept of a closure for our main loop - here we don;t actually use one function to return another; but use one function to define another and then use setInterval to call that function - effectively in the closure.

The concept is:

function call_me(var1,var2) {
var closure_var1=xx;
var timer=setInterval(function() {my_hidden_loop(var1,var2);},interval);


  function my_hidden_loop(my_var1,my_var2) {
   // do what we want and alter / remove timer if necessary..
  clearInterval(timer);
  timer=setInterval(function() {my_hidden_loop(var1,var2);},new_interval);
  }
}

So if the code calls "call_me(x,y)" - then the function "my_hidden_loop" will start running at regular intervals ... with access to the current values of "x" and "y". More importantly it can change/access the current values for the variables enclosed by "call_me" - including "closure_var1" and "timer" itself.

Firstly the loop in all it's glory:


function handle_timed_mainloop(maze1,ctx2,my_pacman) {
var want_fps=50;
var min_fps=20;
var current_fps=50;
var calc_interval=1000/current_fps;
var timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);


function game_loop(maze,ctx,pacman) {
var start=new Date().getTime();
move_pacman(maze,pacman);
draw_sprites(maze,ctx,pacman);
var end=new Date().getTime();
if( (end-start*2) < calc_interval && current_fps < want_fps) {
// Not taken much time so increment frame rate
clearInterval(timer);
current_fps++;
calc_interval=1000/current_fps;
timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);
console.log("Increased frame rate to ",current_fps);
} else if((end-start*2) > calc_interval && current_fps > min_fps) {
// Asking too much of system, so slow down...
clearInterval(timer);
current_fps--;
calc_interval=1000/current_fps;
timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);
console.log("Decreased frame rate to ",current_fps);


}
// console.log("Duration in ms = ", end-start);
}
}

Firstly notice that the only things that are actually executed in this function are:

var want_fps=50;
var min_fps=20;
var current_fps=50;
var calc_interval=1000/current_fps;
var timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);

The rest of the function is actually the "game_loop" function which is executed by the final line above. The result is that the call to the "handle_timed_mainloop" function will quickly exit - after ensuring that "game_loop" is started.

What the code above does is attempt to start the game with 50 frames per second - as per the "current_fps" setting. The "want_fps" (which is also 50) defines the ideal and the minimum defines what we should not slow down beyond. This information is used to set "calc_interval" and call "game_loop":


var calc_interval=1000/current_fps;
var timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);

Now of course the aim of "main_loop" is to perform all game logic - for the moment just moving the pacman around the maze and listening to events. The events have actually been defined prior as:

document.onkeydown=function() {handle_key(event,my_pacman)};

This updates the "my_pacman" object as appropriate for the "main_loop" programming to act on.

In essence the "main_loop" is actually just two function calls:

move_pacman(maze,pacman);
draw_sprites(maze,ctx,pacman);

The first function call takes in the current position, current direction and next direction and updates the position of the pacman. The second actually draws the pacman at the new position (erasing the old canvas content first of course).

Now the interesting bit... notice that we wrap the above two calls with getting the current time (in milliseconds)?

var start=new Date().getTime();
move_pacman(maze,pacman);
draw_sprites(maze,ctx,pacman);
var end=new Date().getTime();


So by taking the "end" from the "start" we can estimate the time our current browser takes to perform a frame. We can then use this to increment the frame rate if the time is small enough, or decrease it if the time it takes is too long:


if( (end-start*2) < calc_interval && current_fps < want_fps) {
// Not taken much time so increment frame rate
clearInterval(timer);
current_fps++;
calc_interval=1000/current_fps;
timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);
console.log("Increased frame rate to ",current_fps);
} else if((end-start*2) > calc_interval && current_fps > min_fps) {
// Asking too much of system, so slow down...
clearInterval(timer);
current_fps--;
calc_interval=1000/current_fps;
timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);
console.log("Decreased frame rate to ",current_fps);
}

The only points beyond the obvious this the above code are that if we are changing the interval we clear it, calculate a new interval (based on the new attempted frame rate) and set the timer off again:

clearInterval(timer);
...

calc_interval=1000/current_fps;
timer=setInterval( function() {game_loop(maze1,ctx2,my_pacman);},calc_interval);




So there you have it - a game loop with a dynamic frame rate based on the host performance of the previous frame update.

The code in action can be seen here: main_loop_2nd_demo

Current Problems with Advanced Main Loop

Although the adaptive animation frame rate works ... so does have some issues:
  • The pac-man sprite appears to speed up (or slow down)
  • The rate of pac-man animation appears to speed up (or slow down)
The basic problem is the rate of movement and animation is naive; every time the move (and thus animate) routine is called it will perform the action. What really needs to happen is to separate the two; and only make changes after a particular time has past since the other change.

The next post will alter the game to add these features...

Friday, March 16, 2012

Using Skulker to Improve Security

Isn't Skulker used to save disk space?
That is indeed the case; but one feature that can be used is that of the "setperms" function - it can be used to change the permissions, owner and group of matched files (UNIX platforms only unfortunately).

Example 1
A few examples might clarify the use. Consider the following requirement:

Find all world-writeable files in the directory "/data" and remove that permission bit.

Doing this via a rule is easy:


<?xml version="1.0" standalone="yes"?>
<skulker_rules>
<defaults
interval="0D"
/>
<rule
n="10"
type="setperms perms=o-w"
match_pattern="/data/.../.*"
                match_by="has_perm('o','w')"
/>
</skulker_rules>


Notice the use of the "has_perm" function to ensure that only files which have world write permissions are matched. Actaully since the "setperms" function is selecting removing the permissions bit the match_by clause in this case is not needed, and so the above can be simplified a little:

<?xml version="1.0" standalone="yes"?>
<skulker_rules>
<defaults
interval="0D"
/>
<rule
n="10"
type="setperms perms=o-w"
match_pattern="/data/.../.*"
/>
</skulker_rules>

In this case all files in the "/data" directory structure are passed to the "setperms" routine - but only files that are world-writeable will be modified (and shown in the log output).

Example 2
Consider the following requirement:

Ensure all gzip compressed log files in the directories "/data/dir1" and "/data/dir2" are changed to only have owner permissions.

<?xml version="1.0" standalone="yes"?>
<skulker_rules>
<defaults
interval="0D"
/>
<rule
n="10"
type="setperms perms=o-wrx,g-rwx"
match_pattern="/data/dir[12]/.*\.gz$"
/>
</skulker_rules>

A couple of points to note about this rule...

  1. The match_pattern shows the use of directory-level pattern matching (both file and directories can contain regular expressions)
  2. The "perms" argument to "setperms" has two sets of changes; firstly "o-rwx" removes all permissions from others, and then "g-rwx" removes all permissions from group attributes.


Tuesday, March 13, 2012

More on GRI 2 - Meta Reports

Although the key aim of GRI 2 is to allow a single web-interface across a collection of hosts to gather reports in a secure manner (customised for each user), the advantage of the JavaScript front-end is that some processing can be handled by the user system and not the server.

This functionality allows GRI 2 to provide "meta reports". Meta reports basically capture raw data from the hosts in question and format the content dynamically as part of the web-interface. This is best demonstrated with by a couple of examples.

Example 1 - Pie Chart
Consider the following data file on a server that is to be presented via GRI 2 to the user:


Object,Number Sold
pies,22
rolls,33
fish,44

This is saved as a report file name and has a type of "csv". Now if it was considered as a standard report then the content when presented to the user would just be as above (opened via a spreadsheet application).

However if this is defined as a meta-report (with a bit of XML) instead it can be presented to the user as:



Example 2 - Line Chart
In a similar manner consider the following report content - again stored as a CSV file:

Total,Used
Date,Total Space,Used Space
17/01/12,100,40
18/01/12,100,45
19/01/12,100,35
20/01/12,100,55

This content is a bit more complex but still GRI 2 allows a simple XML configuration to describe this data in a graphical manner for representation to the user - this time as a line graph:



Summary
Meta-Reports are a key feature of GRI 2. Although still very much in development the prospect of providing end-users with attractive graphical representations of data with out having to generate those images on the end servers is a considered a most useful feature.

Monday, March 12, 2012

True Packager 2 Release 1.3.3 Features - Part 1

Version 1.3.3 of True Packager 2 (or TP2 for short) has been released. This post includes a quick summary of the major differences that the software now supports.

Elevated Privileges
Early versions of TP2 made only two roles available; the namespace owner and "root". You had to be running a command as either of those for a package install to be successful.  Although this fulfilled the original design requirement of the packaging solution, recently it was requested that a namespace should allow certain other users to install packages; not simply the namespace owner.

The concept was simple; the namespace should be owned by a system/application account, but the installs should be done as normal admin accounts to provide better levels of security and auditing. This of course is often combined with only allowing signed packages to prevent just anything from being installed.

Thus as of 1.3.3 if a normal user attempts to install a package into a namespace they do not own they will get a rather cryptic message (needs to be improved!):

$ tp2 verify --namespace opt

Error: The ALLOW_VERIFIERS setting is missing for namespace 'opt'.

The owner of the namespace (or a user that has the capabilities to create or modify namespaces), for example as root:

# tp2 mod_ns opt ALLOW_VERIFERS root,test

Then examining the namespace configuration:

$ tp2 show_ns --namespace opt
Packages installed                       1
Compress Logs?                           Yes
Current log count                        1
File System Growth Unit (MB)             256
Log space usage (MB)                     0.01
Always repackage?                        Yes
Maximum space for logs (MB)              50
Log compression type                     gzip
Remove on reboot                         <None>
Users allowed to verify packages         root,test
Grow file systems automatically?         Yes
Maximum number of logs to keep           2000
Script to run after pkg install          /usr/bin/true
Namespace root directory                 /opt
Duration to keep preview logs (Days)     10
Perform auditing?                        Yes
Temporary files location                 /var/adm/tp2/ns/opt/spool
Script to run after pkg removal          /usr/bin/true
Namespace Owner                          root
Directory to extract packages in         /var/adm/tp2/ns/opt/spool

At this point the normal user "test" should have access to run the verification process on that namespace:

$ tp2 verify --namespace opt --verbose
Log  : Invocation of namespace verification via daemon starting.
Log  : Loading configuration of packages in "opt" namespace...
Log  : ...Done [1 packages]
Log  : Loading any immutable files details for "opt" namespace...
Log  : ...Done [0 files]
Log  : Checking package "tp2" [directories].
Log  : Checking package "tp2" [files].
Error: [tp2] Incorrect file checksum   : /opt/bin/tp2make_ns [is aaa9854607a9c98655cdebbc9d133c5d26daeac2, should be 6c1c1dbd3e2f37e6cbc5a8a97631262fa0c7d67f]
Log  : Total errors found: 1

At present there are four per-namespace privileges that can be configured:

ALLOW_VERIFIERS - allow the list of users configured to verify package configuration.
ALLOW_INSTALLERS - allow the users to install, upgrade, downgrade and clean packages.
ALLOW_REMOVERS - allow the users to remove package installations.
ALLOW_IMMUTABLES - allow the users to add/remove files to the namespace immutable files list.

What next?
Part two of this post will describe facilities to customise the package installation by allowing certain environment variables to be passed to pre/post install and remove scripts, and also to configure one or more queries that can be used to interact with the users prior to running the above scripts too.



Saturday, March 10, 2012

Loading Images in Javascript - Part 1

If you are attempting to write any type of game it is more than likely that you will want to show some images/sprites/animation. I've previously posted about "sprite sheets", but it is worth getting to grip with loading the necessary images in the first place.

What can possibly go wrong?
At first glance loading an image is easy; for example:



<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>testing ...</h1>
<div id="demo_div">
</div>
<script type="text/javascript">
var my_image;
my_image=new Image();
my_image.src='bigcup.jpg';
var ele = document.getElementById('demo_div');
ele.appendChild(my_image);
</script>
</body></html>


The above will appear to work, but most likely the javascript has completed before the image is loaded, and although the image does appear to load - you are sure when it is ready.

Check it out:

http://www.advantsys.co.uk/demos/demos2

Obviously for a game this really isn't good; you can't start the game knowing half the graphics might not be loaded!

Using the "onload" callback
Fortunately there is a solution, but firstly you must understand the concept of asynchronous code execution and callbacks. For example say we want to make the image smaller once it has loaded, then you might attempt:


<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>testing ...</h1>
<div id="demo_div">
</div>
<script type="text/javascript">
var my_image;
my_image=new Image();
my_image.src='bigcup.jpg';
var ele = document.getElementById('demo_div');
ele.appendChild(my_image);
my_image.height=200;
my_image.width=200;
</script>
</body></html>

If you clear the browser cache (to ensure the image is reloaded), then try using the following link:

http://www.advantsys.co.uk/demos/demos3/

You should see the shrunken image is being loaded, rather than the large image loading and then shrinking! This is because certain features in JavaScript are asynchronous, in this instance:

my_image.src='bigcup.jpg';

This simply sets the name of the image and continues the script. Separately the browser will start loading the image, but the code continues to execute. Fortunately JavaScript can be made to indicate when the image has been loaded using the "onload" callback facility. For example:


<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>testing ...</h1>
<div id="demo_div">
</div>
<script type="text/javascript">
var my_image;
my_image=new Image();
my_image.onload=function() { my_image.height=200; my_image.width=200; };
my_image.src='bigcup.jpg';
var ele = document.getElementById('demo_div');
ele.appendChild(my_image);
</script>
</body></html>


Clear your browser cache and try the following link:

http://www.advantsys.co.uk/demos/demos4/


Now the image should load at the original size and only once loaded will it shrink to the smaller size. This happens because once the image has loaded JavaScript will see the property "onload" is set, and execute that code.

Part 2 will describe how to use the "onload" property of images to load all images for the game being developed and only continue once all images are loaded.


Wednesday, March 7, 2012

HTML 5 Canvas Sprite Sheets 101

What the heck is a sprite sheet?
Probably obvious to lots of people but to me (who last wrote a computer game 20+ years ago) unfortunately not. I'll keep this short and to the point.

Sprite sheets are used for animation of an object - and can be used easily by the HTML 5 Canvas element.

Consider the following four frames of animation for my Pacman-style game:





Now you might simply load these 4 png images as separate files - indeed there is no harm in doing so. However it is more efficient instead to create a Sprite-sheet - joining all the files together:


Now we only need to load one image not four - that is all a sprite-sheet is - a collection of images joined together at known offsets to allow easy extraction of each sub-image.


How to create a sprite sheet?
Since I use Linux for my desk top this was easy; I created the 4 individual images shown above and called them:

pac_right1.png
pac_right2.png
pac_right3.png
pac_right4.png

Then the "montage" command from the "ImageMagick" collection of programs was used to join them together:

montage -background "transparent" -geometry 24x24 -tile 4x1 \
  pac_right[1234].png pac_right.png

The result was a file called "pac_right.png" which contained all 4 images at the original resolution of 24x24 as shown in the second example.

How to use a sprite sheet on a Canvas?
This is a matter of loading an image initially, for example (overly simplistic):

var im=new Image();
im.src='images/pac_right.png';

This is overly simplistic because it does not take into account that images are loaded asynchronously - the image might not be available for a short while above the above code has been run - more on that next time!

Once the image is available it is a matter of using the "drawImage" method made available by the canvas element. This takes the following arguments:

canvas.drawImage(
    image,            // The image to draw (our sprite sheet)
    x_offset,         // The X offset in image of partial image to draw
    y_offset,         // The Y offset in  image of partial image to draw
    width,            // Width in pixels of part of image to draw
    height,           // Height in pixels of part of image to draw
    canvas_x_offset,  // Offset in pixels from left of canvas
    canvas_y_offset,  // Offset in pixels from top of canvas
    target_width,     // Width of image to draw on canvas
    target_height     // Height of image to draw on canvas
);         

Given that the sprite sheet in the example of a tile of 4 images across then the "y_offset" I will use will always be 0. The "x_offset values will be 0,24,48,72 for the for partial images.

So the first five parameters determine the image and the portion of it you wish to display on the canvas. The next two - canvas_x_offset and canvas_y_offset determine where on the canvas to put the image. The final two indicate the size of the image to display. Some points of interest:


  • The offset values on the canvas are typically real numbers, but including fractions may slow down rendering the image size the canvas implementation might attempt to dither the image in an attempt to mimic the fractional positioning - so typically make sure whole values are passed.

  • If the target_width and target_height parameters are different than the width and height parameters then the image will be scaled to this new size.
Putting it together
The following takes the concepts above and creates a canvas, loads a sprite sheet and displays the 4 sub-images on the canvas with scaling:



<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var pac_left;
pac_left=new Image();
pac_left.onload=function() { draw_it(); };
pac_left.src='pac_left.png';


function draw_it() {
var canvas = document.getElementById('sprites');
console.log("canvas=",canvas);
var ctx=canvas.getContext('2d');
ctx.drawImage(pac_left,0,0,24,24,0,0,24,24);
ctx.drawImage(pac_left,24,0,24,24,50,0,32,32);
ctx.drawImage(pac_left,48,0,24,24,100,0,40,40);
ctx.drawImage(pac_left,72,0,24,24,200,0,48,48);
}
</script>
</head>
<body>
<h1>testing ...</h1>
<div id="maze" style="position:relative; width:720; height:480">
<canvas id="sprites" style="z-index: 1; position:absolute; left:0px; top:0px" width="720"  height="480"></canvas>
</div>
</body></html>


HTML Canvas Demo 1







Tuesday, March 6, 2012

Games programming - choosing a first language

Introduction
Recently my son has taken an interest in programming (well a bit...) and I thought that was a good excuse for a diverting my attention occasionally and helping him learn in an entertaining manner.

The first hurdle was choosing a language. We originally had a bit of fun with "scratch" and although it helped with the basics doing anything of note quickly became rather painful. However with the concept of logic and variables understood it was a useful exercise.

Next I tried to encourage him with Python; I often use this at work; and thought it clear and precise with a large platform base and mature libraries. No luck however - he simply didn't get interested.

I could go on and laugh at showing him the perversities of Perl (love it) and the obtuse nature of Basic variants. Suffice to say nothing really gelled.

Of course the flash of Flash was a strong contender in his eyes; but the closed-source nature and the limited number of supported platforms put me off.

So in the end the obvious choice was JavaScript.

Why JavaScript?
Personally I was dragged screaming and kicking to JavaScript. This was not out of any kind of stubbornness and an unwillingness to learn something new; it was just that I couldn't see the point. I knew Perl, Python, PHP, C, Tcl and so on - so what did Javascript offer me -  or my son?

However the answer is of course obvious; it is pervasive across browsers and available across different operating systems. More recently with HTML 5 features - particularly the canvas - dynamic graphics are relatively easy.

Performance was always an issue but newer browsers and platforms offer more than enough processing power to pull off most things a 10 year old might want to program.

So JavaScript it is!

Generic Reporting Intranet - Sample soon


In a matter of days a snapshot of the code base for my Report Portal tool should be available via Google Code. Still in development this web-based tool currently looks as below:


I'm currently putting together a short example configuration and supporting document. The tool attempts to act as a portal to gather report content from your many servers and present them via a per-user secure web front-end.

It is fully JavaScript driven and makes use of JSON and AJAX to deliver a desktop-like application experience.

Monday, February 20, 2012

Skulker v1.1.0 Nears Release

It has been a while since the previous release of Skulker and so a few queued requests and an annoying but have been dealt with. See the Skulker page for full details, but the most important being improvements for pattern matching, a new option to set permissions and a working history log.


It has been completed and shortly packages and updated documentation will be posted on the relevant code page.