Loading assets with URL

Hello :slight_smile:

I made an applet and have some annoying problems loading assets from an URL.
My intention was to create a small sized Java-Applet and load assets if needed.
I tested this on about 10 PCs with different Java-Versions, Browsers, Connections, Servers (also localhost) and different nightly-builds of jme.
Multiple problems occurred:

On 4 of 10 PCs it was impossible to load fonts and cursors (.cur), this was reproducible. The other PCs loaded them without a peep. After putting the same fonts and cursors into the asset-folder inside the .jar it works on the 4 PCs. This wasn’t nice because it increased the applet-size, but ok…

Sometimes, this is not reproducible, the AWTLoader crashes when loading an image, grrrrrrrr. So my idea was to try loading this image again and voila it was successful. But why it crashes sometimes the first time?

During startup I load some images and cache them. Before this I register a Locator via applet.getCodeBase() and then loading an image for example with:
[java]
assetManager.loadTexture(“gui/images/arrow-next2.png”);
//result:
network: Verbindung von https://www.my-page.de/applet/media/gui/images/arrow-next2.png mit Proxy=HTTP @ /XXX.XXX.XXX.XXX:XXXX wird hergestellt
[/java]
This works fine and I assume this pic is loaded and cached. If I need this pic again it shouldn’t be loaded again via URL because it’s in the assetManager already, right?
But…
Later using this image again with assetManager.loadTexture(“gui/images/arrow-next2.png”); it fails, because it tries to load again with AWTLoader… why?

[java]
network: Cacheeintrag nicht gefunden [URL: file:/C:/Users/XX/AppData/Local/Temp/lwjglcache/www.my-page.de/bla/code.signed.jar, Version: null]
network: Cacheeintrag nicht gefunden [URL: file:/C:/Users/XX/AppData/Local/Temp/lwjglcache/www.my-page.de/bla/code.signed.jar, Version: null]
network: Verbindung von https://www.my-page.de/applet/media/gui/images/arrow-next2.png mit Proxy=HTTP @ /XXX.XXX.XXX.XXX:XXXX wird hergestellt
network: Server https://www.my-page.de/applet/media/gui/images/arrow-next2.png sendet Anfrage für set-cookie mit “PHPSESSID=dg6789r95vlu6p87l7mqpcmc96; path=/;Secure;HttpOnly”
Exception in thread “LWJGL Renderer Thread” com.jme3.asset.AssetLoadException: The given image cannot be loaded gui/images/arrow-next2.png (Flipped)
at com.jme3.texture.plugins.AWTLoader.load(AWTLoader.java:202)
at com.jme3.asset.DesktopAssetManager.loadAsset(DesktopAssetManager.java:288)
at com.jme3.asset.DesktopAssetManager.loadTexture(DesktopAssetManager.java:346)
at com.jme3.niftygui.RenderImageJme.<init>(RenderImageJme.java:55)
at com.jme3.niftygui.RenderDeviceJme.createImage(RenderDeviceJme.java:166)
at de.lessvoid.nifty.render.NiftyImageManager.addImage(NiftyImageManager.java:127)
at de.lessvoid.nifty.render.NiftyImageManager.registerImage(NiftyImageManager.java:28)
at de.lessvoid.nifty.render.NiftyRenderEngineImpl.createImage(NiftyRenderEngineImpl.java:172)
at de.lessvoid.nifty.effects.impl.ChangeImage.loadImage(ChangeImage.java:45)
at de.lessvoid.nifty.effects.impl.ChangeImage.activate(ChangeImage.java:30)
at de.lessvoid.nifty.effects.Effect.internalStart(Effect.java:112)
at de.lessvoid.nifty.effects.Effect.start(Effect.java:104)
at de.lessvoid.nifty.effects.EffectProcessorImpl.startEffect(EffectProcessorImpl.java:320)
at de.lessvoid.nifty.effects.EffectProcessorImpl.processStartHover(EffectProcessorImpl.java:213)
at de.lessvoid.nifty.effects.EffectManager.handleHoverStartAndEnd(EffectManager.java:179)
at de.lessvoid.nifty.elements.Element.mouseEventHover(Element.java:1607)
at de.lessvoid.nifty.elements.Element.mouseEvent(Element.java:1601)
at de.lessvoid.nifty.screen.MouseOverHandler.processMouseEvent(MouseOverHandler.java:105)
at de.lessvoid.nifty.screen.Screen.forwardMouseEventToLayers(Screen.java:369)
at de.lessvoid.nifty.screen.Screen.mouseEvent(Screen.java:339)
at de.lessvoid.nifty.Nifty.forwardMouseEventToScreen(Nifty.java:308)
at de.lessvoid.nifty.Nifty.access$1600(Nifty.java:77)
at de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processEvent(Nifty.java:1373)
at de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processMouseEvent(Nifty.java:1329)
at com.jme3.niftygui.InputSystemJme.onMouseMotionEventQueued(InputSystemJme.java:222)
at com.jme3.niftygui.InputSystemJme.forwardEvents(InputSystemJme.java:294)
at de.lessvoid.nifty.Nifty.update(Nifty.java:288)
at com.jme3.niftygui.InputSystemJme.endInput(InputSystemJme.java:113)
at com.jme3.input.InputManager.processQueue(InputManager.java:819)
at com.jme3.input.InputManager.update(InputManager.java:883)
at com.jme3.app.Application.update(Application.java:604)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:231)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglCanvas.runLoop(LwjglCanvas.java:229)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Unknown Source)
[/java]

