Preliminaries (tentative)

I discovered something else last-night, and while I should probably keep it to myself for the competitive advantage…
I just can’t bare to see other peoples fantastic games reaching less than their full potencial because they were a few bytes short.

So, if you use the old Java1.0 event model, you significantly reduce the number of class and method references in the constants pool.

Component#handleEvent(Event)
  1. It receives all events (no filtering), so you completely eliminate the call to Component#enableEvents(long).
  2. The Event class is general purpose (used for all Event types), so it completely eliminates references to java.awt.event.KeyEvent & java.awt.event.MouseEvent.
  3. The Event class even has public members, rather than getters! (eliminating references to getKeyCode(), getID(), getButton(), getX() and getY() !!!)
  4. “java.awt.Event” is 3 chars shorter than “java.awt.AWTEvent” =)
  5. “handleEvent” is 1 char shorter than “processEvent” =))))

By my guesstimates, that should save ~50-100 bytes of compressed code!
Just think what Left4KDead could have done with that space! You could have made it a full-on 3D FPS no problemo :wink:

It’s like the Java1.0 event model was written specifically for the 4K contest!!

Only problem (sounded too good to be true) is it’s deprecated (obviously), and while it appears to work in Windows, there’s no guarantee it’ll work correctly on Mac or Linux.

Works on Linux with 6.0.14, but two additional caveats for people handling key input:

  1. You may need to process 4 key event types, not 2, because “action” keys get different types.
  2. Some keys (meta, shift, ctrl) don’t fire events. (Particular nuisance for me because I was thinking of making a Shift-like).

Nothing a little &1 can’t fix!

inputs[e.key] = e.id&1;

The lack of events for modifier keys sux a bit; Java1.0 must have been completely rubbish!
Looks like it’s going to be WASD+Space - sorry to those with ‘wrong’ key layouts ;D

Shudder. You’re going to lose key presses that way.

Tested and it increases code size. You need a field to be able to determine whether the event is the first one, so there go most of the savings from removing the start() method. Then you need additional logic to handle isActive() potentially being false when you enter run(). I tried two different approaches to the latter and they added respectively 18 and 27 bytes after proguard and kzip optimisation.

Of course, you might be able to argue that you shouldn’t get events before start(), and so isActive() should be true. I tried that too, and it added 11 bytes. Instead of a method which unconditionally calls “new Thread(this).start()” you need a guard on isRunning==0 and to set isRunning to 1; and you also need to reset isRunning to 0 at the end of the run() method.

Edit: also, note that I’ve just discovered that with appletviewer 1.6.0_14 the stuff in the API doc about “An applet is marked active just before its start method is called. It becomes inactive just before its stop method is called.” is not true. Printing isActive() in the start() method prints “false”. This explains a problem I’ve observed in the past… (Addendum: having added a workaround for it, I’ve finally got Gravitational Fourks to work reliably in the browser). Anyway, the prudent thing is to assume that !isActive() when entering run.

Relevant code is in AppletPanel.run lines 457 to 492:

                  case APPLET_START:
                  {
                      if (status != APPLET_INIT && status != APPLET_STOP) {
                          showAppletStatus("notinited");
                          break;
                      }
                      applet.resize(currentAppletSize);
                      applet.start();

                      // Validate and show the applet in event dispatch thread
                      // to avoid deadlock.                       
                      try {                           
                          final AppletPanel p = this;
                          final Applet a = applet;

                          EventQueue.invokeAndWait(new Runnable() {
                                  public void run() {
                                      p.validate();
                                      a.setVisible(true);

                                      // Fix for BugTraq ID 4041703.
                                      // Set the default focus for an applet.
                                      if (hasInitialFocus())
					setDefaultFocus();
                                  }
                              });
                      } 
                      catch(InterruptedException ie) {
                      }
                      catch(InvocationTargetException ite) {
                      }

                      status = APPLET_START;
                      showAppletStatus("started");
                      break;
                  }

