This commit is contained in:
parent
d3e2da1864
commit
bc9031383e
@ -30,7 +30,7 @@ jobs:
|
||||
echo "done!"
|
||||
- name: "setup go for release script"
|
||||
run: |
|
||||
wget --no-check-certificate https://go.dev/dl/go1.21.6.linux-amd64.tar.gz
|
||||
wget https://golang.google.cn/dl/go1.21.6.linux-amd64.tar.gz
|
||||
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.6.linux-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/bin/go
|
||||
go version
|
||||
|
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>cn.lunadeer</groupId>
|
||||
<artifactId>BetterChunkUnload</artifactId>
|
||||
<version>0.1</version>
|
||||
<version>1.7</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BetterChunkUnload</name>
|
||||
|
@ -3,9 +3,12 @@ package cn.lunadeer.betterchunkunload;
|
||||
import cn.lunadeer.betterchunkunload.utils.ChunkClear;
|
||||
import cn.lunadeer.betterchunkunload.utils.ConfigManager;
|
||||
import cn.lunadeer.betterchunkunload.utils.XLogger;
|
||||
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyValue;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -18,6 +21,15 @@ public final class BetterChunkUnload extends JavaPlugin {
|
||||
// Plugin startup logic
|
||||
instance = this;
|
||||
config = new ConfigManager(instance);
|
||||
scheduler = instance.getServer().getGlobalRegionScheduler();
|
||||
|
||||
// register events
|
||||
getServer().getPluginManager().registerEvents(new Events(), this);
|
||||
|
||||
// start tps checker
|
||||
scheduler.runAtFixedRate(instance, (instance) -> {
|
||||
tpsChecker();
|
||||
}, config.getTpsCheckIntervalTicks(), config.getTpsCheckIntervalTicks());
|
||||
|
||||
XLogger.info("BetterChunkUnload 已加载");
|
||||
XLogger.info("版本: " + getPluginMeta().getVersion());
|
||||
@ -39,12 +51,13 @@ public final class BetterChunkUnload extends JavaPlugin {
|
||||
|
||||
public static BetterChunkUnload instance;
|
||||
public static ConfigManager config;
|
||||
public static GlobalRegionScheduler scheduler;
|
||||
public static Boolean enable = true;
|
||||
private static final Map<String, ChunkList> chunkListMap = new HashMap<>();
|
||||
|
||||
public static ChunkList getChunkList(String playerName) {
|
||||
if (!chunkListMap.containsKey(playerName)) {
|
||||
chunkListMap.put(playerName, new ChunkList());
|
||||
chunkListMap.put(playerName, new ChunkList(playerName));
|
||||
}
|
||||
return chunkListMap.get(playerName);
|
||||
}
|
||||
@ -59,12 +72,29 @@ public final class BetterChunkUnload extends JavaPlugin {
|
||||
chunkListMap.remove(playerName);
|
||||
}
|
||||
|
||||
public static Boolean isChunkLoadByOtherPlayer(Chunk chunk) {
|
||||
for (ChunkList chunkList : chunkListMap.values()) {
|
||||
if (chunkList.contains(chunk)) {
|
||||
public static Boolean isChunkLoadByOtherPlayer(Chunk chunk, String player) {
|
||||
for (Map.Entry<String, ChunkList> entry : chunkListMap.entrySet()) {
|
||||
if (entry.getKey().equals(player)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getValue().contains(chunk)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void tpsChecker() {
|
||||
XLogger.debug("TPS 检查开始");
|
||||
if (!BetterChunkUnload.enable) {
|
||||
return;
|
||||
}
|
||||
if (BetterChunkUnload.instance.getServer().getTPS()[0] > BetterChunkUnload.config.getMinimumTpsThreshold()) {
|
||||
return;
|
||||
}
|
||||
XLogger.warn("TPS: " + BetterChunkUnload.instance.getServer().getTPS()[0] + " 低于 " + BetterChunkUnload.config.getMinimumTpsThreshold() + " 开始清理区块");
|
||||
for (ChunkList chunkList : BetterChunkUnload.getChunkList()) {
|
||||
chunkList.popFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,97 @@
|
||||
package cn.lunadeer.betterchunkunload;
|
||||
|
||||
import cn.lunadeer.betterchunkunload.utils.ChunkClear;
|
||||
import cn.lunadeer.betterchunkunload.utils.XLogger;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ChunkList {
|
||||
private final ArrayList<Chunk> _chunks = new ArrayList<>();
|
||||
|
||||
public ChunkList(String player) {
|
||||
_player = player;
|
||||
}
|
||||
|
||||
private final ArrayList<String> _chunks = new ArrayList<>();
|
||||
private final String _player;
|
||||
|
||||
public void pushBack(Chunk chunk) {
|
||||
if (BetterChunkUnload.config.getMaxDelayChunkPerPlayer() <= _chunks.size()) {
|
||||
popFront();
|
||||
}
|
||||
chunk.setForceLoaded(true);
|
||||
_chunks.add(chunk);
|
||||
BetterChunkUnload.scheduler.run(BetterChunkUnload.instance, (instance) -> {
|
||||
if (chunk.isForceLoaded()) {
|
||||
// skip force loaded chunk
|
||||
return;
|
||||
}
|
||||
if (contains(chunk)) {
|
||||
// skip duplicate chunk
|
||||
return;
|
||||
}
|
||||
if (BetterChunkUnload.config.getMaxDelayChunkPerPlayer() <= _chunks.size()) {
|
||||
popFront();
|
||||
}
|
||||
chunk.setForceLoaded(true);
|
||||
String chunkKey = getChunkKey(chunk);
|
||||
_chunks.add(chunkKey);
|
||||
XLogger.debug("ChunkList: " + chunk.getX() + " " + chunk.getZ() + " 加入列表");
|
||||
});
|
||||
}
|
||||
|
||||
public void popFront() {
|
||||
if (_chunks.isEmpty()) {
|
||||
return;
|
||||
BetterChunkUnload.scheduler.run(BetterChunkUnload.instance, (instance) -> {
|
||||
if (_chunks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String chunkKey = _chunks.get(0);
|
||||
Chunk chunk = getChunkFromKey(chunkKey);
|
||||
if (chunk != null) {
|
||||
if (!BetterChunkUnload.isChunkLoadByOtherPlayer(chunk, _player)) {
|
||||
chunk.setForceLoaded(false);
|
||||
XLogger.debug("ChunkList: " + chunk.getX() + " " + chunk.getZ() + " 从列表移除");
|
||||
}
|
||||
}
|
||||
_chunks.remove(0);
|
||||
});
|
||||
}
|
||||
|
||||
private static String getChunkKey(Chunk chunk) {
|
||||
return chunk.getWorld().getName() + ":" + chunk.getX() + ":" + chunk.getZ();
|
||||
}
|
||||
|
||||
private static Chunk getChunkFromKey(String chunkKey) {
|
||||
String[] chunkKeyArray = chunkKey.split(":");
|
||||
int chunkX = Integer.parseInt(chunkKeyArray[1]);
|
||||
int chunkZ = Integer.parseInt(chunkKeyArray[2]);
|
||||
World world = BetterChunkUnload.instance.getServer().getWorld(chunkKeyArray[0]);
|
||||
if (world != null) {
|
||||
return world.getChunkAt(chunkX, chunkZ);
|
||||
}
|
||||
Chunk chunk = _chunks.get(0);
|
||||
if (!BetterChunkUnload.isChunkLoadByOtherPlayer(chunk)) {
|
||||
chunk.setForceLoaded(false);
|
||||
}
|
||||
_chunks.remove(0);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clearAll() {
|
||||
XLogger.debug("ChunkList: 清空列表 size: " + _chunks.size());
|
||||
for (int i = 0; i < _chunks.size(); i++) {
|
||||
popFront();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearEntities() {
|
||||
for (Chunk chunk : _chunks) {
|
||||
for (String chunk_key : _chunks) {
|
||||
Chunk chunk = getChunkFromKey(chunk_key);
|
||||
ChunkClear.clearEntitiesWithoutCustomName(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearItems() {
|
||||
for (Chunk chunk : _chunks) {
|
||||
for (String chunk_key : _chunks) {
|
||||
Chunk chunk = getChunkFromKey(chunk_key);
|
||||
ChunkClear.clearItemDrops(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(Chunk chunk) {
|
||||
return _chunks.contains(chunk);
|
||||
String chunkKey = getChunkKey(chunk);
|
||||
return _chunks.contains(chunkKey);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.lunadeer.betterchunkunload;
|
||||
|
||||
import cn.lunadeer.betterchunkunload.utils.XLogger;
|
||||
import io.papermc.paper.event.packet.PlayerChunkLoadEvent;
|
||||
import io.papermc.paper.event.packet.PlayerChunkUnloadEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -18,11 +19,12 @@ public class Events implements Listener {
|
||||
}
|
||||
Player player = event.getPlayer();
|
||||
ChunkList chunkList = BetterChunkUnload.getChunkList(player.getName());
|
||||
chunkList.pushBack(event.getChunk());
|
||||
chunkList.pushBack(player.getChunk());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerLogout(PlayerQuitEvent event){
|
||||
XLogger.debug("玩家 " + event.getPlayer().getName() + " 退出游戏");
|
||||
Player player = event.getPlayer();
|
||||
BetterChunkUnload.removeChunkListOfPlayer(player.getName());
|
||||
}
|
||||
|
@ -1,34 +1,39 @@
|
||||
package cn.lunadeer.betterchunkunload.utils;
|
||||
|
||||
import cn.lunadeer.betterchunkunload.BetterChunkUnload;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
public class ChunkClear {
|
||||
public static void clearEntitiesWithoutCustomName(Chunk chunk) {
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
Entity[] entities = chunk.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
if (entity.getType().name().equals("DROPPED_ITEM")) {
|
||||
continue;
|
||||
BetterChunkUnload.scheduler.run(BetterChunkUnload.instance, (instance) -> {
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
if (entity.customName() == null) {
|
||||
entity.remove();
|
||||
Entity[] entities = chunk.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
if (entity.getType().name().equals("DROPPED_ITEM")) {
|
||||
continue;
|
||||
}
|
||||
if (entity.customName() == null) {
|
||||
entity.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void clearItemDrops(Chunk chunk) {
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
Entity[] entities = chunk.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
if (entity.getType().name().equals("DROPPED_ITEM")) {
|
||||
entity.remove();
|
||||
BetterChunkUnload.scheduler.run(BetterChunkUnload.instance, (instance) -> {
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Entity[] entities = chunk.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
if (entity.getType().name().equals("DROPPED_ITEM")) {
|
||||
entity.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@ public class ConfigManager {
|
||||
_debug = _file.getBoolean("Debug", false);
|
||||
_MaxDelayChunkPerPlayer = _file.getInt("MaxDelayChunkPerPlayer", 10);
|
||||
_MinimumTpsThreshold = (float) _file.getDouble("MinimumTpsThreshold", 15.0);
|
||||
_TpsCheckIntervalTicks = _file.getInt("TpsCheckIntervalTicks", 600);
|
||||
}
|
||||
|
||||
public Boolean isDebug() {
|
||||
@ -50,6 +51,16 @@ public class ConfigManager {
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
public Integer getTpsCheckIntervalTicks() {
|
||||
return _TpsCheckIntervalTicks;
|
||||
}
|
||||
|
||||
public void setTpsCheckIntervalTicks(Integer TpsCheckIntervalTicks) {
|
||||
_TpsCheckIntervalTicks = TpsCheckIntervalTicks;
|
||||
_file.set("TpsCheckIntervalTicks", TpsCheckIntervalTicks);
|
||||
_plugin.saveConfig();
|
||||
}
|
||||
|
||||
|
||||
private final BetterChunkUnload _plugin;
|
||||
private FileConfiguration _file;
|
||||
@ -57,4 +68,5 @@ public class ConfigManager {
|
||||
|
||||
private Integer _MaxDelayChunkPerPlayer;
|
||||
private Float _MinimumTpsThreshold;
|
||||
private Integer _TpsCheckIntervalTicks;
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
package cn.lunadeer.betterchunkunload.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class Scheduler {
|
||||
private static Boolean IS_FOLIA = null;
|
||||
|
||||
private static boolean tryFolia() {
|
||||
try {
|
||||
Bukkit.getAsyncScheduler();
|
||||
return true;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Boolean isFolia() {
|
||||
if (IS_FOLIA == null) IS_FOLIA = tryFolia();
|
||||
return IS_FOLIA;
|
||||
}
|
||||
|
||||
public static void runLaterEntity(Entity entity, Plugin plugin, Runnable runnable, int ticks) {
|
||||
if (isFolia()) entity.getScheduler().runDelayed(plugin, (task) -> runnable.run(), null, ticks);
|
||||
else Bukkit.getScheduler().runTaskLater(plugin, runnable, ticks);
|
||||
}
|
||||
|
||||
public static void runAtFixedRateEntity(Entity entity, Plugin plugin, Runnable runnable, int ticks) {
|
||||
if (isFolia()) entity.getScheduler().runAtFixedRate(plugin, (task) -> runnable.run(), null, ticks, ticks);
|
||||
else Bukkit.getScheduler().runTaskTimer(plugin, runnable, ticks, ticks);
|
||||
}
|
||||
|
||||
public static void runAtFixedRate(Plugin plugin, Runnable runnable, int ticks) {
|
||||
Bukkit.getScheduler().runTaskTimer(plugin, runnable, ticks, ticks);
|
||||
}
|
||||
}
|
@ -5,4 +5,6 @@ MaxDelayChunkPerPlayer: 10
|
||||
MinimumTpsThreshold: 15.0
|
||||
|
||||
# 每次检查tps的间隔 单位为tick 20tick=1s
|
||||
TpsCheckIntervalTicks: 600
|
||||
TpsCheckIntervalTicks: 600
|
||||
|
||||
Debug: false
|
Loading…
Reference in New Issue
Block a user