Being like Minecraft (or how to run your libgdx app in web browser)

What's libgdx? It's a game/application development framework in Java. It allows you to write an app, and then run it on Android, on desktop and in the browser. Awesome! If you want to know more, read what its creators say about it.
In this tutorial I'll show you how to run a game written with libgdx in your browser (as a Java applet). So why Minecraft, you might ask - well, Minecraft runs as an applet! (and it's written with Lwjgl - more on that later) And talking about Minecraft is so snazzy these times, you know.
I'm going to use libgdx's "Hello world" as an example "game". I won't cover setting up the project here, Mario (father of libgdx) did it already here.

You should start with something like that:


For applet we'll need to use Lwjgl backend (instead of default Jogl). Don't worry, just one change is required. Edit the HelloWorldDesktop.java file and make it look like that:
 1 2 3 4 5 6 7 8 910
package com.badlogic.gdx.helloworld;
//import com.badlogic.gdx.backends.jogl.JoglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
public class HelloWorldDesktop {
public static void main (String[] argv) {
new LwjglApplication(new HelloWorld(), "Hello World", 480, 320, false);
}
}
Now you're using Lwjgl as your backend! All the cool kids use it, so you can consider yourself as cool too. Cool, eh?
Next step: add a new file called HelloWorldApplet.java (in the same directory as other source files). It's similar to what we had before:
 1 2 3 4 5 6 7 8 910111213
package com.badlogic.gdx.helloworld;
import com.badlogic.gdx.backends.lwjgl.LwjglApplet;
public class HelloWorldApplet extends LwjglApplet
{
private static final long serialVersionUID = 1L;
public HelloWorldApplet()
{
super(new HelloWorld(), false);
}
}

Create a directory called applet in your project dir. It will contain all files needed to run your game through a browser. Put the following files in there:
  • gdx.jar
  • gdx-backend-lwjgl.jar
  • gdx-backend-lwjgl-natives.jar
  • gdx-natives.jar
You can copy them from your libgdx distribution. But that's not all! You will need a few more files:
  • lwjgl_util_applet.jar - this is the actual applet launcher. The Lwjgl guys made a sophisticated tool for that. It is configurable via an html file, which we will add in a moment. Get this launcher by downloading the latest Lwjgl distribution. It's 2.7.1 as I write it. Copy lwjgl-2.7.1/jar/lwjgl_util_applet.jar to your applet dir.
  • helloworld.jar - this is your game. Just export the whole project as jar in Eclipse:
    You can click "Finish" in this window.
  • index.html - this is the actual page that your browser will open. You should set some options for lwjgl_util_applet.jar here. I'm using these:
     1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>HelloApplet!</title>
    </head>
    <body>
    <div id="applet_container">
    <applet code="org.lwjgl.util.applet.AppletLoader" archive="lwjgl_util_applet.jar" codebase="." width="800" height="480">
    <param name="al_title" value="HelloApplet">
    <param name="al_main" value="com.badlogic.gdx.helloworld.HelloWorldApplet">
    <param name="al_logo" value="appletlogo.png">
    <param name="al_progressbar" value="appletprogress.gif">
    <param name="al_bgcolor" value="000000">
    <param name="al_fgcolor" value="ffffff">
    <param name="al_jars" value="helloworld.jar, gdx.jar, gdx-backend-lwjgl.jar">
    <param name="al_windows" value="gdx-natives.jar, gdx-backend-lwjgl-natives.jar">
    <param name="al_linux" value="gdx-natives.jar, gdx-backend-lwjgl-natives.jar">
    <param name="al_mac" value="gdx-natives.jar, gdx-backend-lwjgl-natives.jar">
    <param name="al_solaris" value="gdx-natives.jar, gdx-backend-lwjgl-natives.jar">
    <param name="codebase_lookup" value="false">
    <param name="java_arguments" value="-Dsun.java2d.noddraw=true -Dsun.awt.noerasebackground=true -Dsun.java2d.d3d=false -Dsun.java2d.opengl=false -Dsun.java2d.pmoffscreen=false -Xmx800M">
    <param name="lwjgl_arguments" value="-Dorg.lwjgl.input.Mouse.allowNegativeMouseCoords=true">
    <param name="separate_jvm" value="true">
    <param name="codebase_lookup" value="false">
    </applet>
    </div>
    </body>
    </html>
    All the options are documented here.
