Applet Issue

Started by roadkillguy, October 11, 2011, 01:41:58

Previous topic - Next topic

roadkillguy

Hey guys,

I've having some trouble with LWJGL in an applet.  When I first load the page I'm testing it on, everything works great.  I'm able to render a simple white quad.  After refreshing the page, it no longer works.  This is what the console looks like:

Java Plug-in 1.6.0_26
Using JRE version 1.6.0_26-b03 Java HotSpot(TM) Client VM
User home directory = /home/david

YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE
AYE THE APPLET BE STARTIN' NOW

[THIS IS WHEN I REFRESH THE PAGE]  (Edit: The error actually comes after this)

java.lang.Exception: comp is null
	at sun.plugin2.applet.Plugin2Manager.runOnEDT(Plugin2Manager.java:3515)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Plugin2Manager.java:1701)
	at java.lang.Thread.run(Thread.java:662)
AYE THE APPLET BE STOPPIN' NOW
YARR THE SYSTEM BE DESTROYIN' YER APPLET AYE
org.lwjgl.LWJGLException: X Error - disp: 0x876eef8 serial: 517 error: BadWindow (invalid Window parameter) request_code: 2 minor_code: 0
	at org.lwjgl.opengl.LinuxDisplay.globalErrorHandler(LinuxDisplay.java:313)
	at org.lwjgl.opengl.LinuxKeyboard.nSetDetectableKeyRepeat(Native Method)
	at org.lwjgl.opengl.LinuxKeyboard.setDetectableKeyRepeat(LinuxKeyboard.java:152)
	at org.lwjgl.opengl.LinuxKeyboard.destroy(LinuxKeyboard.java:163)
	at org.lwjgl.opengl.LinuxDisplay.destroyKeyboard(LinuxDisplay.java:1181)
	at org.lwjgl.input.Keyboard.destroy(Keyboard.java:349)
	at org.lwjgl.opengl.Display.destroyWindow(Display.java:349)
	at org.lwjgl.opengl.Display.access$400(Display.java:62)
	at org.lwjgl.opengl.Display$5.destroy(Display.java:886)
	at org.lwjgl.opengl.Display.destroy(Display.java:1136)
	at ClientMain.destroy(ClientMain.java:91)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Plugin2Manager.java:1803)
	at java.lang.Thread.run(Thread.java:662)
YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE
LWJGLException:
org.lwjgl.LWJGLException: X Error - disp: 0x876eef8 serial: 521 error: GLXBadWindow request_code: 155 minor_code: 32
	at org.lwjgl.opengl.LinuxDisplay.globalErrorHandler(LinuxDisplay.java:313)
	at org.lwjgl.opengl.LinuxDisplay.nDestroyWindow(Native Method)
	at org.lwjgl.opengl.LinuxDisplay.destroyWindow(LinuxDisplay.java:560)
	at org.lwjgl.opengl.Display.destroyWindow(Display.java:351)
	at org.lwjgl.opengl.Display.setParent(Display.java:470)
	at ClientMain.init(ClientMain.java:34)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Plugin2Manager.java:1637)
	at java.lang.Thread.run(Thread.java:662)

Sorry about the pirate speak, I'm just testing :D.  (At least you can tell what happens when)

EDIT: It seems like it can do this, freeze the browser, or immediately throw a LWJGLException.

Java Plug-in 1.6.0_26
Using JRE version 1.6.0_26-b03 Java HotSpot(TM) Client VM
User home directory = /home/david

YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE
LWJGLException:
org.lwjgl.LWJGLException: X Error - disp: 0x9bae9e0 serial: 32 error: BadValue (integer parameter out of range for operation) request_code: 1 minor_code: 0
	at org.lwjgl.opengl.LinuxDisplay.globalErrorHandler(LinuxDisplay.java:313)
	at org.lwjgl.opengl.LinuxDisplay.nCreateWindow(Native Method)
	at org.lwjgl.opengl.LinuxDisplay.createWindow(LinuxDisplay.java:473)
	at org.lwjgl.opengl.Display.createWindow(Display.java:306)
	at org.lwjgl.opengl.Display.create(Display.java:899)
	at org.lwjgl.opengl.Display.create(Display.java:808)
	at org.lwjgl.opengl.Display.create(Display.java:769)
	at org.lwjgl.opengl.Display.create(Display.java:790)
	at ClientMain.init(ClientMain.java:36)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Plugin2Manager.java:1637)
	at java.lang.Thread.run(Thread.java:662)



