1263 words
6 minutes
How I Updated Nixpkgs' forge-mtg to 2.0.02
2025-04-05

How I Updated forge-mtg to 2.0.02#

So, I just spent some time figuring out how to update forge-mtg from 1.6.65 to 2.0.02 in the Nixpkgs repository. It was a bit of a journey, but in the end I’ve managed to get it working. Since I’m a bit too lazy to open a PR and get this merged, I thought I’d still put it out there for anyone else who might be interested in doing the same.

Sweet dreams are made of attrs#

I started by trying to update the package using overrideAttrs. This is a common approach in Nixpkgs to modify package attributes without having to fork the entire repository.

  pkgs.forge-mtg.overrideAttrs (old: rec {
      version = "2.0.02";
      # because this is built using pkgs.maven.buildMavenPackage we also need to update the maven hash
      mvnHash = lib.fakeHash;

      src = pkgs.fetchFromGitHub {
        owner = "Card-Forge";
        repo = "forge";
        rev = "forge-${version}";
        hash = lib.fakeHash;
      };
  })

So, build this to get the hashes and done I thought. Nope. The patch that removes the launch4j Maven plugin couldn’t be applied. So, check out the repo, do the changes, get the patch using git diff, save it to a file and add it to our attribute set:

      patches = [ ./forge-mtg-2.0.02-launch4j.patch ];

We build again… and a new error:

[WARNING] The POM for org.apache.maven.wagon:wagon-ftp:jar:3.5.3 is missing, no dependency information available
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Unresolveable build extension: Plugin org.apache.maven.wagon:wagon-ftp:3.5.3 or one of its dependencies could not be resolved:
        Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact org.apache.maven.wagon:wagon-ftp:jar:3.5.3 has not been downloaded from it before.
 @
 @
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR]   The project forge:forge:2.0.02 (/build/source/pom.xml) has 1 error
[ERROR]     Unresolveable build extension: Plugin org.apache.maven.wagon:wagon-ftp:3.5.3 or one of its dependencies could not be resolved:
[ERROR]         Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact org.apache.maven.wagon:wagon-ftp:jar:3.5.3 has not been downloaded from it before.
[ERROR]     -> [Help 2]

Hmm, weird. One of the dependencies cannot be resolved during Nix’s build step (where maven does not have access to the internet). So, let’s investigate this further. Checking the source of buildMavenPackage, I see there’s a manualMvnArtifacts attribute that is used to specify the dependencies that should be downloaded from the internet.

Just wanting this to get done, I thought ok, let’s not investigate, but just quickly fix it. I add it to the overrideAttrs, aaaand still nothing. I think overrideAttrs cannot be used like this here, because when I override the attributes, it’s already an evaluation of buildMavenPackage and not the source.

If you can’t beat them, fork them#

So, just wanting this to get done, I copy the original package from nixpkgs into my config and go to work. I change the hashes to lib.fakeHash, reference my new patch and build and it is looking promising.

It takes a while, but then it complains about the hash mismatch. Score.

I update the hash and build again. This time, another hash mismatch, the mvnHash. I update that too and build again.

Welp, another error, seems like some of the binaries changed names, so quick change of that as well and build again. This time, it builds successfully. I run the package and it works. Nice. Finally done.

Conclusion#

So, I just wanted to share this experience with you all. It was a bit of a journey, but in the end, I managed to get forge-mtg updated to 2.0.02 in Nixpkgs. I’m not sure if I’ll even play it, I’ve already spent way too much time on it already. But hey, at least I learned a bit more about Nixpkgs (even if I’m still not sure how to use overrideAttrs properly).

And last but not least, here are the changes I made to the package:

Update (+30m after publication): Well, turns out it did not actually work yet, but was crashing with a NullPointerException when starting forge.

