Skip to content

Commit a0f0d6f

Browse files
committed
Added configurable gui layout option, fixed exp from spawner after server restart and stack
1 parent 6c919d2 commit a0f0d6f

19 files changed

Lines changed: 636 additions & 146 deletions

File tree

core/src/main/java/github/nighter/smartspawner/SmartSpawner.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import github.nighter.smartspawner.hooks.rpg.AuraSkillsIntegration;
2323
import github.nighter.smartspawner.language.MessageService;
2424
import github.nighter.smartspawner.migration.SpawnerDataMigration;
25+
import github.nighter.smartspawner.spawner.gui.layout.GuiLayoutConfig;
2526
import github.nighter.smartspawner.spawner.gui.main.ItemCache;
2627
import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuAction;
2728
import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuUI;
@@ -84,6 +85,7 @@ public class SmartSpawner extends JavaPlugin implements SmartSpawnerPlugin {
8485
private SpawnerItemFactory spawnerItemFactory;
8586

8687
// Core UI components
88+
private GuiLayoutConfig guiLayoutConfig;
8789
private final ItemCache itemCache = new ItemCache(500, 30);
8890
private SpawnerMenuUI spawnerMenuUI;
8991
private SpawnerStorageUI spawnerStorageUI;
@@ -239,6 +241,7 @@ private void initializeCoreComponents() {
239241
this.spawnerFileHandler = new SpawnerFileHandler(this);
240242
this.spawnerManager = new SpawnerManager(this);
241243
this.spawnerManager.reloadAllHolograms();
244+
this.guiLayoutConfig = new GuiLayoutConfig(this);
242245
this.spawnerStorageUI = new SpawnerStorageUI(this);
243246
this.filterConfigUI = new FilterConfigUI(this);
244247
this.spawnerMenuUI = new SpawnerMenuUI(this);
@@ -400,9 +403,12 @@ private boolean checkPlugin(String pluginName, PluginCheck checker, boolean logS
400403
}
401404

402405
public void reload() {
403-
// reload static components
404-
this.spawnerStorageUI = new SpawnerStorageUI(this);
405-
this.filterConfigUI = new FilterConfigUI(this);
406+
// reload gui components
407+
guiLayoutConfig.reloadLayouts();
408+
spawnerStorageAction.reload();
409+
spawnerStorageUI.reload();
410+
filterConfigUI.reload();
411+
406412
// reload services
407413
if (auraSkillsIntegration != null) {
408414
auraSkillsIntegration.reloadConfig();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package github.nighter.smartspawner.spawner.gui.layout;
2+
3+
import lombok.Getter;
4+
import org.bukkit.Material;
5+
6+
@Getter
7+
public class GuiButton {
8+
private final String buttonType;
9+
private final int slot;
10+
private final Material material;
11+
private final boolean enabled;
12+
13+
public GuiButton(String buttonType, int slot, Material material, boolean enabled) {
14+
this.buttonType = buttonType;
15+
this.slot = slot;
16+
this.material = material;
17+
this.enabled = enabled;
18+
}
19+
20+
@Override
21+
public String toString() {
22+
return "GuiButton{" +
23+
"buttonType='" + buttonType + '\'' +
24+
", slot=" + slot +
25+
", material=" + material +
26+
", enabled=" + enabled +
27+
'}';
28+
}
29+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package github.nighter.smartspawner.spawner.gui.layout;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.Optional;
6+
import java.util.Set;
7+
8+
public class GuiLayout {
9+
private final Map<String, GuiButton> buttons = new HashMap<>();
10+
private final Map<Integer, String> slotToButtonType = new HashMap<>();
11+
12+
public void addButton(String buttonType, GuiButton button) {
13+
// Remove old button if it exists
14+
GuiButton oldButton = buttons.get(buttonType);
15+
if (oldButton != null) {
16+
slotToButtonType.remove(oldButton.getSlot());
17+
}
18+
19+
buttons.put(buttonType, button);
20+
slotToButtonType.put(button.getSlot(), buttonType);
21+
}
22+
23+
public GuiButton getButton(String buttonType) {
24+
return buttons.get(buttonType);
25+
}
26+
27+
public Optional<String> getButtonTypeAtSlot(int slot) {
28+
return Optional.ofNullable(slotToButtonType.get(slot));
29+
}
30+
31+
public boolean hasButton(String buttonType) {
32+
return buttons.containsKey(buttonType) && buttons.get(buttonType).isEnabled();
33+
}
34+
35+
public Set<String> getButtonTypes() {
36+
return buttons.keySet();
37+
}
38+
39+
public Map<String, GuiButton> getAllButtons() {
40+
return new HashMap<>(buttons);
41+
}
42+
43+
public Set<Integer> getUsedSlots() {
44+
return slotToButtonType.keySet();
45+
}
46+
47+
public boolean isSlotUsed(int slot) {
48+
return slotToButtonType.containsKey(slot);
49+
}
50+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package github.nighter.smartspawner.spawner.gui.layout;
2+
3+
import github.nighter.smartspawner.SmartSpawner;
4+
import org.bukkit.Material;
5+
import org.bukkit.configuration.file.FileConfiguration;
6+
import org.bukkit.configuration.file.YamlConfiguration;
7+
8+
import java.io.File;
9+
import java.util.logging.Level;
10+
11+
public class GuiLayoutConfig {
12+
private static final String GUI_LAYOUTS_DIR = "gui_layouts";
13+
private static final String STORAGE_GUI_FILE = "storage_gui.yml";
14+
private static final String DEFAULT_LAYOUT = "default";
15+
private static final int MIN_SLOT = 1;
16+
private static final int MAX_SLOT = 9;
17+
private static final int SLOT_OFFSET = 44;
18+
19+
private final SmartSpawner plugin;
20+
private final File layoutsDir;
21+
private String currentLayout;
22+
private GuiLayout currentGuiLayout;
23+
24+
public GuiLayoutConfig(SmartSpawner plugin) {
25+
this.plugin = plugin;
26+
this.layoutsDir = new File(plugin.getDataFolder(), GUI_LAYOUTS_DIR);
27+
loadLayout();
28+
}
29+
30+
public void loadLayout() {
31+
this.currentLayout = plugin.getConfig().getString("gui_layout", DEFAULT_LAYOUT);
32+
initializeLayoutsDirectory();
33+
this.currentGuiLayout = loadCurrentLayout();
34+
}
35+
36+
private void initializeLayoutsDirectory() {
37+
if (!layoutsDir.exists()) {
38+
layoutsDir.mkdirs();
39+
}
40+
autoSaveLayoutFiles();
41+
}
42+
43+
private void autoSaveLayoutFiles() {
44+
try {
45+
String[] layoutNames = new String[]{DEFAULT_LAYOUT, "DonutSMP"};
46+
47+
for (String layoutName : layoutNames) {
48+
File layoutDir = new File(layoutsDir, layoutName);
49+
if (!layoutDir.exists()) {
50+
layoutDir.mkdirs();
51+
}
52+
53+
File storageFile = new File(layoutDir, STORAGE_GUI_FILE);
54+
String resourcePath = GUI_LAYOUTS_DIR + "/" + layoutName + "/" + STORAGE_GUI_FILE;
55+
56+
if (!storageFile.exists()) {
57+
try {
58+
plugin.saveResource(resourcePath, false);
59+
} catch (Exception e) {
60+
plugin.getLogger().log(Level.WARNING,
61+
"Failed to auto-save layout resource for " + layoutName + ": " + e.getMessage(), e);
62+
}
63+
}
64+
}
65+
} catch (Exception e) {
66+
plugin.getLogger().log(Level.SEVERE, "Failed to auto-save layout files", e);
67+
}
68+
}
69+
70+
private GuiLayout loadCurrentLayout() {
71+
File layoutDir = new File(layoutsDir, currentLayout);
72+
File storageFile = new File(layoutDir, STORAGE_GUI_FILE);
73+
74+
if (storageFile.exists()) {
75+
GuiLayout layout = loadStorageLayout(storageFile);
76+
if (layout != null) {
77+
plugin.getLogger().info("Loaded GUI layout: " + currentLayout);
78+
return layout;
79+
}
80+
}
81+
82+
if (!currentLayout.equals(DEFAULT_LAYOUT)) {
83+
plugin.getLogger().warning("Layout '" + currentLayout + "' not found. Attempting to use default layout.");
84+
File defaultLayoutDir = new File(layoutsDir, DEFAULT_LAYOUT);
85+
File defaultStorageFile = new File(defaultLayoutDir, STORAGE_GUI_FILE);
86+
87+
if (defaultStorageFile.exists()) {
88+
GuiLayout defaultLayout = loadStorageLayout(defaultStorageFile);
89+
if (defaultLayout != null) {
90+
plugin.getLogger().info("Loaded default layout as fallback");
91+
return defaultLayout;
92+
}
93+
}
94+
}
95+
96+
plugin.getLogger().severe("No valid layout found! Creating empty layout as fallback.");
97+
return new GuiLayout();
98+
}
99+
100+
private GuiLayout loadStorageLayout(File file) {
101+
try {
102+
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
103+
GuiLayout layout = new GuiLayout();
104+
105+
if (!config.contains("buttons")) {
106+
plugin.getLogger().warning("No buttons section found in GUI layout: " + file.getName());
107+
return layout;
108+
}
109+
110+
for (String buttonKey : config.getConfigurationSection("buttons").getKeys(false)) {
111+
if (!loadButton(config, layout, buttonKey)) {
112+
continue;
113+
}
114+
}
115+
116+
return layout;
117+
} catch (Exception e) {
118+
plugin.getLogger().log(Level.WARNING,
119+
"Failed to load storage layout from " + file.getName() + ": " + e.getMessage(), e);
120+
return null;
121+
}
122+
}
123+
124+
private boolean loadButton(FileConfiguration config, GuiLayout layout, String buttonKey) {
125+
String path = "buttons." + buttonKey;
126+
127+
if (!config.getBoolean(path + ".enabled", true)) {
128+
return false;
129+
}
130+
131+
int slot = config.getInt(path + ".slot", -1);
132+
String materialName = config.getString(path + ".material", "STONE");
133+
134+
if (!isValidSlot(slot)) {
135+
plugin.getLogger().warning(String.format(
136+
"Invalid slot %d for button %s. Must be between %d and %d.",
137+
slot, buttonKey, MIN_SLOT, MAX_SLOT));
138+
return false;
139+
}
140+
141+
Material material = parseMaterial(materialName, buttonKey);
142+
int actualSlot = SLOT_OFFSET + slot;
143+
144+
GuiButton button = new GuiButton(buttonKey, actualSlot, material, true);
145+
layout.addButton(buttonKey, button);
146+
return true;
147+
}
148+
149+
private boolean isValidSlot(int slot) {
150+
return slot >= MIN_SLOT && slot <= MAX_SLOT;
151+
}
152+
153+
private Material parseMaterial(String materialName, String buttonKey) {
154+
try {
155+
return Material.valueOf(materialName.toUpperCase());
156+
} catch (IllegalArgumentException e) {
157+
plugin.getLogger().warning(String.format(
158+
"Invalid material %s for button %s. Using STONE instead.",
159+
materialName, buttonKey));
160+
return Material.STONE;
161+
}
162+
}
163+
164+
public GuiLayout getCurrentLayout() {
165+
return currentGuiLayout;
166+
}
167+
168+
public void reloadLayouts() {
169+
loadLayout();
170+
}
171+
}

core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ private void handleStackDecrease(Player player, SpawnerData spawner, int removeA
294294
chunkSpawnerLimiter.unregisterSpawner(spawner.getSpawnerLocation(), actualChange);
295295

296296
// Update stack size and give spawners to player
297-
spawner.setStackSize(targetSize, player);
297+
spawner.setStackSize(targetSize);
298298
giveSpawnersToPlayer(player, actualChange, spawner.getEntityType());
299299

300300
// Play sound
@@ -358,7 +358,7 @@ private void handleStackIncrease(Player player, SpawnerData spawner, int changeA
358358
chunkSpawnerLimiter.registerSpawnerStack(spawner.getSpawnerLocation(), actualChange);
359359

360360
removeValidSpawnersFromInventory(player, requiredType, actualChange, scanResult.spawnerSlots);
361-
spawner.setStackSize(currentSize + actualChange, player);
361+
spawner.setStackSize(currentSize + actualChange);
362362

363363
// Notify if max stack reached
364364
if (actualChange < changeAmount) {

0 commit comments

Comments
 (0)