So it looks like I'm not initializing something correctly.  I'm not understanding why any given run would be different than another..  What could be causing this?  What should I do?

Here's my key functions:

   
public void init()
	{
		System.out.println("YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE");
		
		try
		{
			setLayout(new BorderLayout());
			
			m_canvas = new Canvas();
			add(m_canvas);
			
			m_canvas.setIgnoreRepaint(true);
			setVisible(true);
			
			Display.setParent(m_canvas);
			Display.setDisplayMode(new DisplayMode(640,480));
			Display.create();
			
			m_loaded = true;
			showStatus("Done Loading.");
		}
		catch(LWJGLException e)
		{
			System.out.println("LWJGLException:");
			e.printStackTrace();
			//System.exit(0);
		}
	}
	
	public void start()
	{
		if(m_loaded)
		{
			System.out.println("AYE THE APPLET BE STARTIN' NOW");
		
			long logicTimer = System.currentTimeMillis();
		
			m_stateEngine.changeState(MainState.instance());
		
			while (m_stateEngine.isRunning())
			{
				pollInput();
			
				long current = System.currentTimeMillis();
			
				if(current > logicTimer)
				{
					while(current > logicTimer) //CATCH UP IF NECESSARY
					{
						logicTimer += 16; //60 Updates Per Second
					
						m_stateEngine.logic();
					}
				}
				else
				{
					m_stateEngine.render();
				
					Display.update();
				}
			}
		}
	}
	public void stop()
	{
		System.out.println("AYE THE APPLET BE STOPPIN' NOW");
	}
	public void destroy()
	{
		System.out.println("YARR THE SYSTEM BE DESTROYIN' YER APPLET AYE");
		
		Display.destroy();
		remove(m_canvas);
		super.destroy();
	}


Thanks a bunch.

Matzon


roadkillguy

Unfortunately, no.  It gets halfway, and then quits with the message (In the Java Console):

This occurred while 'Extracting downloaded packages'
The certificate(s) in linux_natives.jar do not match the AppletLoader!
java.lang.Exception: The certificate(s) in linux_natives.jar do not match the AppletLoader!
	at org.lwjgl.util.applet.AppletLoader.extractNatives(AppletLoader.java:1819)
	at org.lwjgl.util.applet.AppletLoader.run(AppletLoader.java:876)
	at java.lang.Thread.run(Thread.java:662)


Could my error be my while loop?  As far as I know, Display.isCloseRequested() doesn't work in this case because it's an applet, not its own window.

I apologize, but I've never really used java before and I'm coming from c++.  I understand OpenGL, just not applets.

Thanks for the help.

kappa

just curious, what type of Linux system are you running? also which JRE (Oracle or OpenJDK)?
I've run http://lwjgl.org/applet on 64bit linux, Oracle JRE 1.6 and it works without issue.
Does it happen every time? even if you refresh?

As for your first post, it seems that you are creating the Display before the canvas is visible properly, you should have a look at Basic Applet Tutorial on the wiki, which shows a method to create the display once canvas is visible.

roadkillguy

I'm using Ubuntu, and Oracle(The package name is sun-java-6) I believe.  I removed OpenJDK(Iced tea?) because I didn't have the Java Control panel.  (Maybe that was wrong?)

QuoteAs for your first post, it seems that you are creating the Display before the canvas is visible properly, you should have a look at Basic Applet Tutorial on the wiki, which shows a method to create the display once canvas is visible.
Interesting.  I'll mess with that.  The problem is, I cant get my browser (Google Chrome or Firefox) to stop caching my applet in ram.  I have to restart the entire browser.

roadkillguy

Ahh there we go.

Quote from: the wikiHowever before we can create the Display we need to make sure that the canvas is ready to be drawn on. To ensure this we will use the addNotify() method of the awt canvas to tell us when the canvas is ready and that we can create the Display. Similarly we will use the removeNotify() to inform us that the canvas is about to be destroyed and we should clean up and close the native Display.

