DNF90 服务端与启动器完整源码(含 GM 工具功能增强)

服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
hanfuit committed 2026-11-04 14:31:55 +08:00
commit 645b64fe21
4388 files changed
+1405457

No files matched your search

+113
View File
@@ -0,0 +1,113 @@
// ============================================================
// DNF90 GM 工具 — API 客户端与通用工具
// ============================================================
const API = {
async request(method, path, body) {
const opts = { method, headers: {} };
if (body !== undefined) {
opts.headers['Content-Type'] = 'application/json';
opts.body = JSON.stringify(body);
}
const res = await fetch(path, opts);
let data;
try { data = await res.json(); }
catch (e) { data = { ok: false, error: '响应解析失败' }; }
if (!res.ok || data.ok === false) {
const err = new Error(data.error || `请求失败 (${res.status})`);
err.data = data;
throw err;
}
return data.data;
},
get(path) { return this.request('GET', path); },
post(path, body) { return this.request('POST', path, body ?? {}); },
};
// ---------- Toast ----------
function toast(msg, type = 'info', ms = 3200) {
let wrap = document.querySelector('.toast-wrap');
if (!wrap) {
wrap = document.createElement('div');
wrap.className = 'toast-wrap';
document.body.appendChild(wrap);
}
const el = document.createElement('div');
el.className = 'toast ' + type;
const icon = type === 'ok' ? '✓' : type === 'err' ? '✕' : 'ℹ';
el.innerHTML = `<span style="font-weight:700">${icon}</span><span>${escapeHtml(msg)}</span>`;
wrap.appendChild(el);
setTimeout(() => {
el.style.transition = 'opacity 0.3s, transform 0.3s';
el.style.opacity = '0';
el.style.transform = 'translateX(30px)';
setTimeout(() => el.remove(), 300);
}, ms);
}
function escapeHtml(s) {
if (s === null || s === undefined) return '';
return String(s).replace(/[&<>"']/g, c => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
}[c]));
}
function fmt(n) {
if (n === null || n === undefined || n === '') return '—';
const num = Number(n);
if (isNaN(num)) return String(n);
return num.toLocaleString('zh-CN');
}
function rarityColor(r) { return `rarity-${r}`; }
function rarityName(r) {
const names = ['普通', '高级', '稀有', '神器', '传说', '史诗', '神话'];
return names[r] || '未知';
}
// ---------- Modal ----------
function openModal({ title, body, foot, wide = false }) {
const mask = document.createElement('div');
mask.className = 'modal-mask';
mask.innerHTML = `
<div class="modal ${wide ? 'wide' : ''}">
<div class="modal-head">
<span>${escapeHtml(title)}</span>
<button class="close" data-close>×</button>
</div>
<div class="modal-body"></div>
<div class="modal-foot"></div>
</div>`;
const modal = mask.querySelector('.modal');
const bodyEl = mask.querySelector('.modal-body');
const footEl = mask.querySelector('.modal-foot');
if (typeof body === 'string') bodyEl.innerHTML = body;
else if (body) bodyEl.appendChild(body);
if (typeof foot === 'string') footEl.innerHTML = foot;
else if (foot) footEl.appendChild(foot);
const close = () => mask.remove();
mask.addEventListener('click', e => { if (e.target === mask) close(); });
mask.querySelector('[data-close]').addEventListener('click', close);
document.body.appendChild(mask);
return { mask, modal, bodyEl, footEl, close };
}
// ---------- Loading ----------
function loadingHtml(text = '加载中…') {
return `<div class="loading"><div class="spinner"></div><div>${escapeHtml(text)}</div></div>`;
}
// 连接状态
let CONN = { connected: false, base_url: '' };
function updateConnDot() {
const dots = document.querySelectorAll('.conn-dot');
dots.forEach(d => {
d.className = 'conn-dot ' + (CONN.connected ? 'on' : 'off');
});
const txt = document.querySelector('.conn-text');
if (txt) txt.textContent = CONN.connected
? `已连接 ${CONN.base_url || ''}`
: '未连接服务端';
}