Also, this isn’t reproducible again lol, I could cry. After loading all textures, scene, appstates and so on the viewport is black. There was no exception, nothing. If I enable another viewport with wireframe mode the scene is there. Only the first viewport is still black.

Maybe some issues (not cursors/fonts problem) are caused of multithreading? Loading nifty, caching images, viewports, scene and so on are done in a seperate thread (don’t want to block the update loop during startup) and assetManager/AWTLoader has problems with it, don’t know??

As a workaround, cache it yourself for the moment.
I have the feeling that the assetcache is sometimes doing strange stuff, but no hard proof for now.

Thanks. That’s a great idea.
I hope you mean something like this (example):
[java]
Texture imgTex;
try {
URL url = new URL(getAppletHarnessRoot().getCodeBase(), “examples/strawberry.jpg”);
BufferedImage img = ImageIO.read(url);

AWTLoader awtL = new AWTLoader();
com.jme3.texture.Image imgJME = awtL.load(img, false);
	    
imgTex = new Texture2D(imgJME);
// own cache.add(imgTex)	    

} catch (IOException e) {
}
// instead of
// assetManager.loadTexture(“examples/strawberry.jpg”);
// and to prevent nifty using assetmanager:
NiftyImage test = new NiftyImage(nifty.getRenderEngine(), new RenderImageJme(imgTex));
[/java]

