Special Custom Class Development

I need a couple pointers on setting up custom classes in special circumstances. The special circumstances are as follows: Using a custom class in vassal where the class extends an existing class and must override external members of the original package that are “protected”. Normally, this is not possible, but @Brent_Easton has indicated that I can bring over a whole package into my custom class file in order to make my custom class a member of the package. I could therefore override whatever I needed in the package members. Presumably, this would supplant the original package with the modified package at runtime. The only problem is, this whole procedure is a new idea to me and I’m not sure what needs to be done to pull it off.

Hi Harald,

The first place to start is to fork the vassal-module-template repository. This will have a maven pom.xml all set up and ready to go, you shouldn’t have to change it. With an IDE like Intellij, just open the pom.xml and maven will do the rest. The pom.xml is already set up pointing to the latest Vassal source and the dependencies you need.

Thinking about it further, I don’t believe it is possible any longer to over-ride code in core Vassal with the same classes in custom code. It was a security loophole. Instead, you need to replace VASSAL components referenced in the buildfile with copied versions of your own.

From the Vassal source, copy the folder VASSAL/build/module/turn over to the my_custom_component folder in your new module template. Change the package declarations in the new source from VASSAL.build.module.turn to my_custom_component.turn.

Then, in your buildfile of the module, change any occurence of VASSAL.build.module.turn to my_custom_component.turn.

After copying the my_custom_component folder from the target/classes folder into your module, plus the new buildfile, you should be ready go, running your custom version of the turn tracker.

If the changes you need end up incorporated into base vassal, you can just reverse the process to revert to a Vanilla turn tracker.

BEaston,

I really appreciate you taking the time to step through this for me. I’m going to make another pass at it and see what happens. It would be nice to get this figured out.

Best regards,
Harald

1 Like

Incidentally, when you have a moment (take your time) do you have a Vassal use-case for a (Maven) WAR Overlay? Is this a technique we might use?

Sorry, I have no idea what that means or how we would use it. I just read an article about it and I still have no clue! Happy to be educated :slight_smile:

Heh, no worries, that’s about where I am with it. Before you got back to me I was hunting around for some clue about how to get done what I needed and that was the only thing that looked like it could fit the bill.
-Cheers

BEASTon rocks! This worked like a charm. Thank you very much! :+1:

1 Like

@Brent_Easton
thanks for your help it working perfectly

Just saw this. Here’s my thoughts.

In Java, a method that is declared protected can be overwritten by a derived class. For example

class Base {
    public Base() { System.out.println("Base"); }
    public void run() {
	    this.function();
    }
    protected void function() {
	    System.out.println("Base.function");
    }
}
public class Derived extends Base {
    public Derived() { System.out.println("Derived"); }
    protected void function() {
	    System.out.println("Derived.function");
    }
    public static void main(String[] args) {
	    Base obj = new Derived();
	    obj.run();
    }	    
}

and

$ javac Derived.java 
$ java Derived
Base
Derived
Derived.function

private member functions cannot be overwritten in a derived class. Try, for example to change the visibility of Base.function to private above, recompile and run

$ javac Derived.java 
$ java Derived 
Base
Derived
Base.function

As you can see, the Derived.function member function isn’t called.

This means that if you need to derive a class from a VASSAL class, say VASSAL.build.module.turn.TurnTracker, and you for example want to override the protected getTurnString, you could do

package mine;

public class MyTurnTracker extends VASSAL.build.module.turn.TurnTracker
{
    protected String getTurnString() {
	String tmp = super.getTurnString();
	return "My "+tmp;
    }
}

and

$ javac -cp /path/to/vassal/lib/Vengine.jar MyTurnTracker.java 

Then you add MyTurnTracker.class (and probably also the source for good measure) into the module archive (either using the VASSAL editor or some ZIP program), in the sub-folder mine (name of your package). You can now make instances of this component in your module via the VASSAL editor.

The point is that you do not need to recompile all of VASSAL to do this, or make any changes to VASSAL code itself.

If you want to see an example, check out for example AfrikaKorps-ch-1.1.vmod which has the custom hex grid numbering class ak.ObliqueHexGridNumbering to give hexes an old-style AH numbering. The code can be found at GitLab. This particular code does not override any protected member functions, but as noted above, it is only private member function that cannot be overwritten.

As for security, any function that is potentially harmful should be private. But in principle all code is potentially harmful, especially since VASSAL can and will load arbitrary code. This is why we should make our custom classes publicly available so that people can see that nothing bad is going on.

Yours,
Christian

1 Like

Hi Christian,

In general, you will find that most of the Vassal classes are defined as protected for exactly that reason to allow custom code to sub-class them and over-ride any required behavior.

However, sometimes, that just isn’t enough and the only solution is to completely copy and replace the component. This also tends to future-proof your code against future changes to those Vassal classes.

My comment about security was related to a Java security loophole where you used to be able to copy and modify the source for a specific Vassal class without changing the package names and include your modified source in the module and Vassal would load and run your modified code in preference to the Vassal code. This was actually a huge security loophole that was closed with changes to the Java class loader around Java 15 or so.

Regards,
Brent.

Hi,

I’m not sure under which circumstances one would need to complete rewrite a class.

First off, classes are not protected. They can be private (i.e., no public in front of class) or public. Member function, on the other hand, can be protected, which means they are not callable from outside, but can be overwritten in derived classes.

Back to question of rewriting VASSAL classes.

When VASSAL loads a module, more specifically parses the contained XML file buildFile.xml it tries to make an object instance of a class who’s fully qualified name is the tag of the XML element. E.g.,

<foo.bar.Baz ...>

will cause VASSAL to try to do

Object object = new foo.bar.Baz()

which means to make an alternative foo.bar.Baz one would derive from that class, say gnus.Gnat, and instantise that through the XML

<gnus.Gnat ...>

instead. If foo.bar.Baz makes specific instances of another class, say foo.bar.baz.Fubar. like

package foo.bar;
import baz.Fubar;

class Bar {
  ...
  protected Fubar makeFubar() { return new Fubar(); }
}

and one would need to change something in Fubar. then one could derive another class from that, say

package gnus.gnat;

class Fubar extends foo.bar.baz.Fubar 
{
   ...
}

and instantise that instead in Gnat

package gnus;
import gnus.gnat.Fubar;

class Bar {
  ...
  protected Fubar makeFubar() { return Fubar(); }//instance of gnus.gnat.Fubar
}

There’s still no need to redefine core VASSAL classes - this can be handled through class loading. As long as one can specify through the XML file which classes to make objects of, possible indirectly, then the specific class overrides should do the trick.

I may have overlooked something, in which case I’d like to hear what.

I think the reason you cannot override a class with another class with the same fully qualified name is mainly because it would cause confusion to the runtime environment, since you’d have two definitions of the same class. VASSAL still allows you to load and execute arbitrary code through a module. E.g., you could have a module with an embedded class that opens up /etc/passwd and ships that off to some DropBox folder :-/

Yours,
Christian