feat(lohub): Add complete configuration system with 6 config files
- Added messages.yml for all plugin messages with customizable prefix - Added commands.yml for custom commands (discord, website, store, rules, heal, clearinventory) - Added serverselector.yml for GUI server selector menu - Added data.yml for persistent data storage (chat_locked, spawn location) - Updated ConfigManager to load all config files - Updated ChatLockManager to use data.yml instead of chatlock.dat - Updated SpawnManager to use data.yml instead of spawn.yml - Updated all commands to use messages.yml - Updated listeners to use messages.yml - Removed duplicate config files from root directory - Added CONFIG_GUIDE.md documentation
This commit is contained in:
parent
102341bb2c
commit
1e5e8d8638
36 changed files with 1580 additions and 544 deletions
|
|
@ -0,0 +1,81 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MuteHistoryManager {
|
||||
|
||||
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public MuteHistoryManager(File historyFile) {
|
||||
this.historyFile = historyFile;
|
||||
load();
|
||||
}
|
||||
|
||||
public void addEntry(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> entries = history.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
entries.add(new MuteData.MuteHistoryEntry(
|
||||
playerName,
|
||||
duration,
|
||||
reason,
|
||||
mutedBy,
|
||||
System.currentTimeMillis(),
|
||||
false,
|
||||
null,
|
||||
0
|
||||
));
|
||||
}
|
||||
|
||||
public void updateUnmute(UUID uuid, String unmutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> entries = history.get(uuid);
|
||||
if (entries != null && !entries.isEmpty()) {
|
||||
MuteData.MuteHistoryEntry lastEntry = entries.get(entries.size() - 1);
|
||||
if (lastEntry.unmutedBy == null) {
|
||||
lastEntry.unmuted = true;
|
||||
lastEntry.unmutedBy = unmutedBy;
|
||||
lastEntry.unmutedAt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MuteData.MuteHistoryEntry> getHistory(UUID uuid) {
|
||||
return history.getOrDefault(uuid, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try (FileWriter writer = new FileWriter(historyFile)) {
|
||||
gson.toJson(history, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (!historyFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (FileReader reader = new FileReader(historyFile)) {
|
||||
Type type = new TypeToken<Map<UUID, List<MuteData.MuteHistoryEntry>>>() {
|
||||
}.getType();
|
||||
Map<UUID, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
history.putAll(loaded);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken;
|
|||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import com.loki.lochat.gradient.util.FoliaUtil;
|
||||
import com.loki.lochat.utils.TimeFormatter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
|
|
@ -14,23 +15,19 @@ import java.lang.reflect.Type;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса мутов
|
||||
*/
|
||||
public class MuteServiceImpl implements MuteService {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final Map<UUID, MuteData> mutes = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
|
||||
private final MuteHistoryManager historyManager;
|
||||
private final File dataFile;
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public MuteServiceImpl(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
|
||||
this.historyFile = new File(plugin.getDataFolder(), "mute-history.json");
|
||||
this.historyManager = new MuteHistoryManager(new File(plugin.getDataFolder(), "mute-history.json"));
|
||||
load();
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -39,9 +36,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
MuteData data = new MuteData(uuid, playerName, endTime, reason, mutedBy);
|
||||
mutes.put(uuid, data);
|
||||
|
||||
// Добавляем в историю
|
||||
addToHistory(uuid, playerName, duration, reason, mutedBy);
|
||||
|
||||
historyManager.addEntry(uuid, playerName, duration, reason, mutedBy);
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +44,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
public boolean unmute(UUID uuid, String unmutedBy) {
|
||||
MuteData removed = mutes.remove(uuid);
|
||||
if (removed != null) {
|
||||
updateHistoryUnmute(uuid, unmutedBy);
|
||||
historyManager.updateUnmute(uuid, unmutedBy);
|
||||
saveAsync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -59,9 +54,11 @@ public class MuteServiceImpl implements MuteService {
|
|||
@Override
|
||||
public boolean isMuted(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return false;
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime()) {
|
||||
if (isExpired(data)) {
|
||||
mutes.remove(uuid);
|
||||
saveAsync();
|
||||
return false;
|
||||
|
|
@ -71,66 +68,35 @@ public class MuteServiceImpl implements MuteService {
|
|||
|
||||
@Override
|
||||
public MuteData getMuteData(UUID uuid) {
|
||||
if (!isMuted(uuid)) return null;
|
||||
return mutes.get(uuid);
|
||||
return isMuted(uuid) ? mutes.get(uuid) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingTime(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return 0;
|
||||
if (data.getEndTime() == 0) return -1; // Перманентный
|
||||
if (data == null) {
|
||||
return 0;
|
||||
}
|
||||
if (data.getEndTime() == 0) {
|
||||
return -1;
|
||||
}
|
||||
return Math.max(0, data.getEndTime() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatTime(long millis) {
|
||||
if (millis <= 0) return "0с";
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
return hours + "ч " + (minutes % 60) + "м";
|
||||
} else if (minutes > 0) {
|
||||
return minutes + "м " + (seconds % 60) + "с";
|
||||
} else {
|
||||
return seconds + "с";
|
||||
}
|
||||
return TimeFormatter.format(millis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long parseTime(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||||
|
||||
timeStr = timeStr.toLowerCase();
|
||||
long multiplier = 1000; // базово в миллисекундах
|
||||
|
||||
try {
|
||||
if (timeStr.endsWith("d")) {
|
||||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("h")) {
|
||||
return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("m")) {
|
||||
return Long.parseLong(timeStr.replace("m", "")) * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("s")) {
|
||||
return Long.parseLong(timeStr.replace("s", "")) * multiplier;
|
||||
} else {
|
||||
return Long.parseLong(timeStr) * 60 * multiplier;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
return TimeFormatter.parse(timeStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxDuration(Player player) {
|
||||
if (player.hasPermission("lochat.mute.dur.perm")) {
|
||||
return 0; // Перманентный
|
||||
return 0;
|
||||
}
|
||||
|
||||
String[] durations = {"30d", "14d", "7d", "3d", "1d", "12h", "6h", "1h", "30m", "10m"};
|
||||
|
|
@ -140,7 +106,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
}
|
||||
|
||||
return -1; // Нет прав
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -154,37 +120,39 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
|
||||
long maxDuration = getMaxDuration(player);
|
||||
if (maxDuration == -1) return false;
|
||||
if (maxDuration == 0) return true;
|
||||
if (maxDuration == -1) {
|
||||
return false;
|
||||
}
|
||||
if (maxDuration == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return duration <= maxDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid) {
|
||||
return history.getOrDefault(uuid, new ArrayList<>());
|
||||
return historyManager.getHistory(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName) {
|
||||
List<MuteData.MuteHistoryEntry> result = new ArrayList<>();
|
||||
for (List<MuteData.MuteHistoryEntry> playerHistory : history.values()) {
|
||||
for (MuteData.MuteHistoryEntry entry : playerHistory) {
|
||||
if (entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName)) {
|
||||
result.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutes.keySet().forEach(uuid -> {
|
||||
historyManager.getHistory(uuid).stream()
|
||||
.filter(entry -> entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName))
|
||||
.forEach(result::add);
|
||||
});
|
||||
result.sort((a, b) -> Long.compare(b.mutedAt, a.mutedAt));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUIDByName(String name) {
|
||||
for (Map.Entry<UUID, List<MuteData.MuteHistoryEntry>> entry : history.entrySet()) {
|
||||
for (MuteData.MuteHistoryEntry historyEntry : entry.getValue()) {
|
||||
if (historyEntry.playerName != null && historyEntry.playerName.equalsIgnoreCase(name)) {
|
||||
return entry.getKey();
|
||||
for (UUID uuid : mutes.keySet()) {
|
||||
for (MuteData.MuteHistoryEntry entry : historyManager.getHistory(uuid)) {
|
||||
if (entry.playerName != null && entry.playerName.equalsIgnoreCase(name)) {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -193,10 +161,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
|
||||
@Override
|
||||
public Map<UUID, MuteData> getActiveMutes() {
|
||||
mutes.entrySet().removeIf(entry -> {
|
||||
MuteData data = entry.getValue();
|
||||
return data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime();
|
||||
});
|
||||
mutes.entrySet().removeIf(entry -> isExpired(entry.getValue()));
|
||||
return new HashMap<>(mutes);
|
||||
}
|
||||
|
||||
|
|
@ -212,23 +177,37 @@ public class MuteServiceImpl implements MuteService {
|
|||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
if (!dataFile.getParentFile().exists()) {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
|
||||
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(dataFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
ensureDataFolderExists();
|
||||
saveMutes();
|
||||
historyManager.save();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage());
|
||||
plugin.getLogger().warning("Error saving mutes: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExpired(MuteData data) {
|
||||
return data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime();
|
||||
}
|
||||
|
||||
private void ensureDataFolderExists() {
|
||||
if (!dataFile.getParentFile().exists()) {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMutes() throws IOException {
|
||||
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
|
||||
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(dataFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (!dataFile.exists()) return;
|
||||
if (!dataFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Reader reader = new FileReader(dataFile)) {
|
||||
Type type = new TypeToken<Map<String, MuteData>>() {
|
||||
|
|
@ -243,86 +222,11 @@ public class MuteServiceImpl implements MuteService {
|
|||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage());
|
||||
plugin.getLogger().warning("Error loading mutes: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadHistory() {
|
||||
if (!historyFile.exists()) return;
|
||||
|
||||
try (Reader reader = new FileReader(historyFile)) {
|
||||
Type type = new TypeToken<Map<String, List<MuteData.MuteHistoryEntry>>>() {
|
||||
}.getType();
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
history.put(UUID.fromString(key), new ArrayList<>(value));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHistory() {
|
||||
try {
|
||||
if (!historyFile.getParentFile().exists()) {
|
||||
historyFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> toSave = new ConcurrentHashMap<>();
|
||||
history.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(historyFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
playerHistory.add(new MuteData.MuteHistoryEntry(
|
||||
playerName,
|
||||
duration,
|
||||
reason,
|
||||
mutedBy,
|
||||
System.currentTimeMillis(),
|
||||
false,
|
||||
null,
|
||||
0
|
||||
));
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
private void updateHistoryUnmute(UUID uuid, String unmutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.get(uuid);
|
||||
if (playerHistory != null && !playerHistory.isEmpty()) {
|
||||
for (int i = playerHistory.size() - 1; i >= 0; i--) {
|
||||
MuteData.MuteHistoryEntry entry = playerHistory.get(i);
|
||||
if (!entry.unmuted) {
|
||||
entry.unmuted = true;
|
||||
entry.unmutedBy = unmutedBy;
|
||||
entry.unmutedAt = System.currentTimeMillis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
private void saveHistoryAsync() {
|
||||
FoliaUtil.runAsync(plugin, this::saveHistory);
|
||||
}
|
||||
|
||||
private void saveAsync() {
|
||||
FoliaUtil.runAsync(plugin, () -> {
|
||||
save();
|
||||
saveHistory();
|
||||
});
|
||||
FoliaUtil.runAsync(plugin, this::save);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
src/main/java/com/loki/lochat/utils/TimeFormatter.java
Normal file
59
src/main/java/com/loki/lochat/utils/TimeFormatter.java
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package com.loki.lochat.utils;
|
||||
|
||||
public final class TimeFormatter {
|
||||
|
||||
private TimeFormatter() {
|
||||
}
|
||||
|
||||
public static String format(long millis) {
|
||||
if (millis <= 0) {
|
||||
return "0с";
|
||||
}
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
return hours + "ч " + (minutes % 60) + "м";
|
||||
} else if (minutes > 0) {
|
||||
return minutes + "м " + (seconds % 60) + "с";
|
||||
} else {
|
||||
return seconds + "с";
|
||||
}
|
||||
}
|
||||
|
||||
public static long parse(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long totalMillis = 0;
|
||||
StringBuilder number = new StringBuilder();
|
||||
|
||||
for (char c : timeStr.toCharArray()) {
|
||||
if (Character.isDigit(c)) {
|
||||
number.append(c);
|
||||
} else if (number.length() > 0) {
|
||||
long value = Long.parseLong(number.toString());
|
||||
totalMillis += getMillisForUnit(c, value);
|
||||
number.setLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
return totalMillis;
|
||||
}
|
||||
|
||||
private static long getMillisForUnit(char unit, long value) {
|
||||
return switch (Character.toLowerCase(unit)) {
|
||||
case 's', 'с' -> value * 1000;
|
||||
case 'm', 'м' -> value * 60 * 1000;
|
||||
case 'h', 'ч' -> value * 60 * 60 * 1000;
|
||||
case 'd', 'д' -> value * 24 * 60 * 60 * 1000;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue