Nifty seems to ignore new screen controllers

In Mythruna, when a user connects to the server I use a combined ScreenController and AppState as my login screen:

[java]

LoginState loginState = new LoginState(this, host, gameClient);

app.getStateManager().attach( loginState );

nifty.fromXml( “Interface/login-screens.xml”, “login”, loginState );

[/java]



So, when the user clicks “Connect”, I create a GameClient object and run the code above. From that set of screens they can create a new account, login, etc…



However, if they cancel back to the main menu and connect to a different server causing the above code to be run again, Nifty ignores the new controller and continues delegating to my old one.



I could swear that this used to work. Is there some way to get nifty to forget about the old screen → controller linkage and obey my new one?

What if you delete that screen,then go the the new screen?

Paul, do you detach the loginState AppState after the client disconnect? This might be the problem the way I see it.

It’s not the app state. I detach that and anyway it’s the button events that Nifty is delivering that are going to the wrong (old) instance.



@asasinuxp, how do I delete the screen? I’ve poked around the documentation a little but didn’t see anything.

One way to do that would be to call nifty.exit() to properly close that nifty screen iirc.

Yeah, but it doesn’t just close that screen, it shuts down all of nifty and then I have to recreate/set it up as far as I know. Seems excessive for just switching screens.



My theory is that this worked before void added the ability to load new XML on top of an existing configuration.



Anyway, it’s a relatively minor bug from Mythruna’s perspective and I’ll just wait it out.

[java]nifty.removeScreen("screen_to_delete");[/java]

I use that to make a pause menu,I load a xml,then to return to game,I delete that screen.

JME seems to still seems to be working off of the 1.3 snapshot and that’s only available in the release version. I’ve also been looking at the 1.3-snapshot javadocs on sourceforge which is why I didn’t see it.



It’s possible my issue is fixed in the newer version, also. I’ll play with it another day.

I don’t believe that this has ever worked :slight_smile:



ScreenController instances are simply added to a List inside of the Nifty instance and are used to lookup an instance of a ScreenController when Nifty tries to match a classname (from xml or builder). It’s a simple member variable inside the Nifty instance:



[java]private Collection < ScreenController > registeredScreenControllers = new ArrayList < ScreenController >();[/java]



but you can’t currently access this or reset it from the outside - no matter what you do. Sorry! :confused:



Even removing a screen won’t remove entries from that list… you’ll need to play with that at a later day :wink:

Well, that’s a bit counter intuitive… especially since fromXml() historically has blown away all existing screen information… only to leave active instances lying around. Yikes.



good to know.

Great. Just hit that wall.



Keeping a reference to outdated data. So grateful for that. /sarcasm

I put a patch for this into Nifty about a month ago. It’s the screen controller list that keeps growing and nothing ever gets removed. The new method allows you to remove controllers from the list.



Unfortunately JME3 hasn’t been updated to the new version of Nifty and I posted a couple of times asking how/whether/when those updates happened and got no reply. There is some class<->interface switching around happened in Nifty that means you can’t even run JME3 against a new Nifty build. You need to get and build the latest Nifty, rebuild JME3 against it - and then everything is fine.



(You don’t actually need to make any code changes in JME, just do a fresh build against the new Nifty).

Don’t worry, I circumvented the problem, but I shouldn’t have had to do this.



Because this happened after loading a game, the state Nifty kept was stale so now I have to manually set that state with the loaded saved data. If it had been done right, passing a “new” controller would have replaced the old one and nobody would’ve complained.



As for jME vs Nifty, yeah. I’ve gone through the jME-to-Nifty renderer and if something major has changed part of it will have to be redone.

Personally I was surprised to see that it was a list rather than a mapping of class->controller instance (in which case a new one would automatically replace the old and the lookup would be faster).

Yeah, I agree. But, let’s leave it at that.

There probably is no real reason for it being a plain list instead of a map :slight_smile:



In the beginning you were not supposed to change the whole ScreenController business from your code as it was only meant to be used with XML and changed only when another XML is loaded. And I think even that part was a bit broken :smiley:



But there really is no reason to keep it so silly or counter intuitive. Making it a map would help, I think. @zarch: Maybe send another patch?

Should be an easy change to make, I’ll take a look at it. Probably not tomorrow as I need to do a load of paperwork for accounts but I should get a chance in the next few days.

@void256

While I’m in here - is there any particular reason for using Hashtable over HashMap?

@void256. Patch is here, seems to work ok in my app.



[java]

@@ -2,11 +2,10 @@ package de.lessvoid.nifty;





import java.io.IOException;



import java.io.InputStream;



import java.util.ArrayList;



import java.util.Collection;



-import java.util.Hashtable;



import java.util.Iterator;



import java.util.LinkedList;



import java.util.List;



import java.util.Locale;



import java.util.Map;



@@ -63,44 +62,46 @@ import de.lessvoid.nifty.tools.ObjectPool;

import de.lessvoid.nifty.tools.ObjectPool.Factory;



import de.lessvoid.nifty.tools.SizeValue;



import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;



import de.lessvoid.xml.tools.SpecialValuesReplace;



import de.lessvoid.xml.xpp3.Attributes;



+import java.util.HashMap;







