So I work graveyards. And I spent this whole shift on an absolutely insane coding binge, of which I recall very little.
The result? As I come out of my code coma, I realize that clicking Play on this actually works.
This took me about 8 hours of work, 4 programming languages, 2 compilers, a debugger, and about 2 gallons of coffee. Supports XP/VX/Ace, requires no mods to the editor (though I'll likely build an optional SciLexer.dll at some point to see if I can get more appropriate syntax highlighting). It literally requires dropping 3 files in the project directory, done. One of those files is a Game.jar, to launch the game on Linux/Mac (still needs some tinkering for mobile).
Once I get an equivalent to RGSS, and figure out the licensing specifics, this will be freely available (open source).
Edit: Well it did work until I hit DEL instead of PrntScrn and deleted the closing "}".
Big Edit:
Finally got auto-classpath working so that it can add libraries magically. Also added error catching - a mistake in your scripts pops a strangely familiar-looking error on run. Also dropped lwjgl in /Libraries/ and threw together a starter window:
Here's the "script" that powers that window (which is in Scripts.rxdata):
package com.avarisc.javarpg;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import java.util.Arrays;
import java.io.File;
import javax.imageio.ImageIO;
import java.io.IOException;
import org.newdawn.slick.opengl.ImageIOImageData;
import java.nio.ByteBuffer;
import java.lang.Exception;
import org.ini4j.Ini;
import org.ini4j.InvalidFileFormatException;
public class RPG {
public static boolean debugMode = false;
public static int editorVersion = -1;
public static String gameTitle = "RPG";
public static void main(String[] args) {
//check for debug flag
if (Arrays.asList(args).contains("test")){
debugMode = true;
} else if (Arrays.asList(args).contains("debug")){
debugMode = true;
}
Logger.log("Starting Java RPG Maker Engine");
// set icon
try{
Display.setIcon(new ByteBuffer[] {
new ImageIOImageData().imageToByteBuffer(ImageIO.read(new File("Resources/icon16.png")), false, false, null),
new ImageIOImageData().imageToByteBuffer(ImageIO.read(new File("Resources/icon32.png")), false, false, null)
});
} catch (Exception e){
e.printStackTrace();
}
// read ini
parseIni("./Game.ini");
// set display title
Display.setTitle(gameTitle);
// create display
try {
Display.setDisplayMode(new DisplayMode(640,480));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1);
}
// init OpenGL here
while (!Display.isCloseRequested()) {
// render OpenGL here
Display.update();
}
Display.destroy();
}
public static void parseIni(String gameIniFile){
try {
Ini gameIni = new Ini(new File(gameIniFile));
gameTitle = gameIni.get("Game", "Title");
Logger.log("Game Title: " + gameTitle);
String scriptFile = gameIni.get("Game", "Scripts");
String extension = "";
int i = scriptFile.lastIndexOf('.');
if (i > 0) {
extension = scriptFile.substring(i+1);
}
switch(extension){
case "rxdata":
Logger.log("Detected RPG Maker XP Environment");
editorVersion = 1;
break;
case "rvdata":
Logger.log("Detected RPG Maker VX Environment");
editorVersion = 2;
break;
case "rvdata2":
Logger.log("Detected RPG Maker VX Ace Environment");
editorVersion = 3;
break;
default:
Logger.log("Unknown Editor Format. Likely going to crash.");
return;
}
} catch (InvalidFileFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
How it works:
This doesn't use any class-loading nonsense either - if you've changed the timestamp on Scripts.rxdata, then the next time you run from the editor (with "test" or "debug" as commandline options) it will attempt to recompile the RPG.jar in /Data/. Errors during compilation are presented in a nearly identical format to RGSS errors (only reports the first error in a messagebox, then stops trying to compile). If it compiles okay, it generates the classpath information (any .jars in /Libraries/, and points the dll path to /Libraries/Natives/{os name}/), and launches the game. Subsequent launches (when the RPG.jar is not older than Scripts.rxdata) simply launch the game.
Compiling requires javac to be accessible from $PATH - I'll later add a check for this with a notification / browse... option if it can't be located. Running the game requires javaw/java to be on $PATH, I'll add the same check for this. As for extracting and compiling, that's all managed by the launcher jar, using a custom JavaMarshal class I wrote specifically for the task. This class also lets me read the data from the database, though I still have a couple of kinks to iron out regarding floats. I'm debating whether to provide the Marshal service to the RPG engine itself, or simply to load marshal data and re-serialize it in a Java format (again using timestamps to avoid unneccessary repetition).
I parse the Game.ini to check the file extension on the Scripts line - this lets the engine know which editor environment we're in, so it can act accordingly. And of course the editor won't run Game.jar, so I threw together a launcher executable in C++ that simply starts the .jar and forwards any command-line options (and has a pretty icon lol).
Also added logging, it dumps a Game.log in the project folder that contains all details from the last run - including a more verbose compiler log for runs that involved compiling work. Still want to add a progress bar GUI for the compiling process itself.
Syntax highlighting is becoming a pressing concern now that I've worked in the editor for a bit. I'll look into the SciLexer.dll tomorrow to see what can be done about that.
There are a few differences with how the script list works. First, it uses the name as a filename. I.e. a script named "Bob" becomes "Bob.java" and finally "Bob.class" inside the jar. So names have to be unique. I'm thinking of adding something more to this to avoid conflicts. Second, scripts without a name, or scripts without a body, are not considered at all during the compiling process. And finally, scripts whose name begins with a "-" are not considered at all - this addition is simply for convenience, to allow "in-progress" scripts to be easily disabled.
I'm considering changing the script name format to a path-ish, package-based format, i.e. scripts named "com/avarisc/rpg/RPG" instead of just "RPG". It fits acceptably in the script list, as long as we don't get someone too wordy (i.e. "stegasaurus/bartholomew/eventhandlingthings/myincrediblyawesomeeventhandler"). This would prevent filename conflicts. Still trying to think of better ideas.
And it should go without saying that by its very nature this has no dependency on the RGSS dll whatsoever, and is cross-platform friendly.