Ahoy gentlement,
I using JME SDK since 1 week and I really enjoy it!
But I have a little question, about the serialized save for a class, when you implement the Savable class on your custom class.
I take the exemple here :
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:save_and_load?s[]=save#custom_savable_class
My question is : I want to save some specifics class to specifics folders.
For example, i have a Vessel class, and an Army class, and i want to save local armies and local vessels on folders like Saves/Vessels and Save/Armies with the name of the Vessel or Army (decided by the player).
(Ex : Vessel “Dragon” save on Saves/Vessels/Dragon.j3o and when the player load, he will have the list of the Saves/Vessels content)
So, i have implemented the write and read method on my classes, and it look very cool.
But now, when i call my
OneVessel.load(im);
(or a method init who load the vessel)
but, i don’t know what i have to do for the object “im” (who is a JmeImporter object) ?
And how to specify the file to load? I think there is some missing element on the Doc page, or I have missunderstand some element on the first part of the page doc.
The second way is totaly credible, as you can see, i’m not good to speak english, and i have probably miss somes important informations, or actualy, the answer of my question.
So, anyone can explain to me :
How to specify file to load? And what i have to do and how to construct the JmeImporter “im” ?
It’s the same as the BinaryExporter example from that tutorial page, but you use the BinaryImporter.
[java]
BinaryImporter imp=BinaryImporter.getInstance();
imp.setAssetManager(assetManager);
Spatial mySpat =(Spatial)imp.load(myInputStream);[/java]
And yes, there should probably be an example of it on that page. If you get your working code going, care to donate it here and we can put it in the wiki?
Okay, it’s obvious now.
I’ve try an implementation of your advices, and I have somes running issues, and it can be great if someone can help me to fix them :D.
So, my code is now :
class Vessel.java
[java]
public abstract class Vessel implements Savable{
// Blabla Boring code
protected String name; // Test attribute for the save system.
// Boring code again
// Save Vessel method, call at last the write() method of the class :
public void saveVessel() throws IOException{
String userHome = System.getProperty(“user.home”);
File file = new File(userHome+"/Saves/Vessels/"+this.name+".j3o");
BinaryExporter exp = BinaryExporter.getInstance();
exp.save(this, file);
this.write(exp); // LINE 68 ERROR
}
// There is the simple method for save the unique atribute “name”, just follow the doc :
public void write(JmeExporter ex) throws IOException {
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(this.name, “name”, “MISSINGNO”); // LINE 153 ERROR
}
// Load Vessel Method, call at last the load() method. I have made some change,
// with the version you give me last post, because I’ve dont understand the
// Spatial mySpat = (Spatial)imp.load(IStream);
// and because just imp.load(IStream) seems sufficient for compilation.
// Indeed it could be a problem next, and I can change it, but for now, the execution dont
// go so far.
public void loadVessel(String VesselName) throws IOException{
String userHome = System.getProperty(“user.home”);
File file = new File(userHome+"/Saves/Vessels/"+VesselName+".j3o");
BinaryImporter imp = BinaryImporter.getInstance();
imp.setAssetManager(assetManager);
imp.load(file);
this.read(imp);
}
// And the simple read method like the doc said :
public void read(JmeImporter im) throws IOException {
InputCapsule capsule = im.getCapsule(this);
this.name = capsule.readString( “name”, “MISSINGNO”);
}
[/java]
and the Cruiser.java, extends Vessel :
[java]
public class Cruiser extends Vessel{
// Standard constructor, used to build the first vessel.
public Cruiser(int x, int y,String name){
this.name = name;
this.init(VESSELTYPE.CRUISER, x, y);
}
// Other constructor to load a vessel with his name
public Cruiser(String name) throws IOException{
this.loadVessel(name);
}
}
[/java]
and finally the main.java, test class :
[java]
public class Main {
public static void main(String[] args) throws IOException {
Cruiser Test = new Cruiser(0, 0, “Test”);
Magic.show("It must be test = "+Test.getName()); // System.out.println() like indeed.
Test.saveVessel(); // LINE 18 ERROR
Cruiser TestLoad = new Cruiser(“Test”);
Magic.show("And this is test again? = "+TestLoad.getName());
}
}
[/java]
And the output show me the next exception :
[java]
compile-single:
run-single:
It must be test = Test
Exception in thread “main” java.lang.NullPointerException
at com.jme3.export.binary.BinaryOutputCapsule.write(BinaryOutputCapsule.java:438)
at com.jme3.export.binary.BinaryOutputCapsule.writeAlias(BinaryOutputCapsule.java:397)
at com.jme3.export.binary.BinaryOutputCapsule.write(BinaryOutputCapsule.java:241)
at net.nakou.stardust.game.vesselsmanager.Vessel.write(Vessel.java:153)
at net.nakou.stardust.game.vesselsmanager.Vessel.saveVessel(Vessel.java:68)
at net.nakou.stardust.game.vesselsmanager.Main.main(Main.java:18)
Java Result: 1
[/java]
I’m thinking about to come back on a XML classic schema files but it’s bad and sad because i really enjoy this system :s…
So if anyone have correction :D, and we can make a standard for the doc (like Sploreg said).
Using the BinaryImporter directly is not recommended, this was a mistake in the wiki and has been corrected.
If you have a .j3o you can just use the AssetManager to load it. That is the recommended way.
Okay, but i dont have a .j3o yet, because the method write doesnt work x).
Okay, i finally create my save file :
XML :
[java]
File file = new File(Magic.Repertory+“assets/Saves/Vessels/”+this.name+".xml");
XMLExporter exp = XMLExporter.getInstance();
boolean itsokay = exp.save(this, file);
Magic.log(“Vessel save in “+this.name+”.xml”);
[/java]
J3O :
[java]
File file = new File(Magic.Repertory+“assets/Saves/Vessels/”+this.name+".j3o");
BinaryExporter exp = BinaryExporter.getInstance();
boolean itsokay = exp.save(this, file);
Magic.log(“Vessel save in “+this.name+”.j3o”);
[/java]
And the common part :
[java]
public void write(JmeExporter ex){
OutputCapsule capsule = ex.getCapsule(this);
capsule.write(this.name,“name”,“MISSINGNO”);
}
[/java]
Now, I’m tryin to load what I’ve save in the file, with this :
[java]
public void loadVessel(String VesselFile) throws IOException{
if(Magic.getExtentionFile(VesselFile).equals(“xml”)){
File file = new File(Magic.Repertory+“assets/Saves/Vessels/”+VesselFile);
XMLImporter imp = XMLImporter.getInstance();
imp.load(file);
}else if(Magic.getExtentionFile(VesselFile).equals(“j3o”)){
File file = new File(Magic.Repertory+“assets/Saves/Vessels/”+VesselFile);
BinaryImporter imp = BinaryImporter.getInstance();
imp.load(file);
}else{
Magic.log(“Extention not reconized”);
}
}
[/java]
And this is the read() :
[java]
public void read(JmeImporter im) throws IOException {
Magic.show(“HERE!”);
InputCapsule capsule = im.getCapsule(this);
this.name = capsule.readString(“name”, “MISSINGNO”);
}
[/java]
I finaly understand something, it’s the imp.load() and exp.save() who call the read() and write() method.
but it doesnt work for the load, and i get an error in execution :
[java]8 oct. 2012 13:07:52 com.jme3.export.SavableClassUtil fromName
GRAVE: Could not access constructor of class ‘net.nakou.stardust.game.vesselsmanager.Cruiser’!
Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.[/java]
This is my call of Cruiser :
[java]
Cruiser TestLoad = new Cruiser(“Test.j3o”);
[/java]
And my constructor :
[java] public Cruiser(String name) throws IOException{
this.loadVessel(name);
}[/java]
Anyone have an idea why it crash?
Okay, found it,
indeed, he need an empty constructor to make the loaded object.
But there is a problem here, the object created is not the same what i expected , in fact, he create two distinct objects :
I made a ToString to see the hashcode of the object call in the read() method :
[java]
public void read(JmeImporter im) throws IOException {
InputCapsule capsule = im.getCapsule(this);
this.name = capsule.readString(“name”, “MISSINGNO”);
Magic.show("Hashcode 1 : "+this.toString());
}
[/java]
And in place the second one where i call the “load” constructor :
[java]
Cruiser TestLoad = new Cruiser(“Test2.xml”);
Magic.show("Test2 Name = "+TestLoad.getName());
Magic.show(“Hashcode 2”+TestLoad.toString());
[/java]
And this is my results :
[java]
Hashcode 1 : net.nakou.stardust.game.vesselsmanager.Cruiser@7d29f3b5
Test2 Name = = null <---- Must be the name load, and it’s null, because the name is in the previous object.
Hashcode 2 : net.nakou.stardust.game.vesselsmanager.Cruiser@4d3f3045
[/java]
As you can see, it’s not the same object, and there is a problem, how i can identify my object, or catch him (because i can’t see his name and “copy” it…).
Anyone can help me to, catch the new object, or make some change on my load/read method to affect the object “TestLoad”?
You think inside out. You create the object and then assign it to where it belongs. Similarly you de-serialize it and then assign it.
Can you give me an example?
The serialization system does not double the objects that are loaded on one step, you just have to replace all your references with the new one.
[java]
Control control = new MyControl();
Spatial spatial = new Spatial();
spatial.addControl(control);
//spatial added to some list or registered elsewhere
myList.add(spatial);
//saving
SaveGame.saveGame("my/company", "save", spatial);
//later when loading:
Spatial spat = SaveGame.loadGame("my/company", "save");
Control cont = spat.getControl(MyControl.class);
//remove old reference!
myList.remove(spatial);
myList.add(spat);
[/java]
Okay, this is what I’ve understand at your last post.
Good to know, all of it. I can’t test now, but tonight, i will try and I think it will be okay.
Thanks a lot, Normen, and other guys, to help me at those particular points.
Hope that post can clean minds to people who want to made a savable class.