Skip to content

Commit 069a181

Browse files
timilirisclaude
andcommitted
v1.3.6 - Hytale Installation Path Settings
- Add global settings page (Settings > Tools > HytaleDocs) to configure Hytale installation path - Add "Browse..." button in project wizard for manual path selection - Save installation path globally for reuse across projects - Improve auto-detection to check user settings first Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 5a117f7 commit 069a181

File tree

6 files changed

+420
-33
lines changed

6 files changed

+420
-33
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pluginGroup = com.hytaledocs.hytale
44
pluginName = Hytale Development Tools
55
pluginRepositoryUrl = https://github.com/HytaleDocs/hytale-intellij-plugin
66
# SemVer format -> https://semver.org
7-
pluginVersion = 1.3.5
7+
pluginVersion = 1.3.6
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 253
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.hytaledocs.intellij.settings
2+
3+
import com.intellij.openapi.application.ApplicationManager
4+
import com.intellij.openapi.components.*
5+
import com.intellij.util.xmlb.XmlSerializerUtil
6+
7+
/**
8+
* Application-level settings for Hytale plugin.
9+
* These settings are global and available before project creation.
10+
*
11+
* Accessible via Settings > Tools > Hytale
12+
*/
13+
@State(
14+
name = "HytaleAppSettings",
15+
storages = [Storage("hytaleApp.xml")]
16+
)
17+
class HytaleAppSettings : PersistentStateComponent<HytaleAppSettings.State> {
18+
19+
data class State(
20+
/**
21+
* Path to the Hytale game installation directory.
22+
* This is the folder containing the Server/ directory with HytaleServer.jar
23+
*
24+
* Example paths:
25+
* - Windows: C:\Users\Username\AppData\Roaming\Hytale\install\release\package\game\latest
26+
* - macOS: ~/Library/Application Support/Hytale/install/release/package/game/latest
27+
* - Linux: ~/.local/share/Hytale/install/release/package/game/latest
28+
*/
29+
var hytaleInstallationPath: String = "",
30+
31+
/**
32+
* Whether to auto-detect the installation path on startup
33+
*/
34+
var autoDetectInstallation: Boolean = true
35+
)
36+
37+
private var myState = State()
38+
39+
override fun getState(): State = myState
40+
41+
override fun loadState(state: State) {
42+
XmlSerializerUtil.copyBean(state, myState)
43+
}
44+
45+
var hytaleInstallationPath: String
46+
get() = myState.hytaleInstallationPath
47+
set(value) { myState.hytaleInstallationPath = value }
48+
49+
var autoDetectInstallation: Boolean
50+
get() = myState.autoDetectInstallation
51+
set(value) { myState.autoDetectInstallation = value }
52+
53+
/**
54+
* Check if a custom installation path is configured
55+
*/
56+
fun hasCustomInstallationPath(): Boolean {
57+
return hytaleInstallationPath.isNotBlank()
58+
}
59+
60+
companion object {
61+
fun getInstance(): HytaleAppSettings {
62+
return ApplicationManager.getApplication().getService(HytaleAppSettings::class.java)
63+
}
64+
}
65+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.hytaledocs.intellij.settings
2+
3+
import com.hytaledocs.intellij.wizard.HytaleModuleBuilder
4+
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
5+
import com.intellij.openapi.options.BoundConfigurable
6+
import com.intellij.openapi.ui.DialogPanel
7+
import com.intellij.openapi.ui.TextFieldWithBrowseButton
8+
import com.intellij.ui.JBColor
9+
import com.intellij.ui.dsl.builder.*
10+
import java.awt.Color
11+
import java.nio.file.Files
12+
import java.nio.file.Paths
13+
import javax.swing.JLabel
14+
15+
/**
16+
* Application-level settings panel for HytaleDocs plugin.
17+
* Accessible via Settings > Tools > HytaleDocs
18+
*
19+
* This configurable allows users to set the Hytale game installation path
20+
* which is used when creating new projects.
21+
*/
22+
class HytaleAppSettingsConfigurable : BoundConfigurable("HytaleDocs") {
23+
24+
private val settings = HytaleAppSettings.getInstance()
25+
private lateinit var installationPathField: TextFieldWithBrowseButton
26+
private lateinit var statusLabel: JLabel
27+
28+
override fun createPanel(): DialogPanel {
29+
return panel {
30+
group("Hytale Installation") {
31+
row {
32+
comment("""
33+
Configure the path to your Hytale game installation.
34+
This is used when creating new plugin projects to copy server files automatically.
35+
""".trimIndent())
36+
}
37+
38+
row("Installation path:") {
39+
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
40+
.withTitle("Select Hytale Installation Directory")
41+
42+
installationPathField = textFieldWithBrowseButton(descriptor)
43+
.bindText(settings::hytaleInstallationPath)
44+
.comment("Path to the Hytale game folder (containing Server/HytaleServer.jar)")
45+
.align(AlignX.FILL)
46+
.onChanged { updateStatus() }
47+
.component
48+
49+
installationPathField.textField.document.addDocumentListener(object : javax.swing.event.DocumentListener {
50+
override fun insertUpdate(e: javax.swing.event.DocumentEvent) = updateStatus()
51+
override fun removeUpdate(e: javax.swing.event.DocumentEvent) = updateStatus()
52+
override fun changedUpdate(e: javax.swing.event.DocumentEvent) = updateStatus()
53+
})
54+
}
55+
56+
row {
57+
button("Auto-detect") {
58+
autoDetectInstallation()
59+
}.comment("Search for Hytale in common installation locations")
60+
}
61+
62+
row {
63+
statusLabel = label("").component
64+
updateStatus()
65+
}
66+
}
67+
68+
group("Detection Settings") {
69+
row {
70+
checkBox("Auto-detect installation on startup")
71+
.bindSelected(settings::autoDetectInstallation)
72+
.comment("Automatically search for Hytale installation when no path is configured")
73+
}
74+
}
75+
76+
group("Expected Directory Structure") {
77+
row {
78+
comment("""
79+
The installation path should point to the game directory containing:
80+
81+
<path>/
82+
├── Server/
83+
│ └── HytaleServer.jar
84+
└── Assets.zip (optional)
85+
86+
Common locations:
87+
• Windows: %APPDATA%\Hytale\install\release\package\game\latest
88+
• macOS: ~/Library/Application Support/Hytale/install/release/package/game/latest
89+
• Linux: ~/.local/share/Hytale/install/release/package/game/latest
90+
""".trimIndent())
91+
}
92+
}
93+
}
94+
}
95+
96+
private fun autoDetectInstallation() {
97+
val detected = HytaleModuleBuilder.autoDetectHytaleInstallation()
98+
if (detected != null) {
99+
installationPathField.text = detected.basePath.toString()
100+
updateStatus()
101+
} else {
102+
statusLabel.text = "Could not auto-detect Hytale installation"
103+
statusLabel.foreground = JBColor(Color(180, 140, 60), Color(200, 160, 80))
104+
}
105+
}
106+
107+
private fun updateStatus() {
108+
if (!::installationPathField.isInitialized) return
109+
110+
val path = installationPathField.text.trim()
111+
112+
if (path.isEmpty()) {
113+
statusLabel.text = "No installation path configured"
114+
statusLabel.foreground = JBColor.GRAY
115+
return
116+
}
117+
118+
val basePath = Paths.get(path)
119+
if (!Files.exists(basePath)) {
120+
statusLabel.text = "Path does not exist"
121+
statusLabel.foreground = JBColor(Color(180, 60, 60), Color(200, 80, 80))
122+
return
123+
}
124+
125+
val serverJar = basePath.resolve("Server/HytaleServer.jar")
126+
val assetsZip = basePath.resolve("Assets.zip")
127+
128+
val hasServerJar = Files.exists(serverJar)
129+
val hasAssets = Files.exists(assetsZip)
130+
131+
val status = buildString {
132+
if (hasServerJar && hasAssets) {
133+
append("Valid installation (Server + Assets)")
134+
} else if (hasServerJar) {
135+
append("Valid installation (Server only)")
136+
} else if (hasAssets) {
137+
append("Assets found, but HytaleServer.jar missing in Server/ folder")
138+
} else {
139+
append("Invalid path: HytaleServer.jar not found in Server/ folder")
140+
}
141+
}
142+
143+
statusLabel.text = status
144+
statusLabel.foreground = when {
145+
hasServerJar -> JBColor(Color(40, 160, 80), Color(80, 200, 120))
146+
hasAssets -> JBColor(Color(180, 140, 60), Color(200, 160, 80))
147+
else -> JBColor(Color(180, 60, 60), Color(200, 80, 80))
148+
}
149+
}
150+
}

src/main/kotlin/com/hytaledocs/intellij/wizard/HytaleModuleBuilder.kt

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.hytaledocs.intellij.wizard
22

33
import com.hytaledocs.intellij.HytaleIcons
4+
import com.hytaledocs.intellij.settings.HytaleAppSettings
45
import com.intellij.ide.util.projectWizard.ModuleBuilder
56
import com.intellij.ide.util.projectWizard.ModuleWizardStep
67
import com.intellij.ide.util.projectWizard.WizardContext
@@ -13,6 +14,7 @@ import com.intellij.openapi.util.io.FileUtil
1314
import com.intellij.openapi.vfs.LocalFileSystem
1415
import java.io.File
1516
import java.nio.file.Files
17+
import java.nio.file.Path
1618
import java.nio.file.Paths
1719
import java.nio.file.StandardCopyOption
1820
import javax.swing.Icon
@@ -26,10 +28,65 @@ class HytaleModuleBuilder : ModuleBuilder() {
2628

2729
companion object {
2830
/**
29-
* Detects if Hytale game is installed and returns the installation path.
30-
* Checks multiple common installation locations.
31+
* Detects Hytale installation, first checking user-configured path, then auto-detecting.
32+
* @return HytaleInstallation if found, null otherwise
3133
*/
3234
fun detectHytaleInstallation(): HytaleInstallation? {
35+
// Priority 0: Check user-configured path in application settings
36+
try {
37+
val appSettings = HytaleAppSettings.getInstance()
38+
if (appSettings.hasCustomInstallationPath()) {
39+
val customPath = Paths.get(appSettings.hytaleInstallationPath)
40+
val installation = checkInstallationPath(customPath)
41+
if (installation != null) {
42+
return installation
43+
}
44+
}
45+
} catch (e: Exception) {
46+
// Settings might not be available yet (e.g., during startup)
47+
}
48+
49+
// Priority 1+: Auto-detect
50+
return autoDetectHytaleInstallation()
51+
}
52+
53+
/**
54+
* Creates a HytaleInstallation from a custom path.
55+
* @param customPath The path to check for Hytale installation
56+
* @return HytaleInstallation if valid, null otherwise
57+
*/
58+
fun fromCustomPath(customPath: String): HytaleInstallation? {
59+
if (customPath.isBlank()) return null
60+
return checkInstallationPath(Paths.get(customPath))
61+
}
62+
63+
/**
64+
* Check if a given path contains a valid Hytale installation.
65+
*/
66+
private fun checkInstallationPath(basePath: Path): HytaleInstallation? {
67+
if (!Files.exists(basePath)) return null
68+
69+
val serverJar = basePath.resolve("Server/HytaleServer.jar")
70+
val assetsZip = basePath.resolve("Assets.zip")
71+
72+
val hasServerJar = Files.exists(serverJar)
73+
val hasAssets = Files.exists(assetsZip)
74+
75+
if (hasServerJar || hasAssets) {
76+
return HytaleInstallation(
77+
basePath = basePath,
78+
serverJarPath = if (hasServerJar) serverJar else null,
79+
assetsPath = if (hasAssets) assetsZip else null
80+
)
81+
}
82+
83+
return null
84+
}
85+
86+
/**
87+
* Auto-detects Hytale game installation by checking common locations.
88+
*/
89+
fun autoDetectHytaleInstallation(): HytaleInstallation? {
3390
val userHome = System.getProperty("user.home")
3491
val osName = System.getProperty("os.name").lowercase()
3592
val isWindows = osName.contains("win")

0 commit comments

Comments
 (0)