The setting of status to APPLET_START constitutes setting isActive() to true, so after Applet.start() is called it’s almost guaranteed to yield to another thread before isActive().

This seems to work for me.
Granted I still haven’t bothered trying it in a browser yet ::slight_smile:


import java.applet.Applet;
import java.awt.Color;
import java.awt.Event;
import java.awt.Graphics;

public class H extends Applet implements Runnable {

	private static final int MOUSE_EVENTS = 500;
	private static final int MOUSE_X = 1, MOUSE_Y = 2, MOUSE_DOWN = 3;
	int[] input;
	
	public boolean handleEvent(Event e) {
		if(input==null) {
			// use the presence of the input [] to indicate whether the Thread has been started.
			input = new int[0xFFFF]; //TODO, change the dimension of the array to be a value used elsewhere in the program. 
			new Thread(this).start();
		}
		final int id = e.id;
		if((id&~3)==MOUSE_EVENTS) {
			input[MOUSE_X] = e.x;
			input[MOUSE_Y] = e.y;
			//input[MOUSE_DOWN] = blahblah TODO
		}
		input[e.key] = id&1;
		return true;
	}
	
	public void run() {
		boolean started = false;
		do {
			if(isActive()) {
				started = true;
				
				Graphics g = getGraphics();
				g.setColor(new Color((int)(Math.random()*0xFFFFFF)));
				g.fillRect(0, 0, getWidth(), getHeight());
			}
			else {
				if(started) {
					input = null;
					return;
				}
			}
		}
		while(true);
	}
}

Nice trick to reuse a field - I should have thought of that. However, you might get away with your implementation, but it’s certainly not thread-safe. Make it thread-safe with

		int[] input = this.input;
		if (input == null)
		{
			this.input = input = new int[NUM_KEYS];
			new Thread(this).start();
		}

and (with my skeleton class, at least) it uses exactly the same number of bytes as the version with a start() method. (It’s slightly larger uncompressed, but compresses better).

I don’t think that is necessary.
The Thread I spawn only ever sets the input [] to null.
The EDT Thread only ever assigns a value to the input [] if it is already null.

Multiple Threads can never be concurrently executing the EDT method handleEvent(…).
Multiple Threads can only be concurrently executing run(…) in the code immediately proceeding the ‘input = null’ assignment. (and as this assignment is the last action the Thread takes, it’s safe)

There’s a risk of NPE if the run thread sets input to null between the EDT starting the run thread and finishing the handleEvent call, and Murphy’s law says that it will happen when the judges are testing your game…

Ahhhhhhh, NPEs in the EDT handleEvent(…)…yeah, IAOOB is possible in there too; but who cares - the EDT isn’t killed by Exceptions.
The one thing that mustn’t happen is the application Thread dying to an Exception, as it’ll exit without the input [] being set to null. Consequently preventing the app. ever restarting (until the Browser is restarted).

I checked the JavaDoc for start() and it suggests that the component may not be showing when start() is called, which potentially means that it still might not be showing when run() starts, in which case a call to getGraphics() will probably return null. I found the following article which suggests this can happen
http://java.sun.com/j2se/1.4.2/docs/guide/deployment/deployment-guide/upgrade-guide/article-09.html

Supposing I want to grab the graphics context once (before entering the main game loop), it may be worth doing the following:


run() {
// Game Initialisation
while((g=getGraphics())==null)); // Wait for a valid graphics context
do {
// Game Loop
yield(); // Allow the other threads some execution time
} while (isActive()); // Possibly leave this test to the end of the game loop and hope it is set by now.

Edit: I wonder if the graphics context can go invalid, before I catch isActive() going inactive. Not a nice thought.

It does happen. I’ve seen it, and now check for it.

Yes. Minimise and maximise the browser. If you’re holding onto the graphics object you can get some serious weirdness, especially with Windows (due to Sun deliberately violating the contract with DirectX).

Updated: Here’s my current skeleton for keyboard-only games, using various of Abuse’s suggestions from this thread. 934 bytes compressed with Proguard 4.2 and kzip.
Updated again following criticism of starting the thread in the ctor and making run() contain an infinite loop.

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

