Skip to content

Commit 90b7770

Browse files
Copilotptthanh02
andcommitted
Implement complete GuiLayoutUpdater with version management
Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com>
1 parent 71d5305 commit 90b7770

2 files changed

Lines changed: 193 additions & 2 deletions

File tree

core/src/main/java/github/nighter/smartspawner/spawner/gui/layout/GuiLayoutConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void loadLayout() {
4141
initializeLayoutsDirectory();
4242

4343
// Check and update layout files before loading
44-
// layoutUpdater.checkAndUpdateLayouts();
44+
layoutUpdater.checkAndUpdateLayouts();
4545

4646
this.currentStorageLayout = loadCurrentStorageLayout();
4747
this.currentMainLayout = loadCurrentMainLayout();

core/src/main/java/github/nighter/smartspawner/updates/GuiLayoutUpdater.java

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import org.bukkit.configuration.file.FileConfiguration;
66
import org.bukkit.configuration.file.YamlConfiguration;
77

8-
import java.io.File;
8+
import java.io.*;
9+
import java.nio.charset.StandardCharsets;
910
import java.nio.file.Files;
1011
import java.nio.file.StandardCopyOption;
1112
import java.util.*;
@@ -24,4 +25,194 @@ public GuiLayoutUpdater(SmartSpawner plugin) {
2425
this.plugin = plugin;
2526
this.currentVersion = plugin.getDescription().getVersion();
2627
}
28+
29+
/**
30+
* Check if GUI layouts need to be updated and update them if necessary
31+
*/
32+
public void checkAndUpdateLayouts() {
33+
File layoutsDir = new File(plugin.getDataFolder(), GUI_LAYOUTS_DIR);
34+
35+
// Ensure layouts directory exists
36+
if (!layoutsDir.exists()) {
37+
layoutsDir.mkdirs();
38+
}
39+
40+
// Check and update each layout
41+
for (String layoutName : LAYOUT_NAMES) {
42+
File layoutDir = new File(layoutsDir, layoutName);
43+
if (!layoutDir.exists()) {
44+
layoutDir.mkdirs();
45+
}
46+
47+
// Check and update each layout file
48+
for (String fileName : LAYOUT_FILES) {
49+
checkAndUpdateLayoutFile(layoutDir, layoutName, fileName);
50+
}
51+
}
52+
}
53+
54+
/**
55+
* Check and update a specific layout file
56+
*/
57+
private void checkAndUpdateLayoutFile(File layoutDir, String layoutName, String fileName) {
58+
File layoutFile = new File(layoutDir, fileName);
59+
60+
// If layout file doesn't exist, create it with the version header
61+
if (!layoutFile.exists()) {
62+
createDefaultLayoutWithHeader(layoutDir, layoutName, fileName);
63+
return;
64+
}
65+
66+
FileConfiguration currentLayout = YamlConfiguration.loadConfiguration(layoutFile);
67+
String layoutVersionStr = currentLayout.getString(GUI_LAYOUT_VERSION_KEY, "0.0.0");
68+
Version layoutVersion = new Version(layoutVersionStr);
69+
Version pluginVersion = new Version(currentVersion);
70+
71+
if (layoutVersion.compareTo(pluginVersion) >= 0) {
72+
return;
73+
}
74+
75+
plugin.getLogger().info("Updating GUI layout " + layoutName + "/" + fileName + " from version " + layoutVersionStr + " to " + currentVersion);
76+
77+
try {
78+
Map<String, Object> userValues = flattenConfig(currentLayout);
79+
80+
// Create temp file with new default layout
81+
File tempFile = new File(layoutDir, fileName + ".new");
82+
createDefaultLayoutWithHeader(layoutDir, layoutName, fileName, tempFile);
83+
84+
FileConfiguration newLayout = YamlConfiguration.loadConfiguration(tempFile);
85+
newLayout.set(GUI_LAYOUT_VERSION_KEY, currentVersion);
86+
87+
// Check if there are actual differences before creating backup
88+
boolean layoutDiffers = hasLayoutDifferences(userValues, newLayout);
89+
90+
if (layoutDiffers) {
91+
File backupFile = new File(layoutDir, fileName.replace(".yml", "_backup_" + layoutVersionStr + ".yml"));
92+
Files.copy(layoutFile.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
93+
plugin.getLogger().info("GUI layout backup created at " + layoutName + "/" + backupFile.getName());
94+
} else {
95+
plugin.debug("No significant GUI layout changes detected for " + layoutName + "/" + fileName + ", skipping backup creation");
96+
}
97+
98+
// Apply user values and save
99+
applyUserValues(newLayout, userValues);
100+
newLayout.save(layoutFile);
101+
tempFile.delete();
102+
103+
} catch (Exception e) {
104+
plugin.getLogger().log(Level.SEVERE, "Failed to update GUI layout " + layoutName + "/" + fileName + ": " + e.getMessage(), e);
105+
}
106+
}
107+
108+
/**
109+
* Determines if there are actual differences between old and new layout
110+
*/
111+
private boolean hasLayoutDifferences(Map<String, Object> userValues, FileConfiguration newLayout) {
112+
// Get all paths from new layout (excluding gui_layout_version)
113+
Map<String, Object> newLayoutMap = flattenConfig(newLayout);
114+
115+
// Check for removed or changed keys
116+
for (Map.Entry<String, Object> entry : userValues.entrySet()) {
117+
String path = entry.getKey();
118+
Object oldValue = entry.getValue();
119+
120+
// Skip gui_layout_version key
121+
if (path.equals(GUI_LAYOUT_VERSION_KEY)) continue;
122+
123+
// Check if path no longer exists
124+
if (!newLayout.contains(path)) {
125+
return true; // Found a removed path
126+
}
127+
128+
// Check if default value changed
129+
Object newDefaultValue = newLayout.get(path);
130+
if (newDefaultValue != null && !newDefaultValue.equals(oldValue)) {
131+
return true; // Default value changed
132+
}
133+
}
134+
135+
// Check for new keys
136+
for (String path : newLayoutMap.keySet()) {
137+
if (!path.equals(GUI_LAYOUT_VERSION_KEY) && !userValues.containsKey(path)) {
138+
return true; // Found a new path
139+
}
140+
}
141+
142+
return false; // No significant differences
143+
}
144+
145+
/**
146+
* Create a default layout file with version header
147+
*/
148+
private void createDefaultLayoutWithHeader(File layoutDir, String layoutName, String fileName) {
149+
createDefaultLayoutWithHeader(layoutDir, layoutName, fileName, new File(layoutDir, fileName));
150+
}
151+
152+
/**
153+
* Create a default layout file with version header at specific destination
154+
*/
155+
private void createDefaultLayoutWithHeader(File layoutDir, String layoutName, String fileName, File destinationFile) {
156+
try {
157+
// Ensure parent directory exists
158+
File parentDir = destinationFile.getParentFile();
159+
if (parentDir != null && !parentDir.exists()) {
160+
parentDir.mkdirs();
161+
}
162+
163+
String resourcePath = GUI_LAYOUTS_DIR + "/" + layoutName + "/" + fileName;
164+
try (InputStream in = plugin.getResource(resourcePath)) {
165+
if (in != null) {
166+
List<String> defaultLines = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
167+
.lines()
168+
.toList();
169+
170+
List<String> newLines = new ArrayList<>();
171+
newLines.add("# GUI Layout version - Do not modify this value");
172+
newLines.add(GUI_LAYOUT_VERSION_KEY + ": " + currentVersion);
173+
newLines.add("");
174+
newLines.addAll(defaultLines);
175+
176+
Files.write(destinationFile.toPath(), newLines, StandardCharsets.UTF_8);
177+
} else {
178+
plugin.getLogger().warning("Default GUI layout " + resourcePath + " not found in the plugin's resources.");
179+
destinationFile.createNewFile();
180+
}
181+
}
182+
} catch (IOException e) {
183+
plugin.getLogger().log(Level.SEVERE, "Failed to create default GUI layout with header for " + layoutName + "/" + fileName + ": " + e.getMessage(), e);
184+
}
185+
}
186+
187+
/**
188+
* Flattens a configuration section into a map of path -> value
189+
*/
190+
private Map<String, Object> flattenConfig(ConfigurationSection config) {
191+
Map<String, Object> result = new HashMap<>();
192+
for (String key : config.getKeys(true)) {
193+
if (!config.isConfigurationSection(key)) {
194+
result.put(key, config.get(key));
195+
}
196+
}
197+
return result;
198+
}
199+
200+
/**
201+
* Applies the user values to the new layout
202+
*/
203+
private void applyUserValues(FileConfiguration newLayout, Map<String, Object> userValues) {
204+
for (Map.Entry<String, Object> entry : userValues.entrySet()) {
205+
String path = entry.getKey();
206+
Object value = entry.getValue();
207+
208+
// Don't override gui_layout_version
209+
if (path.equals(GUI_LAYOUT_VERSION_KEY)) continue;
210+
211+
if (newLayout.contains(path)) {
212+
newLayout.set(path, value);
213+
} else {
214+
plugin.getLogger().warning("GUI layout path '" + path + "' from old layout no longer exists in new layout");
215+
}
216+
}
217+
}
27218
}

0 commit comments

Comments
 (0)