Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

How to customize the player versioning system

Developers
2011-01-04
2013-06-06
  • I know that the objects have a version number (protocol version). Where can I find an example on how to handle different versions of an object. I know there must be one in Stendhal but can't find it.

    Thanks in advance!

     
  • Hello javydreamercsw,

    there are two kinds of versions:

    Protocol Version

    That is the version of the client/server protocol. As you know Marauroa currently uses the same mechanism to save a player object to the database as binary blob. When the datatype "map" was introduced, we had to modify the protocol. That is what the protocol version byte in the network protocol and the database column is about. An example where that is used is in the reading of RPObject.

    This is completely internal and games should never be bothered about it. (Well of course they cannot use maps if the client uses a Marauroa version that is more than 6 month old).

    Object Version

    I think your question is about something, I'd call "Object Version". That is with new releases of the game, new attributes are added to the player object, other attributes are removed, the types or even the meaning are changed. In Stendhal we do this in the class UpdateConverter which is called indirectly from StendhalRPRuleProcessor.onInit(). Mostly we just check if the attributes/slots are there and add them with appropriate default values if not.

    We have a release attribute in which we store the current Stendhal version number. Its original intent was to tell the client that it is outdated. But recently it game in hand when we noticed that we had to fix a calculation bug in the attribute age.

     
  • Seems like the same can be doen from within the RPObject's update method right? If that's the case then I'm having issues adding a Map to my object. I defined it like this on my custom object

    player.addAttribute(COLLECTION, Type.MAP, Definition.PRIVATE);
    

    Then in it's update method:

    if (!hasMap(COLLECTION)) {
                addMap(COLLECTION);
    

    }

    I get NPE trying to save th RPObject.

    java.lang.NullPointerException
            at marauroa.server.game.db.RPObjectDAO.storeRPObject(RPObjectDAO.java:199)
            at marauroa.server.game.db.CharacterDAO.addCharacter(CharacterDAO.java:71)
            at simple.server.core.account.CharacterCreator.create(CharacterCreator.java:84)
            at simple.server.core.engine.SimpleRPRuleProcessor.createCharacter(SimpleRPRuleProcessor.java:335)
            at marauroa.server.game.rp.RPServerManager.createCharacter(RPServerManager.java:320)
            at marauroa.server.game.messagehandler.CreateCharacterHandler.createCharacter(CreateCharacterHandler.java:103)
            at marauroa.server.game.messagehandler.CreateCharacterHandler.process(CreateCharacterHandler.java:73)
            at marauroa.server.game.messagehandler.MessageDispatcher.dispatchMessage(MessageDispatcher.java:107)
            at marauroa.server.game.GameServerManager.run(GameServerManager.java:271)

    I assume the Map is a null object or something. How can I fix this? Or better yet point me to an example.

     
  • Hello javy,

    I think the NullPointerException is unrelated. Please check that you are not passing null as parameter to CharacterDAO.addCharacter() from CharacterCreator.create().

    Once a map is defined on the RPClass with addAttribute, you can use the put(attribute, key, value) and get(attribute, key) without further preparation. addMap is not required.

     
  • The NPE occurs when I try to add something to the map, right when the put(string, key, value) is executed. All values are valid. Are you sure the Map doesn't needs to be initialized? Using getMap(name) returns null.

     
  • Actually RPObject.addMap is throwing a IllegalArgumentException("The type of the attribute "+map+" is not MAP type.");

    Here's my full class:

    public static void generateRPClass() {
            if (!RPClass.hasRPClass(RPCLASS_NAME)) {
                jWrestlingRPClass player = new jWrestlingRPClass(RPCLASS_NAME);
                player.isA("rpentity");
                //This will hold the match id of player's current match
                player.addAttribute(Player.getMATCHID(), Type.INT);
                player.addRPEvent("text", Definition.VOLATILE);
                //This is the assigned key for encryption purposes on the client
                player.addAttribute(ClientObject.KEY, Type.LONG_STRING, Definition.PRIVATE);
                player.addAttribute("outfit", Type.INT);
                player.addAttribute("outfit_org", Type.INT);
                player.addAttribute("wins", Type.INT);
                player.addAttribute("losses", Type.INT);
                player.addAttribute("draws", Type.INT);
                player.addAttribute("away", Type.LONG_STRING, Definition.VOLATILE);
                player.addAttribute("grumpy", Type.LONG_STRING, Definition.VOLATILE);
                // Use this for admin menus and usage.
                player.addAttribute("admin", Type.FLAG);
                player.addAttribute("adminlevel", Type.INT);
                player.addAttribute("invisible", Type.FLAG, Definition.HIDDEN);
                player.addAttribute("ghostmode", Type.FLAG);
                player.addAttribute("release", Type.STRING, Definition.PRIVATE);
                player.addAttribute("age", Type.INT);
                player.addAttribute("online", Type.LONG_STRING,
                        (byte) (Definition.PRIVATE | Definition.VOLATILE));
                player.addAttribute("offline", Type.LONG_STRING,
                        (byte) (Definition.PRIVATE | Definition.VOLATILE));
                player.addAttribute("karma", Type.FLOAT, Definition.PRIVATE);
                player.addAttribute("sentence", Type.STRING, Definition.HIDDEN);
                // The guild name
                player.addAttribute("guild", Type.STRING);
                /**
                 * Add event
                 * player.addRPEvent("<Event RPClassName>", Definition.VOLATILE);
                 */
                player.addRPEvent(ChallengeEvent.getRPClassName(), Definition.VOLATILE);
                player.addRPEvent(MatchCRUDEvent.getRPClassName(), Definition.VOLATILE);
                player.addRPEvent(MatchEvent.getRPClassName(), Definition.VOLATILE);
                player.addRPEvent(RuleChangeEvent.getRPClassName(), Definition.VOLATILE);
                player.addRPEvent(PrivateTextEvent.getRPClassName(), Definition.PRIVATE);
                player.addRPEvent(RoomEvent.getRPClassName(), Definition.VOLATILE);
                // We use this for the buddy system
                player.addRPSlot("!buddy", 1, Definition.PRIVATE);
                player.addRPSlot("!ignore", 1, Definition.HIDDEN);
                player.addRPSlot("!quests", 1, Definition.HIDDEN);
                player.addRPSlot("!tutorial", 1, Definition.HIDDEN);
                player.addRPSlot("!skills", 1, (byte) (Definition.HIDDEN | Definition.VOLATILE));
                player.addRPSlot("!visited", 1, Definition.HIDDEN);
                // ClientObject features
                player.addRPSlot("!features", 1, Definition.PRIVATE);
                //Player's collection
                player.addAttribute(COLLECTION, Type.MAP, Definition.PRIVATE);
            }
        }
    

    As you can see it is declared as a map. You think is the Private flag?

     
  • Hi Javy,

    the stacktrace you posted shows that your RPObject itself is null. This explains why the put(map, key, value) directly throws an NPE. If the problem persists after you fixed that, please post a new Stacktrace.

    Don't call addMap.

    Example:

    import marauroa.common.game.Definition;
    import marauroa.common.game.Definition.Type;
    import marauroa.common.game.RPClass;
    import marauroa.common.game.RPObject;
    public class Test {
        public static void main(String[] args) {
            RPClass rpClass = new RPClass("test_class");
            rpClass.addAttribute("test_map", Type.MAP, Definition.PRIVATE);
            RPObject testObject = new RPObject();
            testObject.setRPClass("test_class");
            System.out.println("Reading non existing map entriy: "
                + testObject.get("test_map", "key"));
            testObject.put("test_map", "key", "value");
            System.out.println("Reading existing map entriy: "
                    + testObject.get("test_map", "key"));
        }
    }
    
     
  • I checked and the RPObject is indeed null but it's because on the process of adding the map (using put(attribute, key, value) ) and without calling AddMap .

    I wrapped my method so the Exception is displayed and here's the stack trace:

    ERROR [verManager] PlayerRPC                (68  ) - The type of the attribute collection is not MAP type.
    java.lang.IllegalArgumentException: The type of the attribute collection is not MAP type.
            at marauroa.common.game.RPObject.addMap(RPObject.java:783)
            at marauroa.common.game.RPObject.put(RPObject.java:614)
            at games.jwrestling.server.entity.player.PlayerRPC.addToCollection(PlayerRPC.java:476)
            at games.jwrestling.server.entity.player.PlayerRPC.addToCollection(PlayerRPC.java:467)
            at games.jwrestling.server.entity.player.PlayerRPC.updateCollection(PlayerRPC.java:487)
            at games.jwrestling.server.entity.player.PlayerRPC.update(PlayerRPC.java:462)
            at simple.server.core.entity.Entity.<init>(Entity.java:61)
            at simple.server.core.entity.RPEntity.<init>(RPEntity.java:74)
            at simple.server.core.entity.clientobject.ClientObject.<init>(ClientObject.java:97)
            at games.jwrestling.server.entity.player.PlayerRPC.<init>(PlayerRPC.java:65)
            at games.jwrestling.server.entity.player.PlayerRPC.createDefaultClientObject(PlayerRPC.java:762)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
            at java.lang.reflect.Method.invoke(Method.java:597)
            at simple.server.core.engine.SimpleRPObjectFactory.createDefaultClientObject(SimpleRPObjectFactory.java:139)
            at simple.server.core.account.CharacterCreator.create(CharacterCreator.java:75)
            at simple.server.core.engine.SimpleRPRuleProcessor.createCharacter(SimpleRPRuleProcessor.java:335)
            at marauroa.server.game.rp.RPServerManager.createCharacter(RPServerManager.java:320)
            at marauroa.server.game.messagehandler.CreateCharacterHandler.createCharacter(CreateCharacterHandler.java:103)
            at marauroa.server.game.messagehandler.CreateCharacterHandler.process(CreateCharacterHandler.java:73)
            at marauroa.server.game.messagehandler.MessageDispatcher.dispatchMessage(MessageDispatcher.java:107)
            at marauroa.server.game.GameServerManager.run(GameServerManager.java:271)
    

    And this is line 476 at games.jwrestling.server.entity.player.PlayerRPC.addToCollection():

    put(COLLECTION, e.getKey().toString().substring(e.getKey().toString().indexOf(" ") + 1), "" + amount);
    
     
  • Figured it out. I was trying to do it at the wrong time. Trying after the RPObject is converted to my custom RPObject did the trick. Thanks!

     
  • Is there any guide or example which wrongly hints to configure H2 in MySQL-compatibility mode? Is there a way to detect this missconfiguration?

     
  • Yes. Its part of the URL so it'll be easy to figure out. Something like this can be part of the url:

    ;MODE=MySQL

    There are other compatibility modes so probably looking for ;MODE should be enough. Maybe displaying a warning and removing that from the URL?

     
  • Marauroa now tests that there is no mode parameter in H2 JDBC-urls.

    H2 does support keys, indexes, and NOT NULL constraints on its own. Putting it into MySQL mode actually destroys support for NOT NULL constraints accodring to the documentation:

    When inserting data, if a column is defined to be NOT NULL and NULL is inserted, then a 0 (or empty string, or the current timestamp for timestamp columns) value is used

    . I consider this behavior a bug in MySQL; at least it is violating SQL, so I surely don't want it in H2.

     


Anonymous


Cancel   Add attachments