Skip to content

Commit

Permalink
活跃度更新
Browse files Browse the repository at this point in the history
现在TimeRecorder会综合多个指标统计活跃度。活跃度权重可在配置文件中调整。
  • Loading branch information
xia-mc committed Mar 26, 2024
1 parent 38a6cc1 commit 4575945
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 29 deletions.
1 change: 0 additions & 1 deletion src/main/java/top/infsky/timerecorder/TimeRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public void onInitialize() {
ServerLifecycleEvents.SERVER_STARTED.register(this::onServerStarted);
ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopping);
ServerLifecycleEvents.SERVER_STOPPED.register(this::onServerStopped);

}

public void init() {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/top/infsky/timerecorder/config/AddonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ public class AddonConfig extends AutoLoadTomlConfig {
private boolean allowOPBypassFilter = false;
@TableField(rightComment = "过滤消息部分")
private List<String> filterWords = List.of("fuck");
@TableField(rightComment = "允许计算OP活跃度 (Vanish状态下也会计算活跃度!)")
private boolean allowOPActiveCount = true;
@TableField(rightComment = "活跃度权重:移动距离")
private double moveDistanceWeight = 140;
@TableField(rightComment = "活跃度权重:拾起物品")
private double itemPickWeight = 375;
@TableField(rightComment = "活跃度权重:破坏方块")
private double blockBreakWeight = 450;
@TableField(rightComment = "活跃度权重:放置方块")
private double blockPlaceWeight = 500;
@TableField(rightComment = "活跃度权重:击杀生物")
private double entityKilledWeight = 600;

public AddonConfig() {
super(null);
Expand Down
48 changes: 40 additions & 8 deletions src/main/java/top/infsky/timerecorder/data/FamilyReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import oshi.util.tuples.Pair;
import top.infsky.timerecorder.log.LogUtils;

import java.util.*;
Expand All @@ -19,22 +20,26 @@ public static String getString(Map<UUID, PlayerData> dataMap) {

val longestOnlinePlayer = getLongestOnlinePlayer(dataMap, false);
val longestOnlineBot = getLongestOnlinePlayer(dataMap, true);
val mostActivePlayer = getMostActivePlayer(dataMap, false);
return String.format(
"""
----------服务器日报----------
玩家日活:%s | 机器人日活:%s
上线最久的玩家:%s %s
上线最久的机器人:%s %s
最活跃的玩家:%s %s
今日活跃玩家:
%s
感谢各位玩家对服务器做出的贡献!
""",
playerData.keySet().size(),
botData.keySet().size(),
longestOnlinePlayer.get(0),
(Long) longestOnlinePlayer.get(1) < 1200 ? (Long) longestOnlinePlayer.get(1) / 20 + "秒" : (Long) longestOnlinePlayer.get(1) / 1200 + "分钟",
longestOnlineBot.get(0),
(Long) longestOnlineBot.get(1) < 1200 ? (Long) longestOnlineBot.get(1) / 20 + "秒" : (Long) longestOnlineBot.get(1) / 1200 + "分钟",
longestOnlinePlayer.getA(),
longestOnlinePlayer.getB() < 1200 ? longestOnlinePlayer.getB() / 20 + "秒" : longestOnlinePlayer.getB() / 1200 + "分钟",
longestOnlineBot.getA(),
longestOnlineBot.getB() < 1200 ? longestOnlineBot.getB() / 20 + "秒" : longestOnlineBot.getB() / 1200 + "分钟",
mostActivePlayer.getA(),
mostActivePlayer.getB(),
getOnlinePlayerList(playerData)
);
}
Expand All @@ -44,17 +49,28 @@ public static String getString(@NotNull PlayerData data, boolean online, boolean
"""
§r§n§l%s§r: §7%s §r%s§r
§r上线时长:§7%s§r%s%s
§r活跃度:%s
§r移动距离:%s
§r捡起物品:%s
§r挖掘/放置方块:%s %s
§r击杀生物:%s
""",
isFakePlayer ? "机器人" : isOp ? "管理员" : "玩家",
data.getName(),
online ? "§a在线" : "§4离线",
data.getPlayTime() < 1200 ? data.getPlayTime() / 20 + "秒" : data.getPlayTime() / 1200 + "分钟",
isOp ? "\n §r使用指令:" : "",
isOp ? getCommandUsed(data) : ""
isOp ? getCommandUsed(data) : "",
data.getActive(),
data.statsObject.getCachedMoveDistance(),
data.statsObject.getCachedItemPick(),
data.statsObject.getCachedBlockBreak(),
data.statsObject.getCachedBlockPlace(),
data.statsObject.getCachedEntityKilled()
);
}

public static @NotNull @Unmodifiable List<Object> getLongestOnlinePlayer(@NotNull Map<UUID, PlayerData> dataMap, boolean isFakePlayer) {
public static @NotNull @Unmodifiable Pair<String, Long> getLongestOnlinePlayer(@NotNull Map<UUID, PlayerData> dataMap, boolean isFakePlayer) {
String name = null;
long maxTime = 0;

Expand All @@ -65,8 +81,24 @@ public static String getString(@NotNull PlayerData data, boolean online, boolean
}
}

if (name != null) return List.of(name, maxTime);
return List.of("无", 0L);
if (name != null) return new Pair<>(name, maxTime);
return new Pair<>("无", 0L);
}

public static @NotNull @Unmodifiable Pair<String, Float> getMostActivePlayer(@NotNull Map<UUID, PlayerData> dataMap, boolean isFakePlayer) {
String name = null;
float maxActive = 0;

for (PlayerData data : dataMap.values()) {
final float currentActive = data.getActive();
if (currentActive > maxActive && data.isFakePlayer() == isFakePlayer) {
name = data.getName();
maxActive = currentActive;
}
}

if (name != null) return new Pair<>(name, maxActive);
return new Pair<>("无", 0F);
}

public static @NotNull String getCommandUsed(@NotNull PlayerData data) {
Expand Down
43 changes: 24 additions & 19 deletions src/main/java/top/infsky/timerecorder/data/PlayerData.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package top.infsky.timerecorder.data;

import lombok.Getter;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.ServerStatsCounter;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.infsky.timerecorder.Utils;
import top.infsky.timerecorder.compat.CarpetCompat;
import top.infsky.timerecorder.config.ModConfig;
import top.infsky.timerecorder.data.mcstats.StatsObject;
import top.infsky.timerecorder.log.LogUtils;

import java.util.*;
Expand All @@ -22,6 +22,8 @@ public class PlayerData {
*/
@Nullable
public Player player; // 玩家
@Nullable
public ServerStatsCounter vanillaStats; // 原版统计信息

public String name; // 名字

Expand All @@ -33,6 +35,8 @@ public class PlayerData {

public long playTime; // 当天游玩tick数

public StatsObject statsObject; // 原版统计信息offset

public List<String> OPCommandUsed; // 当天使用OP指令的列表

public Deque<MessageObject> messageSent; // 玩家的消息历史
Expand All @@ -47,12 +51,8 @@ public PlayerData(@NotNull Player gamePlayer, boolean isFakePlayer) {
playTime = 0;
OPCommandUsed = new LinkedList<>();
messageSent = new LinkedBlockingDeque<>();

// TODO 1.0.3应移除
if (name.equals("Hatsuki")) { // 😭😭😭
player.sendSystemMessage(Component.literal("§b§lHatsuki,欢迎回来。"));
player.playSound(SoundEvent.createVariableRangeEvent(new ResourceLocation("entity.experience_orb.pickup")));
}
vanillaStats = ((ServerPlayer) player).getStats();
statsObject = new StatsObject((ServerPlayer) player, vanillaStats);
}

/**
Expand All @@ -77,14 +77,13 @@ public PlayerData(String name, UUID UUID, boolean OP, boolean fakePlayer, long p
}

public void playerBuilder() {
if (player != null) return;
assert Utils.getSERVER() != null;
try {
if (Utils.getSERVER() != null) {
if (player == null)
player = Utils.getSERVER().getPlayerList().getPlayer(uuid);
return;
}
throw new RuntimeException("意外的playerBuilder()当无任何有效连接");
} catch (RuntimeException e) {
if (vanillaStats == null && player != null)
vanillaStats = ((ServerPlayer) player).getStats();
} catch (Exception e) {
LogUtils.LOGGER.error(String.format("恢复玩家 %s 失败。", uuid), e);
}
}
Expand Down Expand Up @@ -114,10 +113,16 @@ public void onChat(int messageId, String message) {
}

/**
* 一天过完了
* 获取活跃度
*/
public void reset() {
playTime = 0;
OPCommandUsed = new LinkedList<>();
public float getActive() {
return (float) (
statsObject.getMoveDistance(vanillaStats) * ModConfig.INSTANCE.getAddon().getMoveDistanceWeight() +
((double) statsObject.getItemPick(vanillaStats)) * ModConfig.INSTANCE.getAddon().getItemPickWeight() +
((double) statsObject.getBlockBreak(vanillaStats)) * ModConfig.INSTANCE.getAddon().getBlockBreakWeight() +
((double) statsObject.getBlockPlace(vanillaStats)) * ModConfig.INSTANCE.getAddon().getBlockPlaceWeight() +
((double) statsObject.getEntityKilled(vanillaStats)) * ModConfig.INSTANCE.getAddon().getEntityKilledWeight()
) / playTime;

}
}
2 changes: 1 addition & 1 deletion src/main/java/top/infsky/timerecorder/data/StatsData.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public String getFullReport() {
}

if (result.isEmpty()) return "";
result.delete(result.length() - 3, result.length() - 1);
result.delete(result.length() - 2, result.length() - 1);
return result.toString();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package top.infsky.timerecorder.data.mcstats;

import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.*;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;

import java.util.List;

public class McStatsManager {
public static final List<ResourceLocation> moveList = List.of(Stats.WALK_ONE_CM, Stats.SPRINT_ONE_CM, Stats.CROUCH_ONE_CM,
Stats.SWIM_ONE_CM, Stats.WALK_ON_WATER_ONE_CM, Stats.WALK_UNDER_WATER_ONE_CM);

public static double getMoveDistance(ServerStatsCounter stats) {
double distance = 0;
for (ResourceLocation stat : moveList) {
distance += stats.getValue(Stats.CUSTOM.get(stat));
}
return distance / 100; // 转换到block
}

public static long getItemPick(ServerStatsCounter stats) {
long count = 0;
for (Stat<Item> stat : Stats.ITEM_PICKED_UP) {
count += stats.getValue(stat);
}
return count;
}

public static long getBlockBreak(ServerStatsCounter stats) {
long count = 0;
for (Stat<Block> stat : Stats.BLOCK_MINED) {
count += stats.getValue(stat);
}
return count;
}

public static long getBlockPlace(ServerStatsCounter stats) {
long count = 0;
for (Stat<Item> stat : Stats.ITEM_USED) {
if (stat.getValue() instanceof BlockItem)
count += stats.getValue(stat);
}
return count;
}

public static long getEntityKilled(ServerStatsCounter stats) {
long count = 0;
for (Stat<EntityType<?>> stat : Stats.ENTITY_KILLED) {
count += stats.getValue(stat);
}
return count;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package top.infsky.timerecorder.data.mcstats;

import lombok.Getter;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.ServerStatsCounter;
import top.infsky.timerecorder.config.ModConfig;

@Getter
public class StatsObject {
private final ServerPlayer player;
private final double startMoveDistance;
private double cachedMoveDistance = 0;
private final long startItemPick;
private long cachedItemPick = 0;
private final long startBlockBreak;
private long cachedBlockBreak = 0;
private final long startBlockPlace;
private long cachedBlockPlace = 0;
private final long startEntityKilled;
private long cachedEntityKilled = 0;

public StatsObject(ServerPlayer player, double startMoveDistance, long startItemPick, long startBlockBreak, long startBlockPlace, long startEntityKilled) {
this.player = player;
this.startMoveDistance = startMoveDistance;
this.startItemPick = startItemPick;
this.startBlockBreak = startBlockBreak;
this.startBlockPlace = startBlockPlace;
this.startEntityKilled = startEntityKilled;
}

public StatsObject(ServerPlayer player, ServerStatsCounter statsCounter) {
this(player, McStatsManager.getMoveDistance(statsCounter),
McStatsManager.getItemPick(statsCounter),
McStatsManager.getBlockBreak(statsCounter),
McStatsManager.getBlockPlace(statsCounter),
McStatsManager.getEntityKilled(statsCounter)
);
}

private boolean disabled() {
return !ModConfig.INSTANCE.getAddon().isAllowOPActiveCount() && player.hasPermissions(2);
}

public double getMoveDistance(ServerStatsCounter statsCounter) {
if (disabled()) return 0;
cachedMoveDistance = McStatsManager.getMoveDistance(statsCounter) - startMoveDistance;
return cachedMoveDistance;
}

public long getItemPick(ServerStatsCounter statsCounter) {
if (disabled()) return 0;
cachedItemPick = McStatsManager.getItemPick(statsCounter) - startItemPick;
return cachedItemPick;
}

public long getBlockBreak(ServerStatsCounter statsCounter) {
if (disabled()) return 0;
cachedBlockBreak = McStatsManager.getBlockBreak(statsCounter) - startBlockBreak;
return cachedBlockBreak;
}

public long getBlockPlace(ServerStatsCounter statsCounter) {
if (disabled()) return 0;
cachedBlockPlace = McStatsManager.getBlockPlace(statsCounter) - startBlockPlace;
return cachedBlockPlace;
}

public long getEntityKilled(ServerStatsCounter statsCounter) {
if (disabled()) return 0;
cachedEntityKilled = McStatsManager.getEntityKilled(statsCounter) - startEntityKilled;
return cachedEntityKilled;
}
}

0 comments on commit 4575945

Please sign in to comment.