添加嵌入式卡片,还没想好怎么用
This commit is contained in:
parent
ce84bcb20c
commit
721ebd6ad1
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user