It was my lack of addNotify and removeNotify functions.  I can see why that would randomly crash now.  It could have also been that there was no Canvas.setSize function (Causing the value out of range (Maybe that's just a c++ memory thing))

Edit: addNotify() and removeNotify() definitely stopped the freezing problem, but it's still giving the bad window error on refresh.  Invalid window parameter?  My code is pretty much the same as the final threaded code on the wiki.

YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE
AYE THE APPLET BE STARTIN' NOW

[REFRESH HERE]

AYE THE APPLET BE STOPPIN' NOW
YARR THE SYSTEM BE DESTROYIN' YER APPLET AYE
YARR THE SYSTEM BE INITIALIZIN' YER APPLET AYE
org.lwjgl.LWJGLException: X Error - disp: 0xffffffffb2c15448 serial: 969 error: BadWindow (invalid Window parameter) request_code: 2 minor_code: 0
	at org.lwjgl.opengl.LinuxDisplay.globalErrorHandler(LinuxDisplay.java:313)
	at org.lwjgl.opengl.LinuxKeyboard.nSetDetectableKeyRepeat(Native Method)
	at org.lwjgl.opengl.LinuxKeyboard.setDetectableKeyRepeat(LinuxKeyboard.java:152)
	at org.lwjgl.opengl.LinuxKeyboard.destroy(LinuxKeyboard.java:163)
	at org.lwjgl.opengl.LinuxDisplay.destroyKeyboard(LinuxDisplay.java:1181)
	at org.lwjgl.input.Keyboard.destroy(Keyboard.java:349)
	at org.lwjgl.opengl.Display.destroyWindow(Display.java:349)
	at org.lwjgl.opengl.Display.setParent(Display.java:470)
	at ClientMain$1.run(ClientMain.java:28)
Exception in thread "Thread-16" java.lang.NullPointerException
	at org.lwjgl.opengl.GL11.glMatrixMode(GL11.java:2052)
	at MainState.init(MainState.java:16)
	at StateEngine.changeState(StateEngine.java:26)
	at ClientMain.mainLoop(ClientMain.java:164)
	at ClientMain.access$000(ClientMain.java:13)
	at ClientMain$1.run(ClientMain.java:37)
AYE THE APPLET BE STARTIN' NOW


I guess the plus side to this is that it's 100% reproducible, and it will go away after 3-4 refreshes.  (It starts over)

roadkillguy

I found my problem! ;D

Display.destroy() wasn't being called.  In my first applet, it was in the destroy function, and it was throwing an error every time (The Canvas wasn't ready.)  In my second time around (Copied code from the wiki with threading and stuff) I left that command out.  After further examination, I found that I left it out and put it back in.  It now works perfectly.

Thanks for the help.

jediTofu

I think with applets, it's fine to have your deletion code in destroy, as long as you have your creation code in init.  According to the official java documentation, these methods are supposed to be guaranteed to be called (and only once; however, I put checks in my code just in case).  However, the destroy method does have a time cap, but this time cap would also be indirectly applied to the removeNotify method.

I believe addNotify and removeNotify are more for non-applet window games.  In my opinion, this still isn't a great way for LWJGL to do things though...possibly a shutdown hook would be better?

This is a copy & paste of what I usually use, with no problems reported from users yet.  Config class contains the Canvas as a variable, etc.  Obviously not all of the code was included here:

*Some redundant stuff in code, don't remember why I have it in there (like setting size twice); and I called setPreferredSize without calling pack, but nothing that should affect result

*game.destroy() and game.create() is where I call LWJGL deletion/creation code, but the point is that I'm not using notify

public class AppletMain extends Applet implements Runnable {
  private static final Color BG_COLOR = Color.BLACK;
  private static final Logger LOGGER = Logger.getLogger(AppletMain.class.getName());

  private Config config;
  private boolean isRunning;
  private Thread thread;

  @Override
  public void destroy() {
    isRunning = false;
    if(thread != null) {
      try {
        thread.join();
      }
      catch(InterruptedException ex) {
        LOGGER.log(Level.WARNING,ex.getMessage(),ex);
      }
      thread = null;
    }
    if(config != null && config.canvas != null) {
      remove(config.canvas);
      config.canvas = null;
    }
  }

  @Override
  public void init() {
    if(config != null || thread != null) {
      return;
    }

    config = new Config();
    isRunning = false;

    setBackground(BG_COLOR);
    setIgnoreRepaint(true);
    setLayout(new BorderLayout());

    config.canvas = new Canvas();
    config.canvas.addComponentListener(new ComponentAdapter() {
      @Override
      public void componentResized(ComponentEvent e) {
        config.isResized = true;
      }
    });
    config.canvas.addFocusListener(new FocusAdapter() {
      @Override
      public void focusGained(FocusEvent e) {
        config.canvas.requestFocus();
        config.canvas.requestFocusInWindow();
      }
    });
    config.canvas.setBackground(BG_COLOR);
    config.canvas.setFocusable(true);
    config.canvas.setIgnoreRepaint(true);
    config.canvas.setPreferredSize(new Dimension(getWidth(),getHeight()));
    config.canvas.setSize(getWidth(),getHeight());
    add(config.canvas,BorderLayout.CENTER);
    config.canvas.setVisible(true);
    config.canvas.requestFocus();
    config.canvas.requestFocusInWindow();

    thread = new Thread(this);
    thread.setUncaughtExceptionHandler(new ExceptionHandler());
    isRunning = true;
    thread.start();
  }

  private void load() {
  }

  public void run() {
    Game game = null;
    try {
      load();
      game = new Game(config);

      try {
        game.create();
      }
      catch(Throwable t) {
        LOGGER.log(Level.SEVERE,t.getMessage(),t);
        Util.errorDialog("Failed to load LWJGL. Please read the Manual in the link below.",t);
        return;
      }
      game.load();

      while(isRunning && game.isRunning()) {
        game.run();
      }
    }
    finally {
      if(game != null) {
        game.destroy();
        game = null;
      }
    }
  }

  @Override
  public void start() {
    config.isViewing = true;
  }

  @Override
  public void stop() {
    config.isViewing = false;
  }
}
cool story, bro

roadkillguy

But that's where I first had them... and it failed miserably.

kappa

Do keep in mind when it comes to applets, shutdown hooks aren't guaranteed to work or even be fired, once the page is closed (or you navigate to another) you have only 200ms (it use to be 1000ms) to do all the cleanup, after this time the applet is interrupted and killed immediately. There is some obscure applet parameter somewhere that can allow you to set the timeout value to a maximum of 3000ms but even this isn't much time and doesn''t guarantee that any shutdown code will run.

jediTofu

Quote from: kappa on October 12, 2011, 09:13:05
Do keep in mind when it comes to applets, shutdown hooks aren't guaranteed to work or even be fired, once the page is closed (or you navigate to another) you have only 200ms (it use to be 1000ms) to do all the cleanup, after this time the applet is interrupted and killed immediately. There is some obscure applet parameter somewhere that can allow you to set the timeout value to a maximum of 3000ms but even this isn't much time and doesn''t guarantee that any shutdown code will run.

In applets, I don't use shutdown hooks, and the destroy method is supposed to be guaranteed to be run (according to official java doc since 1.4 or whenever).  Also, the time cap would also indirectly affect removeNotify as well.
cool story, bro

kappa

Quote from: jediTofu on October 12, 2011, 13:20:46
In applets, I don't use shutdown hooks, and the destroy method is supposed to be guaranteed to be run (according to official java doc since 1.4 or whenever).  Also, the time cap would also indirectly affect removeNotify as well.
Yeh sure the destroy method is called, but again this is all included in the 200ms limit.

roadkillguy

But the problem was, destroy was being called before the canvas was ready to be destroyed, and when I called Display.destroy() it crashed and wouldn't refresh.  I know it's guaranteed to be called, but it wasn't being called at the right time.

jediTofu

Quote from: kappa on October 12, 2011, 13:23:18
Quote from: jediTofu on October 12, 2011, 13:20:46
In applets, I don't use shutdown hooks, and the destroy method is supposed to be guaranteed to be run (according to official java doc since 1.4 or whenever).  Also, the time cap would also indirectly affect removeNotify as well.
Yeh sure the destroy method is called, but again this is all included in the 200ms limit.

The ms limit would also apply to removeNotify though.  In the samples provided in lwjgl zip, they do a thread.join() in remove notify.  Remove notify is not called until in destroy where they call remove(canvas) (or potentially after if you do not call remove(canvas)).  I just go ahead and call the thread.join() in destroy.

They equate to the same thing in applets.  I feel though that I may save some time not relying on #remove and then #removeNotify, but going directly to thread.join().  I also feel that it's a more "non-hacky" solution, as #removeNotify is not intended to be overridden.

Either way, it's the same thing, and both have the 200 ms limit or w/e, so I guess it doesn't really matter...
cool story, bro