@OPM87 said: Maybe some issues (not cursors/fonts problem) are caused of multithreading? Loading nifty, caching images, viewports, scene and so on are done in a seperate thread (don't want to block the update loop during startup) and assetManager/AWTLoader has problems with it, don't know??

But you are only modifying the attached scene graph from the render thread, right? Right…?

If I load assets or doing nifty stuff I don’t modify the scenegraph. The scenegraph looks good and if not i would get an exception ala don’t modify from another thread, this is not the case.
How does nitfy load or cache the images or fonts?
I recognized that nifty ignores loaded images or fonts. I debugged it with wireshark… if i load an asset with assetManager.loadAsset(“gui/images/arrow-next2.png”); or .loadTexture the assetManager loads it GET /my-applet/media/gui/images/arrow-next2.png HTTP/1.1 . After 2 seconds nifty loads the xml with <image filename=“gui/images/arrow-next2.png”> and the assetManager loads the same image again.

I looked into some old exceptions, the font problem occured also in nifty.
The problem got only a few PCs, reproducible. After putting fonts in the assetfolder in the .jar the problem was gone.

[java]
network: Verbindung von http://my-applet/applet/media/gui/fonts/Calibri_20.fnt mit Proxy=DIRECT wird hergestellt
java.lang.RuntimeException: java.lang.NullPointerException
at de.lessvoid.nifty.Nifty.loadFromFile(Nifty.java:577)
at de.lessvoid.nifty.Nifty.fromXml(Nifty.java:480)
at j3d.core.Gui.initializeLoadingAppState(Gui.java:205)
at j3d.core.ApplicationRoot$3.call(ApplicationRoot.java:547)
at j3d.core.ApplicationRoot$3.call(ApplicationRoot.java:542)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at com.jme3.font.BitmapFont.getPageSize(BitmapFont.java:120)
at com.jme3.font.BitmapText.<init>(BitmapText.java:64)
at com.jme3.font.BitmapText.<init>(BitmapText.java:56)
at com.jme3.niftygui.RenderFontJme.<init>(RenderFontJme.java:52)
at com.jme3.niftygui.RenderDeviceJme.createFont(RenderDeviceJme.java:170)
at de.lessvoid.nifty.render.ScalingRenderDevice.createFont(ScalingRenderDevice.java:32)
at de.lessvoid.nifty.render.NiftyRenderEngineImpl.createFont(NiftyRenderEngineImpl.java:188)
at de.lessvoid.nifty.loaderv2.types.apply.Convert.font(Convert.java:44)
at de.lessvoid.nifty.loaderv2.types.apply.ApplyRenderText.apply(ApplyRenderText.java:25)
at de.lessvoid.nifty.elements.Element.initializeFromAttributes(Element.java:434)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyAttributes(ElementType.java:218)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyStandard(ElementType.java:172)
at de.lessvoid.nifty.loaderv2.types.ElementType.create(ElementType.java:144)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyChildren(ElementType.java:251)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyStandard(ElementType.java:175)
at de.lessvoid.nifty.loaderv2.types.ElementType.create(ElementType.java:144)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyChildren(ElementType.java:251)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyStandard(ElementType.java:175)
at de.lessvoid.nifty.loaderv2.types.ElementType.create(ElementType.java:144)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyChildren(ElementType.java:251)
at de.lessvoid.nifty.loaderv2.types.ElementType.applyStandard(ElementType.java:175)
at de.lessvoid.nifty.loaderv2.types.ElementType.create(ElementType.java:144)
at de.lessvoid.nifty.loaderv2.types.ScreenType.create(ScreenType.java:80)
at de.lessvoid.nifty.loaderv2.types.NiftyType.create(NiftyType.java:137)
at de.lessvoid.nifty.Nifty.loadFromFile(Nifty.java:570)
… 10 more
[/java]

Have you confirmed that your URLs consistently load from all machines without ever erroring?

When assets are in the assets jar then they are loaded from the jar. It seems like the images you are downloading by URL are failing to download for some reason.

And actually, it looks like in both cases the URL returns something that is not what is requested. Perhaps an error page. Otherwise, you’d get asset not found errors or something. As it is, some content is returned but it is not the expected content.

In the case of fonts, the font file reader is very permissive and will read the whole result and just not find anything font-like… so later when you go to request things about the font (like page size) you get NullPointerExecptions because there is nothing setup about the font.

In the case of images, we use ImageIO.read(InputStream) which tries to recognize the stream as an image format. This method returns null if it fails to find a valid format. So the stream must be some non-image data (like an error page). A problem with only partially downloading an image would cause an IOException.

So my guess is that your server is occasionally returning bad data for these URLs.

It’s hard to debug because these errors are rare. But it’s possible that the servers return bad data sometimes. So I guess the only solution is to put it in a try-catch block and do it again.
Do you have an idea how to preload nifty images? I want to leave the xml-file as: <image filename=”gui/images/arrow-next2.png”>. Preloading images with assetManager.loadTexture(“gui/images/arrow-next2.png”); seams not to work.
With the fonts I’m not sure but I think the problem could be the java version especially the j7u45 with it’s security restrictions. In the .fnt is the name of the .png file and this .png will not be loaded or fails.
Could a currupted texture (no error messages) can cause a black scene?

In the error you showed, it was the fnt file itself that hadn’t loaded properly. It got data but none of the data was font data. It’s a very permissive reader.

…so it’s like it got an error page instead. See, that’s the thing… it’s not really bad data it’s an error page. It would be better if your server sent nothing at all than an error page in these cases.

If it were me, I’d compile my own JME with some logging in the loaders to dump the contents of the fnt file received (since the whole thing is read up front). Configure logging to log to disk and then wait to see the error again… then you will finally get to see what the server is telling you and then fix the server maybe.

Edit: actually the whole fnt may not be read up front but every line is read… so it is possible to log the raw data. I don’t remember exactly which way it works.

1 Like

Ok, I made a major step forward. I found out that using assetManager.loadXY to preload/cache something isnt’ threadsafe enough or something like that, what ever. The image was in the smartcache but nifty ignores it and sometimes some images were missing.
This works for me now:
[java]
TextureKey test = new TextureKey(“gui/images/arrow-next2.png”);
((DesktopAssetManager)am).addToCache(new AssetKey<TextureKey>(“gui/images/arrow-next2.png”), test);
((DesktopAssetManager)am).loadAsset(test);
[/java]
Now Nifty uses this cached images instead of loading them again. A nice try catch block around it and 2-3 repetitions if it fails and I am happy :slight_smile:

The font problem had only a few PCs and this every time. That’s the curious fact.
The server wasn’t the problem because I tested 3 different servers and always a error page on the same PCs? Mmmh.
As you mentioned logging would be a good option. If I have the time I will try it again.

which kind of server are you using ? If you use something like apache, you have logs for every access. So, you could heve some clue here. About the awt loader : it could (could !) be a solution to get an inputstream from the assetmanager (via the locateAsset method which return an AssetInfo which has a openStream method) then use the ImageIO to get the bufferedimage. In that way, you have a full access on the stream, so you can see if garbage arrives on it.

1 Like

I’m starting to half remember that when run as an applet, web connections go through the browser. It’s been a long time since I’ve done this kind of applet development. Perhaps those browsers have smart errors enabled or something. Either way, logging would be helpful in finding the issue.

For development I’m using an apache (xampp, localhost) server. The other two servers don’t know, would be hard to get the log-files from there.
At the moment I have the .fonts and cursors in .jar , this works. Because it’s very time consuming to debug this naughty error, I will wait some weeks, then I get an certificate to sign everything. So I could exlude java security restrictions at the same time. Thanks for your hints, I will test it asap.

you don’t need to wait for weeks to have a certificate. You can still create a self-signed certificate and add it into the “trusted certificate” list in your browser.

1 Like