/**


  • The main Nifty class.


  • @author void



    */



    public class Nifty {


  • private Logger log = Logger.getLogger(Nifty.class.getName());


  • private static final Logger log = Logger.getLogger(Nifty.class.getName());



    +



    private NiftyRenderEngine renderEngine;



    private SoundSystem soundSystem;


  • private Map < String, Screen > screens = new Hashtable < String, Screen >();


  • private Map < String, PopupType > popupTypes = new Hashtable < String, PopupType >();


  • private Map < String, Element > popups = new Hashtable < String, Element >();


  • private Map < String, StyleType > styles = new Hashtable < String, StyleType >();


  • private Map < String, ControlDefinitionType > controlDefintions = new Hashtable < String, ControlDefinitionType >();


  • private Map < String, RegisterEffectType > registeredEffects = new Hashtable < String, RegisterEffectType >();


  • private Map < String, Screen > screens = new HashMap < String, Screen >();


  • private Map < String, PopupType > popupTypes = new HashMap < String, PopupType >();


  • private Map < String, Element > popups = new HashMap < String, Element >();


  • private Map < String, StyleType > styles = new HashMap < String, StyleType >();


  • private Map < String, ControlDefinitionType > controlDefintions = new HashMap < String, ControlDefinitionType >();


  • private Map < String, RegisterEffectType > registeredEffects = new HashMap < String, RegisterEffectType >();



    private Screen currentScreen = new NullScreen();



    private String currentLoaded;



    private boolean exit;



    private boolean resolutionChanged;



    private TimeProvider timeProvider;



    private List < ClosePopUp > closePopupList = new ArrayList < ClosePopUp >();



    private NiftyLoader loader;



    private List < ControlToAdd > controlsToAdd = new ArrayList < ControlToAdd >();



    private List < EndOfFrameElementAction > endOfFrameElementActions = new ArrayList < EndOfFrameElementAction >();



    private MouseInputEventProcessor mouseInputEventProcessor;


  • private Collection < ScreenController > registeredScreenControllers = new ArrayList < ScreenController >();


  • private Map < String, ScreenController > registeredScreenControllers = new HashMap < String, ScreenController >();



    private String alternateKeyForNextLoadXml;



    private long lastTime;



    private InputSystem inputSystem;



    private boolean gotoScreenInProgess;



    private String alternateKey;



    private Collection < DelayedMethodInvoke > delayedMethodInvokes = new ArrayList < DelayedMethodInvoke > ();


  • private Map<String, String> resourceBundleSource = new Hashtable<String, String>();


  • private Map<String, ResourceBundle> resourceBundles = new Hashtable<String, ResourceBundle>();


  • private Map<String, String> resourceBundleSource = new HashMap<String, String>();


  • private Map<String, ResourceBundle> resourceBundles = new HashMap<String, ResourceBundle>();



    private Locale locale = Locale.getDefault();



    private Properties globalProperties;



    private RootLayerFactory rootLayerFactory = new RootLayerFactory();



    private NiftyMouseImpl niftyMouse;



    private NiftyInputConsumerImpl niftyInputConsumer = new NiftyInputConsumerImpl();



    @@ -662,11 +663,11 @@ public class Nifty {





    private void removeScreenInternal(final String id) {



    Screen screen = screens.remove(id);



    if (screen == null ||



    screen.getLayerElements() == null ||


  •    screen.getLayerElements().size() == 0) {<br />
    

+ screen.getLayerElements().isEmpty()) {

return;

}

for (int i=0; i<screen.getLayerElements().size(); i++) {

removeElement(screen, screen.getLayerElements().get(i));

}

@@ -1055,31 +1056,26 @@ public class Nifty {
* Register a ScreenController instance.

* @param controllers ScreenController

*/

public void registerScreenController(final ScreenController ... controllers) {

for (ScreenController c : controllers) {

- registeredScreenControllers.add(c);

+ registeredScreenControllers.put(c.getClass().getName(), c);

}

}



/**

* find a ScreenController instance that matches the given controllerClass name.

* @param controllerClass controller class name

* @return ScreenController instance

*/

public ScreenController findScreenController(final String controllerClass) {

- for (ScreenController controller : registeredScreenControllers) {

- if (controller.getClass().getName().equals(controllerClass)) {

- return controller;

- }

- }

- return null;

+ return registeredScreenControllers.get(controllerClass);

}



public void unregisterScreenController(final ScreenController ... controllers) {

for (ScreenController c : controllers) {

- registeredScreenControllers.remove(c);

+ registeredScreenControllers.remove(c.getClass().getName());

}

}



public NiftyLoader getLoader() {

return loader;

@@ -1486,16 +1482,16 @@ public class Nifty {
public String specialValuesReplace(final String value) {

return SpecialValuesReplace.replace(value, getResourceBundles(), currentScreen == null ? null : currentScreen.getScreenController(), globalProperties);

}



private class SubscriberRegistry {

- private Map < Screen, Map < String, List < ClassSaveEventTopicSubscriber >>> screenBasedSubscribers = new Hashtable < Screen, Map < String, List < ClassSaveEventTopicSubscriber >>>();

+ private Map < Screen, Map < String, List < ClassSaveEventTopicSubscriber >>> screenBasedSubscribers = new HashMap < Screen, Map < String, List < ClassSaveEventTopicSubscriber >>>();



public void register(final Screen screen, final String elementId, final ClassSaveEventTopicSubscriber subscriber) {

Map < String, List < ClassSaveEventTopicSubscriber >> elements = screenBasedSubscribers.get(screen);

if (elements == null) {

- elements = new Hashtable < String, List < ClassSaveEventTopicSubscriber >>();

+ elements = new HashMap < String, List < ClassSaveEventTopicSubscriber >>();

screenBasedSubscribers.put(screen, elements);

}

List < ClassSaveEventTopicSubscriber > list = elements.get(elementId);

if (list == null) {

list = new ArrayList < ClassSaveEventTopicSubscriber >();

[/java]
1 Like

No particular reason to use Hashtable. HashMap is more suitable (and more modern, I guess) :slight_smile:



I’m fine with the change from Hashtable to HashMap but somehow I have problems with copy’n’paste of the patch to a new file and applying it :confused: Can you send it by email directly to me? void (at) lessvoid (dot) com?