Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Plugin/src/main/java/dev/lrxh/neptune/Neptune.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ public final class Neptune extends JavaPlugin {
@Setter
private boolean allowMatches;

private boolean errored;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The errored field is a plain boolean without the volatile keyword. When setErrored() is called, it could theoretically be called from a thread other than the main server thread (e.g., if an error were to occur in a background task that triggers this). The onDisable() method reads errored on the main server thread. Without volatile, there is no guarantee that the write from one thread is visible to the read on another thread. Consider declaring errored as volatile boolean errored or using AtomicBoolean to ensure safe cross-thread visibility.

Suggested change
private boolean errored;
private volatile boolean errored;

Copilot uses AI. Check for mistakes.
public void setErrored() {
errored = true;
}

public static Neptune get() {
return instance;
}
Expand Down Expand Up @@ -215,8 +220,10 @@ private void loadCommandManager() {

@Override
public void onDisable() {
stopService(KitService.get(), KitService::save);
stopService(ArenaService.get(), ArenaService::save);
if (!errored) {
stopService(KitService.get(), KitService::save);
stopService(ArenaService.get(), ArenaService::save);
}
stopService(MatchService.get(), MatchService::stopAllGames);
stopService(TaskScheduler.get(), TaskScheduler::stopAllTasks);
stopService(ProfileService.get(), ProfileService::saveAll);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.lrxh.api.arena.IArena;
import dev.lrxh.api.arena.IArenaService;
import dev.lrxh.neptune.Neptune;
import dev.lrxh.neptune.configs.ConfigService;
import dev.lrxh.neptune.providers.manager.IService;
import dev.lrxh.neptune.providers.manager.Value;
Expand Down Expand Up @@ -37,8 +38,14 @@ public void load() {
FileConfiguration config = ConfigService.get().getArenasConfig().getConfiguration();
if (config.contains("arenas")) {
for (String arenaName : getKeys("arenas")) {
Arena arena = loadArena(arenaName);
arenas.add(arena);
try {
Arena arena = loadArena(arenaName);
arenas.add(arena);
} catch (Exception e) {
Neptune.get().getLogger().severe("Error occurred while loading an arena with key: " + arenaName);
Neptune.get().setErrored();
throw e;
}
}
}
}
Expand Down
65 changes: 36 additions & 29 deletions Plugin/src/main/java/dev/lrxh/neptune/game/kit/KitService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.lrxh.api.arena.IArena;
import dev.lrxh.api.kit.IKit;
import dev.lrxh.api.kit.IKitService;
import dev.lrxh.neptune.Neptune;
import dev.lrxh.neptune.configs.ConfigService;
import dev.lrxh.neptune.game.arena.Arena;
import dev.lrxh.neptune.game.arena.ArenaService;
Expand Down Expand Up @@ -36,41 +37,47 @@ public void load() {
FileConfiguration config = ConfigService.get().getKitsConfig().getConfiguration();
if (config.contains("kits")) {
for (String kitName : getKeys("kits")) {
String path = "kits." + kitName + ".";
String displayName = config.getString(path + "displayName", kitName);
ItemStack icon = ItemUtils.deserializeItem(config.getString(path + "icon", ""));

List<ItemStack> items = ItemUtils.deserialize(config.getString(path + "items", ""));
int slot = config.getInt(path + "slot", kits.size() + 1);
int kitEditorSlot = config.getInt(path + "kitEditor-slot", slot);
double health = config.getDouble(path + "health", 20);
double damageMultiplier = config.getDouble(path + "damage-multiplier", 1.0);

HashSet<Arena> arenas = new HashSet<>();
if (!config.getStringList(path + "arenas").isEmpty()) {
for (String arenaName : config.getStringList(path + "arenas")) {
Arena arena = ArenaService.get().getArenaByName(arenaName);
if (arena == null) {
ServerUtils.error("KitService: Arena " + arenaName + " not found for kit " + kitName);
continue;
try {
String path = "kits." + kitName + ".";
String displayName = config.getString(path + "displayName", kitName);
ItemStack icon = ItemUtils.deserializeItem(config.getString(path + "icon", ""));

List<ItemStack> items = ItemUtils.deserialize(config.getString(path + "items", ""));
int slot = config.getInt(path + "slot", kits.size() + 1);
int kitEditorSlot = config.getInt(path + "kitEditor-slot", slot);
double health = config.getDouble(path + "health", 20);
double damageMultiplier = config.getDouble(path + "damage-multiplier", 1.0);

HashSet<Arena> arenas = new HashSet<>();
if (!config.getStringList(path + "arenas").isEmpty()) {
for (String arenaName : config.getStringList(path + "arenas")) {
Arena arena = ArenaService.get().getArenaByName(arenaName);
if (arena == null) {
ServerUtils.error("KitService: Arena " + arenaName + " not found for kit " + kitName);
continue;
}
arenas.add(arena);
}
arenas.add(arena);
}
}

HashMap<KitRule, Boolean> rules = new HashMap<>();
for (KitRule kitRule : KitRule.values()) {
rules.put(kitRule, config.getBoolean(path + kitRule.getSaveName(), false));
}
HashMap<KitRule, Boolean> rules = new HashMap<>();
for (KitRule kitRule : KitRule.values()) {
rules.put(kitRule, config.getBoolean(path + kitRule.getSaveName(), false));
}

List<PotionEffect> potionEffects = new ArrayList<>();
if (!config.getStringList(path + "potionEffects").isEmpty()) {
for (String potion : config.getStringList(path + "potionEffects")) {
potionEffects.add(PotionEffectUtils.deserialize(potion));
List<PotionEffect> potionEffects = new ArrayList<>();
if (!config.getStringList(path + "potionEffects").isEmpty()) {
for (String potion : config.getStringList(path + "potionEffects")) {
potionEffects.add(PotionEffectUtils.deserialize(potion));
}
}
}

kits.add(new Kit(kitName, displayName, items, arenas, icon, rules, slot, health, kitEditorSlot, potionEffects, damageMultiplier));
kits.add(new Kit(kitName, displayName, items, arenas, icon, rules, slot, health, kitEditorSlot, potionEffects, damageMultiplier));
} catch (Exception e) {
Neptune.get().getLogger().severe("Error occurred while loading a kit with key: " + kitName);
Neptune.get().setErrored();
throw e;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import dev.lrxh.neptune.Neptune;
import dev.lrxh.neptune.feature.queue.QueueService;
import dev.lrxh.neptune.profile.impl.Profile;
import dev.lrxh.neptune.utils.CC;
import org.bukkit.entity.Player;

import java.util.UUID;
Expand All @@ -29,7 +30,13 @@ public static ProfileService get() {

public CompletableFuture<Void> createProfile(Player player) {
return Profile.create(player.getName(), player.getUniqueId(), plugin, false)
.thenAccept(profile -> profiles.put(player.getUniqueId(), profile));
.thenAccept(profile -> profiles.put(player.getUniqueId(), profile))
.whenComplete((result, throwable) -> {
if (throwable != null) {
Neptune.get().getLogger().severe("Error occurred while loading profile for player: " + player.getName());
player.kick(CC.color("<aqua>[Neptune] <dark_red>An error occurred while loading your profile information! Report to the developers."));
}
});
}

public CompletableFuture<Profile> createFakeProfile(UUID uuid) {
Expand Down