重写缓存逻辑、优化在大量领地数据下的检索速度
Java CI-CD with Maven / build (push) Successful in 9m15s Details

This commit is contained in:
zhangyuheng 2024-05-29 00:57:54 +08:00
parent 1c6f9f410f
commit da10ce05e2
6 changed files with 164 additions and 118 deletions

View File

@ -6,7 +6,7 @@
<groupId>cn.lunadeer</groupId> <groupId>cn.lunadeer</groupId>
<artifactId>Dominion</artifactId> <artifactId>Dominion</artifactId>
<version>1.28.12-beta</version> <version>1.29.0-beta</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Dominion</name> <name>Dominion</name>

View File

@ -10,10 +10,7 @@ import de.bluecolored.bluemap.api.markers.MarkerSet;
import de.bluecolored.bluemap.api.math.Color; import de.bluecolored.bluemap.api.math.Color;
import de.bluecolored.bluemap.api.math.Shape; import de.bluecolored.bluemap.api.math.Shape;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class BlueMapConnect { public class BlueMapConnect {
public static void render() { public static void render() {
@ -22,14 +19,20 @@ public class BlueMapConnect {
} }
try { try {
BlueMapAPI.getInstance().ifPresent(api -> { BlueMapAPI.getInstance().ifPresent(api -> {
for (Map.Entry<String, List<Integer>> world_dominions : Cache.instance.getWorldDominions().entrySet()) { Map<String, List<DominionDTO>> world_dominions = new HashMap<>();
api.getWorld(world_dominions.getKey()).ifPresent(world -> { for (DominionDTO dominion : Cache.instance.getDominions()) {
if (!world_dominions.containsKey(dominion.getWorld())) {
world_dominions.put(dominion.getWorld(), new ArrayList<>());
}
world_dominions.get(dominion.getWorld()).add(dominion);
}
for (Map.Entry<String, List<DominionDTO>> d : world_dominions.entrySet()) {
api.getWorld(d.getKey()).ifPresent(world -> {
MarkerSet markerSet = MarkerSet.builder() MarkerSet markerSet = MarkerSet.builder()
.label("Dominion") .label("Dominion")
.build(); .build();
for (Integer id : world_dominions.getValue()) { for (DominionDTO dominion : d.getValue()) {
DominionDTO dominion = Cache.instance.getDominion(id);
Collection<Vector2d> vectors = new ArrayList<>(); Collection<Vector2d> vectors = new ArrayList<>();
vectors.add(new Vector2d(dominion.getX1() + 0.001, dominion.getZ1() + 0.001)); vectors.add(new Vector2d(dominion.getX1() + 0.001, dominion.getZ1() + 0.001));
vectors.add(new Vector2d(dominion.getX2() - 0.001, dominion.getZ1() + 0.001)); vectors.add(new Vector2d(dominion.getX2() - 0.001, dominion.getZ1() + 0.001));
@ -58,7 +61,7 @@ public class BlueMapConnect {
} }
for (BlueMapMap map : world.getMaps()) { for (BlueMapMap map : world.getMaps()) {
map.getMarkerSets().put(world_dominions.getKey() + "-" + markerSet.getLabel(), markerSet); map.getMarkerSets().put(d.getKey() + "-" + markerSet.getLabel(), markerSet);
} }
}); });
} }

View File

@ -2,6 +2,7 @@ package cn.lunadeer.dominion;
import cn.lunadeer.dominion.dtos.DominionDTO; import cn.lunadeer.dominion.dtos.DominionDTO;
import cn.lunadeer.dominion.dtos.Flag; import cn.lunadeer.dominion.dtos.Flag;
import cn.lunadeer.dominion.dtos.PlayerDTO;
import cn.lunadeer.dominion.dtos.PlayerPrivilegeDTO; import cn.lunadeer.dominion.dtos.PlayerPrivilegeDTO;
import cn.lunadeer.minecraftpluginutils.ParticleRender; import cn.lunadeer.minecraftpluginutils.ParticleRender;
import cn.lunadeer.minecraftpluginutils.Scheduler; import cn.lunadeer.minecraftpluginutils.Scheduler;
@ -18,6 +19,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static cn.lunadeer.dominion.DominionNode.getLocInDominionDTO;
public class Cache { public class Cache {
public Cache() { public Cache() {
@ -49,24 +52,23 @@ public class Cache {
private void loadDominionsExecution() { private void loadDominionsExecution() {
id_dominions = new ConcurrentHashMap<>(); id_dominions = new ConcurrentHashMap<>();
world_dominions = new ConcurrentHashMap<>(); world_dominion_tree = new ConcurrentHashMap<>();
dominion_children = new ConcurrentHashMap<>(); dominion_children = new ConcurrentHashMap<>();
List<DominionDTO> dominions = DominionDTO.selectAll(); List<DominionDTO> dominions = DominionDTO.selectAll();
Map<String, List<DominionDTO>> world_dominions = new HashMap<>();
for (DominionDTO d : dominions) { for (DominionDTO d : dominions) {
if (!dominion_children.containsKey(d.getId())) {
dominion_children.put(d.getId(), new ArrayList<>());
}
id_dominions.put(d.getId(), d);
if (!world_dominions.containsKey(d.getWorld())) { if (!world_dominions.containsKey(d.getWorld())) {
world_dominions.put(d.getWorld(), new ArrayList<>()); world_dominions.put(d.getWorld(), new ArrayList<>());
} }
world_dominions.get(d.getWorld()).add(d.getId()); world_dominions.get(d.getWorld()).add(d);
if (d.getParentDomId() != -1) { id_dominions.put(d.getId(), d);
if (!dominion_children.containsKey(d.getParentDomId())) { if (!dominion_children.containsKey(d.getParentDomId())) {
dominion_children.put(d.getParentDomId(), new ArrayList<>()); dominion_children.put(d.getParentDomId(), new ArrayList<>());
}
dominion_children.get(d.getParentDomId()).add(d.getId());
} }
dominion_children.get(d.getParentDomId()).add(d.getId());
}
for (Map.Entry<String, List<DominionDTO>> entry : world_dominions.entrySet()) {
world_dominion_tree.put(entry.getKey(), DominionNode.BuildNodeTree(-1, entry.getValue()));
} }
BlueMapConnect.render(); BlueMapConnect.render();
_last_update_dominion.set(System.currentTimeMillis()); _last_update_dominion.set(System.currentTimeMillis());
@ -117,64 +119,50 @@ public class Cache {
* @return 玩家当前所在领地 * @return 玩家当前所在领地
*/ */
public DominionDTO getPlayerCurrentDominion(Player player) { public DominionDTO getPlayerCurrentDominion(Player player) {
Integer dominion_id = player_current_dominion_id.get(player.getUniqueId()); Integer last_in_dom_id = player_current_dominion_id.get(player.getUniqueId());
DominionDTO dominion = null; DominionDTO last_dominion = null;
if (dominion_id != null) { if (last_in_dom_id != null) {
dominion = id_dominions.get(dominion_id); last_dominion = id_dominions.get(last_in_dom_id);
} }
if (dominion != null) { if (last_in_dom_id != null && dominion_children.get(last_in_dom_id).isEmpty() && isInDominion(last_dominion, player)) {
if (!isInDominion(dominion, player)) { // 如果玩家仍在领地内且领地没有子领地则直接返回
if (dominion.isTopDom()) { return last_dominion;
// Dominion.notification.info(player, "您已离开领地:%s", dominion.getName());
player.sendMessage(Component.text(dominion.getLeaveMessage()));
dominion = null;
} else {
// Dominion.notification.info(player, "您已离开子领地:%s", dominion.getName());
player.sendMessage(Component.text(dominion.getLeaveMessage()));
dominion = id_dominions.get(dominion.getParentDomId());
}
update_player_current_dominion(player, dominion);
} else {
// 如果在领地内则检查是否在子领地内
List<Integer> children = dominion_children.get(dominion.getId());
for (Integer child_id : children) {
DominionDTO child = id_dominions.get(child_id);
if (isInDominion(child, player)) {
dominion = child;
// Dominion.notification.info(player, "您正在进入子领地:%s", dominion.getName());
player.sendMessage(Component.text(dominion.getJoinMessage()));
update_player_current_dominion(player, dominion);
break;
}
}
}
} }
if (dominion == null) { DominionDTO current_dominion = getLocInDominionDTO(world_dominion_tree.get(player.getWorld().getName()), player.getLocation());
List<DominionDTO> in_dominions = getDominionsParentAndChildren(player.getLocation()); int last_dom_id = last_dominion == null ? -1 : last_dominion.getId();
if (in_dominions.size() != 0) { int current_dom_id = current_dominion == null ? -1 : current_dominion.getId();
dominion = in_dominions.get(0); if (last_dom_id == current_dom_id) {
// Dominion.notification.info(player, "您正在进入领地:%s", dominion.getName()); return last_dominion;
player.sendMessage(Component.text(dominion.getJoinMessage())); }
} if (last_dom_id != -1) {
update_player_current_dominion(player, dominion); // if (last_dominion.getParentDomId() == -1)
// Notification.info(player, "您已离开领地:%s", last_dominion.getName());
// else
// Notification.info(player, "您已离开子领地:%s", last_dominion.getName());
player.sendActionBar(Component.text(last_dominion.getLeaveMessage()));
}
if (current_dom_id != -1) {
// if (current_dominion.getParentDomId() == -1)
// Notification.info(player, "您正在进入领地:%s", current_dominion.getName());
// else
// Notification.info(player, "您正在进入子领地:%s", current_dominion.getName());
player.sendActionBar(Component.text(current_dominion.getJoinMessage()));
} }
return dominion;
}
private void update_player_current_dominion(Player player, DominionDTO dominion) { lightOrNot(player, current_dominion); // 发光检查
lightOrNot(player, dominion); // 发光检查 flyOrNot(player, current_dominion); // 飞行检查
flyOrNot(player, dominion); // 飞行检查 if (current_dominion == null) {
if (dominion == null) {
player_current_dominion_id.put(player.getUniqueId(), null); player_current_dominion_id.put(player.getUniqueId(), null);
return; return null;
} }
player_current_dominion_id.put(player.getUniqueId(), dominion.getId()); player_current_dominion_id.put(player.getUniqueId(), current_dominion.getId());
// show border // show border
if (dominion.getFlagValue(Flag.SHOW_BORDER)) { if (current_dominion.getFlagValue(Flag.SHOW_BORDER)) {
ParticleRender.showBoxFace(Dominion.instance, player, ParticleRender.showBoxFace(Dominion.instance, player,
dominion.getLocation1(), current_dominion.getLocation1(),
dominion.getLocation2()); current_dominion.getLocation2());
} }
return current_dominion;
} }
/** /**
@ -227,29 +215,24 @@ public class Cache {
} }
} }
private List<DominionDTO> getDominionsParentAndChildren(Location loc) { public DominionDTO getDominion(Location loc) {
// todo: 可能需要进一步优化性能考虑将领地按照mca文件分组减少遍历次数 List<DominionNode> tree = world_dominion_tree.get(loc.getWorld().getName());
long start = System.currentTimeMillis(); if (tree == null) return null;
String world = loc.getWorld().getName(); return getLocInDominionDTO(tree, loc);
List<Integer> dominions_id = world_dominions.get(world);
List<DominionDTO> in_dominions = new ArrayList<>();
if (dominions_id == null) return in_dominions;
for (Integer id : dominions_id) {
DominionDTO d = id_dominions.get(id);
if (isInDominion(d, loc)) {
in_dominions.add(d);
}
}
in_dominions.sort(Comparator.comparingInt(DominionDTO::getId));
long end = System.currentTimeMillis();
// XLogger.debug("getDominionsParentAndChildren: %d ms", end - start);
return in_dominions;
} }
public DominionDTO getDominion(Location loc) { public List<DominionNode> getDominionTreeByPlayer(String player_name) {
List<DominionDTO> in_dominions = getDominionsParentAndChildren(loc); List<DominionNode> dominionTree = new ArrayList<>();
if (in_dominions.size() == 0) return null; PlayerDTO player = PlayerDTO.select(player_name);
return in_dominions.get(in_dominions.size() - 1); if (player == null) return dominionTree;
for (List<DominionNode> tree : world_dominion_tree.values()) {
for (DominionNode node : tree) {
if (node.dominion.getOwner().equals(player.getUuid())) {
dominionTree.add(node);
}
}
}
return dominionTree;
} }
/** /**
@ -276,21 +259,6 @@ public class Cache {
z >= dominion.getZ1() && z <= dominion.getZ2(); z >= dominion.getZ1() && z <= dominion.getZ2();
} }
private static boolean isInDominion(@Nullable DominionDTO dominion, Location location) {
if (dominion == null) return false;
if (!Objects.equals(dominion.getWorld(), location.getWorld().getName())) return false;
double x = location.getX();
double y = location.getY();
double z = location.getZ();
return x >= dominion.getX1() && x <= dominion.getX2() &&
y >= dominion.getY1() && y <= dominion.getY2() &&
z >= dominion.getZ1() && z <= dominion.getZ2();
}
public Map<String, List<Integer>> getWorldDominions() {
return world_dominions;
}
public DominionDTO getDominion(Integer id) { public DominionDTO getDominion(Integer id) {
return id_dominions.get(id); return id_dominions.get(id);
} }
@ -312,7 +280,7 @@ public class Cache {
public static Cache instance; public static Cache instance;
private ConcurrentHashMap<Integer, DominionDTO> id_dominions; private ConcurrentHashMap<Integer, DominionDTO> id_dominions;
private ConcurrentHashMap<String, List<Integer>> world_dominions; // 所有领地 private ConcurrentHashMap<String, List<DominionNode>> world_dominion_tree;
private ConcurrentHashMap<UUID, ConcurrentHashMap<Integer, PlayerPrivilegeDTO>> player_uuid_to_privilege; // 玩家所有的特权 private ConcurrentHashMap<UUID, ConcurrentHashMap<Integer, PlayerPrivilegeDTO>> player_uuid_to_privilege; // 玩家所有的特权
private final Map<UUID, Integer> player_current_dominion_id; // 玩家当前所在领地 private final Map<UUID, Integer> player_current_dominion_id; // 玩家当前所在领地
private ConcurrentHashMap<Integer, List<Integer>> dominion_children; private ConcurrentHashMap<Integer, List<Integer>> dominion_children;

View File

@ -0,0 +1,62 @@
package cn.lunadeer.dominion;
import cn.lunadeer.dominion.dtos.DominionDTO;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class DominionNode {
public DominionDTO dominion;
public List<DominionNode> children = new ArrayList<>();
public static List<DominionNode> BuildNodeTree(Integer rootId, List<DominionDTO> dominions) {
List<DominionNode> dominionTree = new ArrayList<>();
for (DominionDTO dominion : dominions) {
if (Objects.equals(dominion.getParentDomId(), rootId)) {
DominionNode node = new DominionNode();
node.dominion = dominion;
node.children = BuildNodeTree(dominion.getId(), dominions);
dominionTree.add(node);
}
}
return dominionTree;
}
public static DominionNode getLocInDominionNode(@NotNull List<DominionNode> nodes, @NotNull Location loc) {
for (DominionNode node : nodes) {
if (isInDominion(node.dominion, loc)) {
if (node.children.isEmpty()) {
return node;
} else {
DominionNode childDominion = getLocInDominionNode(node.children, loc);
if (childDominion == null) {
return node;
} else {
return childDominion;
}
}
}
}
return null;
}
public static DominionDTO getLocInDominionDTO(@NotNull List<DominionNode> nodes, @NotNull Location loc) {
DominionNode dominionNode = getLocInDominionNode(nodes, loc);
return dominionNode == null ? null : dominionNode.dominion;
}
private static boolean isInDominion(@Nullable DominionDTO dominion, Location location) {
if (dominion == null) return false;
if (!Objects.equals(dominion.getWorld(), location.getWorld().getName())) return false;
double x = location.getX();
double y = location.getY();
double z = location.getZ();
return x >= dominion.getX1() && x <= dominion.getX2() &&
y >= dominion.getY1() && y <= dominion.getY2() &&
z >= dominion.getZ1() && z <= dominion.getZ2();
}
}

View File

@ -334,10 +334,6 @@ public class DominionDTO {
return parentDomId; return parentDomId;
} }
public boolean isTopDom() {
return parentDomId == -1;
}
public DominionDTO setParentDomId(Integer parentDomId) { public DominionDTO setParentDomId(Integer parentDomId) {
this.parentDomId = parentDomId; this.parentDomId = parentDomId;
return update(this); return update(this);

View File

@ -1,17 +1,21 @@
package cn.lunadeer.dominion.tuis; package cn.lunadeer.dominion.tuis;
import cn.lunadeer.dominion.Cache;
import cn.lunadeer.dominion.DominionNode;
import cn.lunadeer.minecraftpluginutils.stui.ListView; import cn.lunadeer.minecraftpluginutils.stui.ListView;
import cn.lunadeer.minecraftpluginutils.stui.ViewStyles;
import cn.lunadeer.minecraftpluginutils.stui.components.Button; import cn.lunadeer.minecraftpluginutils.stui.components.Button;
import cn.lunadeer.minecraftpluginutils.stui.components.Line; import cn.lunadeer.minecraftpluginutils.stui.components.Line;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static cn.lunadeer.dominion.commands.Apis.playerOnly; import static cn.lunadeer.dominion.commands.Apis.playerOnly;
import static cn.lunadeer.dominion.commands.Helper.playerAdminDominions; import static cn.lunadeer.dominion.commands.Helper.playerAdminDominions;
import static cn.lunadeer.dominion.commands.Helper.playerOwnDominions;
import static cn.lunadeer.dominion.tuis.Apis.getPage; import static cn.lunadeer.dominion.tuis.Apis.getPage;
public class ListDominion { public class ListDominion {
@ -20,20 +24,33 @@ public class ListDominion {
if (player == null) return; if (player == null) return;
int page = getPage(args); int page = getPage(args);
ListView view = ListView.create(10, "/dominion list"); ListView view = ListView.create(10, "/dominion list");
List<String> own_dominions = playerOwnDominions(sender); // 根据id从小到大排序
List<String> admin_dominions = playerAdminDominions(sender); List<String> admin_dominions = playerAdminDominions(sender);
view.title("我的领地列表"); view.title("我的领地列表");
view.navigator(Line.create().append(Button.create("主菜单").setExecuteCommand("/dominion menu").build()).append("我的领地")); view.navigator(Line.create().append(Button.create("主菜单").setExecuteCommand("/dominion menu").build()).append("我的领地"));
for (String dominion : own_dominions) { view.addLines(BuildTreeLines(Cache.instance.getDominionTreeByPlayer(player.getName()), 0));
TextComponent manage = Button.createGreen("管理").setExecuteCommand("/dominion manage " + dominion).build(); view.add(Line.create().append(Component.text("-= 以下为你拥有管理员权限的领地 =-", ViewStyles.main_color)));
TextComponent delete = Button.createRed("删除").setExecuteCommand("/dominion delete " + dominion).build();
view.add(Line.create().append(dominion).append(manage).append(delete));
}
for (String dominion : admin_dominions) { for (String dominion : admin_dominions) {
TextComponent manage = Button.createGreen("管理").setExecuteCommand("/dominion manage " + dominion).build(); TextComponent manage = Button.createGreen("管理").setExecuteCommand("/dominion manage " + dominion).build();
view.add(Line.create().append(dominion).append(manage)); view.add(Line.create().append(dominion).append(manage));
} }
view.showOn(player, page); view.showOn(player, page);
} }
private static List<Line> BuildTreeLines(List<DominionNode> dominionTree, Integer depth) {
List<Line> lines = new ArrayList<>();
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < depth; i++) {
prefix.append(" | ");
}
for (DominionNode node : dominionTree) {
TextComponent manage = Button.createGreen("管理").setExecuteCommand("/dominion manage " + node.dominion.getName()).build();
TextComponent delete = Button.createRed("删除").setExecuteCommand("/dominion delete " + node.dominion.getName()).build();
Line line = Line.create().append(prefix + node.dominion.getName()).append(manage).append(delete);
lines.add(line);
lines.addAll(BuildTreeLines(node.children, depth + 1));
}
return lines;
}
} }