3.3.3 release plan

Thus spake Flint1b:

Let’s do it like other modern projects and bring out a beta every week,
or every other week or something like that. Rolling releases. Whatever
gets merged until then gets into the beta.

Uptake of new versions has always struck me as slow. Would a faster release
cycle encourage users to upgrade faster? I’m not sure why I should expect
that over having more versions in use simultaneously.

Have you ever played an American Civil War games where movement can
generate stragglers? (The Gamers’ CWBS is like this.) The faster you march
the more stragglers you get…

What benefit you foresee of having more frequent (beta) releases?

And for the hardcore Vassal enthusiasts, a nightly build every day.
@Joel do you think you could automate the build so we can give it to
Travis’es naked ubuntu, have it download all prerequisites and run the
full release build? Shouldn’t be too much left, we already have
automated JDK download, some of the tools can be installed by ubuntu’s
apt, just these exotic ones that need to be compiled manually seem
tricky but it’s no rocket science to setup an ubuntu to build C stuff.
Then we’d have to handle automatic versioning, tagging and deployment of
the built packages, shouldn’t be too complicated.

Yes, I’m sure I could automate the build. Can we actually download builds
from Travis?


J.

I think if we made it bi-weekly, it wouldn’t be much different from we currently have, the betas already get released roughly every two weeks, except if there is a regular release.

And the nightly releases, you already do those custom builds almost every day, it would be the same just instead of having to build them manually you would tell the people “hey try this nightly from yesterday, see if your bug is fixed, here is the link”.

No Travis is just a “dumb” build server without a filesystem where things get saved, it’s the other way round, we write a script that tells Travis to upload the artifacts somewhere, I think we could upload them to GitHub, to our own server, or to some 3rd party site like Bintray.

We could write the code for that in the Makefile, or an extra shell script we call from the Makefile, setup Travis to do an automatic build of current master every 2 weeks / every night, add some logic which detects that and runs a full release build instead of the usual “mvn clean test”. At the end of the day Travis is just a regular ubuntu machine and can do anything that we tell it to do in our shell script.

Thus spake Cattlesquat:

Okay yeah, if we’re okay with some docs getting finished up during beta
then we’re probably getting close to a point that we could pull the
trigger. Give me an “aim point” date for beta 1 and I’ll try to have
things at least be “clean” by then. And I’ll also try to get a “new
feature page” together though again that can improve over several betas.

I didn’t have as much time as I expected today, as I fixed a floating point
bug in FreeRotator that’s likely been there since it was written.


J.

Prior to the 3.3.3 release, I would like to try and handle the Deprecated method removal more gracefully.

There is also an issue with the SNAPSHOT property Map change that breaks all of my modules, and possibly quite a few others. I’m in the process of updating my custom code, but we will be inundated with bug reports if we don’t do something at the Vassal end.

Yes Yan, I know, we need to get rid of the crap and get module owners to update and all that, but rather than break all the modules, I would like to go the route of the annoying popup messages that will drive the users of such modules batty. I propose the following implementation of Joel’s ‘stupid simple’ approach that I am happy to work on and implement for 3.3.3. This can also be applied to any new methods made Deprecated in the future.

  1. Replace all recently removed Deprecated methods.
  2. Add a Call to popup a Disableable ProblemDialog each time a Deprecated method is called. Each different Deprecated routine called will popup a new Dialog, even if you disabled the last one.
  3. Include a Deprecated date in the popup method call, to generate something like ‘This routine will be removed after 1-Aug-2021’ which will be 1 year after Deprecated date.

In future cleanups, anyone updating a class with a method that has a Deprecated date more than a year in the past can remove that method without guilt.

The Problem with the SNAPSHOT change is that ReportState fails with a Class Cast Exception if any module has custom code that includes the setProperty of SNAPSHOT using the PieceCloner. We have no idea how many modules have custom traits that save the SNAPSHOT as a GamePiece as no Deprecated methods are called in this case. My modules do because I built custom versions of existing classes that did this, so I copied that in my code, I am sure others will as well

The simplest fix is for ReportState to check the type of the SNAPSHOT returned, and if it is a GamePiece, then just export the properties at that point into a map. This works, at the expense of a performance hit for the custom code as it is basically doing both the old and the new method. I propose that at this point we also popup an appropriate ‘Deprecated Code’ dialog when we see this.

I think this is a good compromise. We still get to remove the cruft (after a year), but we get the message in the face of the players and designers and give them a change to fix the problem before we get the support hassles of dealing with it.

Thoughts?

In that case I propose some kind of automatism, since manual handling of all these deprecation notes and dialogs will be way too much work. Something like a special format for the deprecation comment and the date in it, and a code scanner that regularly scans the codebase, finds these comments and reports which of them are due for deletion.

