Compare commits

..

15 Commits

Author SHA1 Message Date
c0fe432ab0 new afk 2025-12-14 17:35:32 +05:00
06ad85d6df new afk 2025-12-14 17:35:21 +05:00
752b116e30 Remove target directory from repository 2025-12-13 22:19:18 +05:00
a96d8c13e5 fix afk time 2025-12-13 22:17:45 +05:00
1099d49a00 add mobs event 2025-12-13 20:51:47 +05:00
9b77bbe9a6 fix amount item sell 2025-12-09 06:38:56 +05:00
4e5e6d1cf7 rework urls in config.yml 2025-12-07 16:28:50 +05:00
8907ffeb51 update config.yml 2025-12-07 16:11:21 +05:00
fd95893576 update .gitignore 2025-12-07 16:10:22 +05:00
4d8c8adece remove extra logs 2025-12-07 16:09:56 +05:00
d331615bde Merge branch 'dev' 2025-12-07 14:45:24 +05:00
46d0fac07b add case endpoint 2025-12-07 04:33:12 +05:00
0534dd3c4f add case endpoint 2025-12-07 04:32:12 +05:00
f4f7a4288c add case endpoint 2025-12-07 00:53:23 +05:00
ee5c0f608c fix player online 2025-12-06 15:58:27 +05:00
13 changed files with 374 additions and 103 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea/vcs.xml
/target/

8
.idea/modules.xml generated
View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/popa.iml" filepath="$PROJECT_DIR$/popa.iml" />
</modules>
</component>
</project>

View File

@ -66,7 +66,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.4-R0.1-SNAPSHOT</version>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -33,23 +33,38 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.attribute.Attribute;
import org.bukkit.event.player.PlayerExpChangeEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
public final class popa extends JavaPlugin implements Listener {
private HttpClient httpClient;
private String apiBase;
private String apiUrl;
private String serverIp;
private String commandsUrl;
private String inventoryRequestsUrl;
private String marketplaceUrl;
private String bonusesUrl;
private int afkTimeoutSeconds;
private final Map<UUID, Long> playerLoginTimes = new HashMap<>();
private final Map<UUID, String> playerNames = new HashMap<>();
private ScheduledExecutorService scheduler;
private Gson gson;
private final Map<UUID, Long> lastActivityTimes = new HashMap<>();
private final Map<UUID, Boolean> afkStates = new HashMap<>();
private long afkThresholdMillis;
private String afkPrefix;
@Override
public void onEnable() {
@ -57,13 +72,28 @@ public final class popa extends JavaPlugin implements Listener {
saveDefaultConfig();
reloadConfig();
afkTimeoutSeconds = getConfig().getInt("afk-timeout-seconds", 300);
// База для всех урлов
apiBase = getConfig().getString("api-base", "http://localhost:8000");
// Получаем настройки из конфига
String apiBase = getConfig().getString("api-base", "http://localhost:8000");
apiUrl = getConfig().getString("api-url", apiBase + "/api/server/events");
commandsUrl = getConfig().getString("commands-url", apiBase + "/api/server/commands");
inventoryRequestsUrl = getConfig().getString("inventory-requests-url", apiBase + "/api/server/inventory/requests");
apiUrl = apiBase + "/api/server/events";
commandsUrl = apiBase + "/api/server/commands";
inventoryRequestsUrl = apiBase + "/api/server/inventory/requests";
marketplaceUrl = apiBase + "/api/marketplace";
bonusesUrl = apiBase + "/api/bonuses/effects";
serverIp = getConfig().getString("server-ip", getServerIp());
// Интервалы из конфига (в секундах)
int commandsInterval = getConfig().getInt("commands-interval-seconds", 5);
int inventoryInterval = getConfig().getInt("inventory-interval-seconds", 5);
int onlineUpdateInterval = getConfig().getInt("online-update-interval-seconds", 60);
int marketplaceInterval = getConfig().getInt("marketplace-interval-seconds", 3);
int bonusesInterval = getConfig().getInt("bonuses-interval-seconds", 10);
// Инициализируем HTTP клиент
httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
@ -73,29 +103,76 @@ public final class popa extends JavaPlugin implements Listener {
// Инициализируем Gson для работы с JSON
gson = new Gson();
// Регистрируем события
getServer().getPluginManager().registerEvents(this, this);
// ИНИЦИАЛИЗАЦИЯ УЖЕ ОНЛАЙН-ИГРОКОВ (после registerEvents)
long now = System.currentTimeMillis();
for (Player player : Bukkit.getOnlinePlayers()) {
UUID id = player.getUniqueId();
playerLoginTimes.put(id, now);
playerNames.put(id, player.getName());
lastActivityTimes.put(id, now);
}
// Запускаем планировщик для проверки команд
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this::checkAndExecuteCommands, 5, 5, TimeUnit.SECONDS);
// Запускаем планировщик для проверки запросов инвентаря
scheduler.scheduleAtFixedRate(this::checkInventoryRequests, 5, 5, TimeUnit.SECONDS);
// Отправка текущих онлайн-игроков при старте плагина
scheduler.scheduleAtFixedRate(this::sendOnlinePlayersUpdate, 60, 60, TimeUnit.SECONDS);
marketplaceUrl = getConfig().getString("api-base", "http://localhost:8000") + "/api/marketplace";
scheduler.scheduleAtFixedRate(this::checkMarketplaceOperations, 3, 3, TimeUnit.SECONDS);
// Проверка команд
scheduler.scheduleAtFixedRate(
this::checkAndExecuteCommands,
commandsInterval,
commandsInterval,
TimeUnit.SECONDS
);
bonusesUrl = getConfig().getString("api-base", "http://localhost:8000") + "/api/bonuses/effects";
scheduler.scheduleAtFixedRate(this::checkPlayerBonuses, 10, 10, TimeUnit.SECONDS);
// Проверка запросов инвентаря
scheduler.scheduleAtFixedRate(
this::checkInventoryRequests,
inventoryInterval,
inventoryInterval,
TimeUnit.SECONDS
);
// Отправка текущих онлайн-игроков
scheduler.scheduleAtFixedRate(
this::sendOnlinePlayersUpdate,
onlineUpdateInterval,
onlineUpdateInterval,
TimeUnit.SECONDS
);
// Маркетплейс
scheduler.scheduleAtFixedRate(
this::checkMarketplaceOperations,
marketplaceInterval,
marketplaceInterval,
TimeUnit.SECONDS
);
// Бонусы
scheduler.scheduleAtFixedRate(
this::checkPlayerBonuses,
bonusesInterval,
bonusesInterval,
TimeUnit.SECONDS
);
getLogger().info("PopaPlugin has been enabled!");
getLogger().info("API base: " + apiBase);
getLogger().info("API URL: " + apiUrl);
getLogger().info("Commands URL: " + commandsUrl);
getLogger().info("Inventory Requests URL: " + inventoryRequestsUrl);
getLogger().info("Marketplace URL: " + marketplaceUrl);
getLogger().info("Bonuses URL: " + bonusesUrl);
getLogger().info("Server IP: " + serverIp);
afkThresholdMillis = getConfig().getLong("afk-threshold-seconds", 60) * 1000L;
afkPrefix = getConfig().getString("afk-prefix", "[AFK] ");
// Периодически проверяем, кто ушёл в AFK (на основном потоке)
Bukkit.getScheduler().runTaskTimer(this, this::checkAfkTransitions, 20L, 20L * 5L);
}
@Override
@ -128,30 +205,72 @@ public final class popa extends JavaPlugin implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
// Игнорируем микродвижения (изменение направления взгляда)
if (event.getFrom().getX() != event.getTo().getX() ||
event.getFrom().getY() != event.getTo().getY() ||
event.getFrom().getZ() != event.getTo().getZ()) {
lastActivityTimes.put(event.getPlayer().getUniqueId(), System.currentTimeMillis());
if (!event.getFrom().toVector().equals(event.getTo().toVector())) {
markActivity(event.getPlayer());
}
}
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
// чат — асинхронный, переключаемся на основной поток
Bukkit.getScheduler().runTask(this, () -> markActivity(event.getPlayer()));
}
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent event) {
markActivity(event.getPlayer());
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
markActivity(event.getPlayer());
}
@EventHandler
public void onInvClick(InventoryClickEvent event) {
if (event.getWhoClicked() instanceof Player p) {
markActivity(p);
}
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
markActivity(event.getPlayer());
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
markActivity(event.getPlayer());
}
@EventHandler
public void onHotbar(PlayerItemHeldEvent event) {
markActivity(event.getPlayer());
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
UUID playerId = player.getUniqueId();
String playerName = player.getName();
lastActivityTimes.put(playerId, System.currentTimeMillis());
long now = System.currentTimeMillis();
playerLoginTimes.put(playerId, System.currentTimeMillis());
// Считаем, что игрок активен с момента входа
playerLoginTimes.put(playerId, now);
playerNames.put(playerId, playerName);
lastActivityTimes.put(playerId, now);
String joinPayload = String.format(
"{\"event_type\":\"player_join\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\"}",
playerId, playerName, serverIp
);
sendEventToBackend(joinPayload);
// сразу обновим онлайн-лист на бэкенде
sendOnlinePlayersUpdate();
lastActivityTimes.put(playerId, System.currentTimeMillis());
afkStates.put(playerId, false);
}
@EventHandler
@ -175,12 +294,98 @@ public final class popa extends JavaPlugin implements Listener {
);
sendEventToBackend(quitPayload);
// Полностью чистим все карты
playerLoginTimes.remove(playerId);
playerNames.remove(playerId);
lastActivityTimes.remove(playerId);
sendOnlinePlayersUpdate();
// Восстанавливаем базовые атрибуты
player.getAttribute(Attribute.MAX_HEALTH).setBaseValue(20.0);
lastActivityTimes.remove(playerId);
afkStates.remove(playerId);
}
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
LivingEntity entity = event.getEntity();
Player killer = entity.getKiller();
if (killer == null) return; // не игрок убил
UUID playerId = killer.getUniqueId();
String playerName = killer.getName();
String mob = entity.getType().name(); // "SPIDER", "ZOMBIE", ...
String payload = String.format(
"{\"event_type\":\"mob_kill\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"mob\":\"%s\",\"count\":1,\"server_ip\":\"%s\"}",
playerId, playerName, mob, serverIp
);
sendEventToBackend(payload);
}
private void pushOnlineUpdateAsync() {
Bukkit.getScheduler().runTaskAsynchronously(this, this::sendOnlinePlayersUpdate);
}
private void markActivity(Player player) {
UUID id = player.getUniqueId();
long now = System.currentTimeMillis();
lastActivityTimes.put(id, now);
if (afkStates.getOrDefault(id, false)) {
afkStates.put(id, false);
player.sendMessage("Вы больше не AFK");
// 1) опционально: отдельное событие выхода из AFK
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
sendEventToBackend(String.format(
"{\"event_type\":\"player_afk_end\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\",\"timestamp\":%d}",
id, player.getName(), serverIp, now
));
});
// 2) и сразу пушим обновление списка онлайна (чтобы бэкенд обновился мгновенно)
pushOnlineUpdateAsync();
}
}
private void setAfk(Player player) {
UUID id = player.getUniqueId();
if (afkStates.getOrDefault(id, false)) return;
afkStates.put(id, true);
player.sendMessage("Вы AFK");
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
sendEventToBackend(String.format(
"{\"event_type\":\"player_afk_start\",\"player_id\":\"%s\",\"player_name\":\"%s\",\"server_ip\":\"%s\",\"timestamp\":%d}",
id, player.getName(), serverIp, System.currentTimeMillis()
));
});
pushOnlineUpdateAsync();
}
private void checkAfkTransitions() {
long now = System.currentTimeMillis();
for (Player p : Bukkit.getOnlinePlayers()) {
UUID id = p.getUniqueId();
// если ещё нет активности — считаем что активен “сейчас”
long last = lastActivityTimes.getOrDefault(id, now);
lastActivityTimes.putIfAbsent(id, last);
boolean isAfk = afkStates.getOrDefault(id, false);
boolean shouldBeAfk = (now - last) > afkThresholdMillis;
if (!isAfk && shouldBeAfk) {
setAfk(p);
}
}
}
private void sendOnlinePlayersUpdate() {
@ -192,19 +397,8 @@ public final class popa extends JavaPlugin implements Listener {
UUID playerId = entry.getKey();
String playerName = playerNames.get(playerId);
Long lastActivity = lastActivityTimes.get(playerId);
// Если время последней активности не записано, пропускаем игрока
if (lastActivity == null) {
continue;
}
// Вычисляем, сколько секунд игрок неактивен
long inactiveSeconds = (currentTime - lastActivity) / 1000;
// Если игрок неактивен больше минуты (60 секунд), пропускаем его
if (inactiveSeconds > 60) {
continue;
}
boolean isAfk = ((currentTime - lastActivity) > afkThresholdMillis);
String nameForSite = isAfk ? (afkPrefix + playerName) : playerName;
long onlineTime = (currentTime - entry.getValue()) / 1000;
@ -212,8 +406,8 @@ public final class popa extends JavaPlugin implements Listener {
first = false;
playersJson.append(String.format(
"{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d}",
playerId, playerName, onlineTime
"{\"player_id\":\"%s\",\"player_name\":\"%s\",\"online_time\":%d,\"afk\":%s}",
playerId, nameForSite, onlineTime, isAfk ? "true" : "false"
));
}
@ -227,7 +421,7 @@ public final class popa extends JavaPlugin implements Listener {
sendEventToBackend(payload);
}
private void sendEventToBackend(String jsonPayload) {
void sendEventToBackend(String jsonPayload) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.version(HttpClient.Version.HTTP_1_1)
@ -237,9 +431,14 @@ public final class popa extends JavaPlugin implements Listener {
try {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
getLogger().info("Event sent to backend. Response: " + response.statusCode());
// Логируем ТОЛЬКО если что-то пошло не так
if (response.statusCode() != 200) {
getLogger().severe("Failed to send event to backend. Status: "
+ response.statusCode() + ", body: " + response.body());
}
} catch (IOException | InterruptedException e) {
getLogger().warning("Failed to send event to backend: " + e.getMessage());
getLogger().severe("Failed to send event to backend: " + e.getMessage());
}
}
@ -335,7 +534,7 @@ public final class popa extends JavaPlugin implements Listener {
try {
// Выводим полный URL для отладки
String requestUrl = inventoryRequestsUrl + "?server_ip=" + serverIp;
getLogger().info("Запрашиваем инвентарь по URL: " + requestUrl);
// getLogger().info("Запрашиваем инвентарь по URL: " + requestUrl);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
@ -346,8 +545,14 @@ public final class popa extends JavaPlugin implements Listener {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Выводим код ответа и тело для отладки
getLogger().info("Получен ответ от API: " + response.statusCode());
getLogger().info("Тело ответа: " + response.body());
// getLogger().info("Получен ответ от API: " + response.statusCode());
// getLogger().info("Тело ответа: " + response.body());
if (response.statusCode() != 200) {
getLogger().severe("Ошибка при запросе инвентаря: статус "
+ response.statusCode() + ", тело: " + response.body());
return;
}
if (response.statusCode() == 200) {
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
@ -356,6 +561,12 @@ public final class popa extends JavaPlugin implements Listener {
if (json.has("status") && "success".equals(json.get("status").getAsString())) {
if (json.has("inventory_requests")) {
JsonArray requests = json.getAsJsonArray("inventory_requests");
if (requests.size() == 0) {
// тишина, просто выходим
return;
}
getLogger().info("Найдено запросов инвентаря: " + requests.size());
for (JsonElement req : requests) {
@ -494,7 +705,7 @@ public final class popa extends JavaPlugin implements Listener {
*/
private void sendInventoryToBackend(String jsonPayload) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(getConfig().getString("api-base", "http://localhost:8000") + "/api/server/inventory/submit"))
.uri(URI.create(apiBase + "/api/server/inventory/submit"))
.version(HttpClient.Version.HTTP_1_1)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
@ -508,6 +719,42 @@ public final class popa extends JavaPlugin implements Listener {
}
}
private void handleCaseRewardOperation(JsonObject operation) {
String playerName = operation.get("player_name").getAsString();
JsonObject itemData = operation.getAsJsonObject("item_data");
Player player = Bukkit.getPlayer(playerName);
if (player == null || !player.isOnline()) {
sendOperationError(operation.get("id").getAsString(), "Игрок не в сети");
return;
}
// Создаем предмет из данных (используем уже существующий метод)
ItemStack item = jsonToItemStack(itemData);
if (item == null) {
sendOperationError(operation.get("id").getAsString(), "Не удалось создать предмет кейса");
return;
}
// Пытаемся положить в инвентарь
HashMap<Integer, ItemStack> leftItems = player.getInventory().addItem(item);
// Если не поместилось — дропаем остаток на землю
if (!leftItems.isEmpty()) {
for (ItemStack leftItem : leftItems.values()) {
player.getWorld().dropItem(player.getLocation(), leftItem);
}
}
// Красивое сообщение игроку
String caseName = operation.has("case_name")
? operation.get("case_name").getAsString()
: "кейс";
player.sendMessage("§aВы открыли §e" + caseName + " §aи получили §f"
+ item.getAmount() + "x " + item.getType().name());
}
/**
* Проверяет запросы на операции с торговой площадкой
*/
@ -531,20 +778,22 @@ public final class popa extends JavaPlugin implements Listener {
if (json.has("operations") && json.get("operations").isJsonArray()) {
JsonArray operations = json.getAsJsonArray("operations");
for (JsonElement op : operations) {
JsonObject operation = op.getAsJsonObject();
String type = operation.get("type").getAsString();
String opId = operation.get("id").getAsString();
if ("sell".equals(type)) {
handleSellOperation(operation);
} else if ("buy".equals(type)) {
handleBuyOperation(operation);
} else if ("cancel_sale".equals(type)) {
handleCancelSaleOperation(operation);
} else if ("case_reward".equals(type)) {
handleCaseRewardOperation(operation);
}
// Подтверждаем выполнение операции
confirmOperation(opId);
}
@ -559,19 +808,20 @@ public final class popa extends JavaPlugin implements Listener {
/**
* Обрабатывает операцию продажи предмета
*/
private void handleSellOperation(JsonObject operation) {
void handleSellOperation(JsonObject operation) {
String playerName = operation.get("player_name").getAsString();
int slotIndex = operation.get("slot_index").getAsInt();
int sellAmount = operation.get("amount").getAsInt(); // 👈 берём количество из операции
Player player = Bukkit.getPlayer(playerName);
if (player == null || !player.isOnline()) {
sendOperationError(operation.get("id").getAsString(), "Игрок не в сети");
return;
}
PlayerInventory inv = player.getInventory();
ItemStack item = null;
// Получаем предмет из нужного слота
if (slotIndex >= 0 && slotIndex <= 35) {
item = inv.getItem(slotIndex);
@ -586,35 +836,69 @@ public final class popa extends JavaPlugin implements Listener {
} else if (slotIndex == 40) {
item = inv.getItemInOffHand();
}
if (item == null || item.getType() == Material.AIR) {
sendOperationError(operation.get("id").getAsString(), "Предмет не найден в указанном слоте");
return;
}
// Создаем полную копию предмета с метаданными для отправки на сервер
JsonObject itemData = itemStackToDetailedJson(item, slotIndex);
// Очищаем слот
if (slotIndex >= 0 && slotIndex <= 35) {
inv.setItem(slotIndex, null);
} else if (slotIndex == 36) {
inv.setBoots(null);
} else if (slotIndex == 37) {
inv.setLeggings(null);
} else if (slotIndex == 38) {
inv.setChestplate(null);
} else if (slotIndex == 39) {
inv.setHelmet(null);
} else if (slotIndex == 40) {
inv.setItemInOffHand(null);
int currentAmount = item.getAmount();
// Валидация количества
if (sellAmount <= 0 || sellAmount > currentAmount) {
sendOperationError(operation.get("id").getAsString(),
"Некорректное количество для продажи: " + sellAmount);
return;
}
// Отправляем сообщение игроку
// Создаём отдельный ItemStack для продажи
ItemStack itemToSell = item.clone();
itemToSell.setAmount(sellAmount);
// Формируем данные именно продаваемой части
JsonObject itemData = itemStackToDetailedJson(itemToSell, slotIndex);
// Обновляем инвентарь игрока
if (sellAmount == currentAmount) {
// Продаём весь стак — очищаем слот, как раньше
if (slotIndex >= 0 && slotIndex <= 35) {
inv.setItem(slotIndex, null);
} else if (slotIndex == 36) {
inv.setBoots(null);
} else if (slotIndex == 37) {
inv.setLeggings(null);
} else if (slotIndex == 38) {
inv.setChestplate(null);
} else if (slotIndex == 39) {
inv.setHelmet(null);
} else if (slotIndex == 40) {
inv.setItemInOffHand(null);
}
} else {
// Продаём только часть стака — уменьшаем количество в слоте
int remaining = currentAmount - sellAmount;
item.setAmount(remaining);
if (slotIndex >= 0 && slotIndex <= 35) {
inv.setItem(slotIndex, item);
} else if (slotIndex == 36) {
inv.setBoots(item);
} else if (slotIndex == 37) {
inv.setLeggings(item);
} else if (slotIndex == 38) {
inv.setChestplate(item);
} else if (slotIndex == 39) {
inv.setHelmet(item);
} else if (slotIndex == 40) {
inv.setItemInOffHand(item);
}
}
// Сообщение игроку
int price = operation.get("price").getAsInt();
player.sendMessage("§aВы выставили предмет на продажу за " + price + " монет");
// Отправляем данные о предмете на сервер
player.sendMessage("§aВы выставили " + sellAmount + " шт. за " + price + " монет");
// Отправляем данные о продаваемой части на API
sendItemDetails(operation.get("id").getAsString(), itemData);
}
@ -1047,9 +1331,9 @@ public final class popa extends JavaPlugin implements Listener {
event.setAmount(originalExp + bonusExp);
// Отправляем сообщение игроку (опционально)
if (bonusExp > 0) {
player.sendMessage("§a+" + bonusExp + " бонусного опыта!");
}
// if (bonusExp > 0) {
// player.sendMessage("§a+" + bonusExp + " бонусного опыта!");
// }
break; // Применяем только один бонус к опыту
}

View File

@ -1,6 +1,14 @@
# Настройки API
api-url: "http://localhost:8000/api/events"
commands-url: "http://localhost:8000/api/commands"
api-base: "http://localhost:3001"
# IP сервера (оставьте пустым для автоматического определения)
server-ip: ""
server-ip: "minecraft.hub.popa-popa.ru"
afk-timeout-seconds: 300
# Интервалы обновления (в секундах)
commands-interval-seconds: 5 # проверка команд
inventory-interval-seconds: 5 # запросы инвентаря
online-update-interval-seconds: 60 # отправка онлайна
marketplace-interval-seconds: 3 # операции маркета
bonuses-interval-seconds: 10 # проверка бонусов

View File

@ -1,6 +0,0 @@
# Настройки API
api-url: "http://localhost:8000/api/events"
commands-url: "http://localhost:8000/api/commands"
# IP сервера (оставьте пустым для автоматического определения)
server-ip: ""

View File

@ -1,4 +0,0 @@
name: popa-plugin
version: '1.0-SNAPSHOT'
main: popa.popa
api-version: '1.21'

Binary file not shown.

View File

@ -1,3 +0,0 @@
artifactId=popa
groupId=popa-popa.ru
version=1.0-SNAPSHOT

View File

@ -1 +0,0 @@
D:\Projects\Java\popa\src\main\java\popa\popa.java

Binary file not shown.