Java Curation

From Flashpoint Datahub
Revision as of 20:41, 3 August 2019 by Strawrat (talk | contribs) (Added a new subsection in Troubleshooting on how to fix java.lang.ClassFormatError: Name index 0 in LocalVariableTable has bad constant type in class file)
Jump to navigation Jump to search

This guide will show you how to curate Java games and animations using Flashpoint Core, as well as dealing with any problems that may come up in the process.

Note that Flashpoint doesn't currently support Java Web Start, Microsoft Java or running any Java applets in a browser.

How to recognize them in the wild

Unlike other technologies such as Flash or Shockwave, Java applets are most commonly embedded on a page using an applet tag rather than an object/embed tag. Finding these tags is important since they're necessary to get the games running in Flashpoint.

Here's an example of the most basic type you can find:

<applet code="iceblox.class" width="390" height="350"></applet>

Here's a more complex example that shows certain parameters being passed to the game and that the class files may be packaged in a JAR/ZIP/CAB archive. If you tried to run this game on a browser that didn't support Java, you'd only see "Unfortunately, your browser does not support Java". This can be a good way to know if the web game or animation you're trying to curate uses Java.

<applet code="gamehouse.SuperApplet.class" archive="Collapse.zip" height="400" width="460" viewastext mayscript>
	<param name="cabbase" value="Collapse.cab">
	<param name="canvas" value="Collapse.CollapseCanvas">
	<param name="bgcolor" value="FFFFFF">
	<param name="textcolor" value="000000">
	<param name="buyurl" value="http://get.games.yahoo.com/proddesc?gamekey=spcollapse">
	<param name="target" value="_blank">

	Unfortunately, your browser does not support Java.
</applet>

Java Applets may also be embedded using the object and embed tags, although this appears to be less common:

<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" codebase="http://java.sun.com/products/plugin/1.2/jinstall-12-win32.cab#Version=1,2,0,0" width="100%" height="100%" name="Rockz" alt="Java applet failure">
	<param name="code" value="rockz.Run.class" >
	<param name="archive" value="rockz.jar" >
	<param name="name" value="Rockz" >
	<param name="type" value="application/x-java-applet;version=1.2">

	<embed code="rockz.Run.class" archive="rockz.jar" name="Rockz" type="application/x-java-applet;version=1.2" width="100%" height="100%" alt="Java applet failure" pluginspage="http://java.sun.com/products/plugin/1.2/plugin-install.html"></embed>
</object>

To find these tags, right-click the page with the applet, click "Inspect Element" or "View Page Source" and then search for "applet".

Curation Steps

Flashpoint runs Java applets offline by using an application called the AppletViewer. You can think of it like the equivalent to a Flash projector. Let's take a look at a simple example by curating the game Realspace 2 - Emperor's Revenge from https://www.miniclip.com/games/real-space-2/en/

  1. Open this page in your browser. You should see the text saying that Java is not enabled or installed. This indicates that it's a Java game, so we should take a look at the page's source and try to find the applet tag. If we search for "applet", we'll find the following:
    <applet width="500" height="500" code="gameapp.class" archive="gameapp.jar">
    	<param name="SIZE_SCALE" value="1.0" />
    	If you see this message you do not have Java enabled or do not have Java installed.</br><a href="http://wwww.java.com">Please see Java.com</a> to download the latest version of Java.</a>
    </applet>
    

    Note that the page could have also written the tag dynamically which means that you wouldn't be able to find it by just checking its source. In this case you would use your browser's "Inspect Element" option and search for it there.

  2. In your Flashpoint Core directory, navigate to Server\htdocs. In htdocs, create a series of directories that match the original site's structure. In this case, we'd create four directories, each inside the previous one: www.miniclip.com\games\real-space-2\en\
  3. Navigate to the last directory (in this case \en\) and create the file that will contain the applet tag we found in step 1. You should give it the same name as the original site. In this case there isn't one, so we'll just create a file called index.html.
  4. Open this file you've just created in a text editor and paste the applet tag in there.
    <applet width="500" height="500" code="gameapp.class" archive="gameapp.jar">
    	<param name="SIZE_SCALE" value="1.0" />
    </applet>
    
  5. Download the game's assets (code, images, sounds, etc) and put them in the correct directories so that they also match the original site's structure. It's possible that all the files needed are inside the archive (called gameapp.jar here). If they were and we didn't need any additional files then we could move onto the next step. However, for this and most other Java games, the archive only has the game's code. We still need to download the rest of the assets. This can be done in several ways, including:
    • Checking what the game is requesting by running it, looking at the Redirector/Fiddler and downloading these files manually.
    • Using a tool that automatically downloads these files from the original site as they're requested by the game. See the MAD4FP page for more details on how to do this.
  6. You can now test your curation in Flashpoint Core. Press the "New Game" button on the bottom right corner and enter the game's title, platform, application path and launch command. In this case, they would be:
    Title: Realspace 2 - Emperor's Revenge
    Platform: Java
    Application Path: FPSoftware\startJava.bat
    Launch Command: http://www.miniclip.com/games/real-space-2/en/index.html
    

    The platform and application path should be the same for all Java curations. Make sure that the launch command starts with http:// and not https://

    You can run the game by double clicking its thumbnail in the launcher. You should now see the game running in the AppletViewer:

    Realspace 2 - Emperor's Revenge running in the AppletViewer
  7. Create the necessary metadata files and package your submission according to the Curation Format. For more details on how to do this, refer to that page. Remember to also copy the information in the four fields you filled out in the previous step to the metadata file.
  8. Before uploading your submission, go to the Flashpoint Discord server and ask a Curator in the #curator-lounge to check it. If your submission is valid, you will be given a special Trial Curator role that will allow you to post messages in the Curation channels. Otherwise, you may be asked to correct your submission.
  9. Upload your submission to the Other Game Submissions section in this page. You can also check this page to see if your submission was uploaded successfully.
  10. Finally, you should go to the #other-game-curations channel in the Flashpoint Discord server and post a message with your submission's title. This is helpful to the people responsible for adding curations to the master copy as they will know to check the uploads section linked in the previous step. You may use one of the server's various web technology emojis to specify what platform your game runs in. In this case, you could submit the following message: :java: Realspace 2 - Emperor's Revenge

