init commit

This commit is contained in:
Patrick
2026-05-01 18:43:38 +02:00
commit b9a3f4f0df
26 changed files with 3766 additions and 0 deletions
@@ -0,0 +1,346 @@
package de.winniepat.pierredufaulersack.kits;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.IntPredicate;
import java.util.UUID;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
public class KitService {
private static final int MIN_SLOT = 1;
private static final int MAX_SLOT = 3;
private static final int KIT_CONTENT_SIZE = 41;
private final JavaPlugin plugin;
public KitService(JavaPlugin plugin) {
this.plugin = plugin;
}
public boolean addKit(KitType type, int slot, Player player) {
validateSlot(slot);
String path = path(type, slot);
if (plugin.getConfig().contains(path)) {
return false;
}
setKit(type, slot, player);
return true;
}
public void setKit(KitType type, int slot, Player player) {
setKit(type, slot, player.getInventory().getContents());
}
public void setKit(KitType type, int slot, ItemStack[] contents) {
validateSlot(slot);
ItemStack[] normalizedContents = normalizeContents(contents);
List<Map<String, Object>> serialized = new ArrayList<>(normalizedContents.length);
for (ItemStack item : normalizedContents) {
serialized.add(item == null ? null : item.clone().serialize());
}
plugin.getConfig().set(path(type, slot), serialized);
plugin.saveConfig();
}
public boolean deleteKit(KitType type, int slot) {
validateSlot(slot);
String path = path(type, slot);
if (!plugin.getConfig().contains(path)) {
return false;
}
plugin.getConfig().set(path, null);
plugin.saveConfig();
return true;
}
public List<Integer> listKitSlots(KitType type) {
List<Integer> slots = new ArrayList<>();
for (int slot = MIN_SLOT; slot <= MAX_SLOT; slot++) {
if (hasKit(type, slot)) {
slots.add(slot);
}
}
return slots;
}
public boolean hasKit(KitType type, int slot) {
validateSlot(slot);
return plugin.getConfig().contains(path(type, slot));
}
public Optional<List<ItemStack>> getKit(KitType type, int slot) {
validateSlot(slot);
List<?> raw = plugin.getConfig().getList(path(type, slot));
if (raw == null || raw.isEmpty()) {
return Optional.empty();
}
List<ItemStack> saved = new ArrayList<>(KIT_CONTENT_SIZE);
boolean hasItem = false;
for (int index = 0; index < KIT_CONTENT_SIZE; index++) {
Object element = index < raw.size() ? raw.get(index) : null;
ItemStack item = deserializeItem(element);
if (item != null) {
hasItem = true;
}
saved.add(item);
}
if (!hasItem) {
return Optional.empty();
}
return Optional.of(cloneInventory(saved));
}
public Optional<List<ItemStack>> getKitForPlayer(UUID playerId, KitType type, int slot) {
Optional<List<ItemStack>> maybeBase = getKit(type, slot);
if (maybeBase.isEmpty()) {
return Optional.empty();
}
List<ItemStack> base = maybeBase.get();
Optional<int[]> maybeLayout = getPlayerLayout(playerId, type, slot);
if (maybeLayout.isEmpty()) {
return Optional.of(cloneInventory(base));
}
return Optional.of(applyLayout(base, maybeLayout.get()));
}
public boolean setPlayerLayout(UUID playerId, KitType type, int slot, ItemStack[] arrangedContents) {
validateSlot(slot);
Optional<List<ItemStack>> maybeBase = getKit(type, slot);
if (maybeBase.isEmpty()) {
return false;
}
List<ItemStack> base = maybeBase.get();
ItemStack[] arranged = normalizeContents(arrangedContents);
int[] layout = deriveLayout(base, arranged);
List<Integer> serialized = new ArrayList<>(layout.length);
for (int value : layout) {
serialized.add(value);
}
plugin.getConfig().set(playerLayoutPath(playerId, type, slot), serialized);
plugin.saveConfig();
return true;
}
private Optional<int[]> getPlayerLayout(UUID playerId, KitType type, int slot) {
String path = playerLayoutPath(playerId, type, slot);
List<?> raw = plugin.getConfig().getList(path);
if (raw == null || raw.size() != KIT_CONTENT_SIZE) {
return Optional.empty();
}
int[] layout = new int[KIT_CONTENT_SIZE];
for (int i = 0; i < KIT_CONTENT_SIZE; i++) {
Object element = raw.get(i);
if (!(element instanceof Number number)) {
return Optional.empty();
}
int value = number.intValue();
if (value < 0 || value >= KIT_CONTENT_SIZE) {
return Optional.empty();
}
layout[i] = value;
}
return Optional.of(layout);
}
private static List<ItemStack> applyLayout(List<ItemStack> base, int[] layout) {
List<ItemStack> arranged = new ArrayList<>(KIT_CONTENT_SIZE);
boolean[] consumed = new boolean[KIT_CONTENT_SIZE];
for (int target = 0; target < KIT_CONTENT_SIZE; target++) {
int source = target < layout.length ? layout[target] : target;
if (source < 0 || source >= KIT_CONTENT_SIZE || consumed[source]) {
source = firstUnused(consumed, target);
}
consumed[source] = true;
ItemStack item = source < base.size() ? base.get(source) : null;
arranged.add(item == null ? null : item.clone());
}
return arranged;
}
private static int[] deriveLayout(List<ItemStack> base, ItemStack[] arranged) {
int[] layout = new int[KIT_CONTENT_SIZE];
boolean[] used = new boolean[KIT_CONTENT_SIZE];
for (int target = 0; target < KIT_CONTENT_SIZE; target++) {
ItemStack desired = arranged[target];
int matched = -1;
for (int source = 0; source < KIT_CONTENT_SIZE; source++) {
if (used[source]) {
continue;
}
ItemStack sourceItem = source < base.size() ? base.get(source) : null;
if (isSameStack(sourceItem, desired)) {
matched = source;
break;
}
}
if (matched == -1) {
matched = firstUnused(used, target);
}
used[matched] = true;
layout[target] = matched;
}
return layout;
}
private static int firstUnused(boolean[] used, int fallback) {
if (fallback >= 0 && fallback < used.length && !used[fallback]) {
return fallback;
}
for (int i = 0; i < used.length; i++) {
if (!used[i]) {
return i;
}
}
return 0;
}
private static boolean isSameStack(ItemStack a, ItemStack b) {
if (a == null || a.getType().isAir()) {
return b == null || b.getType().isAir();
}
if (b == null || b.getType().isAir()) {
return false;
}
return a.getAmount() == b.getAmount() && a.isSimilar(b);
}
private static ItemStack deserializeItem(Object element) {
if (element == null) {
return null;
}
if (element instanceof ItemStack itemStack) {
return itemStack.clone();
}
if (element instanceof Map<?, ?> map) {
Map<String, Object> serialized = new LinkedHashMap<>();
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() instanceof String key) {
serialized.put(key, entry.getValue());
}
}
if (!serialized.isEmpty()) {
try {
return ItemStack.deserialize(serialized);
} catch (IllegalArgumentException ignored) {
return null;
}
}
}
return null;
}
public OptionalInt rollKitSlot(KitType type, IntPredicate allowedSlot) {
List<Integer> slots = new ArrayList<>();
List<Integer> weights = new ArrayList<>();
int total = 0;
for (int slot = MIN_SLOT; slot <= MAX_SLOT; slot++) {
if (!allowedSlot.test(slot) || !hasKit(type, slot)) {
continue;
}
int weight = getChance(type, slot);
if (weight <= 0) {
continue;
}
slots.add(slot);
weights.add(weight);
total += weight;
}
if (total <= 0) {
return OptionalInt.empty();
}
int roll = ThreadLocalRandom.current().nextInt(total);
int cursor = 0;
for (int i = 0; i < slots.size(); i++) {
cursor += weights.get(i);
if (roll < cursor) {
return OptionalInt.of(slots.get(i));
}
}
return OptionalInt.empty();
}
public OptionalInt rollKitSlot(KitType type) {
return rollKitSlot(type, slot -> true);
}
private int getChance(KitType type, int slot) {
String chancePath = "kit-chances." + type.key() + "." + slot;
return plugin.getConfig().getInt(chancePath, 0);
}
private static ItemStack[] normalizeContents(ItemStack[] contents) {
ItemStack[] normalized = new ItemStack[KIT_CONTENT_SIZE];
if (contents == null) {
return normalized;
}
int copyLength = Math.min(KIT_CONTENT_SIZE, contents.length);
for (int index = 0; index < copyLength; index++) {
ItemStack item = contents[index];
normalized[index] = item == null ? null : item.clone();
}
return normalized;
}
private static List<ItemStack> cloneInventory(List<ItemStack> contents) {
List<ItemStack> cloned = new ArrayList<>(contents.size());
for (ItemStack item : contents) {
cloned.add(item == null ? null : item.clone());
}
return cloned;
}
private static String path(KitType type, int slot) {
return "kits." + type.key() + "." + slot;
}
private static String playerLayoutPath(UUID playerId, KitType type, int slot) {
return "player-kit-layouts." + playerId + "." + type.key() + "." + slot;
}
private static void validateSlot(int slot) {
if (slot < MIN_SLOT || slot > MAX_SLOT) {
throw new IllegalArgumentException("Slot must be between 1 and 3");
}
}
}