Maybe even a custom annotation, something like

@Deprecated
@VassalDeprecated(since = "2020-08-05")
void method() {
}

This actually looks cool, I’ve never written custom annotations but they are powerful, I think we could hide the whole code for showing the dialog behind them. Then a custom maven plugin that compares the date in these annotations with current date and spits out a warning if a year has passed.

One question that remains is, how to handle deprecated fields? In a method call we can simply add the code for showing the dialog but how do we do that for access of deprecated fields? Could custom annotations handle that? I don’t know.

Another question that I have right away if we do this, can I go on and mark 99% of all protected fields and methods as deprecated? Just in case we might want to delete them in a year. Many (or even most?) of the protected fields and methods are not even meant to be called by subclasses, many classes that have these fields are not even meant to be subclassed, they were just made protected due to … I don’t know, old-school object-oriented approach where everything is per default protected? We have this spotbugs plugin now for instance and it detected a lot of “protected static” fields that should be final but are not, they are constants, we could and should make them final but we don’t know for certain whether modules assign new values to them. If they were private it would be easy going but thanks to this “protected by default” way of writing code we have a lot of trouble managing this code, and a lot of ways for custom modules to break Vassal by simply assigning new values to core constants.

Small example to make things more clear:

So in a module I write this class and break core functionality, thanks to glorious protected visibility:

class VassalBreaker extends ImageIOImageLoader {
  public VassalBreaker() {
    readImage = null;
    readSize = null;
  }
}

Now some will answer “but that’s highly hypothetical, no module should and would do such a thing” but I ask, why then allow this in the first place, why the F make these things protected, why open doors instead of hiding core code behind safe walls?

Overall, I am ambivalent on this issue. Generally I am for marking much more as deprecated and deleting it ASAP, or hiding it from the public API by changing protected to private. But if we do this one-year thing I am for investing some more time and thought up front and building a decent “deprecation system”, and then having a smooth ride in the years to come where we just get notified by the build server of what can be deleted.

I agree, let’s do it properly. I’m not sure that we can build the Dialog display into the Annotation though.

Let’s start by building that and implementing it for the recently departed @Deprecated methods. I think we should also apply it to the other @Deprecated routines that you didn’t remove because they are used by modules hosted on Vassal. That will get the process rolling.

Then we can discuss a plan to make more methods @Deprecated. The problem with Deprecating every protected method in a class is you have to provide alternate names for every Method, so it will be a painful process.

And yes, @Deprecated fields are a problem.

About hiding protected fields/methods, I wouldn’t see a “deprecated” thing as one that will have to be necessarily deleted. I think it’s legitimate to mark something as deprecated and later on change it from protected to private and remove the deprecated mark.

I looked a bit into custom annotations, they are not as powerful as I thought, we cannot hide any implementation behind them. But we could use them as markers that get picked up by the build server, we would need a separate project and the structure would be something like this:

- vassal-deprecation-parent
-- vassal-deprecation-core: contains the annotations
-- vassal-deprecation-maven-plugin: depends on vassal-deprecation-core, scans code during build, looks for the annotations and reports

- vassal-parent
-- vassal-app: depends on vassal-deprecation-core, adds annotations to code, uses vassal-deprecation-maven-plugin during the build

Thus spake Flint1b:

I looked a bit into custom annotations, they are not as powerful as I
thought, we cannot hide any implementation behind them.

Yes, unfortunately they’re not decorators like they are in Python.


J.

I was thinking of something simple like this using the existing @Deprecated tags as follows.

I’m not quite sure what you are suggesting Yan. Are you saying we auto-generate the ‘ProblemDialog.showDeprecated (“2020-08-05”);’ line based on the existence of the @Deprecated tag?

Map:

@Deprecated(since = "2020-08-05", forRemoval = true) public Rectangle componentRectangle(Rectangle r) { ProblemDialog.showDeprecated ("2020-08-05"); return mapToComponent(r); }

ProblemDialog:

[code]
public static Future<?> showDeprecated(String date) {
final StackWalker.StackFrame frame =
StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE), 2)
.walk(f → f.skip(1).limit(1).collect(Collectors.toList())).get(0);
final String method = frame.getClassName() + “.” + frame.getMethodName();
String expiry;
try {
expiry = LocalDate.parse(date, DateTimeFormatter.ofPattern(“uuuu-M-d”)).plusYears(1)
.format(DateTimeFormatter.ofPattern(“d-MMM-uuuu”, Resources.getLocale()));
}
catch (Exception ignored){
expiry = “some later date”;
}

