添加嵌入式卡片,还没想好怎么用

This commit is contained in:
BRanulf 2025-04-14 20:49:11 +08:00
parent ce84bcb20c
commit 721ebd6ad1
4 changed files with 168 additions and 5 deletions

View File

@ -6,7 +6,7 @@ minecraft_version=1.21.4
yarn_mappings=1.21.4+build.8
loader_version=0.16.10
# Mod Properties
mod_version=1.14.514.107
mod_version=1.14.514.111
maven_group=org.example1
archives_base_name=playerOnlineTimeTrackerMod
# Dependencies

View File

@ -22,13 +22,13 @@ public class PlayerTimeMod implements ModInitializer {
try {
LOGGER.info("[在线时间] 初始化玩家在线时长视奸MOD");
// 修改服务器启动部分
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
timeTracker = new PlayerTimeTracker(server);
try {
// 使用配置中的端口
webServer = new WebServer(timeTracker, config.getWebPort());
webServer = new WebServer(timeTracker, config.getWebPort(), server); // 传入 MinecraftServer
webServer.start();
LOGGER.info("[在线时间] Web服务器在端口" + config.getWebPort()+ "启动");
LOGGER.info("[在线时间] Web服务器在端口 " + config.getWebPort() + " 启动");
} catch (Exception e) {
LOGGER.error("[在线时间] 无法启动Web服务器", e);
}

View File

@ -4,6 +4,7 @@ import com.google.gson.Gson;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import net.minecraft.server.MinecraftServer;
import java.io.*;
import java.net.InetSocketAddress;
@ -13,10 +14,13 @@ import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import static com.mojang.text2speech.Narrator.LOGGER;
public class WebServer {
private final HttpServer server;
private final PlayerTimeTracker timeTracker;
private final ExecutorService executor = Executors.newFixedThreadPool(4);
private final MinecraftServer minecraftServer;
private static final Map<String, String> MIME_TYPES = Map.of(
"html", "text/html",
"css", "text/css",
@ -24,7 +28,8 @@ public class WebServer {
"json", "application/json"
);
public WebServer(PlayerTimeTracker timeTracker, int port) throws IOException {
public WebServer(PlayerTimeTracker timeTracker, int port, MinecraftServer minecraftServer) throws IOException {
this.minecraftServer = minecraftServer;
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port number: " + port);
}
@ -88,6 +93,51 @@ public class WebServer {
}
});
server.createContext("/embed", exchange -> {
try {
Map<String, String> stats = timeTracker.getWhitelistedPlayerStats();
int onlinePlayers = minecraftServer.getPlayerManager().getCurrentPlayerCount(); // 使用 minecraftServer
String html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>服务器状态卡片</title>
</head>
<body>
<div class="server-card">
<div class="card-header">
<h3>%s 服务器状态</h3>
</div>
<div class="card-body">
<div class="player-count">在线玩家: %d/%d</div>
<div class="player-list">
%s
</div>
</div>
<div class="card-footer">
<div class="refresh-info">实时更新,忘了几分钟</div>
</div>
</div>
</body>
</html>
""".formatted(
minecraftServer.getServerMotd(), // 使用 minecraftServer
onlinePlayers,
minecraftServer.getPlayerManager().getMaxPlayerCount(),
generatePlayerList(stats)
);
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
sendResponse(exchange, 200, html.getBytes(StandardCharsets.UTF_8), "text/html");
} catch (Exception e) {
LOGGER.error("无法生成嵌入式卡片", e);
sendResponse(exchange, 500, Arrays.toString("Internal Server Error".getBytes(StandardCharsets.UTF_8)));
}
});
server.setExecutor(executor);
}
@ -111,4 +161,18 @@ public class WebServer {
server.stop(0);
executor.shutdown();
}
private String generatePlayerList(Map<String, String> stats) {
if (stats.isEmpty()) return "<p>暂无玩家数据</p>";
StringBuilder sb = new StringBuilder("<ul style='list-style-type: none; padding-left: 5px;'>");
stats.keySet().stream().limit(5).forEach(player -> {
sb.append("<li>• ").append(player).append("</li>");
});
if (stats.size() > 5) {
sb.append("<li>... 等 ").append(stats.size() - 5).append(" 位玩家</li>");
}
sb.append("</ul>");
return sb.toString();
}
}

View File

@ -6,6 +6,59 @@
<title>[在线时间] 玩家在线时间</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- 以下css是嵌入代码可以抄-->
<style>
.draggable-card {
position: fixed;
top: 20px;
left: 20px;
width: 300px;
height: 350px;
border-radius: 12px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
overflow: hidden;
resize: both;
z-index: 1000;
background: white;
}
.drag-handle {
background: linear-gradient(135deg, #1e88e5, #0d47a1);
color: white;
padding: 10px 16px;
cursor: move;
font-weight: bold;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.close-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
transition: background 0.2s;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.draggable-card iframe {
width: 100%;
height: calc(100% - 40px);
border: none;
}
</style>
</head>
<body>
<div class="container">
@ -38,5 +91,51 @@
</div>
</div>
<script src="js/app.js"></script>
<!--卡片本体-->
<div class="draggable-card" id="serverCard">
<div class="drag-handle">
<span>嵌入卡片代码f12可以抄</span>
<button class="close-btn" id="closeBtn">×</button>
</div>
<iframe src="http://localhost:60048/embed" frameborder="0"></iframe>
</div>
<!--卡片相关js脚本-->
<script>
const card = document.getElementById('serverCard');
const handle = card.querySelector('.drag-handle');
const closeBtn = document.getElementById('closeBtn');
let isDragging = false;
let offsetX, offsetY;
handle.addEventListener('mousedown', (e) => {
if (e.target === closeBtn) return;
isDragging = true;
offsetX = e.clientX - card.getBoundingClientRect().left;
offsetY = e.clientY - card.getBoundingClientRect().top;
card.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
card.style.left = (e.clientX - offsetX) + 'px';
card.style.top = (e.clientY - offsetY) + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
card.style.cursor = '';
});
closeBtn.addEventListener('click', () => {
card.style.display = 'none';
// setTimeout(() => { card.style.display = 'block'; }, 2000); // 关闭后超时重新显示,这里暂时放着,看需求
});
</script>
</body>
</html>