Now we've got everything we need! Open index.html with your browser and... it won't work. Why?
By default applets are allowed to use a restricted subset of Java. This is not enough for libgdx/lwjgl, so you need to sign ALL jars in the applet dir. It can be done by standard JDK tools (they may be not in your path though). First, create a keystore with one key. Run the following command in the applet dir:
1
keytool -genkey -alias gdxkey -keypass gdxpassword -validity 360 -keystore .keystore
This command will create a .keystore file, which will contain a single key named "gdxkey". I recommend setting the keystore password the same as your key password ("gdxpassword" in this case).
Now you can (and should) sign all jars. I'm using the following batchfile for that (copy the code and save it as sign-applets.bat in the applet dir):
1234
@for /f %%i in ("%0") do @set curpath=%%~dpi
@cd /d %curpath%
@for /r . %%X in (*.jar) do jarsigner -keystore .keystore -storepass gdxpassword %%X gdxkey
Of course this will work for Windows users only. Linux users are capable enough to write a bash script or anything similar, and Mac users... well, sorry. :)
After signing the jars fire up index.html again. Answer some security question and enjoy libgdx in your browser!

I know it looks pretty complicated, but one you've set it all up, releasing a new applet version of your game is easy:
  1. Export .jar containing your game
  2. Sign this jar
If you're smart, you can write an Ant script for that. Obviously I am smart, but I'm also too lazy to do that.

The last step: show me your game! You need to upload everything except .keystore and sign-applets.bat (so basically all jars and index.html) to some web server and give me the link! :)
I recommend Amazon S3 for hosting purposes (I'm using it for Pixel Slaughter!, Minecraft's using it too), but you can even publish it in your Dropbox. I did this too with my example project.