Curation Tips

  • You may sometimes see a parameter in the applet tag called "cabbase" that specifies a CAB file. This was used to allow the same applet to run in different browsers - Netscape would download the JAR or ZIP in the archive attribute while Internet Explorer would download the CAB in the cabbase parameter. Although the AppletViewer doesn't request this file, you should still include it in your submission for preservation's sake.
  • The AppletViewer ignores everything in the file it reads except for the tags that embed a Java applet. You can see the tag that was read by the AppletViewer by going to Applet > Tag...
  • While running in the AppletViewer, games won't be able to call JavaScript methods or interact with other HTML elements. Some games may rely on calling JavaScript methods to save their progress in the browser's cookies, like Need for Madness or Insaniquarium. You should leave an appropriate note in the metadata if the game doesn't save or load its progress properly. Applets that call JavaScript methods using the JSObject class will always throw the following exception: netscape.javascript.JSException: Unexpected error: This method should not be used unless loaded from plugin.jar.
  • You can pass options to the Java Virtual Machine by putting -J before each one. These options must come before the launch command. For example, you can use -J-XshowSettings http://www.example.com/game.html to show all Java settings, like system properties, available locales, etc.

Debugging

You can see what the applet writes to the standard output and error output by replacing >NUL in the following line in FPSoftware\startJava.bat:

CALL JDKPortable\CommonFiles\JDK\bin\appletviewer.exe -J-Dhttp.proxyHost=127.0.0.1 -J-Dhttp.proxyPort=8888 %* >NUL 2>&1

>NUL normally suppresses the output, but you can also:

  • Remove it to have the output show up in the Flashpoint Launcher's Logs tab.
  • Replace it with >"core_java.log" to write the output to that file. In this case, it would be located in FPSoftware\core_java.log.

Useful Tools and Resources

This section will link to external tools and resources that may be useful to learn more about the technology or to try to get certain games and animations working.

Troubleshooting

This section will cover some of the problems that may arise when curating Java games and animations. If you're experiencing an issue not mentioned in this section be sure to ask for help in the #curator-lounge channel in the Flashpoint Discord server.

The applet closes immediately when I try to run it

The AppletViewer will close immediately if it can't find the specified file or if this file doesn't have an applet tag. You should check if the launch command is correct and if the file has a valid applet tag in it.

The applet is trying to request files that are suspiciously missing from the original site

If the class files are packaged in JAR or ZIP archives, be sure to open them and check if the missing files are there. If they are, extract them to the same directory as the archive. Some Java games have trouble loading assets from inside these archives and will instead try to load them externally. Note that you'll never have to extract class files.

The applet isn't displaying certain characters correctly

Try setting the correct character encoding and locale properties using the -J option in the AppletViewer. For example, if a game from GeoCities Japan needed to display Japanese characters, you would add all of the following before the game's launch command: -J-Dfile.encoding=UTF8 -J-Duser.language=ja -J-Duser.country=JP.