EDT > com.thoughtworks.xstream.converters.ConversionException:
---- Debugging information ----
cause-exception     : java.lang.NullPointerException
cause-message       : Cannot invoke "Object.toString()" because the return value of "java.util.Map$Entry.getValue()" is null
class               : forge.deck.Deck
required-type       : forge.deck.Deck
converter-type      : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
line number         : 116
class[1]            : java.util.ArrayList
required-type[1]    : java.util.ArrayList
converter-type[1]   : com.thoughtworks.xstream.converters.collections.CollectionConverter
class[2]            : forge.gamemodes.gauntlet.GauntletData
required-type[2]    : forge.gamemodes.gauntlet.GauntletData
version             : 2.0.02-SNAPSHOT-04.05
-------------------------------
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:81)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:68)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:52)
        at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readBareItem(AbstractCollectionConverter.java:132)
        at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readItem(AbstractCollectionConverter.java:117)
        at com.thoughtworks.xstream.converters.collections.CollectionConverter.addCurrentElementToCollection(CollectionConverter.java:99)
        at com.thoughtworks.xstream.converters.collections.CollectionConverter.populateCollection(CollectionConverter.java:92)
        at com.thoughtworks.xstream.converters.collections.CollectionConverter.populateCollection(CollectionConverter.java:86)
        at com.thoughtworks.xstream.converters.collections.CollectionConverter.unmarshal(CollectionConverter.java:81)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:74)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:68)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshallField(AbstractReflectionConverter.java:496)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:420)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:277)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:74)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:68)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:52)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:136)
        at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
        at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1468)
        at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1445)
        at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1325)
        at forge.gamemodes.gauntlet.GauntletIO.loadGauntlet(GauntletIO.java:103)
        at forge.screens.home.gauntlet.CSubmenuGauntletContests.updateData(CSubmenuGauntletContests.java:64)
        at forge.screens.home.gauntlet.CSubmenuGauntletContests.initialize(CSubmenuGauntletContests.java:54)
        at forge.view.FView.initialize(FView.java:144)
        at forge.control.FControl.lambda$initialize$0(FControl.java:311)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.NullPointerException: Cannot invoke "Object.toString()" because the return value of "java.util.Map$Entry.getValue()" is null
        at com.thoughtworks.xstream.core.DefaultConverterLookup.lookupConverterForType(DefaultConverterLookup.java:96)
        at com.thoughtworks.xstream.XStream$1.lookupConverterForType(XStream.java:480)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:58)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshallField(AbstractReflectionConverter.java:496)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:420)
        at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:277)
        at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:74)

Some more digging, this seems to have been caused by this change, that removed the add-opens from the mandatory.java.args that get passed to the java command. There was a fix here, but since we removed the launch4j plugin, this was not applied.

So, another patch to quickly just add the $addopen.java.args$ to the java command as well and we’re done for real now.

Derivation#

