| D:\DEV\PROJECTS\SpaceInvaders_Ship\src\spaceinvaders_ship\CDUnit.java |
/* * CDUnit.java */ package spaceinvaders_ship; import java.util.ArrayList; import kewlstuff.lwjgl.examples.spaceinvaders.interfaces.*; import kewlstuff.harbor.client.Vessel; //Added /** * * @author Johnny Kewl * * CDUnits control the application, please refer to the documentation and * other samples to understand CDUnits... * * What I want to talk about here is a trick we using. * Harbor is a Java application server, however games often use * JNI (C++) libraries. * So now what??? How does one get JNI libraries to work on a remote machine? * * The trick is to use the characteristics of Java and understand a little * more about how Harbor works. * A Java resource normally sits inside a Jar file, things like Icons, images * sounds, property files etc etc. * Harbor naturally has the ability to use these resources remotely. * If a remote client say accesses an Icon, in a function like getResourceAsStream, * what Harbor actually does is move that resource to the clients machine. * Thus a user need not worry about an Icon used in many places being downloaded * over and over again, Harbor makes sure it only moves over the wire once. * Now when one uses something like a url = getResource function in Harbor, * what Harbor again does is move the resource to the clients machine and thus * that URL is local to the remote client. * In other words the client application is completely un-aware that its on the * wire and internal functions that use that resource do not have to be internet * enabled, and not only can one write normal applications, optimization is * also taken care of by Harbor. * * So there you have it, its that easy... all we have to do is place the * JNI libs in a Java Package and we know they will end up on the remote client. * So we put them in a package called kewlstuff.spaceinvader.native_libs * and simply by doing this: url = getResource("/kewlstuff/spaceinvader/native_libs/filename * in the software, we know that the URL on the remote machine will point at that * JNI library local *to* the remote client. * * This game library wants the local property * System.setProperty("org.lwjgl.librarypath",pathToLibrary); * set for the JNI libraries and all the code below does is map the * URL back to a file path, and url = getResource for every library. * * Harbor for efficiency reasons does not work with full Jars remotely, it just * gets and stores the actual resource, so we know that the mapping is to * a JNI library file. * * Note that this is not true for the stand alone application.... * This will work in the stand-alone application *only* in the dev environment * but not in a standalone package, because the url reference will * be to the resource inside a Jar file... thus if you want to use this * standalone, you will have to add some code that gets the JNI resource and * copies it out of the jar to another location. However when used in * Harbor on remote machines... the resource is not stored in a Jar, so * the game software will see a normal file. * * This is more than just a clever way to use JNI libraries in Harbor, * its also a great way to package JNI in any application, so one doesnt * have stray files hanging around outside the Java package. */ public class CDUnit implements I_CDUnit{ private I_Welcome w = null; private Vessel vessel = null; //Added /** Creates a new instance of CDUnit */ public CDUnit() { String harborUrl = "http://localhost:8080/harbor/service"; //Added vessel = new Vessel(harborUrl); //Added //vessel.verboseVessel(true); // for debugging vessel.enableProgressDisplay(true); //for internet apps } //Trick to load up JNI libs remotely //Note if you want to package this standalone as well, add code that // moves the resource out of the Jar file. public boolean prepareJniLibs(){ ArrayList theLibs = new ArrayList(); String JniFolder = null; String os = System.getProperty("os.name"); os = os.toUpperCase(); //Please note... I could not test this logic on all systems, guessed ;) if(os.indexOf("WINDOWS") >= 0){ JniFolder = "win32"; theLibs.add("OpenAL32.dll"); theLibs.add("jinput-dx8.dll"); theLibs.add("jinput-raw.dll"); theLibs.add("lwjgl.dll"); }else if(os.indexOf("LINUX") >= 0){ JniFolder = "linux"; theLibs.add("libjinput-linux.so"); theLibs.add("liblwjgl.so"); theLibs.add("liblwjgl64.so"); theLibs.add("libopenal.so"); }else if(os.indexOf("MAC") >= 0){ JniFolder = "macosx"; theLibs.add("libjinput-osx.jnilib"); theLibs.add("liblwjgl.jnilib"); theLibs.add("openal.dylib"); }else{ System.out.println("ERROR: Could not detect operating system"); return false; } //Note the class we working with... the remote Welcome dialog. java.net.URL url = w.getClass().getResource("/kewlstuff/spaceinvader/native_libs/" + JniFolder + "/" + theLibs.get(0)); if(url == null){ System.out.println("ERROR: Libraries are not in a resource"); return false; } //We got the url to the resource, this just converts it back to a normal folder path String pathToLibrary = url.getPath(); int idx = pathToLibrary.indexOf("/" + (String)theLibs.get(0)); pathToLibrary = pathToLibrary.substring(0,idx); java.io.File f = new java.io.File(pathToLibrary); try{pathToLibrary = f.getCanonicalPath();}catch(Exception e){} //The libraries use JNI native libraries and need this system property to find them System.setProperty("org.lwjgl.librarypath",pathToLibrary); //Check all libraries present //On Harbor this will also cause the resource to be loaded remotely //The idea is while the user is looking at the first dialog, we bring down these //resources... //Here we bringing down the JNI libs but one could use the same trick to // preload sound files... any resource. for(int i = 0; i < theLibs.size(); i++){ java.net.URL urlTest = w.getClass().getResource("/kewlstuff/spaceinvader/native_libs/" + JniFolder + "/" + theLibs.get(i)); if(urlTest == null){ System.out.println("ERROR: Library missing: " + urlTest.toString()); return false; } } return true; } public void startGame() { //I_Game i_game = new Game(true); //FullScreen; Class uiApp = vessel.getRemoteClass("kewlstuff.lwjgl.examples.spaceinvaders.Game");//Added if(uiApp != null){ Object[] contructorArgs = new Object[] {false}; //Game has a constructor Class[] contructorTypes = new Class[] {boolean.class}; I_Game i_game = (I_Game)vessel.newInst(uiApp,contructorArgs,contructorTypes);//Added try{ i_game.execute(); }catch(Exception e){System.out.println("ERROR: " + e.getMessage()); e.printStackTrace();} } //Possible bug in game software... will not start twice // which probably means it doesnt dispose of resources properly? //Before you put this on line, fix that... // because its forcing the user to hit the server everytime they want to play // and all the good stuff that Harbor does for you like cache resources, store // classses in remote classloaders etc ... is thrown away because of this. //The ideal is to leave the Welcome screen open and just let the user start it // again and again... it will not come back to the server then. //KILL IT Shouldnt have to do this... but if you dont, machine gets sluggish // and one can see java process working on something. //Apologies but I dont have time to fix the 3rd party game sample. System.exit(0); } public void start() { //I_Welcome w = new Welcome(this); Class uiApp = vessel.getRemoteClass("spaceinvaders.Welcome");//Added if(uiApp != null){ w = (I_Welcome)vessel.newInst(uiApp);//Added w.start(this); } } /** * @param args the command line arguments */ public static void main(String[] args) { new CDUnit().start(); } }