Automating the addition of new decorators

I am implementing the ADC “hidden by distance” algorithm where an opponent’s piece is not displayed if it is too far from any of yours. I’ve done this via a decorator which returns the correct value of the property “INVISIBLE_TO_ME” by looking at all the pieces on the map.

This worked fine but when I saved the module and reloaded it all the new decorators were replaced by markers. I tracked this down to BasicCommandEncoder which I overrode and everything then works.

The downside of having to do this is I cannot share the decorator with a non-technical game module developer since to use it is more than just typing the class name into the import.

So I write an override for createDecorator which attempts to load the class dynamically using the prefix of the type string to generate a class name. I include the source here.

/*

  • $Id: BasicCommandEncoder.java 2893 2008-01-27 20:15:23Z uckelman $
  • Copyright (c) 2000-2003 by Rodney Kinney
  • This library is free software; you can redistribute it and/or
  • modify it under the terms of the GNU Library General Public
  • License (LGPL) as published by the Free Software Foundation.
  • This library is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  • Library General Public License for more details.
  • You should have received a copy of the GNU Library General Public
  • License along with this library; if not, copies are available
  • at opensource.org.
    */

package VASSAL.build.module;

import java.lang.Character;
import java.lang.Class;
import java.lang.reflect.Constructor;

import VASSAL.counters.Decorator;
import VASSAL.counters.GamePiece;
import VASSAL.build.GameModule;
import VASSAL.tools.ErrorUtils;

/**
*

  • @author george
    */
    public class BasicCommandEncoderOverride extends BasicCommandEncoder {

public Decorator createDecorator(String type, GamePiece inner) {
Decorator d = null;
String prefix = type.substring(0,type.indexOf(’;’)+1);
if (prefix.length() == 0) {
prefix = type;
}
DecoratorFactory f = decoratorFactories.get(prefix);
if (f != null) {
d = f.createDecorator(type, inner);
}
else {
String className = “VASSAL.counters.” +
prefix.substring(0,1).toUpperCase()
+ prefix.substring(1, prefix.length()-1);
try {
Class dd = GameModule.getGameModule().getDataArchive()
.loadClass(className);
Constructor cc = dd.getConstructor( String.class, GamePiece.class );
d = (Decorator)cc.newInstance(type, inner);
PieceDefiner.addDefinition( (GamePiece)dd.getConstructor().newInstance());
} catch ( Throwable t) {
ErrorUtils.handleImportClassFailure(t, className);
}

}
return d;

}

}

and changing addDefinition inPieceDefiner to avoid duplicates means the new decorator appears in available traits as soon as it’s added to any piece

/**

  • Plugins can add additional GamePiece definitions
  • @param definition
    */
    public static void addDefinition(GamePiece definition) {
    initDefinitions();
    for (int i = 0,j = availableModel.size(); i < j; ++i) {
    if (definition.getClass().isInstance(availableModel.elementAt(i))) {
    return;
    }
    }
    availableModel.addElement(definition);
    }

A solution I like better is to add a sub-component of BasicCommandEncoder that associates a prefix with a class name. Then the module designer can register the prefix in the module editor and there are no constraints on the Decorator class name. It still requires him to manually add the .class files to the module, though. Plugins are a planned feature to solve this problem.

rk

Post generated using Mail2Forum (mail2forum.com)

This is a feature we’d probably like to see as a standard property in a future release of VASSAL.

  • M.

2008/9/10 Rodney Kinney <rodneykinney@comcast.net (rodneykinney@comcast.net)>

Post generated using Mail2Forum (mail2forum.com)

Making the class name the same as the prefix except for capitalisation is no great constraint and removes any need for action other than adding the .class file to the module. One could even distribute a .sh or .bat file with the .class files to do this automatically for the non-technical designer. It’s having to edit the buildFile to change BasicCommandEncoder and if you want to use decorators from different people you need to write some java. If the process was simple then new features could be shared around module developers without having to modify the VASSAL source code.

Another solution would be to put the module on the classpath before Vengine.jar but this would not be as friendly to the non-technical.

I was concerned about forcing the classes to be in a “VASSAL.counters” package. I’d prefer to keep that package namespace for “official” traits.