diff --git a/pkgs/forge-mtg.nix b/pkgs/forge-mtg.nix
index 1995567..de84721 100644
--- a/pkgs/forge-mtg.nix
+++ b/pkgs/forge-mtg.nix
@@ -26,7 +26,7 @@ maven.buildMavenPackage {
   pname = "forge-mtg";
   inherit version src patches;

-  mvnHash = "sha256-ouF0Ja3oGrlUCcT0PzI5i9FQ+oLdEhE/LvhJ0QGErvI=";
+  mvnHash = "sha256-vyhEHTrJ0UyUHmOD521PUUo4cfuQZoKRFZ+BqwKYUuI=";

   doCheck = false; # Needs a running Xorg

@@ -40,15 +40,15 @@ maven.buildMavenPackage {
       forge-gui-desktop/target/forge-gui-desktop-${version}-jar-with-dependencies.jar \
       forge-gui-mobile-dev/target/forge-adventure.sh \
       forge-gui-mobile-dev/target/forge-gui-mobile-dev-${version}-jar-with-dependencies.jar \
-      forge-adventure/target/forge-adventure-editor.sh \
-      forge-adventure/target/forge-adventure-${version}-jar-with-dependencies.jar \
+      adventure-editor/target/adventure-editor.sh \
+      adventure-editor/target/adventure-editor-jar-with-dependencies.jar \
       forge-gui/res \
       $out/share/forge
     runHook postInstall
   '';

   preFixup = ''
-    for commandToInstall in forge forge-adventure forge-adventure-editor; do
+    for commandToInstall in forge forge-adventure adventure-editor; do
       chmod 555 $out/share/forge/$commandToInstall.sh
       makeWrapper $out/share/forge/$commandToInstall.sh $out/bin/$commandToInstall \
         --prefix PATH : ${

The new forge-mtg.patch#

diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml
index 2bdbb249ba..f5348a7c86 100644
--- a/forge-gui-desktop/pom.xml
+++ b/forge-gui-desktop/pom.xml
@@ -70,62 +70,6 @@
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <groupId>com.akathist.maven.plugins.launch4j</groupId>
-                <artifactId>launch4j-maven-plugin</artifactId>
-                <version>2.5.1</version>
-                <executions>
-                    <execution>
-                        <id>l4j-gui</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>launch4j</goal>
-                        </goals>
-                        <configuration>
-                            <headerType>gui</headerType>
-                            <outfile>${project.build.directory}/forge.exe</outfile>
-                            <jar>${project.build.finalName}-jar-with-dependencies.jar</jar>
-                            <dontWrapJar>true</dontWrapJar>
-                            <errTitle>forge</errTitle>
-                            <downloadUrl>https://bell-sw.com/pages/downloads/#jdk-17-lts</downloadUrl>
-                            <icon>src/main/config/forge.ico</icon>
-                            <classPath>
-                                <mainClass>forge.view.Main</mainClass>
-                                <addDependencies>false</addDependencies>
-                                <preCp>anything</preCp>
-                            </classPath>
-                            <jre>
-                                <minVersion>17</minVersion>
-                                <requiresJdk>true</requiresJdk>
-                                <maxHeapSize>4096</maxHeapSize>
-                                <opts>
-                                    <opt>${mandatory.java.args}</opt>
-                                    <opt>${addopen.java.args}</opt>
-                                </opts>
-                            </jre>
-                            <versionInfo>
-                                <fileVersion>
-                                    ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.0
-                                </fileVersion>
-                                <txtFileVersion>
-                                    ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.0
-                                </txtFileVersion>
-                                <fileDescription>Forge</fileDescription>
-                                <copyright>Forge</copyright>
-                                <productVersion>
-                                    ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.0
-                                </productVersion>
-                                <txtProductVersion>
-                                    ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.0
-                                </txtProductVersion>
-                                <productName>Forge</productName>
-                                <internalName>forge</internalName>
-                                <originalFilename>forge.exe</originalFilename>
-                            </versionInfo>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>com.google.code.maven-replacer-plugin</groupId>
                 <artifactId>replacer</artifactId>
@@ -153,6 +97,10 @@
                             <token>$mandatory.java.args$</token>
                             <value>${mandatory.java.args}</value>
                         </replacement>
+                        <replacement>
+                            <token>$addopen.java.args$</token>
+                            <value>${addopen.java.args}</value>
+                        </replacement>
                     </replacements>
                 </configuration>
             </plugin>
diff --git a/forge-gui-desktop/src/main/config/forge.sh b/forge-gui-desktop/src/main/config/forge.sh
index 3d55512745..f005294a9d 100644
--- a/forge-gui-desktop/src/main/config/forge.sh
+++ b/forge-gui-desktop/src/main/config/forge.sh
@@ -1,3 +1,3 @@
 #!/bin/sh
 cd $(dirname "${0}")
-java $mandatory.java.args$ -jar $project.build.finalName$
+java $mandatory.java.args$ $addopen.java.args$ -jar $project.build.finalName$
diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml
index 25b7a125b4..39150c03ba 100644
--- a/forge-gui-mobile-dev/pom.xml
+++ b/forge-gui-mobile-dev/pom.xml
@@ -60,57 +60,6 @@
                     <target>17</target>
                 </configuration>
             </plugin>
-            <plugin>
-                <groupId>com.akathist.maven.plugins.launch4j</groupId>
-                <artifactId>launch4j-maven-plugin</artifactId>
-                <version>2.5.1</version>
-                <executions>
-                    <execution>
-                        <id>l4j-adv</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>launch4j</goal>
-                        </goals>
-                        <configuration>
-                            <headerType>gui</headerType>
-                            <outfile>${project.build.directory}/forge-adventure.exe</outfile>
-                            <jar>${project.build.finalName}-jar-with-dependencies.jar</jar>
-                            <dontWrapJar>true</dontWrapJar>
-                            <errTitle>forge</errTitle>
-                            <downloadUrl>https://bell-sw.com/pages/downloads/#jdk-17-lts</downloadUrl>
-                            <icon>src/main/config/forge-adventure.ico</icon>
-                            <jre>
-                                <minVersion>17</minVersion>
-                                <requiresJdk>true</requiresJdk>
-                                <maxHeapSize>4096</maxHeapSize>
-                                <opts>
-                                    <opt>${mandatory.java.args}</opt>
-                                </opts>
-                            </jre>
-                            <versionInfo>
-                                <fileVersion>
-                                    1.0.0.0
-                                </fileVersion>
-                                <txtFileVersion>
-                                    1.0.0.0
-                                </txtFileVersion>
-                                <fileDescription>Forge</fileDescription>
-                                <copyright>Forge</copyright>
-                                <productVersion>
-                                    1.0.0.0
-                                </productVersion>
-                                <txtProductVersion>
-                                    1.0.0.0
-                                </txtProductVersion>
-                                <productName>forge-adventure</productName>
-                                <internalName>forge-adventure</internalName>
-                                <originalFilename>forge-adventure.exe</originalFilename>
-                            </versionInfo>
-                        </configuration>
-                    </execution>
-                    <!--extra-->
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>com.google.code.maven-replacer-plugin</groupId>
                 <artifactId>replacer</artifactId>
How I Updated Nixpkgs' forge-mtg to 2.0.02
https://kawaiicyb.org/posts/nixpkgs-forge-mtg-2002/
Author
KawaiiCyborg
Published at
2025-04-05