/**
 * Testbed for the basics.
 */
public class Skeleton extends Applet implements Runnable
{
	// Constants.
	// Must be at least 1026 if using Java 1.0 event model or someone accidentally
	// hitting Ins will get AIOOBE. Pick a number used elsewhere and reuse it to
	// save a constant pool entry.
	private static final int NUM_KEYS = 1026; 

	private static final int WIDTH = 640;
	private static final int HEIGHT = 480;

	// Shared state. In the spirit of lock-free synchronisation, this is written
	// only by the event thread, and it advances values rather than setting /
	// resetting. This ensures that quick key presses aren't lost.
	private int[] keyEventCount = new int[NUM_KEYS];

	@Override
	public void start()
	{
		new Thread(this).start();
	}

	public void run()
	{
		// Set up event handling. Note: keyDown[key] and keyPressed[key] are 0
		// for false; any other value should be interpreted as true.
		int[] prevKeyEventCounts = new int[NUM_KEYS];
		int[] keyDown = new int[NUM_KEYS];
		int[] keyPressed = new int[NUM_KEYS];

		// Game state.
		int x = 320;
		int y = 320;

		// Set up backbuffer for rendering.
		BufferedImage buf = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
		// Uncomment if you want to set pixels directly.
		//int[] ibuf = ((DataBufferInt)buf.getRaster().getDataBuffer()).getData();

		while (!isActive()) /* Busy-wait */;
		while (isActive())
		{
			try
			{
				long now = System.currentTimeMillis();

				// Events.
				for (int i = 0; i < NUM_KEYS; i++)
				{
					int eventCount = keyEventCount[i];
					// An odd number of events means that it's down. Simple!
					// NB If you're worried about key repeat you might want
					// instead to say
					//     keyDown[i] = (delta >> 1) + (eventCount & 1);
					keyDown[i] = eventCount & 1;
					// The key was pressed since the last loop if the number of events
					// since then is at least two, by the pigeonhole principle, or if
					// the number of events since then is positive and the key is down.
					// Since we only need to catch the second case when delta == 1,
					// we can simplify the key-is-down test a bit.
					int delta = eventCount - prevKeyEventCounts[i];
					keyPressed[i] = (delta >> 1) + (delta & eventCount);
					prevKeyEventCounts[i] = eventCount;
				}

				// Logic.
				if (keyDown[Event.LEFT] != 0) x--;
				if (keyDown[Event.RIGHT] != 0) x++;
				if (keyDown[Event.UP] != 0) y--;
				if (keyDown[Event.DOWN] != 0) y++;

				// Render to backbuffer.
				Graphics g = buf.getGraphics();
				g.setColor(new Color(0x000000));
				g.fillRect(0, 0, WIDTH, HEIGHT);
				g.setColor(new Color(0xffffff));
				g.drawString("Score", x, y);

				// Render backbuffer to front buffer and tidy.
				Graphics gapp = getGraphics();
				if (gapp != null)
				{
					gapp.drawImage(buf, 0, 0, this);
					gapp.dispose();
				}
				g.dispose();

				// Sleep. There are various ways of doing this, but IME you need to
				// sleep rather than yield under Linux.
				long t = System.currentTimeMillis();
				long sleep = 20 - (t - now);
				now = t;
				if (sleep > 0)
				{
					Thread.sleep(sleep);
				}
			}
			catch (Throwable ie)
			{
					
			}
		}
	}

	@Override
	public boolean handleEvent(Event evt)
	{
		// Non-key events have 0 for their key field.
		keyEventCount[evt.key]++;
		return false;
	}
}

So in your Skeleton example you’ve opted for leaving the Thread running forever (until the browser is killed)?
If everyone does that, the judges machines might start to choke by the time they come to play the 30th entrant =D

Other than that, worth highlighting these two:

  1. If you arn’t interested in preventing crashes as a result of erroneous Exceptions