// FIXME I18N
return showDisableable(JOptionPane.WARNING_MESSAGE,
  null,
  null, method,
  "Out of date Custom Code",
  "Out of date Custom Code",
  "This module contains Custom code that is calling the VASSAL function " + method +
    ". This function is scheduled to be removed from VASSAL after " + expiry +
    " and this module will cease to function.\n\nPlease check whether there is an updated version of this module. If not, please contact the maintainer of this module and request that it be fixed."
);

}[/code]

This produces

I wasn’t sure myself, but I looked into it and while it would have been cool if we could get these things to auto-generate, we can’t. We can still use annotations though to get the build server to notify us when the one year has passed, otherwise it would be more like a chore, having to manually scan through the code in a regular fashion and fishing out these deprecated tags. We would need a) a custom annotation for that and b) a custom maven plugin that we plug into our build and tell to find our custom tags, calculate the due date and once the date comes spit out a warning or even build error in the build log.

That’s fine. I prefer the deployed code to be the same as the original source anyway.

Do we need a custom tag? Couldn’t it just check the since attribute on the @Deprecated tags?

I didn’t even know the @Deprecated tag had a “since” attribute, I’ve just checked and it is meant to contain a version number not a date, but you are right maybe we could (mis-)use that. The more interesting question is then, how do we evaluate the contents of this “since” attribute as a date, what would it take to evaluate this during the build and print out a warning into the build log.

Surely there must be Maven plugins that look at the Deprecated since attribute? Though they probably compare it to the current version number in Maven. If we could the get source we could roll our own.

It’s even easier than I thought, there is no need for a special maven plugin, we just need our annotation, an annotation processor, and tell the compiler to use this processor. I made an experimental PR, have a look: github.com/vassalengine/vassal/pull/165

We probably could use the regular @Deprecated annotation for this, but I see two reasons why it’s better to write our own: a) we would be misusing the “since” field and put differently formatted information in it than it expects, possibly even get problems with the compiler due to that, and b) annotation processing has this thing where annotations are processed kind of like UI events and a processor can “consume” an annotation thereby hiding it from further processors, it’s probably safer if we don’t risk consuming the @Deprecated annotation and hiding it from the compiler.

Ok, that’s looking really promising. I split this Depraction issue into 2 issues on the tracker. 13243 for displaying the dialogs and 13244 for the maven plugin, which looks like it will be covered instead by this.

The only problem I see with not using @Deprecated is that unless we double annotate each deprecated method with both @Deprecated and @VassalDeprecated, then the compilers, other plugins and our IDE’s won’t recognized these methods as Deprecated and do their usual thing. You have effectively hidden all the Deprecations from the compiler anyway.

I don’t see a problem with using the date like that as a version number. It is not defined what format a version number must be. There’s not nothing to say it can be nnnn-nn-nn instead of nnnn.nn.nn. I believe it’s only documentary anyway since the compiler has nothing to compare it to.

Give it a try with @Deprecated and see what happens, surely there should be a way to stop it consuming them. I think that would be preferable if we can get away with just the single @Deprecated.

I was thinking maybe break the build if it has been more than a year or since is invalid or missing, show a warning if more than, say 10 months, otherwise no message.

The format of the “since” is defined:

    /**
     * Returns the version in which the annotated element became deprecated.
     * The version string is in the same format and namespace as the value of
     * the {@code @since} javadoc tag. The default value is the empty
     * string.
     *
     * @return the version string
     * @since 9
     */
    String since() default "";

And the custom annotation is not meant to be used instead of the regular @Deprecated, it has nothing to do with @Deprecated at all. They can be used side by side.

es, and if you look up the definition of the @since Javadoc tag (which was surprisingly difficult to find), it says

Adds a Since heading with the specified since-text value to the generated documentation. The text has no special internal structure. 

So, you can basically put whatever you like in it, nothing will ever complain. The examples always show a number like 9 or 1.2, but they are only examples. The original usage was ‘@since JDK1.1’ which included characters before it started to be used for non JDK software.

Yes, I understand that, but since every single custom annotation will have to also have an @Deprecated annotation that has a perfectly useful @since attribute, then @Deprecated by itself fully supports the functionality we want.

Well we can use the @Deprecated just as well. My code was just an experiment anyways. We can throw away the custom annotation, adjust the annotation processor to process the @Deprecated, adjust the logic to print warnings after 8 months and error after a year. Just have to be careful to not return “true” from the process() method, this is what consumes the annotation if I understood correctly.

Sounds good, I think it will save on meaningless busy work. I had a play with your DeprecationProcessor, pretty cool. I played around with it and got it sort of working with @Deprecated. Then did something, I do not know what and it doesn’t seem to run at all any more. I’ll leave that one to you.

I thought about the Deprecated fields. They seem to be mostly either not used by any Vassal code anymore, or are closely tied to and used by methods that have also been Deprecated. I think we just add the since= to these as well and delete them after the year.