36 Response to "Being like Minecraft (or how to run your libgdx app in web browser)"

  1. Rod says:

    Creating an applet version of my game was on my list, so you've saved me quite a bit of work figuring it out. Thanks.

    Awesome!

    Anonimowy says:

    nice post, just a quick note as your using lwjgl 2.7.1 the

    < param name="al_errorcolor" value="ff0000">

    is no longer used.

    Thotep says:

    Fixed it, thanks.

    Thanks for posting this, I never made an applet before and wanted to share my libgdx project with some friends online.

    Kubecz3k says:

    Hello,
    Thanks for great article. I will certainly make a good use of it :)

    Now my project runs on my phone and in a browser. Thanks!

    Thotep says:

    Glad it worked! Where's the link then? ;)

    Anonimowy says:

    Nice to hear this is working. May I request a jogl version, or why a jogl version is hard / not possible? Thanks!

    Thotep says:

    Currently you can do libgdx-powered applets only through lwjgl.

    Thank you! It work!
    http://machaonstudio.blogspot.com/p/quadratum-web-version.html

    evan says:

    it's still giving me a class not found exception, and i followed this tutorial to the tee. any suggestions?

    Thotep says:

    At which point do you get this exception? Please provide full stacktrace.

    Anonimowy says:

    When I tried this, I got this message.

    An error occurred while loading the applet
    This occurred while 'Switching applet'

    How can I troubleshoot to find out what is causing the error?

    Thanks

    Thotep says:

    Enable Java console in your browser to get the full error message and a stacktrace.

    Ashiq says:

    I'm getting an invalid SHA1 hash exception for AppLoader.class$2. Any ideas?

    java.lang.SecurityException: invalid SHA1 signature file digest for org/lwjgl/util/applet/AppletLoader$2.class
    at sun.security.util.SignatureFileVerifier.verifySection(Unknown Source)
    at sun.security.util.SignatureFileVerifier.processImpl(Unknown Source)
    at sun.security.util.SignatureFileVerifier.process(Unknown Source)
    at java.util.jar.JarVerifier.processEntry(Unknown Source)
    at java.util.jar.JarVerifier.update(Unknown Source)
    at java.util.jar.JarFile.initializeVerifier(Unknown Source)
    at java.util.jar.JarFile.ensureInitialization(Unknown Source)
    at java.util.jar.JarFile.getCodeSources(Unknown Source)
    at java.util.jar.JavaUtilJarAccessImpl.getCodeSources(Unknown Source)
    at com.sun.deploy.cache.DeployCacheJarAccessImpl.getCodeSources(Unknown Source)
    at com.sun.deploy.security.CPCallbackHandler$ParentCallback.openClassPathElement(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath$JarLoader.getJarFile(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath$JarLoader.access$800(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath$JarLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.deploy.security.DeployURLClassPath$JarLoader.ensureOpen(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath$JarLoader.(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.deploy.security.DeployURLClassPath.getLoader(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath.getLoader(Unknown Source)
    at com.sun.deploy.security.DeployURLClassPath.getResource(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader$2.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.plugin2.applet.Plugin2ClassLoader.findClassHelper(Unknown Source)
    at sun.plugin2.applet.Applet2ClassLoader.findClass(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.plugin2.applet.Plugin2ClassLoader.loadCode(Unknown Source)
    at sun.plugin2.applet.Plugin2Manager.createApplet(Unknown Source)
    at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    Exception: java.lang.SecurityException: invalid SHA1 signature file digest for org/lwjgl/util/applet/AppletLoader$2.class

    Thotep says:

    Did you put the AppletLoader jar inside the jar you exported from your project? Don't do that, they must be separate.

    Ashiq says:

    @Thotep nope, I didn't do that.

    Anonimowy says:

    I get an error while "Extracting downloaded packages", how can I debug this?

    Thotep says:

    Turn on the Java Console.

    Anonimowy says:

    Ok, the error that I'm getting is:

    The certificate(s) in gdx-natives.jar do not match the AppletLoader!
    java.lang.Exception: The certificate(s) in gdx-natives.jar do not match the AppletLoader!
    at org.lwjgl.util.applet.AppletLoader.extractNatives(AppletLoader.java:1821)
    at org.lwjgl.util.applet.AppletLoader.run(AppletLoader.java:880)
    at java.lang.Thread.run(Thread.java:680)

    Thotep says:

    You didn't sign it properly. All jars need to be signed with your certificate AND NO OTHER.

    Anonimowy says:

    I get the same exception for gdx-natives.jar - how is it possible that all the other jars get signed correctly and this doesn't?

    Thotep says:

    Go inside every .jar file (they're juest regular zip files) and remove the dir META-INF (if it exists). This will remove all signatures (including yours). Then sign them all again with your certificate.

    Anonimowy says:

    I had spaces in the path to my applet dir, so the batch file failed for me. Putting the %%X in the batch file in quotes fixes that :)

    Anonimowy says:

    I cannot play my test project, and your example project has the same error. Any ideas?

    basic: Plugin2ClassLoader.getPermissions CeilingPolicy allPerms
    java.lang.Exception: JNLPClassLoaderUtil: couldn't find a valid JNLPClassLoaderIf
    at com.sun.jnlp.JNLPClassLoaderUtil.getInstance(Unknown Source)
    at com.sun.jnlp.JnlpLookupStub.findService(Unknown Source)
    at com.sun.jnlp.JnlpLookupStub.access$000(Unknown Source)
    at com.sun.jnlp.JnlpLookupStub$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.jnlp.JnlpLookupStub.lookup(Unknown Source)
    at javax.jnlp.ServiceManager.lookup(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.badlogic.gdx.backends.lwjgl.LwjglNativesLoader.(LwjglNativesLoader.java:35)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplet.(LwjglApplet.java:31)
    at com.badlogic.gdx.helloworld.HelloWorldApplet.(HelloWorldApplet.java:24)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at java.lang.Class.newInstance0(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at org.lwjgl.util.applet.AppletLoader.switchApplet(AppletLoader.java:1028)
    at org.lwjgl.util.applet.AppletLoader$3.run(AppletLoader.java:781)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
    Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: org.lwjgl.LWJGLException: Pixel format not accelerated
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:97)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.access$0(LwjglApplication.java:93)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:87)
    Caused by: org.lwjgl.LWJGLException: Pixel format not accelerated
    at org.lwjgl.opengl.WindowsPeerInfo.nChoosePixelFormat(Native Method)
    at org.lwjgl.opengl.WindowsPeerInfo.choosePixelFormat(WindowsPeerInfo.java:52)
    at org.lwjgl.opengl.WindowsDisplay.createWindow(WindowsDisplay.java:185)
    at org.lwjgl.opengl.Display.createWindow(Display.java:317)
    at org.lwjgl.opengl.Display.create(Display.java:857)
    at org.lwjgl.opengl.Display.create(Display.java:785)
    at com.badlogic.gdx.backends.lwjgl.LwjglGraphics.setupDisplay(LwjglGraphics.java:153)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:95)
    ... 2 more

    Great tutorial, helped me to port Space Squad (www.spacesquad.co.uk). Libgdx is amazing.

    Jyro117 says:

    I was trying to figure out the best way to deploy a test version. I came across this post which was pretty clear to follow. After a few personal tweeks I will respect your request to view the game!

    http://jyro117.blogspot.ca/2012/09/combatics-evolution-v010.html

    Jacco says:

    Thanks for this post! I'm having some trouble running the applet though, I think it has to do with the .jar that should contain the game.

    I'm using the setup as described here: https://code.google.com/p/libgdx/wiki/ProjectSetupNew . So basically there are seperate projects for the (core) game and its targets. For now I've added both the core project and the desktop project (which also contains the applet code) to the jar.

    The LWJGL-applet will start but hangs while "validating: gdx-backend-lwjgl.jar". Any help would be appreciated.

    Thotep says:

    Please read the comments above, the first thing you need to do is to check the exact error message in Java console.

    This post is a life saver! I am having trouble getting it to work though. My applet keeps crashing with this error:

    This occurred while 'Switching applet'
    Couldn't load shared library 'gdx.dll' for target: Windows 7, 32-bit
    com.badlogic.gdx.utils.GdxRuntimeException: Couldn't load shared library 'gdx.dll' for target: Windows 7, 32-bit
    at com.badlogic.gdx.utils.SharedLibraryLoader.load(SharedLibraryLoader.java:104)
    at com.badlogic.gdx.utils.GdxNativesLoader.load(GdxNativesLoader.java:34)
    at com.badlogic.gdx.backends.lwjgl.LwjglNativesLoader.load(LwjglNativesLoader.java:46)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.(LwjglApplication.java:76)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.(LwjglApplication.java:68)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplet$LwjglAppletApplication.(LwjglApplet.java:34)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplet$2.addNotify(LwjglApplet.java:72)
    at java.awt.Container.addNotify(Unknown Source)
    at java.awt.Panel.addNotify(Unknown Source)
    at java.awt.Container.addImpl(Unknown Source)
    at java.awt.Container.add(Unknown Source)
    at org.lwjgl.util.applet.AppletLoader.switchApplet(AppletLoader.java:1326)
    at org.lwjgl.util.applet.AppletLoader$2.run(AppletLoader.java:909)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
    Caused by: java.lang.NullPointerException
    at com.badlogic.gdx.utils.SharedLibraryLoader.load(SharedLibraryLoader.java:102)
    ... 26 more

    Any ideas? I would greatly appreciate it!

    Thotep says:

    Looks like you forgot to include gdx-natives.

    Where exactly do I need to include it? I have it in the index.html as well as in the applet folder. I built the project in the included project generator and am working in eclipse.

    Hi, I also deveklope fr java,... Java is a very easy to use language once u get a hold of it.... if
    anyone is a beginner in java then they should attend some interesting java
    focused conferences which gives a huge knowledge abt the language nd very
    interesting, easy tips nd tricks which are very useful... few of the famous
    java conferences are Javapolis (devox), JavaOne, and recently i received a mail
    from oracle regarding their JavaOne conference..They have many interesting sessions lined up fr this year... you can take a look at their sessions related topics here: https://www.regpulse.com/javaone2013/register.php?pcode=737266&src=4003&Act=1

    I myself hoping to attend several sessions about Core Java Platform; JavaFX, Java
    Embedded, and Java Card and many more.

    I guess m sharing it here coz It’s the best place to learn about what’s new
    and what’s next, see the latest from Oracle and our partners, connect with
    peers, and meet with experts...
    There are many topics covering Java SE,
    JavaFX, also few sessions on building mobile and embedded Java applications
    targeting next-generation smart devices.. Java EE–based enterprise solutions

    Developing with the latest Oracle database
    technology, business applications in the cloud securely..

    Hope u enjoy

    Jacco says:
    Ten komentarz został usunięty przez autora.
    Anonimowy says:

    JAR manifest application-library-allowable-codebase mismatch for

Prześlij komentarz

Powered by Blogger