public void run() throws Exception

instead of try/catch.

  1. As far as i’m aware calling g.dispose() is unnecessary for Graphics objects that draw onto offscreen images (BufferedImage in your Skeleton example)
    As you are calling dispose() anyway, the saving will be minimal. Though you could always be naughty and not call dispose() on your onscreen Graphics object too; that’d reap more significant size savings, with only a small chance of it blowing up someones machine :smiley:

I am very worried about this. Some programmers might bypass some formalities to make their game work, not caring about what effect leaving idle resources running in the browser will cause, or they might do so inadvertently.

I guess we’ll need a rule to ensure this doesn’t happen, and also some code templates to help programmers to make sure their applet works right.

Maybe there’s a way to define this in the html applet tag?

[quote]I guess we’ll need a rule to ensure this doesn’t happen, and also some code templates to help programmers to make sure their applet works right.
[/quote]
+1

I was rather assuming that the plugin was more intelligent than that. Bah.

[quote]2) As far as i’m aware calling g.dispose() is unnecessary for Graphics objects that draw onto offscreen images (BufferedImage in your Skeleton example)
As you are calling dispose() anyway, the saving will be minimal. Though you could always be naughty and not call dispose() on your onscreen Graphics object too; that’d reap more significant size savings, with only a small chance of it blowing up someones machine :smiley:
[/quote]
You’re probably correct on the first, but it’s only about 3 bytes. On the second, I believe I have had issues with this also in the past.

I wrote a demo Applet and tried to break it…

Using Appletviewer, isActive() is still false about 50% of the time on entry to run(); Internet Explorer, Firefox and JNLP Applet seem to set isActive() earlier. Adding a yield() before checking isActive() improves matters, although sometimes it takes more than one yield(). Adding while(!isActive()) Thread.yield(); at the start of run() makes AppletViewer work 100%, but is it worth the overhead to fix what is predominantly an Appletviewer problem.

If you grab the Graphics context on Internet Explorer at the start of run(), it doesn’t return null (so no exception), but it isn’t linked to the native peer, so nothing displays. Also Internet Explorer changes the device context everytime you minimise and then maximise the browser. Thus we must do getGraphics() inside the game loop. While you can skip the dispose(), this means that unreleased graphics contexts build up until a garbage collection occurs. The question is whether it is worth including the overhead of dispose() to add robustness to the applet.

Lastly, if the browser navigates away from the page containing the applet or you close the browser, then the graphics context obtained at the start of the loop becomes null, even if it wasn’t null when you performed getGraphics(). The probability of this can be reduced by postponing getGraphics() until just before you need it, testing for null and only drawing on the device if it’s valid. However I found that occasionally, even this isn’t sufficient as windows manages a context switch between the test for null and the draw. i.e. the operation is not atomic. Using try {…} catch (Exception e){} around the draw routine catches the exception, but costs more bytes. The question is whether it is worth adding the overhead of try/catch to catch an exception that only occurs when the user is navigating away from your applet. As far as I can see this error doesn’t actually matter, provided you recreate the Thread when the page is revisited. This suggests creating the thread in start() and terminating it when isActive() goes false is prudent. I’m tempted to omit the try/catch and accept the exception occurs.

If we don’t try to catch exceptions when the graphics context goes null, it becomes more important to postpone grabbing the context to as late as possible in the main loop, just in case some browser returns null the first time through the loop. I haven’t seen this, but haven’t tried every possible browser.

Thus there are tradeoffs between robustness and code size. If you don’t care about appletviewer and null pointer exceptions, then IMO a minimum size template would: create the thread in start(); grab the graphics context as late as possible within the game loop and use it immediately; testing for inActive() at the end of the game loop and terminating the thread if it is false. If the game spends a fair bit of time initialising it may even work in Appletviewer.

Thanks for the awesome and in depth post.

Last year I tried to make my games applets, but i didn’t work. So I need to find and “borrow” some 4k applet base.

Several applet base source codes have been posted in this thread. :wink: