Add JSON storage support for clans, homes, and protected blocks

- Implemented the **Clan** class to load, reload, and save clan data (including chunk locations and ownership) using Gson and the Paper API.
 - Added the **Homes** class to manage player home locations, converting JSON-stored coordinates into Bukkit `Location` objects.
 - Created **ProtectedBlocks** and **ProtectedBlock** classes to handle protected block data (owner, key, and block location) from JSON.
This commit is contained in:
DaTTV 2025-02-16 18:35:41 +01:00
parent e7fc7f199f
commit d724d64334
12 changed files with 500 additions and 6 deletions

View file

@ -4,6 +4,10 @@ import org.bukkit.plugin.java.JavaPlugin;
public final class BlazeSMP extends JavaPlugin {
@Override
public void onLoad() {
}
@Override
public void onEnable() {
// Plugin startup logic

View file

@ -1,5 +1,212 @@
package me.freezy.plugins.papermc.blazesmp.module;
public class Clan {
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
@Getter
public class Clan {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final String STORAGE_PATH = "plugins/BlazeSMP/storage/clans/";
private static final Logger LOGGER = Logger.getLogger("Clan");
private final UUID uuid;
@Setter private String name;
@Setter private Component tag;
@Setter private UUID leaderUUID;
@Setter private UUID viceUUID;
private final LinkedList<UUID> members;
private final LinkedList<Chunk> chunks;
private final LinkedHashMap<Chunk, UUID> chunkOwnerMap;
private int chunkAmount;
public Clan(String name, Component tag, UUID leaderUUID) {
this.uuid = UUID.randomUUID();
this.name = name;
this.tag = tag;
this.leaderUUID = leaderUUID;
this.viceUUID = null;
this.members = new LinkedList<>();
this.chunks = new LinkedList<>();
this.chunkOwnerMap = new LinkedHashMap<>();
this.chunkAmount = 0;
}
public Clan(UUID clanUUID, String name, Component tag, UUID leaderUUID, UUID viceUUID,
LinkedList<UUID> members, LinkedList<Chunk> chunks, LinkedHashMap<Chunk, UUID> chunkOwnerMap) {
this.uuid = clanUUID;
this.name = name;
this.tag = tag;
this.leaderUUID = leaderUUID;
this.viceUUID = viceUUID;
this.members = members;
this.chunks = chunks;
this.chunkOwnerMap = chunkOwnerMap;
this.chunkAmount = chunks.size();
}
/**
* Loads a Clan from the JSON file corresponding to the given UUID.
*
* @param uuid The UUID of the clan.
* @return The loaded Clan or null if the file does not exist.
*/
public static Clan load(UUID uuid) {
File file = new File(STORAGE_PATH + uuid + ".json");
if (!file.exists()) {
LOGGER.warning("Clan file " + file.getAbsolutePath() + " does not exist.");
return null;
}
try (FileReader reader = new FileReader(file)) {
ClanJson jsonClan = GSON.fromJson(reader, ClanJson.class);
if (jsonClan == null) {
LOGGER.warning("Failed to parse clan JSON for UUID " + uuid);
return null;
}
Component tagComponent = MiniMessage.miniMessage().deserialize(jsonClan.tag);
UUID leader = (jsonClan.leader == null || jsonClan.leader.isEmpty()) ? null : UUID.fromString(jsonClan.leader);
UUID vice = (jsonClan.vize == null || jsonClan.vize.isEmpty()) ? null : UUID.fromString(jsonClan.vize);
// Convert members
LinkedList<UUID> memberUUIDs = new LinkedList<>();
if (jsonClan.members != null) {
for (String s : jsonClan.members) {
if (s != null && !s.isEmpty()) {
memberUUIDs.add(UUID.fromString(s));
}
}
}
// Process chunks with world information
LinkedList<Chunk> chunkList = new LinkedList<>();
LinkedHashMap<Chunk, UUID> chunkOwnerMap = new LinkedHashMap<>();
if (jsonClan.chunks != null && jsonClan.chunks.locations != null) {
for (LocationJson loc : jsonClan.chunks.locations) {
World world = Bukkit.getWorld(loc.world);
if (world == null) {
LOGGER.warning("World '" + loc.world + "' not found. Skipping chunk at " + loc.x + ", " + loc.z);
continue;
}
int x = Integer.parseInt(loc.x);
int z = Integer.parseInt(loc.z);
Chunk chunk = world.getChunkAt(x, z);
chunkList.add(chunk);
UUID ownerUUID = (loc.owner == null || loc.owner.isEmpty()) ? null : UUID.fromString(loc.owner);
chunkOwnerMap.put(chunk, ownerUUID);
}
}
Clan clan = new Clan(uuid, jsonClan.name, tagComponent, leader, vice, memberUUIDs, chunkList, chunkOwnerMap);
clan.chunkAmount = (jsonClan.chunks != null) ? jsonClan.chunks.amount : chunkList.size();
return clan;
} catch (IOException e) {
LOGGER.severe("Error loading clan: " + e.getMessage());
return null;
}
}
/**
* Reloads the clan from its corresponding JSON file.
*/
public void reload() {
Clan loaded = load(this.uuid);
if (loaded == null) {
LOGGER.warning("Failed to reload clan with UUID: " + this.uuid);
return;
}
this.name = loaded.name;
this.tag = loaded.tag;
this.leaderUUID = loaded.leaderUUID;
this.viceUUID = loaded.viceUUID;
this.members.clear();
this.members.addAll(loaded.members);
this.chunks.clear();
this.chunks.addAll(loaded.chunks);
this.chunkOwnerMap.clear();
this.chunkOwnerMap.putAll(loaded.chunkOwnerMap);
this.chunkAmount = loaded.chunkAmount;
}
/**
* Saves the clan data to its corresponding JSON file.
*/
public void save() {
ClanJson jsonClan = new ClanJson();
jsonClan.name = this.name;
jsonClan.tag = MiniMessage.miniMessage().serialize(this.tag);
jsonClan.leader = (this.leaderUUID == null) ? "" : this.leaderUUID.toString();
jsonClan.vize = (this.viceUUID == null) ? "" : this.viceUUID.toString();
jsonClan.members = this.members.stream().map(UUID::toString).toList();
// Prepare chunks JSON
jsonClan.chunks = new ChunksJson();
jsonClan.chunks.amount = this.chunkAmount;
jsonClan.chunks.locations = new LinkedList<>();
for (Chunk chunk : this.chunks) {
LocationJson loc = new LocationJson();
// Assuming the owner mapping may be null
UUID owner = this.chunkOwnerMap.getOrDefault(chunk, null);
loc.owner = (owner == null) ? "" : owner.toString();
// Store world name along with chunk coordinates
loc.world = chunk.getWorld().getName();
loc.x = String.valueOf(chunk.getX());
loc.z = String.valueOf(chunk.getZ());
jsonClan.chunks.locations.add(loc);
}
// Ensure directory exists
File dir = new File(STORAGE_PATH);
if (!dir.exists()) {
if (dir.mkdirs()) {
LOGGER.info("Successfully created folder structure!");
} else {
LOGGER.severe("Failed to create folder structure!");
}
}
File file = new File(dir, this.uuid + ".json");
try (FileWriter writer = new FileWriter(file)) {
GSON.toJson(jsonClan, writer);
} catch (IOException e) {
LOGGER.severe("Error saving clan: " + e.getMessage());
}
}
// Helper classes to represent the JSON structure
private static class ClanJson {
String name;
String tag;
String leader;
String vize;
List<String> members;
ChunksJson chunks;
}
private static class ChunksJson {
int amount;
List<LocationJson> locations;
}
private static class LocationJson {
String owner;
String world;
String x;
String z;
}
}

View file

@ -0,0 +1,11 @@
package me.freezy.plugins.papermc.blazesmp.module;
import lombok.Getter;
import org.bukkit.Location;
import java.util.UUID;
/**
* Represents a protected block with an owner, a key, and a location.
*/
public record ProtectedBlock(UUID owner, UUID key, Location location) {}

View file

@ -0,0 +1,124 @@
package me.freezy.plugins.papermc.blazesmp.module.manager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
public class Homes {
private static final String FILE_PATH = "plugins/BlazeSMP/storage/homes.json";
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Logger LOGGER = Logger.getLogger("Homes");
// Mapping of player UUID to their home location
@Getter private final LinkedHashMap<UUID, Location> homes;
public Homes() {
this.homes = new LinkedHashMap<>();
}
/**
* Loads homes from the JSON file.
*/
public void load() {
File file = new File(FILE_PATH);
if (!file.exists()) {
LOGGER.info("Homes file does not exist, a new one will be created upon saving.");
return;
}
try (FileReader reader = new FileReader(file)) {
// Use a TypeToken to handle the Map<String, LocationJson> structure
Type type = new TypeToken<Map<String, LocationJson>>() {}.getType();
Map<String, LocationJson> jsonMap = GSON.fromJson(reader, type);
if (jsonMap == null) {
return;
}
for (Map.Entry<String, LocationJson> entry : jsonMap.entrySet()) {
UUID uuid;
try {
uuid = UUID.fromString(entry.getKey());
} catch (IllegalArgumentException ex) {
LOGGER.warning("Invalid UUID key in homes file: " + entry.getKey());
continue;
}
LocationJson locJson = entry.getValue();
// Assume the default world "world" for homes
World world = Bukkit.getWorld("world");
if (world == null) {
LOGGER.warning("Default world 'world' not found. Skipping home for " + uuid);
continue;
}
try {
double x = Double.parseDouble(locJson.x);
double y = Double.parseDouble(locJson.y);
double z = Double.parseDouble(locJson.z);
float yaw = Float.parseFloat(locJson.yaw);
float pitch = Float.parseFloat(locJson.pitch);
Location location = new Location(world, x, y, z, yaw, pitch);
homes.put(uuid, location);
} catch (NumberFormatException ex) {
LOGGER.warning("Invalid number format for home of " + uuid + ": " + ex.getMessage());
}
}
} catch (IOException e) {
LOGGER.severe("Error loading homes: " + e.getMessage());
}
}
/**
* Saves the homes mapping to the JSON file.
*/
public void save() {
// Convert the homes map to a map of String keys (UUIDs) to LocationJson objects.
Map<String, LocationJson> jsonMap = new LinkedHashMap<>();
for (Map.Entry<UUID, Location> entry : homes.entrySet()) {
Location location = entry.getValue();
LocationJson locJson = new LocationJson();
locJson.x = String.valueOf(location.getX());
locJson.y = String.valueOf(location.getY());
locJson.z = String.valueOf(location.getZ());
locJson.yaw = String.valueOf(location.getYaw());
locJson.pitch = String.valueOf(location.getPitch());
jsonMap.put(entry.getKey().toString(), locJson);
}
File file = new File(FILE_PATH);
// Ensure the parent directory exists
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
if (parent.mkdirs()) {
LOGGER.info("Successfully created folder structure!");
} else {
LOGGER.severe("Failed to create folder structure!");
}
}
try (FileWriter writer = new FileWriter(file)) {
GSON.toJson(jsonMap, writer);
} catch (IOException e) {
LOGGER.severe("Error saving homes: " + e.getMessage());
}
}
/**
* Inner class representing the JSON structure for a location.
*/
private static class LocationJson {
String x;
String y;
String z;
String yaw;
String pitch;
}
}

View file

@ -0,0 +1,129 @@
package me.freezy.plugins.papermc.blazesmp.module.manager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.freezy.plugins.papermc.blazesmp.module.ProtectedBlock;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Manager class for loading and saving protected blocks.
*/
public class ProtectedBlocks {
private static final String FILE_PATH = "plugins/BlazeSMP/storage/protected_blocks.json";
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Logger LOGGER = Logger.getLogger("ProtectedBlocks");
// List of protected blocks
private final List<ProtectedBlock> blocks;
public ProtectedBlocks() {
this.blocks = new ArrayList<>();
}
public List<ProtectedBlock> getBlocks() {
return blocks;
}
/**
* Loads protected blocks from the JSON file.
*/
public void load() {
File file = new File(FILE_PATH);
if (!file.exists()) {
LOGGER.info("Protected blocks file does not exist, a new one will be created upon saving.");
return;
}
try (FileReader reader = new FileReader(file)) {
// Deserialize the JSON into a ProtectedBlocksJson object
ProtectedBlocksJson jsonData = GSON.fromJson(reader, ProtectedBlocksJson.class);
if (jsonData == null || jsonData.blocks == null) {
return;
}
blocks.clear();
for (BlockJson blockJson : jsonData.blocks) {
try {
UUID owner = (blockJson.owner == null || blockJson.owner.isEmpty())
? null : UUID.fromString(blockJson.owner);
UUID key = (blockJson.key == null || blockJson.key.isEmpty())
? null : UUID.fromString(blockJson.key);
// Use default world "world" since no world field is provided
World world = Bukkit.getWorld("world");
if (world == null) {
LOGGER.warning("Default world 'world' not found. Skipping block for owner: " + blockJson.owner);
continue;
}
double x = Double.parseDouble(blockJson.location.x);
double y = Double.parseDouble(blockJson.location.y);
double z = Double.parseDouble(blockJson.location.z);
Location location = new Location(world, x, y, z);
blocks.add(new ProtectedBlock(owner, key, location));
} catch (Exception e) {
LOGGER.warning("Error loading a protected block: " + e.getMessage());
}
}
} catch (IOException e) {
LOGGER.severe("Error loading protected blocks: " + e.getMessage());
}
}
/**
* Saves the protected blocks to the JSON file.
*/
public void save() {
ProtectedBlocksJson jsonData = new ProtectedBlocksJson();
jsonData.blocks = new ArrayList<>();
for (ProtectedBlock block : blocks) {
BlockJson blockJson = new BlockJson();
blockJson.owner = (block.owner() == null) ? "" : block.owner().toString();
blockJson.key = (block.key() == null) ? "" : block.key().toString();
blockJson.location = new LocationJson();
blockJson.location.x = String.valueOf(block.location().getX());
blockJson.location.y = String.valueOf(block.location().getY());
blockJson.location.z = String.valueOf(block.location().getZ());
jsonData.blocks.add(blockJson);
}
File file = new File(FILE_PATH);
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
if (parent.mkdirs()) {
LOGGER.info("Successfully created folder structure!");
} else {
LOGGER.severe("Failed to create folder structure!");
} }
try (FileWriter writer = new FileWriter(file)) {
GSON.toJson(jsonData, writer);
} catch (IOException e) {
LOGGER.severe("Error saving protected blocks: " + e.getMessage());
}
}
// Inner classes to match the JSON structure
private static class ProtectedBlocksJson {
List<BlockJson> blocks;
}
private static class BlockJson {
String owner;
String key;
LocationJson location;
}
private static class LocationJson {
String x;
String y;
String z;
}
}