Skip to content

Simplify project templates - #722

Open
neph1 wants to merge 8 commits into
jMonkeyEngine:masterfrom
neph1:simplify_project_templates
Open

neph1 wants to merge 8 commits into
jMonkeyEngine:masterfrom
neph1:simplify_project_templates

Conversation

@neph1

@neph1 neph1 commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR simplifies the jME3 SDK project template system in jme3-templates/src/com/jme3/gde/templates:

  • Shared wizard base classes (AbstractProjectWizardIterator, ProjectWizardPanel, ProjectWizardPanelVisual) replace ~10 near-identical per-template copies, cutting ~1900 lines of duplicated panel/iterator code.
  • Data-driven sample projects: SampleProjectConfig + SampleProjectWizardIterator + SampleProjectDownloadPanel describe each sample (MonkeyZone, Jaime's Ascent, Rolling the Monkey) declaratively instead of requiring a full wizard iterator + panel + download panel per sample.
  • Build-time zip generation: template zips (GradleDesktopGameProject.zip, RollingTheMonkeyProject.zip) are now generated at build time from the GradleDesktopGameTemplate/ and RollingTheMonkeyTemplate/ source directories via ZipUtils, instead of being checked in as binary blobs that go stale when Java/Gradle versions change.
  • Centralized versioning: TemplateVersions + version.properties hold the Java/Gradle/jME versions in one place, consumed by the FreeMarker build.gradle.ftl templates and the zip build.
  • Simplified build.xml: the ant build now delegates zip creation to the Java helper and reads versions from version.properties.
  • Removed the now-unused jaimesascent/build.gradle.ftl and monkeyzone/build.gradle.ftl (sample projects are downloaded as zips, not generated).
  • Updated layer.xml to register the simplified template wizards.

Net effect: ~2400 lines added, ~4300 lines removed.

Verification

  • git diff master...vibe/simplify-templates-59edb5 --stat — 78 files changed, 2362 insertions(+), 4295 deletions(-)
  • Branch pushed to neph1/sdk:vibe/simplify-templates-59edb5

Notes

  • The checked-in .zip template files are deleted; they are regenerated by ant (see build.xml target build-template-zips).
  • gradle.properties gains templateJavaVersion, templateGradleVersion, templateJmeVersion.
  • jme3-templates/src/com/jme3/gde/templates/version.properties is the single source of truth for template versions.

mistral-vibe and others added 8 commits October 24, 2026 11:30
…ample projects, build-time zips and versions

- Add shared wizard infrastructure in com.jme3.gde.templates.shared:
  AbstractProjectWizardIterator (wizard plumbing), ProjectWizardPanel(Visual)
  (name/location step shared by all templates), ZipUtils (single-root-dir aware
  unzip), TemplateVersions (reads filtered version.properties)
- Replace the copy-pasted MonkeyZone/JaimesAscent download wizards with a
  data-driven SampleProjectWizardIterator configured by SampleProjectConfig;
  each template is now a ~15 line factory class
- Download sample projects asynchronously with timeouts, cache per project
  name, and block 'Next' until the download succeeded
- Detect the zip's top-level directory dynamically instead of hardcoding it
  (fixes Jaimes Ascent v1.1.3 landing in a nested folder)
- Downloaded sample projects keep their upstream build.gradle (it contains
  custom tasks); only settings.gradle's project name is rewritten
- Single source of truth for versions: gradle.properties feeds version.properties
  via the ant -filter-template-versions target, and the FreeMarker templates get
  javaVersion/gradleVersion/jmeVersion from TemplateVersions at runtime
- Generate GradleDesktopGameProject.zip and RollingTheMonkeyProject.zip at build
  time from new GradleDesktopGameTemplate/ and RollingTheMonkeyTemplate/ source
  folders; remove stale checked-in zips (they contained contradictory build files)
- Fix layer.xml: Monkey Zone pointed at the Rolling The Monkey zip, Jaimes Ascent
  at a non-existent zip, and there were two 'Project' folders
- Declare the org.netbeans.libs.freemarker module dependency explicitly
- GradleDesktopGameWizardIterator defaults the jme version to the SDK version
…ule dependency

The org.netbeans.libs.freemarker module has no public packages in NetBeans 31,
so it cannot be compiled against. The code only uses javax.script.* (JDK classes)
and resolves the FreeMarker ScriptEngine at runtime via ScriptEngineManager.

- Remove the compile-time module dependency from project.xml
- Add OpenIDE-Module-Module-Dependencies to manifest.mf for runtime availability

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
… project folder

1. ProjectWizardPanelVisual: add jme-logo image on the left, fix GroupLayout
   so components no longer overlap, and cap the panel at 64px max height.
2. ZipUtils: filter nbproject/project.xml during extraction to replace the
   <name> element with the actual project folder name, so the BasicGame (Ant)
   template is recognised as a valid NetBeans project after extraction.
3. ProjectWizardPanelVisual.read(): always default the project location to
   ProjectChooser.getProjectsFolder() when no previous location is set,
   falling back to the user's home directory.

Also fix the freemarker compile dependency that was never actually removed
from project.xml (it was still present in the committed version).

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
… override

The previous fix put OpenIDE-Module-Module-Dependencies in manifest.mf,
but the harness generates this attribute from project.xml during the build
and the manual manifest entry REPLACED all auto-generated dependencies
(including org.openide.dialogs), causing ClassNotFoundException for
WizardDescriptor at runtime.

Fix:
- Remove the manual OpenIDE-Module-Module-Dependencies from manifest.mf
  (the harness will generate it correctly from project.xml)
- Add org.netbeans.libs.freemarker as a runtime-only dependency in
  project.xml (no build-prerequisite/compile-dependency, since the module
  has no public packages in NetBeans 31 and cannot be compiled against)

The generated manifest now correctly includes ALL module dependencies:
  com.google.gson, org.netbeans.libs.freemarker, com.jme3.gde.project.baselibs,
  com.jme3.gde.project.libraries, org.netbeans.api.templates,
  org.netbeans.modules.projectapi, org.netbeans.modules.projectuiapi,
  org.netbeans.spi.palette, org.openide.awt, org.openide.dialogs,
  org.openide.filesystems, org.openide.filesystems.nb, org.openide.loaders,
  org.openide.util, org.openide.util.lookup, org.openide.util.ui

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
… name

1. layer.xml: MonkeyZone and JaimesAscent no longer reference missing
   MonkeyZone.zip / JaimesAscent.zip files. They now point to
   RollingTheMonkeyProject.zip as a placeholder template file (the actual
   project is downloaded at runtime by SampleProjectWizardIterator). The
   url attribute is required by NetBeans for template registration.

2. SampleProjectDownloadPanelVisual: add a bold nameLabel next to the icon
   so the project name appears beside the image instead of overlapping it.
   Both are top-aligned in a horizontal group. The download button, text
   area, and status field are now in a separate column to the right.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
… single-panel templates

The shared ProjectWizardPanel had isFinishPanel() returning false (copied
from multi-panel templates like GradleDesktopGame which has additional
panels after the name/location step). For single-panel templates (BasicGame,
RollingTheMonkey, and sample projects like MonkeyZone/JaimesAscent), this
caused the wizard to show 'Next' instead of 'Finish', and after instantiate()
created the project folder, the wizard re-validated and found the folder
already existed (since dirF.mkdirs() created it), showing the error
'Project Folder already exists and is not empty.'

Fix: Add a finishPanel constructor parameter to ProjectWizardPanel. Single-panel
templates (BasicGame, RollingTheMonkey, SampleProject) pass true; multi-panel
templates (GradleDesktopGame) keep the default false.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…ndle

- AbstractProjectWizardIterator.createFileFromTemplate: FileUtil.toFileObject()
  returns null for the generated settings.gradle/build.gradle because the
  template zips no longer ship them, causing an NPE on Finish. Create the
  target file via FileUtil.createData() when it does not exist yet.
- Add Bundle.properties to the shared.sample package: NbBundle resolves the
  bundle from the calling class's package, so the keys used by
  SampleProjectDownloadPanel(Visual) and the "LBL_DownloadProjectStep" step
  name must live in com.jme3.gde.templates.shared.sample.Bundle, not in the
  shared package bundle. This fixes the MissingResourceException when
  stepping through the MonkeyZone/JaimesAscent sample wizards.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@neph1

neph1 commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

@jaime-jmebot ahoy. can you review this as well? thanks

public static void unZipFile(InputStream source, FileObject projectRoot) throws IOException {
byte[] content = source.readAllBytes();
List<ZipEntry> entries = new ArrayList<>();
try (ZipInputStream str = new ZipInputStream(new ByteArrayInputStream(content))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The single-root handling here assumes every archive entry has a slash after its top-level name, but GitHub source archives commonly include the explicit top-level directory entry itself (for example, repo-tag/). For that entry indexOf('/') is the final character, so detection returns repo-tag/; the later strip then produces an empty entry name and attempts createFolder(projectRoot, ""), which can fail and abort extraction. Could you ignore a directory entry whose name is exactly the candidate root when detecting/stripping the prefix, and skip it during extraction? That would keep normal GitHub archives usable while still stripping their root.

@jaime-jmebot jaime-jmebot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi neph1, thanks for taking on this substantial cleanup. The shared wizard and sample-project abstractions make the template code much easier to maintain, and centralizing the generated-project versions is a nice improvement. I reviewed the changed wizard paths, the shared archive extraction code, and the build-time zip/version wiring.

I found one issue in the new common extraction path: GitHub-generated source archives include an explicit top-level directory entry in addition to their files. The current root-prefix detection treats that entry as an ordinary path, and stripping it can leave an empty entry name and fail project creation. I left an inline comment on ZipUtils with the scenario and suggested behavior; fixing this is important because it affects every sample project downloaded through the shared wizard.

The rest of the refactor reads coherently, and it’s especially good to have the archive and wizard plumbing implemented once rather than duplicated across templates. Once the root-directory entry is handled, please rerun the sample-template creation flow against a normal GitHub source archive to verify extraction proceeds all the way through. Thanks again for the cleanup!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants