some fixes

This commit is contained in:
2025-12-09 17:16:49 +03:00
parent 42f2a97cd4
commit 7ad7b94976
16 changed files with 204 additions and 55 deletions

17
.idea/workspace.xml generated
View File

@@ -5,8 +5,22 @@
</component>
<component name="ChangeListManager">
<list default="true" id="46146984-f46a-4efa-a29f-c0632c66dbea" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.gradle/buildOutputCleanup/buildOutputCleanup.lock" beforeDir="false" afterPath="$PROJECT_DIR$/.gradle/buildOutputCleanup/buildOutputCleanup.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/deploy.sh" beforeDir="false" afterPath="$PROJECT_DIR$/deploy.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/BlockAndSeek.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/BlockAndSeek.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/Config.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/Config.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/CommandManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/CommandManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/MapsManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/MapsManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/TranslationManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/managers/TranslationManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/menus/MapsMenu.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/menus/MapsMenu.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/LazyLocation.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/LazyLocation.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/Rarity.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/Rarity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/TranslationKey.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/hdvtdev/blockandseek/objects/TranslationKey.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -50,6 +64,9 @@
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 0

2
deploy.sh Normal file → Executable file
View File

@@ -1,3 +1,3 @@
#!/usr/bin/bash
gradle deploy
scp -P 8841 Documents/minecraft/Pufferfish/1.21.8/plugins/BlockAndSeek-0.0.1-a.jar hdvt@sasha.hdvtdev.tech:~/pryatki/1.21.8/plugins
scp -P 8841 ~/Documents/minecraft/Pufferfish/1.21.8/plugins/BlockAndSeek-0.0.1-a.jar hdvt@sasha.hdvtdev.tech:~/pryatki/1.21.8/plugins

View File

@@ -135,11 +135,7 @@ public class BlockAndSeek extends JavaPlugin {
TranslationManager.loadLanguages();
MapsManager.loadMaps();
CommandManager.registerCommands("blockandseek");
CommandManager.registerCommands("bs");
CommandManager.registerCommands();
} catch (Exception e) {
getLogger().severe("Cloud err: " + e.getMessage());
e.printStackTrace();

View File

@@ -1,6 +1,8 @@
package hdvtdev.blockandseek;
import hdvtdev.blockandseek.managers.MapsManager;
import hdvtdev.blockandseek.objects.PropBlock;
import hdvtdev.blockandseek.objects.Rarity;
import org.bukkit.*;
import org.bukkit.block.Banner;
import org.bukkit.block.Comparator;
@@ -8,6 +10,7 @@ import org.bukkit.block.data.*;
import org.bukkit.block.data.type.Observer;
import org.bukkit.block.data.type.Piston;
import org.bukkit.block.data.type.RedstoneWire;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.ApiStatus;
@@ -177,6 +180,50 @@ public class BlocksGenerator {
return type.isSolid();
}
public static void applyBlocks(String mapName) {
var map = MapsManager.getMap(mapName);
getSortedBlockStats(map.getSpawn().getLocation(), 16, list -> {
// 1. Находим экстремумы (минимум и максимум кол-ва предметов)
long minCount = list.stream().mapToLong(Map.Entry::getValue).min().orElse(0);
long maxCount = list.stream().mapToLong(Map.Entry::getValue).max().orElse(0);
// 2. Находим "Центр" (Median Point)
// Все, что близко к этому числу -> COMMON. Все, что далеко -> LEGENDARY.
double midPoint = (minCount + maxCount) / 2.0;
// Максимально возможное расстояние от центра до края
double maxDistance = Math.max(maxCount - midPoint, midPoint - minCount);
List<Map.Entry<Material, Rarity>> result = new ArrayList<>();
for (Map.Entry<Material, Long> entry : list) {
long count = entry.getValue();
// 3. Считаем дистанцию от центра (по модулю, чтобы и мало, и много давали плюс)
double distance = Math.abs(count - midPoint);
// 4. Нормализуем в значение от 0.0 до 1.0
// 0.0 = ровно середина
// 1.0 = это либо minCount, либо maxCount
double deviationPercent = distance / maxDistance;
// 5. Определяем редкость
Rarity rarity = Rarity.getByDeviation(deviationPercent);
result.add(Map.entry(entry.getKey(), rarity));
}
map.setBlocks(result.stream().map(e -> new PropBlock(new ItemStack(e.getKey()), e.getValue())).toList());
map.save();
});
}
@ApiStatus.Experimental
public static void getSortedBlockStats(Location center, int chunkRadius, Consumer<List<Map.Entry<Material, Long>>> callback) {
World world = center.getWorld();

View File

@@ -31,7 +31,7 @@ public class Config extends OkaeriConfig {
@Comment("Server options.")
private ServerSettings serverSettings = new ServerSettings();
@Comment("Spawn location. Useless if the Server.forceControl is false.")
private LazyLocation spawn = new LazyLocation("world");
private LazyLocation spawn;
@Comment("Show hidden BlockAndSeek commands.")
private boolean enableDebugCommands = false;

View File

@@ -1,6 +1,7 @@
package hdvtdev.blockandseek.managers;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.BlocksGenerator;
import hdvtdev.blockandseek.Config;
import hdvtdev.blockandseek.GlowUtil;
import hdvtdev.blockandseek.objects.BlockAndSeekGame;
@@ -60,6 +61,40 @@ public final class CommandManager {
var root = commandManager.commandBuilder("blockandseek", "bs");
if (Config.debugEnabled()) testCommands(root);
commandManager.command(
root
.literal("generateBlocks")
.required("worldName", StringParser.stringParser(), MapsManager.mapSuggestions)
.handler(ctx -> {
BlocksGenerator.applyBlocks(ctx.get("worldName"));
})
);
commandManager.command(
root
.literal("newMap")
.required("worldName", StringParser.stringParser(), MapsManager.worldSuggestions)
.handler(ctx -> {
MapsManager.addMap(ctx.get("worldName"));
})
);
commandManager.command(
root
.literal("newwwwGame")
.required("worldName", StringParser.stringParser(), MapsManager.mapSuggestions)
.handler(ctx -> {
GamesManager.createGame(ctx.get("worldName"));
})
);
var partyBase = root.literal("party");
var reloadBase = root.literal("reload").permission(perm); //all, config, langs, maps
var createBase = root.literal("create"); //party, map, game

View File

@@ -13,12 +13,10 @@ import org.bukkit.command.CommandSender;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import org.intellij.lang.annotations.Flow;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public final class MapsManager {
@@ -64,15 +62,14 @@ public final class MapsManager {
it.saveDefaults();
it.load(true);
});
blockAndSeekMap.getSpawn().setWorldName(name);
blockAndSeekMap.getLobby().setWorldName(name);
blockAndSeekMap.save();
maps.put(name, blockAndSeekMap);
}
// --- Suggestions ---
public static final SuggestionProvider<CommandSender> worldSuggestions = (context, input) ->
CompletableFuture.supplyAsync(() -> {
// Лучше брать загруженные миры Bukkit, если карта должна быть в активном мире
// Если нужны папки миров:
File container = Bukkit.getWorldContainer();
File[] files = container.listFiles((dir, name) -> new File(dir, name + "/level.dat").exists());
@@ -81,11 +78,11 @@ public final class MapsManager {
return Arrays.stream(files)
.map(File::getName)
.map(Suggestion::suggestion)
.collect(Collectors.toList());
.toList();
});
public static final SuggestionProvider<CommandSender> mapSuggestions = (context, input) ->
CompletableFuture.supplyAsync(() -> maps.keySet().stream()
.map(Suggestion::suggestion)
.collect(Collectors.toList()));
.toList());
}

View File

@@ -102,10 +102,11 @@ public final class TranslationManager {
break;
}
// Обработка экранирования \%
if (start > cursor && text.charAt(start - 1) == '\\') {
sb.append(text, cursor, start - 1);
sb.append('%');
cursor = start + 1;
sb.append(text, cursor, start - 1); // Добавляем всё до слэша
sb.append('%'); // Добавляем сам процент
cursor = start + 1; // Сдвигаем курсор за процент
continue;
}
@@ -118,15 +119,11 @@ public final class TranslationManager {
sb.append(text, cursor, start);
String key = text.substring(start + 1, end);
String key = text.substring(start, end + 1);
String replacement = params.get(key);
if (replacement != null) {
sb.append(replacement);
} else {
sb.append('%').append(key).append('%');
}
sb.append(Objects.requireNonNullElse(replacement, key));
cursor = end + 1;
}

View File

@@ -21,8 +21,11 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static java.util.Map.entry;
public class GamesMenu implements GuiHolder {
@@ -40,7 +43,7 @@ public class GamesMenu implements GuiHolder {
for (String gameName : GamesManager.getAvailableGames()) {
ItemStack gameItem = new ItemStack(Material.AMETHYST_BLOCK);
ItemMeta itemMeta = gameItem.getItemMeta();
itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.GAME, "%name%", gameName));
itemMeta.displayName(TranslationManager.get(TranslationKey.GAME, entry("%name%", gameName)));
var game = GamesManager.get(gameName);
itemMeta.lore(List.of(Component.text(game.players())));
gameItem.setItemMeta(itemMeta);
@@ -69,13 +72,13 @@ public class GamesMenu implements GuiHolder {
Player p = Bukkit.getPlayer(member);
if (p != null) {
if (!game.addPlayer(p)) {
p.sendMessage(TranslationManager.get(p, TranslationKey.GAME_IS_FULL, "%game%", gameName));
p.sendMessage(TranslationManager.get(p, TranslationKey.GAME_IS_FULL, entry( "%game%", gameName)));
}
}
}
} else {
if (!game.addPlayer(player)) {
player.sendMessage(TranslationManager.get(player, TranslationKey.GAME_IS_FULL, "%game%", gameName));
player.sendMessage(TranslationManager.get(player, TranslationKey.GAME_IS_FULL, entry("%game%", gameName)));
}
}

View File

@@ -20,6 +20,7 @@ import java.util.UUID;
import java.util.stream.Collectors;
import static hdvtdev.blockandseek.objects.TranslationKey.UNKNOWN_MAP;
import static java.util.Map.entry;
public class MapsMenu implements GuiHolder {
@@ -36,7 +37,7 @@ public class MapsMenu implements GuiHolder {
for (String name : MapsManager.getAllMaps()) {
ItemStack gameItem = new ItemStack(Material.MAP);
gameItem.editMeta(itemMeta ->
itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.MAP, "%name%", name)));
itemMeta.displayName(TranslationManager.get(TranslationKey.MAP, entry("%name%", name))));
inventory.addItem(gameItem);
}
}
@@ -75,7 +76,7 @@ public class MapsMenu implements GuiHolder {
}
} else {
player.sendMessage(UNKNOWN_MAP.translate(player, "%map%", mapName, "%maps%", String.join(", ", MapsManager.getAllMaps())));
player.sendMessage(UNKNOWN_MAP.translate(player, entry("%map%", mapName), entry("%maps%", String.join(", ", MapsManager.getAllMaps()))));
}
player.closeInventory();
}

View File

@@ -49,6 +49,8 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import static java.util.Map.entry;
public class BlockAndSeekGame {
private final BlockAndSeekMap map;
@@ -96,6 +98,45 @@ public class BlockAndSeekGame {
} else this.cancel();
}
}.runTaskTimer(BlockAndSeek.getInstance(), 0, 20);
new BukkitRunnable() {
@Override
public void run() {
if (phase == null) {
this.cancel();
}
// Проходим по всем игрокам на сервере
for (UUID uuid : stateManager.getPlayersInState(PlayerType.PROP)) {
// 2. Проверяем, находится ли игрок в воде
// Метод .isInWater() доступен в новых версиях (1.13+).
// Для старых версий используйте проверку блока (см. ниже).
Player player = Bukkit.getPlayer(uuid);
if (player == null) continue;
if (player.isInWater()) {
// 3. Наносим урон
// 1.0 = половина сердца. 2.0 = полное сердце.
player.damage(0.5);
// Опционально: можно проиграть звук или эффект
player.playSound(player.getLocation(), Sound.BLOCK_SCULK_SHRIEKER_SHRIEK, 1f, 1f);
}
}
}
}.runTaskTimer(BlockAndSeek.getInstance(), 0L, 20L);
}
private void endGame() {
@@ -103,7 +144,8 @@ public class BlockAndSeekGame {
GamesManager.remove(name);
}
private void sendToAll(TranslationKey key, String... placeholders) {
@SafeVarargs
private void sendToAll(TranslationKey key, Map.Entry<String, String>... placeholders) {
for (UUID uuid : stateManager.getPlayers()) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
@@ -112,7 +154,8 @@ public class BlockAndSeekGame {
}
}
private void broadcastToAll(TranslationKey key, String... placeholders) {
@SafeVarargs
private void broadcastToAll(TranslationKey key, Map.Entry<String, String>... placeholders) {
for (UUID uuid : stateManager.getPlayers()) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
@@ -121,7 +164,8 @@ public class BlockAndSeekGame {
}
}
private void actionBarToAll(TranslationKey key, String... placeholders) {
@SafeVarargs
private void actionBarToAll(TranslationKey key, Map.Entry<String, String>... placeholders) {
for (UUID uuid : stateManager.getPlayers()) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
@@ -155,7 +199,7 @@ public class BlockAndSeekGame {
}
if (timeLeft == 30 || timeLeft == 15 || timeLeft == 10 || timeLeft <= 5) {
sendToAll(TranslationKey.TIME_TO_START, "%time%", String.valueOf(timeLeft));
sendToAll(TranslationKey.TIME_TO_START, entry("%time%", String.valueOf(timeLeft)));
}
}
}
@@ -189,7 +233,7 @@ public class BlockAndSeekGame {
if (props.size() == 1) {
Player player = Bukkit.getPlayer(props.iterator().next());
if (player != null) {
broadcastToAll(TranslationKey.HIDER_SOLO_WIN, "%player%", player.getName());
broadcastToAll(TranslationKey.HIDER_SOLO_WIN, entry("%player%", player.getName()));
} else broadcastToAll(TranslationKey.HIDERS_WIN);
} else broadcastToAll(TranslationKey.HIDERS_WIN);
this.onDisable();
@@ -216,7 +260,7 @@ public class BlockAndSeekGame {
}
}
}
actionBarToAll(TranslationKey.TIME_LEFT, "%time%", String.valueOf(gameDuration));
actionBarToAll(TranslationKey.TIME_LEFT, entry("%time%", String.valueOf(gameDuration)));
}
gameDuration--;
}

View File

@@ -8,26 +8,23 @@ import eu.okaeri.configs.annotation.Exclude;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.Utils;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.entity.Player;
@NoArgsConstructor
@Setter
public class LazyLocation extends OkaeriConfig {
public LazyLocation(String worldName) {
this.worldName = worldName;
}
@Comment("Name of the world. World will be automatically loaded.")
@CustomKey("world-name")
private String worldName;
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
private double x = 0.0;
private double y = 70.0;
private double z = 0.0;
private float yaw = 0.0f;
private float pitch = 0.0f;
@Exclude
private Location location;

View File

@@ -2,21 +2,24 @@ package hdvtdev.blockandseek.objects;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import lombok.Getter;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.stream.Collectors;
@Getter
public class PropBlock extends OkaeriConfig {
@Comment("Minecraft solid block. ")
@Comment("WARNING: BLOCKS AFFECTED BY GRAVITY, BLOCKS REQUIRING SUPPORT")
@Comment("OR SPECIFIC CONDITIONS (E.G. ADJACENT WATER), AND REDSTONE EMITTERS ARE NOT SUPPORTED.")
@Comment("USING THEM MAY CAUSE MAP CORRUPTION.")
private ItemStack block;
@Comment("Rarity of block. Available rarities:")
@Comment("COMMON, UNCOMMON, RARE, EPIC, MYTHIC, LEGENDARY")
private Rarity rarity = Rarity.COMMON;
public ItemStack getBlock() {
return block;
}
public Rarity getRarity() {
return rarity;
}
public PropBlock(ItemStack itemStack, Rarity rarity) {
this.block = itemStack;
this.rarity = rarity;

View File

@@ -17,4 +17,13 @@ public enum Rarity {
public int getChance() {
return weight;
}
public static Rarity getByDeviation(double percent) {
if (percent >= 0.90) return LEGENDARY;
if (percent >= 0.75) return MYTHIC;
if (percent >= 0.55) return EPIC;
if (percent >= 0.35) return RARE;
if (percent >= 0.15) return UNCOMMON;
return COMMON;
}
}

View File

@@ -4,6 +4,8 @@ import hdvtdev.blockandseek.managers.TranslationManager;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import java.util.Map;
public enum TranslationKey {
// Misc
CHORUS,
@@ -70,7 +72,8 @@ public enum TranslationKey {
DEATH_BELT,
ANTI_GRAVITY;
public Component translate(Player player, String... placeholders) {
@SafeVarargs
public final Component translate(Player player, Map.Entry<String, String>... placeholders) {
return TranslationManager.get(player, this, placeholders);
}