Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package com.github.thesilentpro.headdb.core;

import java.util.List;
import java.util.function.BiConsumer;

import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.thesilentpro.grim.listener.PageListeners;
import com.github.thesilentpro.grim.page.registry.PageRegistry;
import com.github.thesilentpro.headdb.api.HeadAPI;
Expand All @@ -21,16 +32,6 @@
import com.github.thesilentpro.headdb.implementation.BaseHeadAPI;
import com.github.thesilentpro.headdb.implementation.BaseHeadDatabase;
import com.github.thesilentpro.inputs.paper.PaperInputListener;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.function.BiConsumer;

public class HeadDB extends JavaPlugin {

Expand Down Expand Up @@ -158,26 +159,24 @@ public Config getCfg() {
return this.configManager.getConfig();
}

public com.github.thesilentpro.headdb.core.config.MenuConfig getMenuConfig() {
return this.configManager.getMenuConfig();
}

public HeadAPI getHeadApi() {
return headApi;
}

private static final BiConsumer<Config, List<Head>> DATABASE_UPDATE_ACTION = (config, heads) -> {
LOGGER.info("Loaded {} heads!", heads.size());

if (config.isPreloadHeads()) {
int total = heads.size();
if (total == 0) {
LOGGER.info("No heads to preload.");
return;
}
int total = heads.size();
LOGGER.info("Loaded {} heads!", total);

if (config.isPreloadHeads() && total > 0) {
int[] milestones = {25, 50, 75, 100};
int nextMilestoneIndex = 0;

for (int i = 0; i < total; i++) {
Head head = heads.get(i);
head.getItem(); // Loads the ItemStack in memory
heads.get(i).getItem(); // Loads the ItemStack in memory

int percent = (int) (((i + 1) / (double) total) * 100);
if (nextMilestoneIndex < milestones.length && percent >= milestones[nextMilestoneIndex]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.github.thesilentpro.headdb.core.command;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.github.thesilentpro.headdb.core.HeadDB;
import com.github.thesilentpro.headdb.core.command.sub.HDBCommandGive;
import com.github.thesilentpro.headdb.core.command.sub.HDBCommandInfo;
import com.github.thesilentpro.headdb.core.command.sub.HDBCommandOpen;
import com.github.thesilentpro.headdb.core.command.sub.HDBCommandSearch;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HDBSubCommandManager {

private final int totalCommandEntries = 1 + 1; // realCommandCount + totalAliasCount
Expand All @@ -37,9 +37,12 @@ public void registerDefaults() {
}

public void register(HDBSubCommand command) {
this.commands.put(command.getName(), command);
this.realNames.add(command.getName());
for (String alias : command.getAliases()) {
String name = command.getName();
this.commands.put(name, command);
this.realNames.add(name);

String[] aliases = command.getAliases();
for (String alias : aliases) {
this.commands.put(alias, command);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,117 @@
package com.github.thesilentpro.headdb.core.command.sub;

import com.github.thesilentpro.headdb.core.HeadDB;
import com.github.thesilentpro.headdb.core.command.HDBSubCommand;
import com.github.thesilentpro.headdb.core.menu.gui.HeadsGUI;
import com.github.thesilentpro.headdb.core.util.Compatibility;
import java.util.Arrays;
import java.util.List;

import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.List;
import com.github.thesilentpro.headdb.core.HeadDB;
import com.github.thesilentpro.headdb.core.command.HDBSubCommand;
import com.github.thesilentpro.headdb.core.menu.gui.HeadsGUI;
import com.github.thesilentpro.headdb.core.util.Compatibility;

public class HDBCommandOpen extends HDBSubCommand {

private final HeadDB plugin;
private List<String> completions;

public HDBCommandOpen(HeadDB plugin) {
super("open", "Open the database.", "[category]", "o");
super("open", "Open the database.", "[category] [player]", "o");
this.plugin = plugin;
}

@Override
public void handle(CommandSender sender, String[] args) {
if (!(sender instanceof Player player)) {
plugin.getLocalization().sendMessage(sender, "noConsole");
return;
// Определяем целевого игрока
Player targetPlayer = null;
int categoryEndIndex = args.length;

// Проверяем, указан ли игрок в конце команды
if (args.length >= 2) {
Player possiblePlayer = plugin.getServer().getPlayer(args[args.length - 1]);
if (possiblePlayer != null) {
targetPlayer = possiblePlayer;
categoryEndIndex = args.length - 1;

// Проверка прав на открытие меню другому игроку
if (!sender.hasPermission("headdb.command.open.others")) {
plugin.getLocalization().sendMessage(sender, "noPermission");
if (sender instanceof Player) {
Compatibility.playSound(sender, plugin.getSoundConfig().get("noPermission"));
}
return;
}
}
}

if (args.length == 1) {
this.plugin.getMenuManager().getMainMenu().open(player);
plugin.getLocalization().sendMessage(sender, "command.open.opening", msg -> msg.replaceText(builder -> builder.matchLiteral("{category}").replacement("Main")));
Compatibility.playSound(player, plugin.getSoundConfig().get("menu.open"));

// Если целевой игрок не указан, используем отправителя команды
if (targetPlayer == null) {
if (!(sender instanceof Player)) {
plugin.getLocalization().sendMessage(sender, "noConsole");
return;
}
targetPlayer = (Player) sender;
}

final Player finalTarget = targetPlayer;

// Открытие главного меню
if (categoryEndIndex == 1) {
this.plugin.getMenuManager().getMainMenu().open(finalTarget);
if (finalTarget.equals(sender)) {
plugin.getLocalization().sendMessage(sender, "command.open.opening");
} else {
plugin.getLocalization().sendMessage(sender, "command.open.openingFor",
msg -> msg.replaceText(builder -> builder.matchLiteral("{menu}").replacement("Main"))
.replaceText(builder -> builder.matchLiteral("{target}").replacement(finalTarget.getName())));
}
Compatibility.playSound(finalTarget, plugin.getSoundConfig().get("menu.open"));
return;
}

String category = String.join(" ", String.join(" ", Arrays.copyOfRange(args, 1, args.length)));
HeadsGUI categoryGui = plugin.getMenuManager().get(category.replace(" ", "_").replace("&", "_"));
// Открытие категории
final String categoryName = String.join(" ", Arrays.copyOfRange(args, 1, categoryEndIndex));
HeadsGUI categoryGui = plugin.getMenuManager().get(categoryName.replace(" ", "_").replace("&", "_"));
if (categoryGui == null) {
plugin.getLocalization().sendMessage(sender, "command.open.invalidCategory", msg -> msg.replaceText(builder -> builder.matchLiteral("{category}").replacement(category)));
Compatibility.playSound(player, plugin.getSoundConfig().get("failed"));
plugin.getLocalization().sendMessage(sender, "command.open.invalidCategory",
msg -> msg.replaceText(builder -> builder.matchLiteral("{category}").replacement(categoryName)));
if (sender instanceof Player) {
Compatibility.playSound(sender, plugin.getSoundConfig().get("failed"));
}
return;
}

int pageIndex = 0;
if (plugin.getCfg().isTrackPage()) {
pageIndex = categoryGui.getGuiRegistry().getCurrentPage(player.getUniqueId(), categoryGui.getKey()).orElse(0);
pageIndex = categoryGui.getGuiRegistry().getCurrentPage(finalTarget.getUniqueId(), categoryGui.getKey()).orElse(0);
}
categoryGui.open(player, pageIndex);
plugin.getLocalization().sendMessage(sender, "command.open.opening", msg -> msg.replaceText(builder -> builder.matchLiteral("{category}").replacement(category)));
Compatibility.playSound(player, plugin.getSoundConfig().get("menu.open"));
categoryGui.open(finalTarget, pageIndex);

if (finalTarget.equals(sender)) {
plugin.getLocalization().sendMessage(sender, "command.open.opening");
} else {
plugin.getLocalization().sendMessage(sender, "command.open.openingFor",
msg -> msg.replaceText(builder -> builder.matchLiteral("{menu}").replacement(categoryName))
.replaceText(builder -> builder.matchLiteral("{target}").replacement(finalTarget.getName())));
}
Compatibility.playSound(finalTarget, plugin.getSoundConfig().get("menu.open"));
}

@Override
public @Nullable List<String> handleCompletions(CommandSender sender, String[] args) {
if (completions == null) {
completions = plugin.getHeadApi().findKnownCategories();
}

// Если это последний аргумент и у отправителя есть права, предлагаем имена игроков
if (args.length >= 2 && sender.hasPermission("headdb.command.open.others")) {
return plugin.getServer().getOnlinePlayers().stream()
.map(org.bukkit.entity.Player::getName)
.toList();
}

return completions;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.github.thesilentpro.headdb.core.command.sub;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import com.github.thesilentpro.headdb.api.model.Head;
import com.github.thesilentpro.headdb.core.HeadDB;
import com.github.thesilentpro.headdb.core.command.HDBSubCommand;
import com.github.thesilentpro.headdb.core.menu.gui.HeadsGUI;
import com.github.thesilentpro.headdb.core.util.Compatibility;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import net.kyori.adventure.text.Component;

public class HDBCommandSearch extends HDBSubCommand {

Expand All @@ -34,8 +40,16 @@ public void handle(CommandSender sender, String[] args) {
CompletableFuture.supplyAsync(() -> {
// detect & strip --any
// Enables loose search (match if any filter passes instead of all).
boolean any = Arrays.stream(args).anyMatch(a -> a.equalsIgnoreCase("--any"));
List<String> parts = Arrays.stream(args, 1, args.length).filter(a -> !a.equalsIgnoreCase("--any")).toList();
boolean any = false;
List<String> parts = new ArrayList<>(args.length - 1);
for (int i = 1; i < args.length; i++) {
if (args[i].equalsIgnoreCase("--any")) {
any = true;
} else {
parts.add(args[i]);
}
}
final boolean finalAny = any;

// parse filters
String category = null;
Expand Down Expand Up @@ -79,20 +93,23 @@ public void handle(CommandSender sender, String[] args) {
.replaceText(builder -> builder.matchLiteral("{category}").replacement(finalCategory != null ? finalCategory : "/"))
.replaceText(builder -> builder.matchLiteral("{tags}").replacement(!tags.isEmpty() ? String.join(",", tags) : "/"))
.replaceText(builder -> builder.matchLiteral("{ids}").replacement(!ids.isEmpty() ? String.join(",", ids.stream().map(String::valueOf).toArray(String[]::new)) : "/"))
.replaceText(builder -> builder.matchLiteral("{mode}").replacement(any ? "ANY" : "ALL"))
.replaceText(builder -> builder.matchLiteral("{mode}").replacement(finalAny ? "ANY" : "ALL"))
);
});

// lower all your query bits once
String qCat = category == null ? null : category.toLowerCase(Locale.ROOT);
String qName = nameQuery.trim().toLowerCase(Locale.ROOT);
Set<String> tagSet = tags.stream().map(t -> t.toLowerCase(Locale.ROOT)).collect(Collectors.toSet());
Set<String> tagSet = new HashSet<>(tags.size());
for (String tag : tags) {
tagSet.add(tag.toLowerCase(Locale.ROOT));
}
Set<Integer> idSet = new HashSet<>(ids);

List<Head> allHeads = plugin.getHeadApi().getHeads().join();
List<Head> result = new ArrayList<>();

if (any) {
if (finalAny) {
// ANY‑mode: match if _one_ of the filters hits
for (Head h : allHeads) {
// grab once per head
Expand Down
Loading