A comparision between using the English locale (left) and the Japanese locale (right)

You can also see what the current character encoding and locale settings are by using -J-XshowSettings.

If you would like to know more about which character encodings and locales are supported in Java 8, see:

The applet doesn't start and shows the following error: java.lang.ClassFormatError: Incompatible magic value 1008813135 in class file

This error means that the class file is in fact just an HTML file. This is likely due to being served a 404 page because the class file is missing. Try opening the file in a text editor or browser to confirm this. The value 1008813135 represents "<!DO", in other words, the start of an HTML <!DOCTYPE> declaration. You may see a different magic value in the error depending on the wrong type of file you were served. The class file format is always supposed to have 3405691582 (0xCAFEBABE in hexadecimal) as the first four bytes.

The applet doesn't start and shows the following error: java.lang.ClassFormatError: Name index 0 in LocalVariableTable has bad constant type in class file

When compiling Java source files, you can specify certain debug options, one of which generates information about the local variables of each method (the LocalVariableTable attribute). This error happens when there's a single LocalVariableTable that is invalid according to the class file format. It seems to be most common with applets made around 1997-2003. Although it might have been possible to run the applet in older Java versions, we'll have to remove this offending LocalVariableTable in order to run it in Flashpoint. To do this, we'll use a Java class file editor called dirtyJOE.

A comparision between a normal LocalVariableTable (left) and an invalid one (right) using DirtyJOE. Notice the use of an invalid type (NULL) in this last one.

Before starting, you should make a copy of the class files so that your submission includes both the original and the modified files. On Windows, you can run make a copy of the original class files in the current directory by running the following on the Command Prompt: FOR /F "delims=" %c IN ('DIR /B *.class') DO (COPY /B "%c" "original_%c").

If the class files are packaged in JAR or ZIP archives, decompress them. After following the next steps, compress them again and rename the archives (by adding "_hacked" to the end, for example). This way both the original and modified archives are included. Be sure to update the archive attribute in the applet tag so that the modified files are loaded instead of the original ones.

To fix this error, follow the next steps for each class file:

  1. Run dirtyJOE and open the class file.
  2. Click on the Methods tab to see the list of all the methods defined in this class.
  3. Start at the top of the list and find the first method that shows a Code attribute in the Attributes panel. In the majority of the cases, it will be the first method. Note that it's possible for the class file to be fine, even if the applet's other class files suffer from this problem. Only modify files that prevent the applet from running.
  4. Double-click on the Code attribute. You should now see a new window titled Code Editor followed by that method's name.
  5. In the Attributes panel of this new window, right-click on the LocalVariableTable attribute and select "Delete Attribute".
  6. Close the Code Editor window and save the modified class file by pressing Ctrl + S or by going to File > Save. Do not change the class file's name.
A visual guide for the steps mentioned above. Note that the numbers don't match up.

Before submitting your curation, make sure that the metadata says that the game was "Hacked" (see the Curation Format page for more details) and that the notes mention why. For example, you could add one of the following to the beginning of the notes field:

Hacked to fix an issue in every .class file that prevents the game from running. The original unhacked files can be found next to the hacked ones with their names prefixed with "original_".

or

Hacked to fix an issue in every .class file that prevents the game from running. The original unhacked files can be found in game.zip.

Developer Specific Troubleshooting

This section will cover some problems that commonly show up when curating Java games and animations from specific developers. This might also be useful if you're experiencing similar issues in applets made by developers that aren't listed here. If you're experiencing an issue not mentioned in this section be sure to ask for help in the #curator-lounge channel in the Flashpoint Discord server.

MumboJumbo

If a game crashes after your score reaches 1,000 points, try changing the locale property to English (US). You can do this by adding the following before the game's launch command: -J-Duser.language=en -J-Duser.country=US.

Note that you won't experience this issue if this is already your default locale. To avoid curating a game that might crash for another player, try playing the game with the following locale settings: -J-Duser.language=es -J-Duser.country=ES. You can see what the current locale settings are by using -J-XshowSettings.

Why this happens: the game uses NumberFormat.getInstance() to get a number format to format the player's score. This method uses the default locale, meaning that a number like one thousand is formatted as either 1,000 or 1.000 depending on your locale. The game's score font data is only defined for the numbers 0 through 9 and the comma, but not for the period. While the game is iterating over the formatted score string, the image data relative to the period will be returned as null, causing the game to eventually throw a NullPointerException when it accesses the image's attributes.