SimpleApplication.startCanvas() throws NPE *Patch included

Hello,

If you use startCanvas(), startCanvas(true) or startCanvas(false) instead of createCanvas() no context gets initialized resulting in a NPE.



Only a very small change is needes, basically replace the startCanvas(boolean waitFor) in the Application.java with following:

[java]

public void startCanvas(boolean waitFor) {

if (context == null) {

context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);

context.setSystemListener(this);

}

context.create(waitFor);

}

[/java]



Unfortunatly my auto formatter reformated all the code so the patch is quite long :frowning:



[patch]

This patch file was generated by NetBeans IDE

It uses platform neutral UTF-8 encoding and n newlines.

— Base (BASE)

+++ Locally Modified (Based On LOCAL)

@@ -53,11 +53,13 @@

import java.util.logging.Logger;



/**

    • The <code>Application</code> class represents an instance of a
    • real-time 3D rendering jME application.
    • The
    • <code>Application</code> class represents an instance of a real-time 3D
    • rendering jME application.

      *
    • An <code>Application</code> provides all the tools that are commonly used in jME3
    • applications.
    • An
    • <code>Application</code> provides all the tools that are commonly used in
    • jME3 applications.

      *
  • jME3 applications should extend this class and call start() to begin the
  • application.

    @@ -66,21 +68,17 @@

    public class Application implements SystemListener {



    private static final Logger logger = Logger.getLogger(Application.class.getName());

    -

    protected AssetManager assetManager;

    -

    protected AudioRenderer audioRenderer;

    protected Renderer renderer;

    protected RenderManager renderManager;

    protected ViewPort viewPort;

    protected ViewPort guiViewPort;

    -

    protected JmeContext context;

    protected AppSettings settings;

    protected Timer timer = new NanoTimer();

    protected Camera cam;

    protected Listener listener;

    -

    protected boolean inputEnabled = true;

    protected boolean pauseOnFocus = true;

    protected float speed = 1f;

    @@ -91,13 +89,13 @@

    protected TouchInput touchInput;

    protected InputManager inputManager;

    protected AppStateManager stateManager;

    -

    private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();



    /**
  • * Create a new instance of &lt;code&gt;Application&lt;/code&gt;.<br />
    
  • * Create a new instance of<br />
    
  • * &lt;code&gt;Application&lt;/code&gt;.<br />
    

*/

  • public Application(){
  • public Application() {

    initStateManager();

    }



    @@ -113,15 +111,12 @@

    }



    /**
  • * Enable or disable pause on lost focus.<br />
    
  • * &lt;p&gt;<br />
    
  • * By default, pause on lost focus is enabled.<br />
    
  • * If enabled, the application will stop updating<br />
    
  • * when it loses focus or becomes inactive (e.g. alt-tab).<br />
    
  • * For online or real-time applications, this might not be preferable,<br />
    
  • * so this feature should be set to disabled. For other applications,<br />
    
  • * it is best to keep it on so that CPU usage is not used when<br />
    
  • * not necessary.<br />
    
  • * Enable or disable pause on lost focus. &lt;p&gt; By default, pause on lost<br />
    
  • * focus is enabled. If enabled, the application will stop updating when it<br />
    
  • * loses focus or becomes inactive (e.g. alt-tab). For online or real-time<br />
    
  • * applications, this might not be preferable, so this feature should be set<br />
    
  • * to disabled. For other applications, it is best to keep it on so that CPU<br />
    
  • * usage is not used when not necessary.<br />
    

*

  • @param pauseOnLostFocus True to enable pause on lost focus, false
  • otherwise.

    @@ -131,18 +126,19 @@

    }



    @Deprecated
  • public void setAssetManager(AssetManager assetManager){
  •    if (this.assetManager != null)<br />
    
  • public void setAssetManager(AssetManager assetManager) {
  •    if (this.assetManager != null) {<br />
    

throw new IllegalStateException("Can only set asset manager"

  • " before initialization.");
  •    }<br />
    

this.assetManager = assetManager;
}

- private void initAssetManager(){
- if (settings != null){
+ private void initAssetManager() {
+ if (settings != null) {
String assetCfg = settings.getString("AssetConfigURL");
- if (assetCfg != null){
+ if (assetCfg != null) {
URL url = null;
try {
url = new URL(assetCfg);
@@ -158,7 +154,7 @@
assetManager = JmeSystem.newAssetManager(url);
}
}
- if (assetManager == null){
+ if (assetManager == null) {
assetManager = JmeSystem.newAssetManager(
Thread.currentThread().getContextClassLoader()
.getResource("com/jme3/asset/Desktop.cfg"));
@@ -166,37 +162,36 @@
}

/**
- * Set the display settings to define the display created.
- * <p>
- * Examples of display parameters include display pixel width and height,
- * color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
- * If this method is called while the application is already running, then
+ * Set the display settings to define the display created. <p> Examples of
+ * display parameters include display pixel width and height, color bit
+ * depth, z-buffer bits, anti-aliasing samples, and update frequency. If
+ * this method is called while the application is already running, then
* {@link #restart() } must be called to apply the settings to the display.
*
* @param settings The settings to set.
*/
- public void setSettings(AppSettings settings){
+ public void setSettings(AppSettings settings) {
this.settings = settings;
- if (context != null && settings.useInput() != inputEnabled){
+ if (context != null && settings.useInput() != inputEnabled) {
// may need to create or destroy input based
// on settings change
inputEnabled = !inputEnabled;
- if (inputEnabled){
+ if (inputEnabled) {
initInput();
- }else{
+ } else {
destroyInput();
}
- }else{
+ } else {
inputEnabled = settings.useInput();
}
}

/**
- * Sets the Timer implementation that will be used for calculating
- * frame times. By default, Application will use the Timer as returned
- * by the current JmeContext implementation.
+ * Sets the Timer implementation that will be used for calculating frame
+ * times. By default, Application will use the Timer as returned by the
+ * current JmeContext implementation.
*/
- public void setTimer(Timer timer){
+ public void setTimer(Timer timer) {
this.timer = timer;

if (timer != null) {
@@ -208,11 +203,11 @@
}
}

- public Timer getTimer(){
+ public Timer getTimer() {
return timer;
}

- private void initDisplay(){
+ private void initDisplay() {
// aquire important objects
// from the context
settings = context.getSettings();
@@ -225,8 +220,8 @@
renderer = context.getRenderer();
}

- private void initAudio(){
- if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
+ private void initAudio() {
+ if (settings.getAudioRenderer() != null && context.getType() != Type.Headless) {
audioRenderer = JmeSystem.newAudioRenderer(settings);
audioRenderer.initialize();
AudioContext.setAudioRenderer(audioRenderer);
@@ -241,10 +236,10 @@
* projection with 45° field of view, with near and far values 1 and 1000
* units respectively.
*/
- private void initCamera(){
+ private void initCamera() {
cam = new Camera(settings.getWidth(), settings.getHeight());

- cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
+ cam.setFrustumPerspective(45f, (float) cam.getWidth() / cam.getHeight(), 1f, 1000f);
cam.setLocation(new Vector3f(0f, 0f, 10f));
cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);

@@ -261,33 +256,36 @@
}

/**
- * Initializes mouse and keyboard input. Also
- * initializes joystick input if joysticks are enabled in the
- * AppSettings.
+ * Initializes mouse and keyboard input. Also initializes joystick input if
+ * joysticks are enabled in the AppSettings.
*/
- private void initInput(){
+ private void initInput() {
mouseInput = context.getMouseInput();
- if (mouseInput != null)
+ if (mouseInput != null) {
mouseInput.initialize();
+ }

keyInput = context.getKeyInput();
- if (keyInput != null)
+ if (keyInput != null) {
keyInput.initialize();
+ }

touchInput = context.getTouchInput();
- if (touchInput != null)
+ if (touchInput != null) {
touchInput.initialize();
+ }

- if (!settings.getBoolean("DisableJoysticks")){
+ if (!settings.getBoolean("DisableJoysticks")) {
joyInput = context.getJoyInput();
- if (joyInput != null)
+ if (joyInput != null) {
joyInput.initialize();
}
+ }

inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
}

- private void initStateManager(){
+ private void initStateManager() {
stateManager = new AppStateManager(this);

// Always register a ResetStatsState to make sure
@@ -298,14 +296,14 @@
/**
* @return The {@link AssetManager asset manager} for this application.
*/
- public AssetManager getAssetManager(){
+ public AssetManager getAssetManager() {
return assetManager;
}

/**
* @return the {@link InputManager input manager}.
*/
- public InputManager getInputManager(){
+ public InputManager getInputManager() {
return inputManager;
}

@@ -326,7 +324,7 @@
/**
* @return The {@link Renderer renderer} for the application
*/
- public Renderer getRenderer(){
+ public Renderer getRenderer() {
return renderer;
}

@@ -347,14 +345,14 @@
/**
* @return The {@link JmeContext display context} for the application
*/
- public JmeContext getContext(){
+ public JmeContext getContext() {
return context;
}

/**
* @return The {@link Camera camera} for the application
*/
- public Camera getCamera(){
+ public Camera getCamera() {
return cam;
}

@@ -363,22 +361,21 @@
*
* @see #start(com.jme3.system.JmeContext.Type)
*/
- public void start(){
+ public void start() {
start(JmeContext.Type.Display);
}

/**
- * Starts the application.
- * Creating a rendering context and executing
- * the main loop in a separate thread.
+ * Starts the application. Creating a rendering context and executing the
+ * main loop in a separate thread.
*/
- public void start(JmeContext.Type contextType){
- if (context != null && context.isCreated()){
+ public void start(JmeContext.Type contextType) {
+ if (context != null && context.isCreated()) {
logger.warning("start() called when application already created!");
return;
}

- if (settings == null){
+ if (settings == null) {
settings = new AppSettings(true);
}

@@ -389,27 +386,25 @@
}

/**
- * Initializes the application's canvas for use.
- * <p>
- * After calling this method, cast the {@link #getContext() context} to
- * {@link JmeCanvasContext},
- * then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
- * and attach it to an AWT/Swing Frame.
- * The rendering thread will start when the canvas becomes visible on
- * screen, however if you wish to start the context immediately you
- * may call {@link #startCanvas() } to force the rendering thread
- * to start.
+ * Initializes the application's canvas for use. <p> After calling this
+ * method, cast the {@link #getContext() context} to
+ * {@link JmeCanvasContext}, then acquire the canvas with {@link JmeCanvasContext#getCanvas()
+ * }
+ * and attach it to an AWT/Swing Frame. The rendering thread will start when
+ * the canvas becomes visible on screen, however if you wish to start the
+ * context immediately you may call {@link #startCanvas() } to force the
+ * rendering thread to start.
*
* @see JmeCanvasContext
* @see Type#Canvas
*/
- public void createCanvas(){
- if (context != null && context.isCreated()){
+ public void createCanvas() {
+ if (context != null && context.isCreated()) {
logger.warning("createCanvas() called when application already created!");
return;
}

- if (settings == null){
+ if (settings == null) {
settings = new AppSettings(true);
}

@@ -419,89 +414,87 @@
}

/**
- * Starts the rendering thread after createCanvas() has been called.
- * <p>
+ * Starts the rendering thread after createCanvas() has been called. <p>
* Same as calling startCanvas(false)
*
* @see #startCanvas(boolean)
*/
- public void startCanvas(){
+ public void startCanvas() {
startCanvas(false);
}

/**
- * Starts the rendering thread after createCanvas() has been called.
- * <p>
- * Calling this method is optional, the canvas will start automatically
- * when it becomes visible.
+ * Starts the rendering thread after createCanvas() has been called. <p>
+ * Calling this method is optional, the canvas will start automatically when
+ * it becomes visible.
*
- * @param waitFor If true, the current thread will block until the
- * rendering thread is running
+ * @param waitFor If true, the current thread will block until the rendering
+ * thread is running
*/
- public void startCanvas(boolean waitFor){
+ public void startCanvas(boolean waitFor) {
+ if (context == null) {
+ context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
+ context.setSystemListener(this);
+ }
context.create(waitFor);
}

/**
* Internal use only.
*/
- public void reshape(int w, int h){
+ public void reshape(int w, int h) {
renderManager.notifyReshape(w, h);
}

/**
- * Restarts the context, applying any changed settings.
- * <p>
- * Changes to the {@link AppSettings} of this Application are not
- * applied immediately; calling this method forces the context
- * to restart, applying the new settings.
+ * Restarts the context, applying any changed settings. <p> Changes to the
+ * {@link AppSettings} of this Application are not applied immediately;
+ * calling this method forces the context to restart, applying the new
+ * settings.
*/
- public void restart(){
+ public void restart() {
context.setSettings(settings);
context.restart();
}

/**
- * Requests the context to close, shutting down the main loop
- * and making necessary cleanup operations.
+ * Requests the context to close, shutting down the main loop and making
+ * necessary cleanup operations.
*
* Same as calling stop(false)
*
* @see #stop(boolean)
*/
- public void stop(){
+ public void stop() {
stop(false);
}

/**
- * Requests the context to close, shutting down the main loop
- * and making necessary cleanup operations.
- * After the application has stopped, it cannot be used anymore.
+ * Requests the context to close, shutting down the main loop and making
+ * necessary cleanup operations. After the application has stopped, it
+ * cannot be used anymore.
*/
- public void stop(boolean waitFor){
+ public void stop(boolean waitFor) {
logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
context.destroy(waitFor);
}

/**
- * Do not call manually.
- * Callback from ContextListener.
- * <p>
- * Initializes the <code>Application</code>, by creating a display and
- * default camera. If display settings are not specified, a default
- * 640x480 display is created. Default values are used for the camera;
- * perspective projection with 45° field of view, with near
- * and far values 1 and 1000 units respectively.
+ * Do not call manually. Callback from ContextListener. <p> Initializes the
+ * <code>Application</code>, by creating a display and default camera. If
+ * display settings are not specified, a default 640x480 display is created.
+ * Default values are used for the camera; perspective projection with 45°
+ * field of view, with near and far values 1 and 1000 units respectively.
*/
- public void initialize(){
- if (assetManager == null){
+ public void initialize() {
+ if (assetManager == null) {
initAssetManager();
}

initDisplay();
initCamera();

- if (inputEnabled){
+ if (inputEnabled) {
initInput();
}
initAudio();
@@ -516,13 +509,13 @@
/**
* Internal use only.
*/
- public void handleError(String errMsg, Throwable t){
+ public void handleError(String errMsg, Throwable t) {
// Print error to log.
logger.log(Level.SEVERE, errMsg, t);
// Display error message on screen
if (t != null) {
- JmeSystem.showErrorDialog(errMsg + "n" + t.getClass().getSimpleName() +
- (t.getMessage() != null ? ": " + t.getMessage() : ""));
+ JmeSystem.showErrorDialog(errMsg + "n" + t.getClass().getSimpleName()
+ + (t.getMessage() != null ? ": " + t.getMessage() : ""));
} else {
JmeSystem.showErrorDialog(errMsg);
}
@@ -533,7 +526,7 @@
/**
* Internal use only.
*/
- public void gainFocus(){
+ public void gainFocus() {
if (pauseOnFocus) {
paused = false;
context.setAutoFlushFrames(true);
@@ -546,8 +539,8 @@
/**
* Internal use only.
*/
- public void loseFocus(){
- if (pauseOnFocus){
+ public void loseFocus() {
+ if (pauseOnFocus) {
paused = true;
context.setAutoFlushFrames(false);
}
@@ -556,17 +549,14 @@
/**
* Internal use only.
*/
- public void requestClose(boolean esc){
+ public void requestClose(boolean esc) {
context.destroy(false);
}

/**
- * Enqueues a task/callable object to execute in the jME3
- * rendering thread.
- * <p>
- * Callables are executed right at the beginning of the main loop.
- * They are executed even if the application is currently paused
- * or out of focus.
+ * Enqueues a task/callable object to execute in the jME3 rendering thread.
+ * <p> Callables are executed right at the beginning of the main loop. They
+ * are executed even if the application is currently paused or out of focus.
*/
public <V> Future<V> enqueue(Callable<V> callable) {
AppTask<V> task = new AppTask<V>(callable);
@@ -579,7 +569,7 @@
*/
protected void runQueuedTasks() {
AppTask<?> task;
- while( (task = taskQueue.poll()) != null ) {
+ while ((task = taskQueue.poll()) != null) {
if (!task.isCancelled()) {
task.invoke();
}
@@ -587,64 +577,68 @@
}

/**
- * Do not call manually.
- * Callback from ContextListener.
+ * Do not call manually. Callback from ContextListener.
*/
- public void update(){
+ public void update() {
// Make sure the audio renderer is available to callables
AudioContext.setAudioRenderer(audioRenderer);

runQueuedTasks();

- if (speed == 0 || paused)
+ if (speed == 0 || paused) {
return;
+ }

timer.update();

- if (inputEnabled){
+ if (inputEnabled) {
inputManager.update(timer.getTimePerFrame());
}

- if (audioRenderer != null){
+ if (audioRenderer != null) {
audioRenderer.update(timer.getTimePerFrame());
}

// user code here..
}

- protected void destroyInput(){
- if (mouseInput != null)
+ protected void destroyInput() {
+ if (mouseInput != null) {
mouseInput.destroy();
+ }

- if (keyInput != null)
+ if (keyInput != null) {
keyInput.destroy();
+ }

- if (joyInput != null)
+ if (joyInput != null) {
joyInput.destroy();
+ }

- if (touchInput != null)
+ if (touchInput != null) {
touchInput.destroy();
+ }

inputManager = null;
}

/**
- * Do not call manually.
- * Callback from ContextListener.
+ * Do not call manually. Callback from ContextListener.
*/
- public void destroy(){
+ public void destroy() {
stateManager.cleanup();

destroyInput();
- if (audioRenderer != null)
+ if (audioRenderer != null) {
audioRenderer.cleanup();
+ }

timer.reset();
}

/**
- * @return The GUI viewport. Which is used for the on screen
- * statistics and FPS.
+ * @return The GUI viewport. Which is used for the on screen statistics and
+ * FPS.
*/
public ViewPort getGuiViewPort() {
return guiViewPort;
@@ -653,5 +647,4 @@
public ViewPort getViewPort() {
return viewPort;
}
-
}

[/patch]
1 Like

I hate those auto formats when I’m not coding my own stuff. I never auto format code that is not mine, to avoid those weird things ^^

@shirkit said:
I hate those auto formats when I'm not coding my own stuff. I never auto format code that is not mine, to avoid those weird things ^^


Unfortunately ALT-SHIFT-D gets automatically pressed before STRG-S :(

Hehe i know that problem, ^^

The javadocs state quite clearly how you’re supposed to use these methods. I don’t see any reason to change it. You call createCanvas(), attach it, then call startCanvas() to initiate the rendering. Actually with the latest jME3, startCanvas() is useless since rendering starts automatically when the canvas gets a native peer.