A solution for adding custom traits without manual work or coding should be possible. I’ll put that on the to-do list for version 3.2

rk

Post generated using Mail2Forum (mail2forum.com)

Fair enough. Perhaps VASSAL.contrib.counters?

When I’ve tested my Autohide trait how do I go about getting released into VASSAL?

Should I be building with 3.1 Beta 3 source or the latest trunk source?

Thus spake “george973”:

Can you give an argument for why custom code should be under the VASSAL.*
hiearchy? I’d much rather that module designers used their own hiearchy
for their code.

Let us know when you’re ready, and give us the code and documentation.

Right now, I’d build against the trunk.


J.


Messages mailing list
Messages@forums.vassalengine.org
forums.vassalengine.org/mailman/ … engine.org

Post generated using Mail2Forum (mail2forum.com)

Because it is a fixed place to look for classes to automatically load. Also if the traits are generally useful for the module design community they can then be shared and will be part of VASSAL from the point of view of module designers but need NOT be part of the VASSAL source tree.

Anything which allows designers to build and SHARE traits easily reduces the demand on the developers to produce the same thing in the official VASSAL. If a trait could be added just by putting a .class file in the module and importing it then sharing them would be easy. Which is where this thread started.

If you really don’t want the code under VASSAL you can think of another place to put it but since the code is only useful as part of VASSAL it seems to be to logically belong there.

Thus spake “george973”:

There already is a fixed place to look for classes—in the module.

A trait can be added just by putting a .class file in the module and
importing it, right now. You can do this already with the current version
of VASSAL.

Am I correct that what it is about your proposal which would making sharing
custom classes easier is distributing them with VASSAL? Because that’s not
related at all to the namespace issue, so far as I can see.

I’m opposed to distributing custom code with VASSAL, for the following
reason: If we vet it sufficiently that we’re willing to distribute it,
then shouldn’t it become part of VASSAL? If we don’t vet it sufficiently,
then should we really be distributing it at all?

What I think would be better is a place for module developers to share
custom code, say, on a wiki.

I think we’re not talking about the same thing. All I’m suggesting is that
custom code not be in the VASSAL.* namespace. Being in a different
namespace, say org.george973.counters, has no effect at all on whether you
can use those classes, but matters for things like detecting at runtime
whether a bug is due to a module problem or a VASSAL problem.


J.


Messages mailing list
Messages@forums.vassalengine.org
forums.vassalengine.org/mailman/ … engine.org

Post generated using Mail2Forum (mail2forum.com)

You need a fully qualified class name eg “VASSAL.counter.Trait” to load the class. I was suggesting using the prefix and appending it to a fixed place such as “VASSAL.counters” to get the class name rather than using a hash table to map the prefix to a class because when adding a trait you need to override BasicCommandEncoder to include your trait and extract the buildFile from the module jar file edit it to include your subclass of BasicCommandEncoder and put it back in the jar file which isn’t too hard but possibly more difficult than it needs to be for a non-technical module designer. And will need some Java expertise if you want to include custom traits from more than one source

If you really want to keep the VASSAL namespace pure you can either make the prefix a fully qualified class name or create a new namespace eg VASSALContributed

But you can’t. See above about BasicCommandEncoder

I don’t think custom code should be distributed with VASSAL. Sharing modules on a wiki would be fine. It’s just that I think that using a custom trait or whatever should require no more than putting it in the module and importing it which is not the case at present.

I can see your point about spotting if it is a VASSAL bug or custom bug. Where the customs traits are put doesn’t matter to me. I only used VASSAL.counters because that was where the traits were.

It is just that when you put a trait in a module and import it than that should be enough for it to work. Which it isn’t now. See BasicCommandEncoder which will replace all your new trait by Marker when you reload the saved module without warning you. Actually throwing an exception would be better - at least you would know what to fix)

Instead of hard-coding the namespace, just give your BasicCommandEncoderOverride class an attribute that specifies the namespace. You only have to override setAttribute() to pick it up. Then people who use your class can specify their own namespace when they edit their buildFile to swap in your class.

rk

Post generated using Mail2Forum (mail2forum.com)