小修改

This commit is contained in:
BRanulf 2025-06-02 13:08:59 +08:00
parent 550894e8e4
commit 651e7f69f7
3 changed files with 9 additions and 234 deletions

View File

@ -176,6 +176,7 @@ public class PlayerTimeMod implements ModInitializer {
} }
footer.append(Text.literal("§7共 " + lines.size() + " 位玩家")); footer.append(Text.literal("§7共 " + lines.size() + " 位玩家"));
// 死了
if (page < totalPages) { if (page < totalPages) {
int finalPage1 = page; int finalPage1 = page;

View File

@ -4,9 +4,14 @@
"version": "${version}", "version": "${version}",
"name": "玩家在线时长统计Mod", "name": "玩家在线时长统计Mod",
"description": "", "description": "",
"authors": [], "authors": [
"contact": {}, "BRanulf"
"license": "MIT", ],
"contact": {
"homepage": "https://git.branulf.top/Branulf",
"sources": "https://git.branulf.top/Branulf/playerOnlineTimeWeb"
},
"license": "GPL",
"environment": "server", "environment": "server",
"entrypoints": { "entrypoints": {
"main": [ "main": [

View File

@ -1,231 +0,0 @@
<!--下面全是嵌入式卡片的代码复制后找个位置直接粘贴就行个人建议body的最下面-->
<!--记得更改serverUrl地址端口后面建议别动-->
<div id="minecraft-card-container"></div>
<script>
// 感谢deepseek大爹
// ===== 配置 =====
const config = {
serverUrl: "http://localhost:60048/api/widget-data",
updateInterval: 5000,
cardWidth: "300px"
};
(function initCard() {
if(localStorage.getItem('mcCardClosed')) return;
const card = createCard();
document.getElementById('minecraft-card-container').appendChild(card);
setupDragAndClose(card);
fetchData(card).then(() => {
setInterval(() => fetchData(card), config.updateInterval);
});
setTimeout(() => card.style.display = 'block', 100);
})();
function createCard() {
const card = document.createElement('div');
card.id = 'mc-status-card';
card.style.cssText = `
position: fixed; width: ${config.cardWidth}; z-index: 99999;
right: 20px; bottom: 20px; border-radius: 12px; background: white;
box-shadow: 0 4px 20px rgba(0,0,0,0.15); font-family: 'Segoe UI', sans-serif;
overflow: hidden; transition: all 0.2s; display: none;
`;
card.innerHTML = `
<div class="card-header" style="cursor:move;background:linear-gradient(135deg,#6a11cb 0%,#2575fc 100%);
color:white;padding:12px 15px;position:relative;">
<span>服务器玩家状态</span>
<span class="close-btn" style="position:absolute;right:10px;top:10px;color:white;
opacity:0.8;cursor:pointer;transition:opacity 0.2s;">×</span>
</div>
<div class="card-body" style="padding:0;">
<div class="loading" style="padding:20px;text-align:center;">
<div style="width:20px;height:20px;margin:0 auto 10px;border:3px solid rgba(0,0,0,0.1);
border-radius:50%;border-top-color:#2575fc;animation:spin 1s linear infinite;"></div>
<p>连接服务器中...</p>
</div>
</div>
`;
return card;
}
async function fetchData(card) {
const body = card.querySelector('.card-body');
try {
// 方案1尝试直接请求如果同源
let data = await tryDirectFetch();
// 方案2如果失败则使用服务器端代理
if(!data) data = await tryServerProxy();
if(data) {
renderCard(body, data);
} else {
throw new Error('所有数据获取方式均失败');
}
} catch(error) {
body.innerHTML = `
<div style="padding:20px;text-align:center;">
<p style="color:#dc3545;">⚠️ 数据加载失败</p>
<small style="color:#6c757d;">${error.message}</small>
<button onclick="location.reload()" style="margin-top:10px;padding:5px 10px;
background:#f8f9fa;border:1px solid #ddd;border-radius:4px;cursor:pointer;">
重试
</button>
</div>
`;
console.error('数据获取失败:', error);
}
}
async function tryDirectFetch() {
try {
const response = await fetch(config.serverUrl, {
headers: { 'Accept': 'application/json' }
});
return await fetchWidgetData();
} catch {
return null;
}
}
async function fetchWidgetData() {
try {
const response = await fetch('http://localhost:60048/api/widget-data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('获取到的数据:', data);
return data;
} catch (error) {
console.error('获取widget数据时出错:', error);
return {
onlineCount: 0,
whitelistPlayers: [],
topPlayers: [],
timestamp: Date.now()
};
}
}
async function tryServerProxy() {
try {
// 备用的暂时不用deepseek建议留着
const proxyUrl = 'http:// ' +
encodeURIComponent(config.serverUrl);
const response = await fetch(proxyUrl);
return await response.json();
} catch {
return null;
}
}
function renderCard(container, data) {
container.innerHTML = `
<div style="display:flex;align-items:center;padding:10px 15px;border-bottom:1px solid rgba(0,0,0,0.05);">
<span style="font-size:1.2rem;margin-right:12px;color:#2575fc;">👥</span>
<span>在线玩家</span>
<span style="margin-left:auto;background:#0d6efd;color:white;
padding:0.25em 0.6em;border-radius:50rem;font-size:0.75em;">
${data.onlineCount}
</span>
</div>
${data.topPlayers?.length ? `
<div style="padding:8px 15px;background:#f8f9fa;">
<small><span style="color:#ffc107">🏆</span> 时长前三</small>
</div>
${data.topPlayers.map(p => renderPlayer(p)).join('')}
` : ''}
<div style="padding:8px 15px;background:#f8f9fa;">
<small><span style="color:#2575fc">ⓘ</span> 在线玩家</small>
</div>
<div style="max-height:150px;overflow-y:auto;">
${data.whitelistPlayers?.length ?
data.whitelistPlayers.map(p => renderPlayer(p)).join('') :
'<div style="padding:15px;text-align:center;color:#6c757d;font-style:italic">无在线玩家</div>'
}
</div>
<div style="padding:8px;text-align:center;color:#6c757d;font-size:0.8em;">
更新: ${formatTime(data.timestamp)}
</div>
`;
}
function renderPlayer(player) {
return `
<div style="display:flex;align-items:center;padding:8px 15px;border-bottom:1px solid rgba(0,0,0,0.05);">
<div style="width:24px;height:24px;border-radius:50%;background:#e9ecef;margin-right:10px;
display:flex;align-items:center;justify-content:center;font-size:12px;color:#495057;">
${player.name.charAt(0).toUpperCase()}
</div>
<span style="flex-grow:1;">${player.name}</span>
<span style="font-size:0.9rem;color:#6c757d;">${player.time}</span>
</div>
`;
}
function setupDragAndClose(card) {
const header = card.querySelector('.card-header');
let isDragging = false, offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
if(e.target.classList.contains('close-btn')) 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';
card.style.right = 'auto';
card.style.bottom = 'auto';
});
document.addEventListener('mouseup', () => {
isDragging = false;
card.style.cursor = 'grab';
});
card.querySelector('.close-btn').addEventListener('click', () => {
card.style.display = 'none';
localStorage.setItem('mcCardClosed', 'true');
});
}
function formatTime(timestamp) {
const now = Date.now();
const diff = (now - timestamp) / 1000;
if(diff < 60) return '刚刚';
if(diff < 3600) return `${Math.floor(diff/60)}分钟前`;
return `${Math.floor(diff/3600)}小时前`;
}
document.head.insertAdjacentHTML('beforeend', `
<style>
@keyframes spin { to { transform: rotate(360deg); } }
#mc-status-card:hover { transform: translateY(-2px); box-shadow: 0 6px 25px rgba(0,0,0,0.2); }
</style>
`);
</script>