Showing preview only (6,005K chars total). Download the full file or copy to clipboard to get everything.
Repository: miracleEverywhere/dst-management-platform-api
Branch: master
Commit: 88600c80cb38
Files: 251
Total size: 5.6 MB
Directory structure:
gitextract_wjok4e68/
├── .claude/
│ └── skills/
│ └── no-build/
│ └── SKILL.md
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows/
│ └── go.yml
├── .gitignore
├── CLAUDE.md
├── LICENSE
├── README.md
├── app/
│ ├── dashboard/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── logs/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── mod/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── platform/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── player/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── room/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── tools/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ └── user/
│ ├── handler.go
│ ├── i18n.go
│ ├── router.go
│ └── utils.go
├── database/
│ ├── dao/
│ │ ├── base.go
│ │ ├── composite.go
│ │ ├── globalSetting.go
│ │ ├── room.go
│ │ ├── roomSetting.go
│ │ ├── system.go
│ │ ├── uidMap.go
│ │ ├── user.go
│ │ └── world.go
│ ├── db/
│ │ ├── cache.go
│ │ └── database.go
│ └── models/
│ ├── globalSetting.go
│ ├── room.go
│ ├── roomSetting.go
│ ├── system.go
│ ├── uidMap.go
│ ├── user.go
│ └── world.go
├── docker/
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── entry-point.sh
├── docs/
│ └── README.md
├── dst/
│ ├── dst.go
│ ├── logs.go
│ ├── map.go
│ ├── mod.go
│ ├── player.go
│ ├── room.go
│ ├── utils.go
│ └── world.go
├── embedFS/
│ ├── dist/
│ │ ├── assets/
│ │ │ ├── CodeEditor-D4vpmGPS.css
│ │ │ ├── CodeEditor-D9V3e-IU.js
│ │ │ ├── ConfirmBox-Dl9KDCIE.css
│ │ │ ├── ConfirmBox-UdDnLY_r.js
│ │ │ ├── NavbarThemeSwitcher-DznimRXG.js
│ │ │ ├── NavbarThemeSwitcher-HlaVvm-i.css
│ │ │ ├── Result-DI11lCXq.js
│ │ │ ├── TimeRunning-BnGoYIbA.js
│ │ │ ├── VAlert-DhdDkcOZ.css
│ │ │ ├── VAlert-hOsVftqi.js
│ │ │ ├── VBadge-D7oDPA4h.js
│ │ │ ├── VBadge-Di9QvBJE.css
│ │ │ ├── VChip-DNNqEGWs.css
│ │ │ ├── VChip-pydKSkvr.js
│ │ │ ├── VCol-BCVfdzjO.js
│ │ │ ├── VContainer-DLwbkp_b.css
│ │ │ ├── VContainer-Dy0K6N_V.js
│ │ │ ├── VDataTable-BKhtqv_h.css
│ │ │ ├── VDataTable-BRoXfM0Y.js
│ │ │ ├── VDataTableServer-DQucjpu4.js
│ │ │ ├── VDialog-BRzUh5G-.css
│ │ │ ├── VDialog-BetymGlx.js
│ │ │ ├── VExpansionPanels-B0znoeId.js
│ │ │ ├── VExpansionPanels-Cp9PmzBO.css
│ │ │ ├── VForm-D9iQptwo.js
│ │ │ ├── VInput-D4WPJeZb.js
│ │ │ ├── VInput-DAKtY42E.css
│ │ │ ├── VList-Cjh13LKl.css
│ │ │ ├── VList-D_MCDEbx.js
│ │ │ ├── VListItemAction-1-e96EHC.js
│ │ │ ├── VMenu-CXNpVDF7.js
│ │ │ ├── VMenu-Ceirw4NI.css
│ │ │ ├── VNumberInput-C-YY_5Ww.css
│ │ │ ├── VNumberInput-uP7vEYUF.js
│ │ │ ├── VPagination-BEBHkR5c.js
│ │ │ ├── VPagination-DFfzvyji.css
│ │ │ ├── VRadioGroup-C71Ml1om.css
│ │ │ ├── VRadioGroup-DDJeM_aY.js
│ │ │ ├── VRow-CL8Cm54n.css
│ │ │ ├── VRow-DtJTiJKO.js
│ │ │ ├── VSelect-CxsUdWRP.js
│ │ │ ├── VSelect-D5ZZgyw5.css
│ │ │ ├── VSelectionControl-CJfnRnc-.js
│ │ │ ├── VSelectionControl-Cp4tgg3l.css
│ │ │ ├── VSkeletonLoader-ChbR7Nc0.js
│ │ │ ├── VSkeletonLoader-sthcA-pB.css
│ │ │ ├── VSlider-CGi8T5rU.js
│ │ │ ├── VSlider-D2HQrba8.css
│ │ │ ├── VSpacer-63T2Dgcr.css
│ │ │ ├── VSpacer-D758VKS2.js
│ │ │ ├── VSparkline-CxEGkfC3.js
│ │ │ ├── VSwitch-CJSeLO2L.js
│ │ │ ├── VSwitch-DCpyu0YQ.css
│ │ │ ├── VTable-BIlReic3.css
│ │ │ ├── VTable-BLAHjZfY.js
│ │ │ ├── VTabs-B4m-OW3A.js
│ │ │ ├── VTabs-CArruFCC.css
│ │ │ ├── VTextField-C23ADfJX.js
│ │ │ ├── VTextField-DflQVYjD.css
│ │ │ ├── VTimePicker-BiJhf8Qg.js
│ │ │ ├── VTimePicker-D-Nx0mCo.css
│ │ │ ├── VToolbar-BhFwV7qm.js
│ │ │ ├── VToolbar-CJ08_fms.css
│ │ │ ├── VTooltip-BV7Wxv--.js
│ │ │ ├── VTooltip-CZlyHzm0.css
│ │ │ ├── _..-D12iSKdb.css
│ │ │ ├── _...error_-C0r044-X.js
│ │ │ ├── access-C8mK6Ha9.js
│ │ │ ├── add-qw2GdfGL.js
│ │ │ ├── announce-aAv7G3Cd.js
│ │ │ ├── axios-DNCpvJag.js
│ │ │ ├── backup-D4Jzm4dn.js
│ │ │ ├── base-Dq3aguG-.js
│ │ │ ├── base-DzlHa8yA.css
│ │ │ ├── blank-CExWZhJD.css
│ │ │ ├── blank-CdkSWbzI.js
│ │ │ ├── chat-CR9ZeRZy.js
│ │ │ ├── chat-DNbpOz0I.js
│ │ │ ├── clean-C3opSXJz.js
│ │ │ ├── clean-gJusj0Uj.css
│ │ │ ├── colors-DoZdCu1D.js
│ │ │ ├── default-BQziqBIh.js
│ │ │ ├── default-CDmOr08l.css
│ │ │ ├── dialog-transition-B2t_G0u1.js
│ │ │ ├── download-BZbCclQJ.js
│ │ │ ├── download-CEIvObtd.css
│ │ │ ├── download-InxnfqWA.js
│ │ │ ├── eventBus-B8tShFcG.js
│ │ │ ├── filter-Dycv7ILy.js
│ │ │ ├── game-OoVHjMuc.js
│ │ │ ├── history-DUBuZMd3.js
│ │ │ ├── index-BRyNS1GG.js
│ │ │ ├── index-BSTHz1Bk.css
│ │ │ ├── index-BhZX0V6g.js
│ │ │ ├── index-Bt3JTD_C.js
│ │ │ ├── index-But_YWe_.js
│ │ │ ├── index-CBy8Y7rp.js
│ │ │ ├── index-CYxihrrz.css
│ │ │ ├── index-CnNpN6vG.js
│ │ │ ├── index-CxuhU37S.css
│ │ │ ├── index-D9Rb-nie.css
│ │ │ ├── index-Dkfp6G7x.js
│ │ │ ├── index-H_F1Plkt.js
│ │ │ ├── index-Nr3m5Q0F.js
│ │ │ ├── index-Vik-EOmM.js
│ │ │ ├── index-c6y4pbVT.js
│ │ │ ├── index-nsLxUtLe.css
│ │ │ ├── index-roZNrXH_.js
│ │ │ ├── levelDataSetting-1LIRScEY.js
│ │ │ ├── list-Ch7Aeo7N.js
│ │ │ ├── log-B7kZm_Kd.js
│ │ │ ├── log-DWr6Dcj-.css
│ │ │ ├── logs-N8GO9ZBg.js
│ │ │ ├── map-Co9ruaLK.js
│ │ │ ├── metrics-BLfS8pVr.js
│ │ │ ├── mod-3aGhFcVM.js
│ │ │ ├── mod-DUnttE3W.js
│ │ │ ├── mod-y4ETdFdW.js
│ │ │ ├── modInfo-B5E3tm9p.css
│ │ │ ├── modInfo-BFWwrZKU.js
│ │ │ ├── online-Dk4H0O4k.js
│ │ │ ├── overview-CXTE8d7i.js
│ │ │ ├── platform-DXQOU806.js
│ │ │ ├── player-CpbcBwUI.js
│ │ │ ├── player-DzdX72KE.js
│ │ │ ├── preview-B1dPoyad.js
│ │ │ ├── preview-B9nDbvgA.css
│ │ │ ├── room-BLsFZNUm.js
│ │ │ ├── room-BV8LbqUm.js
│ │ │ ├── room-CNrLW54E.css
│ │ │ ├── roomSetting-BQQnBtEO.js
│ │ │ ├── rooms-CPzS3Hz9.js
│ │ │ ├── runtime-CAq17H-b.js
│ │ │ ├── setting-BelOsTLn.css
│ │ │ ├── setting-CRpOCMwz.js
│ │ │ ├── settingForm-Dzr9qn6f.js
│ │ │ ├── settingForm-_XZZJq3p.css
│ │ │ ├── settings-9AVqaQTs.js
│ │ │ ├── singleLog-CUnXBn4A.js
│ │ │ ├── snapshot-BCtcS9MN.js
│ │ │ ├── ssrBoot-BL3GCGCL.js
│ │ │ ├── statistics-DfYUbrAR.js
│ │ │ ├── steam-D68KmYFv.js
│ │ │ ├── token-BDX2Jc5X.css
│ │ │ ├── token-HaL7aTBf.js
│ │ │ ├── tools-BtEQwbyL.js
│ │ │ ├── user-CRly-xJV.js
│ │ │ ├── user-u2SlWTrk.js
│ │ │ ├── vue3-count-to.esm-1l4iYIqi.js
│ │ │ ├── world-Dt9MkwG_.js
│ │ │ └── world-OOh0Ytp7.css
│ │ ├── index.html
│ │ └── loader.css
│ ├── embed.go
│ └── shell/
│ ├── manual_install.sh
│ └── manual_update.sh
├── go.mod
├── go.sum
├── logger/
│ └── logger.go
├── main.go
├── middleware/
│ └── middleware.go
├── run.sh
├── scheduler/
│ ├── global.go
│ ├── init.go
│ ├── jobs.go
│ ├── room.go
│ └── utils.go
├── server/
│ ├── console.go
│ ├── flags.go
│ └── server.go
└── utils/
├── acf.go
├── constants.go
├── crypto.go
├── getter.go
├── i18n.go
├── jwt.go
├── security.go
├── system.go
└── tools.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/skills/no-build/SKILL.md
================================================
---
name: no-build
description: 修改代码后禁止执行 go build、go run 等编译/运行命令。本项目目标平台是 Linux 服务器,代码修改后由用户自行部署测试。
---
# 禁止本地编译/运行
修改代码后**禁止**执行以下命令:
- `go build`
- `go run`
- `go test`
- `go install`
- `go vet`(需要编译)
- 任何会触发 Go 编译的命令
**原因**:
1. 目标平台是 Linux x86_64,代码修改后由用户自行部署到 Linux 服务器上测试
2. 已使用纯 Go 实现 SQLite 驱动,无需 CGO
**允许的操作**:
- `go fmt`(格式化,不编译)
- 静态分析工具(不涉及编译的)
- 编辑、搜索、查看代码
================================================
FILE: .editorconfig
================================================
# @see: http://editorconfig.org
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = tab # 缩进风格[tab | space]
indent_size = 4 # 缩进大小
max_line_length = 130 # 最大行长度
[*.md] # 表示仅对 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格[tab | space]
indent_size = 2 # 缩进大小
[*.yml]
indent_style = space # 缩进风格[tab | space]
indent_size = 2 # 缩进大小
[*.yaml]
indent_style = space # 缩进风格[tab | space]
indent_size = 2 # 缩进大小
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug上报
description: 遇到了问题,点我提交
title: "[Bug] "
labels:
- bug
assignees: miracleEverywhere
body:
- type: input
id: version
attributes:
label: DMP版本
description: 出现Bug的DMP版本
validations:
required: true
- type: input
id: os
attributes:
label: 系统版本
description: DMP部署在什么系统上
placeholder: 例如Ubuntu 24.04 LTS
validations:
required: true
- type: textarea
id: description
attributes:
label: bug描述
description: 注意,请尽量详细的描述此Bug的复现过程,并附带截图和日志,请勿直接上传日志文件,仅需粘贴相关日志
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: 额外信息
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 添加新功能吧
description: 说说你想要什么吧
title: "[Feature] "
labels:
- enhancement
assignees: miracleEverywhere
body:
- type: checkboxes
id: checklist
attributes:
label: 我
options:
- label: 我搜索了issue,并没有发现相同的
required: true
- label: 我觉得超过四分之一的人需要这个功能
required: true
- type: textarea
id: description
attributes:
label: 功能描述
description: 我倒要看看你有什么好点子
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: 额外信息
================================================
FILE: .github/workflows/go.yml
================================================
name: release-packages
on:
push:
tags: [ "v*" ]
env:
REGISTRY_SERVER_ADDRESS: ghcr.io/miracleeverywhere/dst-management-platform-api
jobs:
build-binary:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup Go 1.25.4
uses: actions/setup-go@v5
with:
go-version: 1.25.4
- name: build-linux
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
if: matrix.os == 'ubuntu-latest'
run: CGO_ENABLED=0 go build -ldflags '-s -w' -v -o ./dmp && tar zcf dmp.tgz dmp
- name: release
uses: softprops/action-gh-release@v1
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
with:
draft: true
token: ${{ secrets.GH_TOKEN }}
files: |
./*.tgz
build-image:
runs-on: ubuntu-latest
env:
TAG: ${{ github.sha }}
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Package Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.PACKAGE_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY_SERVER_ADDRESS }}
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
file: ./docker/Dockerfile
context: .
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }},${{ env.REGISTRY_SERVER_ADDRESS }}:${{ env.TAG }}
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .gitignore
================================================
.idea
.claude/settings.local.json
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project overview
DMP (Don't Starve Together Management Platform) is a Go web server that manages DST game servers. It provides a web UI for multi-room, multi-user game server administration — room creation, mod management, backup/restore, player tracking, and scheduled tasks (restart, keepalive, announcements).
## Build and run
```bash
# Build (static binary, no CGO)
CGO_ENABLED=0 go build -ldflags '-s -w' -v -o dmp
# Run
./dmp -bind 80 -dbpath ./data -level info
# CLI flags
# -bind HTTP port (default: 80)
# -dbpath SQLite database directory (default: ./data)
# -level Log level: debug, info, warn, error (default: info)
# -v Print version and exit
```
There are no tests in this repository.
## Architecture
### Startup flow (`server/server.go`)
1. Parse CLI flags
2. Initialize zap logger (writes to `logs/access.log` and `logs/runtime.log`)
3. Extract embedded files (LuaJIT libs, shell scripts) from `embedFS/` to disk
4. Open SQLite database via GORM + glebarez/sqlite driver, auto-migrate all models
5. Start the gocron scheduler with global + per-room jobs
6. Register Gin routes for all `app/` modules, serve embedded SPA frontend as static files
7. `gin.Run()`
### App layer (`app/`)
Each subdirectory is a feature module following the same pattern:
- `handler.go` — HTTP handler struct and methods. The `Handler` struct holds the DAOs it needs.
- `router.go` — `RegisterRoutes(r *gin.Engine)` attaches endpoints under `/v3/<module>/...`
- `utils.go` — Request/response types and helper logic specific to the module
- `i18n.go` — Module-specific i18n messages merged into the global `utils.I18n`
Routes use `middleware.TokenCheck()` for authenticated endpoints and `middleware.AdminOnly()` for admin-only endpoints. Token is passed via `X-DMP-TOKEN` header.
### DST game controller (`dst/`)
The `Game` struct wraps a room + worlds + settings and operates on the DST server via:
- **File I/O**: reads/writes cluster.ini, server.ini, modoverrides.lua, adminlist.txt, etc. under `~/.klei/DoNotStarveTogether/Cluster_<id>/`
- **Screen commands**: starts/stops worlds using `screen` sessions, sends Lua console commands via `screen -X stuff`
- **Lua parsing**: uses `yuin/gopher-lua` to parse `modinfo.lua` (mod configuration forms) and `modoverrides.lua` (mod enabled state and options)
- **Session reading**: reads DST binary session files through Lua VM to extract game stats (cycles, season, phase)
`NewGameController(room, worlds, setting, lang)` creates a ready-to-use controller. It initializes paths, world configs, player lists, and mod directories.
### Database
- **ORM**: GORM with `glebarez/sqlite` driver, SQLite in WAL mode, single-connection (SetMaxOpenConns=1)
- **Models** (`database/models/`): User, Room, World, RoomSetting, GlobalSetting, System, UidMap
- **DAO** (`database/dao/`): Generic `BaseDAO[T]` provides CRUD + paginated query; typed DAOs (UserDAO, RoomDAO, etc.) embed BaseDAO and add domain-specific queries
- **In-memory cache** (`database/db/cache.go`): JWT secret, players statistics (per-room player snapshots), players online time, system metrics (CPU/memory/disk/network), internet IP, mod download state
### Scheduler (`scheduler/`)
Uses `go-co-op/gocron`. Jobs are defined in `initJobs()` and managed dynamically:
- **Global jobs**: online player polling, system metrics collection, game update check, internet IP refresh, temp mod cleanup
- **Per-room jobs**: backup (multiple times/day), backup cleanup, scheduled restart, scheduled start/stop, keepalive (world crash detection via log timestamps), announcements
`UpdateJob()` and `DeleteJob()` allow dynamic job management at runtime when room settings change.
### Middleware (`middleware/`)
- `TokenCheck()` — validates `X-DMP-TOKEN` JWT, sets username/nickname/role in Gin context, auto-refreshes token when >50% expired
- `AdminOnly()` — rejects non-admin users (role != "admin")
- `CacheControl()` — sets 48-hour cache headers on static asset extensions
### EmbedFS (`embedFS/`)
Embeds the frontend SPA (`dist/`), LuaJIT shared libraries (`luajit/`), and shell scripts (`shell/`). The SPA is served via `gin-static` as the catch-all route; LuaJIT libs and scripts are extracted to disk at startup.
### Utils (`utils/`)
- `constants.go` — version, API prefix, paths, external API URLs
- `jwt.go` — JWT generation/validation with HS256
- `i18n.go` — request-scoped i18n via `X-I18n-Lang` header (zh/en), each app module registers its own messages
- `security.go` — `IsSafeString` (prevents command injection in world/screen names), `IsSafePath` (prevents path traversal)
- `getter.go` — obfuscated Steam API key and DST token retrieval
- Various helpers for file I/O, zip/unzip, bash command execution, system metrics (CPU/memory/disk/network)
## CI/CD
GitHub Actions in `.github/workflows/go.yml` triggers on `v*` tags:
- Builds static binary with `CGO_ENABLED=0`, creates `.tgz` and draft GitHub release
- Builds and pushes Docker image to `ghcr.io/miracleeverywhere/dst-management-platform-api`
Docker setup: multi-stage build (Go build → Ubuntu 24.04 runtime with screen + wget), entry point in `docker/entry-point.sh`.
## Key conventions
- Indentation: tabs (Go), spaces for YAML/markdown — see `.editorconfig`
- API prefix: `/v3/` (defined in `utils.ApiVersion`)
- All API responses use `{"code": 200, "message": "...", "data": ...}`; HTTP status is always 200, errors signaled by `code` field
- Logger is the global `logger.Logger` (zap SugaredLogger); use `*f` formatted methods: `logger.Logger.Errorf("msg, err: %v", err)`
- DST game state is controlled through the `dst.Game` controller — never manipulate DST files or screen sessions directly from handlers
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Miracle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
### [中文文档] | [[README](docs/README.md)]
# DMP 饥荒管理平台





[](https://zread.ai/miracleEverywhere/dst-management-platform-api)
<div>
<img src="docs/images/dmp.svg" height="200" alt="DMP"/>
<h3>:sparkling_heart: 支持多房间、多用户、多权限管理 :sparkling_heart:</h3>
<h3>:star2: 一键开启你的饥荒之旅 :star2:</h3>
<h3>:tada: <a href="https://docs.miraclesses.top/faq/group.html">欢迎加群</a> :tada:</h3>
</div>
---
## :strawberry: 详细文档
**项目主页请前往** [https://miraclesses.top](https://miraclesses.top)
主页包含作者和用户制作的视频教程、在线预览等
**项目文档请前往** [https://docs.miraclesses.top](https://miraclesses.top)
文档包含快速开始、详细文档等,萌新大佬均可无痛阅读
---
## :love_letter: 代码贡献
欢迎所有开发者贡献代码,以改善和扩展这个项目!
感谢下方开发者对本项目的代码贡献

---
## :cherries: 推广
[汉堡云服务器](https://hbyidc.com/recommend/OKkxTzgMP6k7),专为饥荒打造!官方合作商「汉堡云」带来高性能游戏服务器,首月低至6折!
- 云产品:NAT云(游戏云电脑)/ 云服务器ECS等多款上云产品
- 地域选择: 湖北襄阳 / 内蒙古呼和浩特 / 江苏宿迁 / 中国香港 / 美国洛杉矶
- CPU选择:从E5入门CPU到英特尔铂金CPU再到i9-12900K消费级CPU,均有涉猎
- 新用户尊享DMP渠道价,云服务器ECS/轻量云LS/游戏云GYC产品首月6折
**优惠码**:`DMPqd6666` [点我立即购买](https://hbyidc.com/recommend/OKkxTzgMP6k7)
[](https://hbyidc.com/recommend/OKkxTzgMP6k7)
---
## :balloon: 截图





---
## :sparkling_heart: 致谢
本项目[前端页面](https://github.com/miracleEverywhere/dst-management-platform-web)
基于[materio-vuetify-vuejs-admin-template-free](https://github.com/themeselection/materio-vuetify-vuejs-admin-template-free)二次开发,感谢开源
v1与v2版本基于[koi-ui](https://github.com/KoiKite/koi-ui)二次开发,感谢开源
感谢加速站点:
- [github.akams.cn](https://github.akams.cn/)
- [gh.llkk.cc](https://gh.llkk.cc/)
- [github.dpik.top](https://github.dpik.top/)
- [ghfast.top](https://ghfast.top/)
感谢[饥荒百科](https://dontstarve.huijiwiki.com/wiki/饥荒)
感谢[长鸽门徒](https://www.bilibili.com/opus/814456568458248259)提供的饥荒emoji字体
---
## :sparkles: 打赏
如果你觉得饥荒管理平台对你有帮助的话,可以请我喝一杯咖啡
[爱发电(https://afdian.com/a/miracle-dmp)](https://afdian.com/a/miracle-dmp)
================================================
FILE: app/dashboard/handler.go
================================================
package dashboard
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/scheduler"
"dst-management-platform-api/utils"
"fmt"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
func (h *Handler) execGamePost(c *gin.Context) {
type ReqForm struct {
Type string `json:"type"`
RoomID int `json:"roomID"`
WorldID int `json:"worldID"`
Extra string `json:"extra"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败:%v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
switch reqForm.Type {
case "startup":
// 启动
if reqForm.Extra == "all" {
err = game.StartAllWorld()
if err != nil {
logger.Logger.Errorf("启动失败:%v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "startup game fail"), "data": nil})
return
}
} else {
err = game.StartWorld(reqForm.WorldID)
if err != nil {
logger.Logger.Errorf("启动失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "startup game fail"), "data": nil})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "startup game success"), "data": nil})
return
case "shutdown":
// 关闭
if reqForm.Extra == "all" {
err = game.StopAllWorld()
if err != nil {
logger.Logger.Errorf("关闭失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "shutdown game fail"), "data": nil})
return
}
} else {
err = game.StopWorld(reqForm.WorldID)
if err != nil {
logger.Logger.Errorf("关闭失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "shutdown game fail"), "data": nil})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "shutdown game success"), "data": nil})
return
case "restart":
// 重启
_ = game.StopAllWorld()
err = game.StartAllWorld()
if err != nil {
logger.Logger.Errorf("启动失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "restart game fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "restart game success"), "data": nil})
return
case "update":
// 更新,需要管理员权限
role, _ := c.Get("role")
if role.(string) != "admin" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
go func() {
db.DstUpdating = true
updateCmd := fmt.Sprintf("cd ~/steamcmd && ./steamcmd.sh +login anonymous +force_install_dir ~/dst +app_update 343050 validate +quit")
_ = utils.BashCMD(updateCmd)
db.DstUpdating = false
// 如果需要重启,则重启激活的房间
var globalSettings models.GlobalSetting
err = h.globalSettingDao.GetGlobalSetting(&globalSettings)
if err != nil {
logger.Logger.Errorf("获取全局设置失败: %v", err)
return
}
if !globalSettings.AutoUpdateRestart {
return
}
roomBasic, err := h.roomDao.GetRoomBasic()
if err != nil {
logger.Logger.Errorf("获取全局房间信息失败: %v", err)
return
}
for _, rb := range *roomBasic {
if !rb.Status {
continue
}
room, worlds, roomSetting, err = dao.FetchGameInfo(rb.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败: %v", err)
continue
}
game = dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
_ = game.StopAllWorld()
_ = game.StartAllWorld()
time.Sleep(5 * time.Second)
}
}()
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "updating"), "data": nil})
return
case "reset":
if reqForm.Extra == "force" {
err = game.Reset(true)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "reset game fail"), "data": nil})
return
}
} else {
err = game.Reset(false)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "reset game fail"), "data": nil})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "reset game success"), "data": nil})
return
case "delete":
defer func(game *dst.Game, id int) {
// 清空世界中的存档数据后,启动该世界
err = game.StartWorld(id)
if err != nil {
logger.Logger.Errorf("启动世界失败: %v", err)
}
}(game, reqForm.WorldID)
// 先关闭世界
err = game.StopWorld(reqForm.WorldID)
if err != nil {
logger.Logger.Warnf("关闭世界失败,可能是该世界已被关闭: %v", err)
}
err = game.DeleteWorld(reqForm.WorldID)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete game fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete game success"), "data": nil})
return
case "announce":
if reqForm.Extra == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "announce fail"), "data": nil})
return
}
err = game.Announce(reqForm.Extra)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "announce fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "announce success"), "data": nil})
return
case "systemMsg":
if reqForm.Extra == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "system msg fail"), "data": nil})
return
}
err = game.SystemMsg(reqForm.Extra)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "system msg fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "system msg success"), "data": nil})
return
case "console":
if reqForm.Extra == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "exec fail"), "data": nil})
return
}
err = game.ConsoleCmd(reqForm.Extra, reqForm.WorldID)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "exec fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "exec success"), "data": nil})
return
default:
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
}
func (h *Handler) infoBaseGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
type GameWorldInfo struct {
*models.World
Status bool `json:"status"`
PerformanceStatus dst.PerformanceStatus `json:"performanceStatus"`
}
var gameWorldInfo []GameWorldInfo
for _, world := range *worlds {
gameWorldInfo = append(gameWorldInfo, GameWorldInfo{
World: &world,
Status: game.WorldUpStatus(world.ID),
PerformanceStatus: game.WorldPerformanceStatus(world.ID),
})
}
type Data struct {
Room models.Room `json:"room"`
Worlds []GameWorldInfo `json:"worlds"`
RoomSetting models.RoomSetting `json:"roomSetting"`
Session dst.RoomSessionInfo `json:"session"`
Players []db.PlayerInfo `json:"players"`
}
db.PlayersStatisticMutex.Lock()
defer db.PlayersStatisticMutex.Unlock()
var players []db.PlayerInfo
if len(db.PlayersStatistic[reqForm.RoomID]) > 0 {
players = db.PlayersStatistic[reqForm.RoomID][len(db.PlayersStatistic[reqForm.RoomID])-1].PlayerInfo
} else {
players = []db.PlayerInfo{}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": Data{
Room: *room,
Worlds: gameWorldInfo,
RoomSetting: *roomSetting,
Session: *game.SessionInfo(),
Players: players,
}})
}
func (h *Handler) infoSysGet(c *gin.Context) {
type Data struct {
Cpu float64 `json:"cpu"`
Memory float64 `json:"memory"`
Updating bool `json:"updating"`
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": Data{
Cpu: utils.CpuUsage(),
Memory: utils.MemoryUsage(),
Updating: db.DstUpdating,
}})
}
func (h *Handler) connectionCodeGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var (
reqForm ReqForm
err error
)
if err = c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
var connectionCode string
if roomSetting.CustomIP == "" {
// 返回默认直连代码
var (
internetIp string
masterPort int
)
if db.InternetIP == "" {
internetIp, err = scheduler.GetInternetIP1()
if err != nil {
logger.Logger.Warnf("调用公网ip接口1失败: %v", err)
internetIp, err = scheduler.GetInternetIP2()
if err != nil {
logger.Logger.Warnf("调用公网ip接口2失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "connection code fail"), "data": nil})
return
}
}
db.InternetIP = internetIp
} else {
logger.Logger.Debug("发现缓存的公网IP")
internetIp = db.InternetIP
}
for _, world := range *worlds {
if world.IsMaster {
masterPort = world.ServerPort
}
}
if masterPort == 0 {
masterPort = (*worlds)[0].ServerPort
}
if room.Password == "" {
connectionCode = fmt.Sprintf("c_connect('%s', %d)", internetIp, masterPort)
} else {
connectionCode = fmt.Sprintf("c_connect('%s', %d, '%s')", internetIp, masterPort, room.Password)
}
} else {
// 返回自定义直连代码
if room.Password == "" {
connectionCode = fmt.Sprintf("c_connect('%s', %d)", roomSetting.CustomIP, roomSetting.CustomPort)
} else {
connectionCode = fmt.Sprintf("c_connect('%s', %d, '%s')", roomSetting.CustomIP, roomSetting.CustomPort, room.Password)
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": connectionCode})
}
func (h *Handler) connectionCodePut(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
IP string `json:"ip"`
Port int `json:"port"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
roomSetting.CustomIP = reqForm.IP
roomSetting.CustomPort = reqForm.Port
err = h.roomSettingDao.UpdateRoomSetting(roomSetting)
if err != nil {
logger.Logger.Errorf("修改房间设置失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "update fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "update success"), "data": nil})
}
func checkLobbyPost(c *gin.Context) {
type ReqForm struct {
GameName string `json:"gameName"`
MaxPlayer int `json:"maxPlayer"`
Regions []string `json:"regions"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.GameName == "" || reqForm.MaxPlayer == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
var urls []string
for _, region := range reqForm.Regions {
urls = append(urls, getDSTRoomsApi(region))
}
rooms, err := checkDstLobbyRoom(urls, reqForm.GameName)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "check lobby fail"), "data": false})
return
}
for _, room := range rooms {
if room.MaxConnections == reqForm.MaxPlayer {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": true})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": false})
}
================================================
FILE: app/dashboard/i18n.go
================================================
package dashboard
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["startup game fail"] = "启动失败"
i.ZH["startup game success"] = "启动成功"
i.ZH["shutdown game fail"] = "关闭失败"
i.ZH["shutdown game success"] = "关闭成功"
i.ZH["restart game fail"] = "重启失败"
i.ZH["restart game success"] = "重启成功"
i.ZH["updating"] = "更新中,请耐心等待"
i.ZH["reset game fail"] = "重置失败"
i.ZH["reset game success"] = "重置成功"
i.ZH["delete game fail"] = "清空世界失败"
i.ZH["delete game success"] = "清空世界成功"
i.ZH["announce fail"] = "宣告失败"
i.ZH["announce success"] = "宣告成功"
i.ZH["system msg fail"] = "通知失败"
i.ZH["system msg success"] = "通知成功"
i.ZH["exec fail"] = "执行失败"
i.ZH["exec success"] = "执行成功"
i.ZH["connection code fail"] = "直连代码获取失败"
i.ZH["check lobby fail"] = "检查世界失败"
i.EN["startup game fail"] = "Startup Fail"
i.EN["startup game success"] = "Startup Success"
i.EN["shutdown game fail"] = "Shutdown Fail"
i.EN["shutdown game success"] = "Shutdown Success"
i.EN["restart game fail"] = "Restart Fail"
i.EN["restart game success"] = "Restart Success"
i.EN["updating"] = "Updating, please wait patiently"
i.EN["reset game fail"] = "Reset Fail"
i.EN["reset game success"] = "Reset Success"
i.EN["delete game fail"] = "Delete Fail"
i.EN["delete game success"] = "Delete Success"
i.EN["announce fail"] = "Announce Fail"
i.EN["announce success"] = "Announce Success"
i.EN["system msg fail"] = "System Message Send Fail"
i.EN["system msg success"] = "System Message Send Success"
i.EN["exec fail"] = "Execute Fail"
i.EN["exec success"] = "Execute Success"
i.EN["connection code fail"] = "Get Connection Code Fail"
i.EN["check lobby fail"] = "Check Lobby Fail"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/dashboard/router.go
================================================
package dashboard
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
dashboard := v.Group("dashboard")
dashboard.Use(middleware.TokenCheck())
{
dashboard.POST("/exec/game", h.execGamePost)
dashboard.GET("/info/base", h.infoBaseGet)
dashboard.GET("/info/sys", h.infoSysGet)
dashboard.GET("/connection_code", h.connectionCodeGet)
dashboard.PUT("/connection_code", h.connectionCodePut)
dashboard.POST("/check/lobby", checkLobbyPost)
}
}
}
================================================
FILE: app/dashboard/utils.go
================================================
package dashboard
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
)
type Handler struct {
userDao *dao.UserDAO
roomDao *dao.RoomDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
globalSettingDao *dao.GlobalSettingDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO, globalSettingDao *dao.GlobalSettingDAO) *Handler {
return &Handler{
userDao: userDao,
roomDao: roomDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
globalSettingDao: globalSettingDao,
}
}
func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
role, _ := c.Get("role")
username, _ := c.Get("username")
// 管理员直接返回true
if role.(string) == "admin" {
return true
} else {
dbUser, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Error("查询数据库失败")
return false
}
roomIDs := strings.Split(dbUser.Rooms, ",")
for _, id := range roomIDs {
if id == roomID {
return true
}
}
}
return false
}
func getDSTRoomsApi(region string) string {
return fmt.Sprintf("https://lobby-v2-cdn.klei.com/%s-Steam.json.gz", region)
}
type Room struct {
Name string `json:"name"`
MaxConnections int `json:"maxconnections"`
}
type NeededResponse struct {
GET []Room `json:"GET"`
}
func checkDstLobbyRoom(urls []string, clusterName string) ([]Room, error) {
var (
mu sync.Mutex
wg sync.WaitGroup
rooms []Room
errChanel = make(chan error, len(urls))
)
client := &http.Client{
Timeout: utils.HttpTimeout * time.Second,
}
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
resp, err := client.Get(u)
if err != nil {
logger.Logger.Errorf("请求失败: %v, url: %s", err, u)
errChanel <- err
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
logger.Logger.Warnf("非200相应,跳过, url: %s", u)
errChanel <- fmt.Errorf("非200响应")
return
}
var neededResponse NeededResponse
if err := json.NewDecoder(resp.Body).Decode(&neededResponse); err != nil {
logger.Logger.Errorf("解析JSON失败: %v", err)
errChanel <- err
return
}
mu.Lock()
for _, room := range neededResponse.GET {
if room.Name == clusterName {
rooms = append(rooms, room)
}
}
mu.Unlock()
}(url)
}
go func() {
wg.Wait()
close(errChanel)
}()
for err := range errChanel {
if err != nil {
return []Room{}, err
}
}
return rooms, nil
}
================================================
FILE: app/logs/handler.go
================================================
package logs
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func (h *Handler) contentGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
WorldID int `json:"worldID" form:"worldID"`
LogType string `json:"logType" form:"logType"`
Lines int `json:"lines" form:"lines"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.LogType == "game" || reqForm.LogType == "chat" {
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
logContent := game.LogContent(reqForm.LogType, reqForm.WorldID, reqForm.Lines)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": logContent})
} else {
var logPath string
switch reqForm.LogType {
case "steam":
logPath = "Steam/logs/bootstrap_log.txt"
case "access":
logPath = "logs/access.log"
case "runtime":
logPath = "logs/runtime.log"
}
logContent := utils.GetFileLastNLines(logPath, reqForm.Lines)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": logContent})
}
}
func (h *Handler) historyListGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
WorldID int `json:"worldID" form:"worldID"`
LogType string `json:"logType" form:"logType"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
list := game.HistoryFileList(reqForm.LogType, reqForm.WorldID)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": list})
}
func (h *Handler) historyContentGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
WorldID int `json:"worldID" form:"worldID"`
LogType string `json:"logType" form:"logType"`
LogFile string `json:"logFile" form:"logFile"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
content := game.HistoryFileContent(reqForm.LogType, reqForm.LogFile, reqForm.WorldID)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": content})
}
func (h *Handler) cleanInfoGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": game.LogsInfo()})
}
func (h *Handler) cleanDelete(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
dst.CleanLogs
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
cl := dst.CleanLogs{
Game: reqForm.Game,
Chat: reqForm.Chat,
Steam: reqForm.Steam,
Access: reqForm.Access,
Runtime: reqForm.Runtime,
}
if game.LogsClean(&cl) {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
} else {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
}
}
func (h *Handler) downloadGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
role, _ := c.Get("role")
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
fileList := game.LogsList(role.(string) == "admin")
zipFilePath := fmt.Sprintf("%s/tmp/%d", utils.DmpFiles, reqForm.RoomID)
defer func(dirPath string) {
err := utils.RemoveDir(dirPath)
if err != nil {
logger.Logger.Error(err.Error())
}
}(zipFilePath)
err = utils.EnsureDirExists(zipFilePath)
if err != nil {
logger.Logger.Errorf("创建目录失败, err: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": message.Get(c, "download fail"), "data": nil})
return
}
zipFile := fmt.Sprintf("%s/logs.zip", zipFilePath)
err = utils.ZipFiles(fileList, zipFile)
if err != nil {
logger.Logger.Errorf("创建压缩文件失败, err: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": message.Get(c, "download fail"), "data": nil})
return
}
c.File(zipFile)
}
================================================
FILE: app/logs/i18n.go
================================================
package logs
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["startup game fail"] = "启动失败"
i.ZH["download fail"] = "下载失败"
i.EN["startup game fail"] = "Startup Fail"
i.EN["download fail"] = "Download Fail"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/logs/router.go
================================================
package logs
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
logs := v.Group("logs")
logs.Use(middleware.TokenCheck())
{
logs.GET("/content", h.contentGet)
logs.GET("/history/list", h.historyListGet)
logs.GET("/history/content", h.historyContentGet)
logs.GET("/clean/info", middleware.AdminOnly(), h.cleanInfoGet)
logs.DELETE("/clean", middleware.AdminOnly(), h.cleanDelete)
logs.GET("/download", h.downloadGet)
}
}
}
================================================
FILE: app/logs/utils.go
================================================
package logs
import (
"dst-management-platform-api/database/dao"
)
type Handler struct {
userDao *dao.UserDAO
roomDao *dao.RoomDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO) *Handler {
return &Handler{
userDao: userDao,
roomDao: roomDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
}
}
================================================
FILE: app/mod/handler.go
================================================
package mod
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func modSearchGet(c *gin.Context) {
lang, _ := c.Get("lang")
langStr := "zh" // 默认语言
if strLang, ok := lang.(string); ok {
langStr = strLang
}
type SearchForm struct {
SearchType string `form:"searchType" json:"searchType"`
SearchText string `form:"searchText" json:"searchText"`
Page int `form:"page" json:"page"`
PageSize int `form:"pageSize" json:"pageSize"`
}
var searchForm SearchForm
if err := c.ShouldBindQuery(&searchForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(searchForm))
if searchForm.SearchType == "id" {
id, err := strconv.Atoi(searchForm.SearchText)
if err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
data, err := SearchModById(id, langStr)
if err != nil {
logger.Logger.Errorf("获取mod信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "search fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
return
}
if searchForm.SearchType == "text" {
data, err := SearchMod(searchForm.Page, searchForm.PageSize, searchForm.SearchText, langStr)
if err != nil {
logger.Logger.Errorf("获取mod信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "search fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
return
}
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
}
func (h *Handler) downloadPost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
ID int `json:"id"`
FileURL string `json:"file_url"`
Update bool `json:"update"`
Size string `json:"size"`
Name string `json:"name"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
reqSize, err := strconv.Atoi(reqForm.Size)
if err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
reqSize64 := int64(reqSize)
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err, modSize := game.DownloadMod(reqForm.ID, reqForm.FileURL)
if err != nil || modSize != reqSize64 {
logger.Logger.Debugf("模组大小与预期不符, %d, %d", modSize, reqSize64)
if reqForm.Update {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": reqForm.Name + " " + message.Get(c, "update fail"), "data": nil})
return
} else {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": reqForm.Name + " " + message.Get(c, "download fail"), "data": nil})
return
}
}
if reqForm.Update {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": reqForm.Name + " " + message.Get(c, "update success"), "data": nil})
} else {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": reqForm.Name + " " + message.Get(c, "download success"), "data": nil})
}
}
func (h *Handler) downloadedModsGet(c *gin.Context) {
type ReqForm struct {
RoomID int `form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
downloadedMods := game.GetDownloadedMods()
err = addDownloadedModInfo(downloadedMods, c.Request.Header.Get("X-I18n-Lang"))
if err != nil {
logger.Logger.Error("添加模组额外信息失败")
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": downloadedMods})
}
func (h *Handler) settingModConfigStructGet(c *gin.Context) {
type ReqForm struct {
RoomID int `form:"roomID"`
WorldID int `form:"worldID"`
ID int `form:"id"`
FileURL string `form:"file_url"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
options, err := game.GetModConfigureOptions(reqForm.WorldID, reqForm.ID, reqForm.FileURL == "")
if err != nil {
logger.Logger.Error("获取模组设置失败")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "mod configuration options error"), "data": options})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": options})
}
func (h *Handler) settingModConfigValueGet(c *gin.Context) {
type ReqForm struct {
RoomID int `form:"roomID"`
WorldID int `form:"worldID"`
ID int `form:"id"`
FileURL string `form:"file_url"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
options, err := game.GetModConfigureOptionsValues(reqForm.WorldID, reqForm.ID, reqForm.FileURL == "")
if err != nil {
logger.Logger.Error("获取模组设置失败")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "mod configuration values error"), "data": options})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": options})
}
func (h *Handler) settingModConfigValuePut(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
WorldID int `json:"worldID"`
ID int `json:"id"`
ModORConfig dst.ModORConfig `json:"modORConfig"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.ModConfigureOptionsValuesChange(reqForm.WorldID, reqForm.ID, &reqForm.ModORConfig)
if err != nil {
logger.Logger.Error("修改模组设置失败")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "modify mod configuration values error"), "data": nil})
return
}
err = h.roomDao.UpdateRoom(room)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(worlds)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "modify mod configuration values success"), "data": nil})
}
func (h *Handler) addEnablePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
WorldID int `json:"worldID"`
ID int `json:"id"`
FileURL string `json:"file_url"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.ModEnable(reqForm.WorldID, reqForm.ID, reqForm.FileURL == "")
if err != nil {
logger.Logger.Errorf("模组启用失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "mod enable fail"), "data": nil})
return
}
err = h.roomDao.UpdateRoom(room)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(worlds)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "mod enable success"), "data": nil})
}
func (h *Handler) addDisablePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
WorldID int `json:"worldID"`
ID int `json:"id"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.ModDisable(reqForm.ID)
if err != nil {
logger.Logger.Errorf("模组禁用失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "mod disable fail"), "data": nil})
return
}
err = h.roomDao.UpdateRoom(room)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(worlds)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "mod disable success"), "data": nil})
}
func (h *Handler) getEnabledModsGet(c *gin.Context) {
type ReqForm struct {
RoomID int `form:"roomID"`
WorldID int `form:"worldID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
modsID, err := game.GetEnabledMods(reqForm.WorldID)
if err != nil {
logger.Logger.Error("获取模组设置失败")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "get enabled mod fail"), "data": modsID})
return
}
err = addDownloadedModInfo(&modsID, c.Request.Header.Get("X-I18n-Lang"))
if err != nil {
logger.Logger.Error("添加模组额外信息失败")
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": modsID})
}
func (h *Handler) deletePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
ID int `json:"id"`
FileURL string `json:"file_url"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.ModDelete(reqForm.ID, reqForm.FileURL)
if err != nil {
logger.Logger.Errorf("删除模组失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
func (h *Handler) acfDelete(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.DeleteAcf()
if err != nil {
logger.Logger.Errorf("删除acf文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
================================================
FILE: app/mod/i18n.go
================================================
package mod
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["downloading"] = "开始下载模组"
i.ZH["update completed"] = "更新完成"
i.ZH["mod configuration options error"] = "获取模组配置信息失败"
i.ZH["mod configuration values error"] = "获取模组配置失败"
i.ZH["modify mod configuration values error"] = "修改模组配置失败"
i.ZH["modify mod configuration values success"] = "修改模组配置成功"
i.ZH["mod enable fail"] = "模组启用失败"
i.ZH["mod enable success"] = "模组启用成功"
i.ZH["mod disable fail"] = "模组禁用失败"
i.ZH["mod disable success"] = "模组禁用成功"
i.ZH["get enabled mod fail"] = "获取启用模组失败"
i.EN["downloading"] = "Downloading Mod"
i.EN["update completed"] = "Update Completed"
i.EN["mod configuration options error"] = "Generate Mod Configuration Options Error"
i.EN["mod configuration values error"] = "Generate Mod Configurations Error"
i.EN["modify mod configuration values error"] = "Modify Mod Configuration Error"
i.EN["modify mod configuration values success"] = "Modify Mod Configuration Success"
i.EN["mod enable fail"] = "Mod Enable Fail"
i.EN["mod enable success"] = "Mod Enable Success"
i.EN["mod disable fail"] = "Mod Disable Fail"
i.EN["mod disable success"] = "Mod Disable Success"
i.EN["get enabled mod fail"] = "Get Enabled Mods Fail"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/mod/router.go
================================================
package mod
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
mod := v.Group("mod")
mod.Use(middleware.TokenCheck())
{
mod.GET("/search", modSearchGet)
mod.POST("/download", h.downloadPost)
mod.GET("/downloaded", h.downloadedModsGet)
mod.POST("/add/enable", h.addEnablePost)
mod.POST("/setting/disable", h.addDisablePost)
mod.GET("/setting/mod_config_struct", h.settingModConfigStructGet)
mod.GET("/setting/mod_config_value", h.settingModConfigValueGet)
mod.PUT("/setting/mod_config_value", h.settingModConfigValuePut)
mod.GET("/setting/enabled", h.getEnabledModsGet)
mod.POST("/delete", h.deletePost)
mod.DELETE("/delete/acf", h.acfDelete)
}
}
}
================================================
FILE: app/mod/utils.go
================================================
package mod
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
type Handler struct {
roomDao *dao.RoomDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
}
func NewHandler(roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO) *Handler {
return &Handler{
roomDao: roomDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
}
}
type JSONResponse struct {
Response Response `json:"response"`
}
type Response struct {
Total int `json:"total"`
Publishedfiledetails []PublishedFileDetails `json:"publishedfiledetails"`
}
type PublishedFileDetails struct {
ID string `json:"publishedfileid"`
FileSize string `json:"file_size"`
FileDescription string `json:"file_description"`
FileUrl string `json:"file_url"`
Title string `json:"title"`
Tags []Tags `json:"tags"`
PreviewUrl string `json:"preview_url"`
VoteData VoteData `json:"vote_data"`
TimeCreated int `json:"time_created"`
TimeUpdated int `json:"time_updated"`
Subscriptions int `json:"subscriptions"`
}
type Data struct {
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
Rows []ModInfo `json:"rows"`
}
type ModInfo struct {
Name string `json:"name"`
ID int `json:"id"`
Size string `json:"size"`
Tags []Tags `json:"tags"`
PreviewUrl string `json:"preview_url"`
FileDescription string `json:"file_description"`
FileUrl string `json:"file_url"`
VoteData VoteData `json:"vote_data"`
DownloadedReady bool `json:"downloadedReady"`
TimeCreated int `json:"time_created"`
TimeUpdated int `json:"time_updated"`
Subscriptions int `json:"subscriptions"`
}
type Tags struct {
Tag string `json:"tag"`
DisplayName string `json:"display_name"`
}
type VoteData struct {
Score float64 `json:"score"`
VotesUp int `json:"votes_up"`
VotesDown int `json:"votes_down"`
}
func SearchMod(page int, pageSize int, searchText string, lang string) (Data, error) {
var (
language int
url string
)
if lang == "zh" {
language = 6
} else {
language = 0
}
url = fmt.Sprintf("%s?appid=322330&return_vote_data=true&return_children=true&", utils.SteamApiModSearch)
url = url + "requiredtags[0]=server_only_mod&requiredtags[1]=all_clients_require_mod&match_all_tags=false&"
if searchText == "" {
url = url + fmt.Sprintf("language=%d&key=%s&page=%d&numperpage=%d",
language,
utils.GetSteamApiKey(),
page,
pageSize,
)
} else {
url = url + fmt.Sprintf("language=%d&key=%s&page=%d&numperpage=%d&search_text=%s",
language,
utils.GetSteamApiKey(),
page,
pageSize,
searchText,
)
}
client := &http.Client{
Timeout: utils.HttpTimeout * time.Second,
}
httpResponse, err := client.Get(url)
if err != nil {
return Data{}, err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
logger.Logger.Errorf("请求关闭失败, err: %v", err)
}
}(httpResponse.Body) // 确保在函数结束时关闭响应体
// 检查 HTTP 状态码
if httpResponse.StatusCode != http.StatusOK {
return Data{}, err
}
var jsonResp JSONResponse
if err := json.NewDecoder(httpResponse.Body).Decode(&jsonResp); err != nil {
logger.Logger.Errorf("解析JSON失败, err: %v", err)
return Data{}, err
}
var modInfoList []ModInfo
for _, i := range jsonResp.Response.Publishedfiledetails {
modInfo := ModInfo{
ID: func() int { id, _ := strconv.Atoi(i.ID); return id }(),
Name: i.Title,
Size: i.FileSize,
Tags: i.Tags,
PreviewUrl: i.PreviewUrl,
FileDescription: i.FileDescription,
FileUrl: i.FileUrl,
VoteData: i.VoteData,
TimeCreated: i.TimeCreated,
TimeUpdated: i.TimeUpdated,
Subscriptions: i.Subscriptions,
}
modInfoList = append(modInfoList, modInfo)
}
data := Data{
Total: jsonResp.Response.Total,
Page: page,
PageSize: pageSize,
Rows: modInfoList,
}
return data, nil
}
func SearchModById(id int, lang string) (Data, error) {
var (
language int
url string
)
if lang == "zh" {
language = 6
} else {
language = 0
}
url = fmt.Sprintf("%s?language=%d&key=%s", utils.SteamApiModDetail, language, utils.GetSteamApiKey())
url = url + fmt.Sprintf("&publishedfileids[0]=%d", id)
client := &http.Client{
Timeout: utils.HttpTimeout * time.Second,
}
httpResponse, err := client.Get(url)
if err != nil {
return Data{}, err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
logger.Logger.Errorf("请求关闭失败, err: %v", err)
}
}(httpResponse.Body) // 确保在函数结束时关闭响应体
// 检查 HTTP 状态码
if httpResponse.StatusCode != http.StatusOK {
return Data{}, err
}
var jsonResp JSONResponse
if err := json.NewDecoder(httpResponse.Body).Decode(&jsonResp); err != nil {
logger.Logger.Errorf("解析JSON失败, err: %v", err)
return Data{}, err
}
var modInfoList []ModInfo
for _, i := range jsonResp.Response.Publishedfiledetails {
modInfo := ModInfo{
ID: func() int { id, _ := strconv.Atoi(i.ID); return id }(),
Name: i.Title,
Size: i.FileSize,
Tags: i.Tags,
PreviewUrl: i.PreviewUrl,
FileDescription: i.FileDescription,
FileUrl: i.FileUrl,
VoteData: i.VoteData,
}
modInfoList = append(modInfoList, modInfo)
}
data := Data{
Total: 1,
Page: 1,
PageSize: 1,
Rows: modInfoList,
}
return data, nil
}
func addDownloadedModInfo(mods *[]dst.DownloadedMod, lang string) error {
if len(*mods) == 0 {
return nil
}
var language int
if lang == "zh" {
language = 6
} else {
language = 0
}
url := fmt.Sprintf("%s?language=%d&key=%s", utils.SteamApiModDetail, language, utils.GetSteamApiKey())
for index, mod := range *mods {
logger.Logger.Debug(fmt.Sprintf("mod id %d", mod.ID))
url = url + fmt.Sprintf("&publishedfileids[%d]=%d", index, mod.ID)
}
client := &http.Client{
Timeout: utils.HttpTimeout * time.Second,
}
httpResponse, err := client.Get(url)
if err != nil {
return err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
logger.Logger.Errorf("请求关闭失败, err: %v", err)
}
}(httpResponse.Body) // 确保在函数结束时关闭响应体
// 检查 HTTP 状态码
if httpResponse.StatusCode != http.StatusOK {
return err
}
var jsonResp JSONResponse
if err := json.NewDecoder(httpResponse.Body).Decode(&jsonResp); err != nil {
logger.Logger.Errorf("解析JSON失败, err: %v", err)
return err
}
for _, i := range jsonResp.Response.Publishedfiledetails {
id := func() int { id, _ := strconv.Atoi(i.ID); return id }()
for idx := range *mods {
if (*mods)[idx].ID == id {
(*mods)[idx].Name = i.Title
(*mods)[idx].FileURL = i.FileUrl
(*mods)[idx].PreviewURL = i.PreviewUrl
(*mods)[idx].ServerSize = i.FileSize
}
}
}
return nil
}
================================================
FILE: app/platform/handler.go
================================================
package platform
import (
"context"
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/scheduler"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"time"
"github.com/creack/pty"
"github.com/gin-gonic/gin"
"github.com/olahol/melody"
)
func (h *Handler) overviewGet(c *gin.Context) {
type Data struct {
RunningTime int64 `json:"runningTime"`
Memory uint64 `json:"memory"`
RoomCount int64 `json:"roomCount"`
WorldCount int64 `json:"worldCount"`
UserCount int64 `json:"userCount"`
UidCount int64 `json:"uidCount"`
MaxCpu float64 `json:"maxCpu"`
MaxMemory float64 `json:"maxMemory"`
MaxNetUp float64 `json:"maxNetUp"`
MaxNetDown float64 `json:"maxNetDown"`
}
// 运行时间
t := time.Since(utils.StartTime).Seconds()
// 内存占用
mem := getRES()
// 房间数
roomCount, err := h.roomDao.Count(nil)
if err != nil {
logger.Logger.Error("统计房间数失败")
roomCount = 0
}
// 世界数
worldCount, err := h.worldDao.Count(nil)
if err != nil {
logger.Logger.Error("统计世界数失败")
worldCount = 0
}
// 用户数
userCount, err := h.userDao.Count(nil)
if err != nil {
logger.Logger.Error("统计用户数失败")
userCount = 0
}
// uid数
uidCount, err := h.uidMapDao.Count(nil)
if err != nil {
logger.Logger.Error("统计用户数失败")
uidCount = 0
}
// 1小时cpu内存网络最大值
db.SystemMetricsMutex.RLock()
systemMetricsLength := len(db.SystemMetrics)
reqLength := 60
var systemMetricsData []db.SysMetrics
if systemMetricsLength > reqLength {
systemMetricsData = db.SystemMetrics[systemMetricsLength-reqLength:]
} else {
systemMetricsData = db.SystemMetrics
}
db.SystemMetricsMutex.RUnlock()
var maxCpu, maxMemory, maxNetUp, maxNetDown float64
for _, m := range systemMetricsData {
if m.Cpu > maxCpu {
maxCpu = m.Cpu
}
if m.Memory > maxMemory {
maxMemory = m.Memory
}
if m.NetUplink > maxNetUp {
maxNetUp = m.NetUplink
}
if m.NetDownlink > maxNetDown {
maxNetDown = m.NetDownlink
}
}
// TODO 玩家数最多的的房间Top3
data := Data{
RunningTime: int64(t),
Memory: mem,
RoomCount: roomCount,
WorldCount: worldCount,
UserCount: userCount,
UidCount: uidCount,
MaxCpu: maxCpu,
MaxMemory: maxMemory,
MaxNetUp: maxNetUp,
MaxNetDown: maxNetDown,
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
}
func gameVersionGet(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": scheduler.GetDSTVersion()})
}
func websshWS(c *gin.Context) {
// JWT 认证
token := c.Query("token")
tokenSecret := db.JwtSecret
claims, err := utils.ValidateJWT(token, []byte(tokenSecret))
if err != nil {
logger.Logger.Errorf("token认证失败: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
return
}
if claims.Role != "admin" {
logger.Logger.Errorf("越权请求: 用户角色为 %s", claims.Role)
c.JSON(http.StatusForbidden, gin.H{"error": "权限不足"})
return
}
// 创建PTY进程 - 使用login shell确保正确的环境
cmd := exec.Command("bash", "-l")
// 设置正确的环境变量
cmd.Env = append(os.Environ(),
"TERM=xterm-256color",
"LANG=en_US.UTF-8",
"LC_ALL=en_US.UTF-8",
)
f, err := pty.StartWithSize(cmd, &pty.Winsize{
Rows: 30,
Cols: 120,
})
if err != nil {
logger.Logger.Errorf("创建PTY失败: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "终端创建失败"})
return
}
defer func() {
if cmd.Process != nil {
err = cmd.Process.Kill()
if err != nil {
logger.Logger.Error(err.Error())
}
}
}()
// 创建melody实例
m := melody.New()
m.Config.MaxMessageSize = 1024 * 1024
// 使用context管理goroutine
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// PTY读取goroutine - 改进的数据读取
go func() {
buf := make([]byte, 1024) // 减小缓冲区大小
for {
select {
case <-ctx.Done():
return
default:
read, err := f.Read(buf)
if err != nil {
if err != io.EOF {
logger.Logger.Warnf("PTY读取错误: %v", err)
}
return
}
// 直接发送原始数据
if read > 0 {
data := make([]byte, read)
copy(data, buf[:read])
// 使用BroadcastBinary确保二进制数据正确传输
if err := m.BroadcastBinary(data); err != nil {
logger.Logger.Warnf("广播数据失败: %v", err)
}
}
}
}
}()
// WebSocket消息处理
m.HandleMessage(func(s *melody.Session, msg []byte) {
// 限制消息大小
if len(msg) > 1024 {
logger.Logger.Warnf("消息过大: %d", len(msg))
return
}
// 检查是否是调整终端大小的消息
if len(msg) > 0 && msg[0] == '{' {
var resizeMsg struct {
Type string `json:"type"`
Cols int `json:"cols"`
Rows int `json:"rows"`
}
if err := json.Unmarshal(msg, &resizeMsg); err == nil && resizeMsg.Type == "resize" {
// 调整PTY大小
if err := pty.Setsize(f, &pty.Winsize{
Rows: uint16(resizeMsg.Rows),
Cols: uint16(resizeMsg.Cols),
}); err != nil {
logger.Logger.Warnf("调整终端大小失败: %v", err)
}
return
}
}
// 处理普通输入数据
_, err := f.Write(msg)
if err != nil {
logger.Logger.Warnf("PTY写入失败: %v", err)
//s.CloseWithMessage([]byte("PTY写入失败"))
}
})
// 连接关闭处理
m.HandleClose(func(s *melody.Session, code int, reason string) error {
logger.Logger.Infof("WebSocket连接关闭 --> code: %d, reason: %s", code, reason)
cancel()
return nil
})
// 连接建立处理
m.HandleConnect(func(s *melody.Session) {
logger.Logger.Infof("新的WebSSH连接建立, 用户: %s", claims.Username)
})
// 处理WebSocket升级
err = m.HandleRequest(c.Writer, c.Request)
if err != nil {
logger.Logger.Errorf("WebSocket升级失败: %v", err)
return
}
// 等待命令结束
err = cmd.Wait()
if err != nil {
logger.Logger.Error(err.Error())
}
logger.Logger.Infof("WebSSH会话结束, 用户: %s", claims.Username)
}
func osInfoGet(c *gin.Context) {
osInfo, err := getOSInfo()
if err != nil {
logger.Logger.Errorf("获取系统信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "get os info fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": osInfo})
}
func metricsGet(c *gin.Context) {
type ReqForm struct {
TimeRange int `json:"timeRange" form:"timeRange"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
db.SystemMetricsMutex.RLock()
systemMetricsLength := len(db.SystemMetrics)
reqLength := reqForm.TimeRange * 60
if reqLength <= 0 {
reqLength = 60 // 默认1小时
}
if systemMetricsLength > reqLength {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": db.SystemMetrics[systemMetricsLength-reqLength:]})
} else {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": db.SystemMetrics})
}
db.SystemMetricsMutex.RUnlock()
}
func (h *Handler) globalSettingsGet(c *gin.Context) {
var globalSettings models.GlobalSetting
err := h.globalSettingDao.GetGlobalSetting(&globalSettings)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": globalSettings})
}
func (h *Handler) globalSettingsPost(c *gin.Context) {
var reqForm models.GlobalSetting
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
var dbGlobalSettings models.GlobalSetting
err := h.globalSettingDao.GetGlobalSetting(&dbGlobalSettings)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
needUpdateDB := false
if dbGlobalSettings.PlayerGetFrequency != reqForm.PlayerGetFrequency || dbGlobalSettings.PlayerInfoSaveTime != reqForm.PlayerInfoSaveTime || dbGlobalSettings.UIDMaintainEnable != reqForm.UIDMaintainEnable {
needUpdateDB = true
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: "onlinePlayerGet",
Func: scheduler.OnlinePlayerGet,
Args: []any{reqForm.PlayerGetFrequency, reqForm.PlayerInfoSaveTime, reqForm.UIDMaintainEnable},
TimeType: scheduler.SecondType,
Interval: reqForm.PlayerGetFrequency,
DayAt: "",
})
db.PlayersStatisticMutex.Lock()
for roomID := range db.PlayersStatistic {
if len(db.PlayersStatistic[roomID])*reqForm.PlayerGetFrequency > scheduler.ParsePlayerInfoSaveTime(reqForm.PlayerInfoSaveTime) {
n := int(scheduler.ParsePlayerInfoSaveTime(reqForm.PlayerInfoSaveTime) / reqForm.PlayerGetFrequency)
db.PlayersStatistic[roomID] = utils.GetLastNElements(db.PlayersStatistic[roomID], n)
}
}
db.PlayersStatisticMutex.Unlock()
if err != nil {
logger.Logger.Errorf("定时任务设置失败, err: %v, name: %v", err, "onlinePlayerGet")
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "update fail"), "data": nil})
return
}
}
if dbGlobalSettings.SysMetricsEnable != reqForm.SysMetricsEnable || dbGlobalSettings.SysMetricsSetting != reqForm.SysMetricsSetting {
needUpdateDB = true
if reqForm.SysMetricsEnable {
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: "systemMetricsGet",
Func: scheduler.SystemMetricsGet,
Args: []any{reqForm.SysMetricsSetting},
TimeType: scheduler.MinuteType,
Interval: 1,
DayAt: "",
})
if err != nil {
logger.Logger.Errorf("定时任务设置失败, err: %v, name: %v", err, "systemMetricsGet")
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "update fail"), "data": nil})
return
}
} else {
scheduler.DeleteJob("systemMetricsGet")
db.SystemMetricsMutex.Lock()
db.SystemMetrics = []db.SysMetrics{}
db.SystemMetricsMutex.Unlock()
}
}
if dbGlobalSettings.AutoUpdateEnable != reqForm.AutoUpdateEnable || dbGlobalSettings.AutoUpdateSetting != reqForm.AutoUpdateSetting || dbGlobalSettings.AutoUpdateRestart != reqForm.AutoUpdateRestart {
needUpdateDB = true
if reqForm.AutoUpdateEnable {
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: "gameUpdate",
Func: scheduler.GameUpdate,
Args: []any{reqForm.AutoUpdateEnable, reqForm.AutoUpdateRestart},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: reqForm.AutoUpdateSetting,
})
if err != nil {
logger.Logger.Errorf("定时任务设置失败, err: %v, name: %v", err, "gameUpdate")
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "update fail"), "data": nil})
return
}
} else {
scheduler.DeleteJob("gameUpdate")
}
}
if needUpdateDB {
err = h.globalSettingDao.UpdateGlobalSetting(&reqForm)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "update success"), "data": nil})
}
func (h *Handler) screenRunningGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
screens, err := game.RunningScreens()
if err != nil {
logger.Logger.Errorf("获取正在运行的screen失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "get screens fail"), "data": []string{}})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": screens})
}
func screenKillPost(c *gin.Context) {
type ReqForm struct {
ScreenName string `json:"screenName"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.ScreenName == "" {
logger.Logger.Infof("请求参数错误, api: %s", c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// 校验 ScreenName 只允许字母、数字、下划线和连字符,防止命令注入
for _, ch := range reqForm.ScreenName {
if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-') {
logger.Logger.Infof("ScreenName包含非法字符, api: %s", c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
}
cmd := fmt.Sprintf("screen -X -S %s quit", reqForm.ScreenName)
err := utils.BashCMD(cmd)
if err != nil {
logger.Logger.Warnf("关闭Screen失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "kill screen fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "kill screen success"), "data": nil})
}
================================================
FILE: app/platform/i18n.go
================================================
package platform
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
// 复制基础翻译
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
// 添加扩展翻译
i.ZH["get os info fail"] = "获取系统信息失败"
i.ZH["get screens fail"] = "获取Screens失败"
i.ZH["kill screen fail"] = "关闭Screens失败"
i.ZH["kill screen success"] = "关闭Screens成功"
i.EN["get os info fail"] = "Get OS Info Fail"
i.EN["get screens fail"] = "Get Screens Fail"
i.EN["kill screen fail"] = "Kill Screens Fail"
i.EN["kill screen success"] = "Kill Screens Success"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/platform/router.go
================================================
package platform
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
platform := v.Group("platform")
{
platform.GET("/overview", middleware.TokenCheck(), middleware.AdminOnly(), h.overviewGet)
platform.GET("/game_version", middleware.TokenCheck(), gameVersionGet)
platform.GET("/webssh", websshWS)
platform.GET("/os_info", middleware.TokenCheck(), osInfoGet)
platform.GET("/metrics", middleware.TokenCheck(), middleware.AdminOnly(), metricsGet)
platform.GET("/global_settings", middleware.TokenCheck(), middleware.AdminOnly(), h.globalSettingsGet)
platform.POST("/global_settings", middleware.TokenCheck(), middleware.AdminOnly(), h.globalSettingsPost)
platform.GET("/screen/running", middleware.TokenCheck(), middleware.AdminOnly(), h.screenRunningGet)
platform.POST("/screen/kill", middleware.TokenCheck(), middleware.AdminOnly(), screenKillPost)
}
}
}
================================================
FILE: app/platform/utils.go
================================================
package platform
import (
"dst-management-platform-api/database/dao"
"os"
"runtime"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/process"
)
type Handler struct {
userDao *dao.UserDAO
roomDao *dao.RoomDAO
worldDao *dao.WorldDAO
systemDao *dao.SystemDAO
globalSettingDao *dao.GlobalSettingDAO
uidMapDao *dao.UidMapDAO
roomSettingDao *dao.RoomSettingDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, systemDao *dao.SystemDAO, globalSettingDao *dao.GlobalSettingDAO, uidMapDao *dao.UidMapDAO, roomSettingDao *dao.RoomSettingDAO) *Handler {
return &Handler{
userDao: userDao,
roomDao: roomDao,
worldDao: worldDao,
systemDao: systemDao,
globalSettingDao: globalSettingDao,
uidMapDao: uidMapDao,
roomSettingDao: roomSettingDao,
}
}
func getRES() uint64 {
p, err := process.NewProcess(int32(os.Getpid()))
if err != nil {
return 0
}
memoryInfo, err := p.MemoryInfo()
if err != nil {
return 0
}
return memoryInfo.RSS
}
type OSInfo struct {
Architecture string
OS string
CPUModel string
CPUCores int
MemorySize uint64
Platform string
PlatformVersion string
Uptime uint64
}
func getOSInfo() (*OSInfo, error) {
architecture := runtime.GOARCH
// 获取CPU信息
cpuInfo, err := cpu.Info()
if err != nil {
return nil, err
}
cpuModel := cpuInfo[0].ModelName
cpuCount, _ := cpu.Counts(true)
cpuCore := cpuCount
// 获取内存信息
virtualMemory, err := mem.VirtualMemory()
if err != nil {
return nil, err
}
memorySize := virtualMemory.Total
// 获取主机信息
hostInfo, err := host.Info()
if err != nil {
return nil, err
}
platformVersion := hostInfo.PlatformVersion
platform := hostInfo.Platform
uptime := hostInfo.Uptime
osName := hostInfo.OS
// 返回系统信息
return &OSInfo{
Architecture: architecture,
OS: osName,
CPUModel: cpuModel,
CPUCores: cpuCore,
MemorySize: memorySize,
Platform: platform,
Uptime: uptime,
PlatformVersion: platformVersion,
}, nil
}
================================================
FILE: app/player/handler.go
================================================
package player
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func (h *Handler) onlineGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
db.PlayersStatisticMutex.Lock()
defer db.PlayersStatisticMutex.Unlock()
var players []db.PlayerInfo
if len(db.PlayersStatistic[reqForm.RoomID]) > 0 {
players = db.PlayersStatistic[reqForm.RoomID][len(db.PlayersStatistic[reqForm.RoomID])-1].PlayerInfo
} else {
players = []db.PlayerInfo{}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": players})
}
func (h *Handler) listPost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
UIDS []string `json:"uids"`
ListType string `json:"listType"`
ActionType string `json:"actionType"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
if reqForm.ActionType == "add" {
err = game.AddPlayerList(reqForm.UIDS, reqForm.ListType)
if err != nil {
logger.Logger.Infof("修改player list失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "add fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "add success"), "data": nil})
} else {
err = game.RemovePlayerList(reqForm.UIDS[0], reqForm.ListType)
if err != nil {
logger.Logger.Infof("修改player list失败: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
}
func (h *Handler) listGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
ListType string `json:"listType" form:"listType"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(reqForm))
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
list := game.GetPlayerList(reqForm.ListType)
logger.Logger.Debug(utils.StructToFlatString(list))
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": list})
}
func (h *Handler) uidMapGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
uidMap, err := h.uidMapDao.GetUidMapByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取uidmap失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": uidMap})
}
func (h *Handler) statisticsOnlineTimeGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
db.PlayersOnlineTimeMutex.Lock()
defer db.PlayersOnlineTimeMutex.Unlock()
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": db.PlayersOnlineTime[reqForm.RoomID]})
}
func (h *Handler) statisticsPlayerCountGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
TimeRange int `json:"timeRange" form:"timeRange"` // 前端传回来需要多少秒的数据
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
db.PlayersStatisticMutex.Lock()
defer db.PlayersStatisticMutex.Unlock()
var globalSettings models.GlobalSetting
err := h.globalSettingDao.GetGlobalSetting(&globalSettings)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if reqForm.TimeRange == 0 {
reqForm.TimeRange = 24 * 60 * 60
}
dataCount := int(reqForm.TimeRange / globalSettings.PlayerGetFrequency) // 返回多少个数据
dataLength := len(db.PlayersStatistic[reqForm.RoomID]) // 当前房间统计数据的个数
if dataLength > dataCount {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": db.PlayersStatistic[reqForm.RoomID][dataLength-dataCount:]})
} else {
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": db.PlayersStatistic[reqForm.RoomID]})
}
}
func (h *Handler) chatGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
Lines int `json:"lines" form:"lines"`
NeedTime bool `json:"needTime" form:"needTime"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(reqForm))
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
chat, err := game.ChatMessages(reqForm.Lines, reqForm.NeedTime)
if err != nil {
logger.Logger.Errorf("获取玩家聊天信息失败:%s", err.Error())
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "chat message fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": chat})
}
================================================
FILE: app/player/i18n.go
================================================
package player
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["chat message fail"] = "玩家聊天信息获取失败"
i.EN["chat message fail"] = "get chat message fail"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/player/router.go
================================================
package player
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
player := v.Group("player")
player.Use(middleware.TokenCheck())
{
player.GET("/online", h.onlineGet)
player.GET("/list", h.listGet)
player.POST("/list", h.listPost)
player.GET("/uidmap", h.uidMapGet)
player.GET("/statistics/online_time", h.statisticsOnlineTimeGet)
player.GET("/statistics/player_count", h.statisticsPlayerCountGet)
player.GET("/chat", h.chatGet)
}
}
}
================================================
FILE: app/player/utils.go
================================================
package player
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/logger"
"strings"
"github.com/gin-gonic/gin"
)
type Handler struct {
userDao *dao.UserDAO
roomDao *dao.RoomDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
uidMapDao *dao.UidMapDAO
globalSettingDao *dao.GlobalSettingDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO, uidMapDao *dao.UidMapDAO, globalSettingDao *dao.GlobalSettingDAO) *Handler {
return &Handler{
userDao: userDao,
roomDao: roomDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
uidMapDao: uidMapDao,
globalSettingDao: globalSettingDao,
}
}
func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
role, _ := c.Get("role")
username, _ := c.Get("username")
// 管理员直接返回true
if role.(string) == "admin" {
return true
} else {
dbUser, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Error("查询数据库失败")
return false
}
roomIDs := strings.Split(dbUser.Rooms, ",")
for _, id := range roomIDs {
if id == roomID {
return true
}
}
}
return false
}
================================================
FILE: app/room/handler.go
================================================
package room
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/scheduler"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
// createPost 创建房间
func (h *Handler) roomPost(c *gin.Context) {
permission, err := h.hasCreatePermission(c)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if permission {
var reqForm XRoomTotalInfo
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
//logger.Logger.Debug(utils.StructToFlatString(reqForm))
reqForm.RoomData.ID = 0
reqForm.RoomData.Status = true
room, errCreateRoom := h.roomDao.CreateRoom(&reqForm.RoomData)
if errCreateRoom != nil {
logger.Logger.Errorf("创建房间失败, err: %v", errCreateRoom)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
for _, world := range reqForm.WorldData {
world.RoomID = room.ID
if errCreateWorld := h.worldDao.Create(&world); errCreateWorld != nil {
logger.Logger.Errorf("创建房间失败, err: %v", errCreateWorld)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
reqForm.RoomSettingData.RoomID = room.ID
reqForm.RoomSettingData.AnnounceSetting = "[]"
if errCreate := h.roomSettingDao.Create(&reqForm.RoomSettingData); errCreate != nil {
logger.Logger.Errorf("创建房间失败, err: %v", errCreate)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(&reqForm.RoomData, &reqForm.WorldData, &reqForm.RoomSettingData, c.Request.Header.Get("X-I18n-Lang"))
err = game.SaveAll()
if err != nil {
logger.Logger.Errorf("配置写入磁盘失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "write file fail"),
"data": nil,
})
}
processJobs(game, reqForm.RoomData.ID, reqForm.RoomSettingData)
// 如果用户不是管理员,且拥有房间创建权限,需要在rooms字段中新增房间id
role, _ := c.Get("role")
username, _ := c.Get("username")
if role.(string) != "admin" {
user, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Errorf("获取用户信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
var rooms []string
if user.Rooms != "" {
rooms = strings.Split(user.Rooms, ",")
}
rooms = append(rooms, strconv.Itoa(reqForm.RoomSettingData.RoomID))
roomsStr := strings.Join(rooms, ",")
user.Rooms = roomsStr
err = h.userDao.UpdateUser(user)
if err != nil {
logger.Logger.Errorf("更新用户信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "create success"), "data": room})
return
}
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
// roomPut 修改房间
func (h *Handler) roomPut(c *gin.Context) {
var reqForm XRoomTotalInfo
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// logger.Logger.Debug(utils.StructToFlatString(reqForm))
permission := h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomData.ID))
if !permission {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
err := h.roomDao.UpdateRoom(&reqForm.RoomData)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(&reqForm.WorldData)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.roomSettingDao.UpdateRoomSetting(&reqForm.RoomSettingData)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(&reqForm.RoomData, &reqForm.WorldData, &reqForm.RoomSettingData, c.Request.Header.Get("X-I18n-Lang"))
err = game.SaveAll()
if err != nil {
logger.Logger.Errorf("配置写入磁盘失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "write file fail"),
"data": nil,
})
}
if reqForm.RoomData.Status {
processJobs(game, reqForm.RoomData.ID, reqForm.RoomSettingData)
} else {
// 删除所有的定时任务
jobNames := scheduler.GetJobsByRoomID(reqForm.RoomData.ID)
logger.Logger.Debug(utils.StructToFlatString(jobNames))
for _, jobName := range jobNames {
scheduler.DeleteJob(jobName)
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "update success"), "data": reqForm.RoomData})
}
// listGet 按分页获取集群信息,并附带对应世界信息
func (h *Handler) listGet(c *gin.Context) {
type ReqForm struct {
Partition
GameName string `json:"gameName" form:"gameName"`
}
var reqForm ReqForm
var data dao.PaginatedResult[XRoomWorld]
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": data})
return
}
//logger.Logger.Debug(utils.StructToFlatString(reqForm))
role, _ := c.Get("role")
var (
rooms *dao.PaginatedResult[models.Room]
err error
)
if role.(string) == "admin" {
// 管理员返回所有房间
rooms, err = h.roomDao.ListRooms([]int{}, reqForm.GameName, reqForm.Page, reqForm.PageSize)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
} else {
username, _ := c.Get("username")
user, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
// 非管理员无房间权限直接返回
if user.Rooms == "" {
data.Page = reqForm.Page
data.PageSize = reqForm.PageSize
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
return
}
// 非管理员返回有权限的房间 user.Rooms like "1,2,3"
roomSlice := strings.Split(user.Rooms, ",")
var roomIDs []int
for _, id := range roomSlice {
intID, err := strconv.Atoi(id)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
roomIDs = append(roomIDs, intID)
}
rooms, err = h.roomDao.ListRooms(roomIDs, reqForm.GameName, reqForm.Page, reqForm.PageSize)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
}
var globalSetting models.GlobalSetting
err = h.globalSettingDao.GetGlobalSetting(&globalSetting)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
data.Page = rooms.Page
data.PageSize = rooms.PageSize
data.TotalCount = rooms.TotalCount
// 为房间加上世界信息
for _, room := range rooms.Data {
xRoomWorld := XRoomWorld{
Room: room,
Worlds: []models.World{},
}
worlds, errWorld := h.worldDao.GetWorldsByRoomIDWthPage(room.ID)
if errWorld != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", errWorld)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
xRoomWorld.Worlds = worlds.Data
if len(db.PlayersStatistic[room.ID]) > 0 {
dataLength := 3600 / globalSetting.PlayerGetFrequency
// 返回最近一个小时的数据
if len(db.PlayersStatistic[room.ID]) > dataLength {
xRoomWorld.Players = db.PlayersStatistic[room.ID][len(db.PlayersStatistic[room.ID])-dataLength:]
} else {
xRoomWorld.Players = db.PlayersStatistic[room.ID]
}
} else {
xRoomWorld.Players = []db.Players{}
}
data.Data = append(data.Data, xRoomWorld)
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
}
// roomGet 返回房间、世界、房间设置等所有信息
func (h *Handler) roomGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"id" form:"id"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(reqForm))
if !h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
var data XRoomTotalInfo
room, err := h.roomDao.GetRoomByID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
data.RoomData = *room
worlds, err := h.worldDao.GetWorldsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
data.WorldData = *worlds
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
data.RoomSettingData = *roomSetting
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
}
// factorGet 前端自动分配端口
func (h *Handler) factorGet(c *gin.Context) {
roomCount, err := h.roomDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
worldCount, err := h.worldDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
type Data struct {
Room int64 `json:"roomCount"`
World int64 `json:"worldCount"`
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": Data{
Room: roomCount,
World: worldCount,
}})
}
// allRoomBasicGet 获取room基本信息 name和id
func (h *Handler) allRoomBasicGet(c *gin.Context) {
rooms, err := h.roomDao.GetRoomBasic()
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": rooms})
}
func (h *Handler) roomWorldsGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
worlds, err := h.worldDao.GetWorldsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
type Data struct {
ID int `json:"id"`
WorldName string `json:"worldName"`
}
var data []Data
for _, world := range *worlds {
data = append(data, Data{
ID: world.ID,
WorldName: world.WorldName,
})
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
}
func (h *Handler) uploadPost(c *gin.Context) {
roomIDStr := c.PostForm("roomID")
roomID := 0
newRoom := false
if roomIDStr == "" {
// 新建房间,新建权限验证
permission, _ := h.hasCreatePermission(c)
if !permission {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
newRoom = true
} else {
// 修改当前房间,修改权限验证
permission := h.hasRoomPermission(c, roomIDStr)
if !permission {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
var err error
roomID, err = strconv.Atoi(roomIDStr)
if err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
}
file, err := c.FormFile("file")
if err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
currentTS := utils.GetTimestamp()
// 创建上传文件保存目录
uploadPath := fmt.Sprintf("%s/upload/%d", utils.DmpFiles, currentTS)
err = utils.EnsureDirExists(uploadPath)
if err != nil {
logger.Logger.Errorf("创建上传目录失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "upload save fail"),
"data": nil,
})
return
}
defer func() {
err = utils.RemoveDir(uploadPath)
if err != nil {
logger.Logger.Errorf("清理上传文件失败, err: %v", err)
}
}()
//保存上传的文件
unzipPath := fmt.Sprintf("%s/", uploadPath)
savePath := fmt.Sprintf("%s/%s", unzipPath, file.Filename)
if err = c.SaveUploadedFile(file, savePath); err != nil {
logger.Logger.Errorf("文件保存失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "upload save fail"),
"data": nil,
})
return
}
var (
room models.Room
worlds []models.World
roomSetting models.RoomSetting
uploadExtraInfo UploadExtraInfo
)
errMsg, err := handleUpload(savePath, unzipPath, &room, &worlds, &roomSetting, &uploadExtraInfo)
if err != nil {
logger.Logger.Errorf("处理上传文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, errMsg),
"data": nil,
})
return
}
if len(uploadExtraInfo.worldPath) == 0 {
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "no available worlds found"),
"data": nil,
})
return
}
// 设置所有的port和roomSetting
if newRoom {
room.Status = true
// port
roomCount, err := h.roomDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
worldCount, err := h.worldDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
room.MasterPort = 21000 + int(roomCount) + 1
for index, world := range worlds {
worlds[index].ServerPort = 11000 + int(worldCount) + index + 1
logger.Logger.Debugf("正在设置ServerPort, world.ServerPort: %v", world.ServerPort)
worlds[index].MasterServerPort = 31000 + int(worldCount) + index + 1
logger.Logger.Debugf("正在设置MasterServerPort, world.MasterServerPort: %v", world.MasterServerPort)
worlds[index].AuthenticationPort = 41000 + int(worldCount) + index + 1
logger.Logger.Debugf("正在设置AuthenticationPort, world.AuthenticationPort: %v", world.AuthenticationPort)
}
// roomSetting
roomSetting.BackupEnable = true
roomSetting.BackupSetting = "[{\"time\":\"06:00:00\"}]"
roomSetting.BackupCleanEnable = false
roomSetting.BackupCleanSetting = 30
roomSetting.RestartEnable = false
roomSetting.RestartSetting = "06:30:00"
roomSetting.KeepaliveEnable = false
roomSetting.KeepaliveSetting = 30
roomSetting.ScheduledStartStopEnable = false
roomSetting.ScheduledStartStopSetting = "{\"start\":\"07:00:00\",\"stop\":\"01:00:00\"}"
roomSetting.StartType = "32-bit"
} else {
dbRoom, err := h.roomDao.GetRoomByID(roomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
room.MasterPort = dbRoom.MasterPort
// 设置roomID
room.ID = roomID
dbWorlds, err := h.worldDao.GetWorldsByRoomID(roomID)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if len(worlds) != len(*dbWorlds) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "number of worlds does not match"), "data": nil})
return
}
for index := range worlds {
worlds[index].ServerPort = (*dbWorlds)[index].ServerPort
worlds[index].MasterServerPort = (*dbWorlds)[index].MasterServerPort
worlds[index].AuthenticationPort = (*dbWorlds)[index].AuthenticationPort
// 设置roomID
worlds[index].RoomID = roomID
}
}
// 判断是否为统一模组
if len(worlds) >= 2 {
if worlds[0].ModData == worlds[1].ModData {
// 当世界个数大于等于2,并且世界0和世界1的模组配置相同
// 则设置ModInOne
room.ModInOne = true
room.ModData = worlds[0].ModData
for index := range worlds {
worlds[index].ModData = ""
}
} else {
// 当世界个数大于等于2,并且世界0和世界1的模组配置不同
// 则设置Not ModInOne
room.ModInOne = false
}
} else {
// 当世界个数小于2,也就是只有一个世界
// 则设置ModInOne
room.ModInOne = true
}
// 写入数据库
if newRoom {
_, err = h.roomDao.CreateRoom(&room)
if err != nil {
logger.Logger.Errorf("创建房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
for _, world := range worlds {
world.RoomID = room.ID
if errCreateWorld := h.worldDao.Create(&world); errCreateWorld != nil {
logger.Logger.Errorf("创建房间失败, err: %v", errCreateWorld)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
roomSetting.RoomID = room.ID
if errCreate := h.roomSettingDao.Create(&roomSetting); errCreate != nil {
logger.Logger.Errorf("创建房间失败, err: %v", errCreate)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
} else {
err = h.roomDao.UpdateRoom(&room)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(&worlds)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
//不更新roomSetting
}
game := dst.NewGameController(&room, &worlds, &roomSetting, c.Request.Header.Get("X-I18n-Lang"))
_ = game.StopAllWorld()
err = game.SaveAll()
if err != nil {
logger.Logger.Errorf("配置写入磁盘失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{
"code": 201,
"message": message.Get(c, "write file fail"),
"data": nil,
})
return
}
clusterPath := fmt.Sprintf("%s/Cluster_%d", utils.ClusterPath, room.ID)
// 设置三个名单
err = utils.TruncAndWriteFile(fmt.Sprintf("%s/adminlist.txt", clusterPath), uploadExtraInfo.adminlist)
if err != nil {
logger.Logger.Errorf("设置管理员失败, err: %v", err)
}
err = utils.TruncAndWriteFile(fmt.Sprintf("%s/blocklist.txt", clusterPath), uploadExtraInfo.blocklist)
if err != nil {
logger.Logger.Errorf("设置黑名单失败, err: %v", err)
}
err = utils.TruncAndWriteFile(fmt.Sprintf("%s/whitelist.txt", clusterPath), uploadExtraInfo.whitelist)
if err != nil {
logger.Logger.Errorf("设置预留位失败, err: %v", err)
}
// 覆盖save目录
for _, world := range uploadExtraInfo.worldPath {
err = utils.RemoveDir(fmt.Sprintf("%s/%s/save", clusterPath, world.name))
if err != nil {
logger.Logger.Errorf("删除旧存档数据失败, err: %v", err)
continue
}
cmd := fmt.Sprintf("cp -r %s/save %s", world.path, fmt.Sprintf("%s/%s/", clusterPath, world.name))
logger.Logger.Debug(cmd)
err = utils.BashCMD(cmd)
if err != nil {
logger.Logger.Errorf("复制存档数据失败, err: %v", err)
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "upload success"), "data": nil})
}
func (h *Handler) deactivatePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, err := h.roomDao.GetRoomByID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
worlds, err := h.worldDao.GetWorldsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
// 关闭游戏进程
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
_ = game.StopAllWorld()
// 删除定时任务
jobNames := scheduler.GetJobsByRoomID(reqForm.RoomID)
logger.Logger.Debug(utils.StructToFlatString(jobNames))
for _, jobName := range jobNames {
scheduler.DeleteJob(jobName)
}
// 删除玩家统计
db.PlayersStatisticMutex.Lock()
defer db.PlayersStatisticMutex.Unlock()
delete(db.PlayersStatistic, reqForm.RoomID)
// 更新数据库
room.Status = false
err = h.roomDao.UpdateRoom(room)
if err != nil {
logger.Logger.Errorf("写入数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "deactivate success"), "data": nil})
}
func (h *Handler) activatePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, err := h.roomDao.GetRoomByID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
worlds, err := h.worldDao.GetWorldsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
// 启动游戏
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
_ = game.StartAllWorld()
// 更新数据库
room.Status = true
err = h.roomDao.UpdateRoom(room)
if err != nil {
logger.Logger.Errorf("写入数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
// 添加定时任务
processJobs(game, reqForm.RoomID, *roomSetting)
// 添加定时通知
jobNames := scheduler.GetJobsByType(reqForm.RoomID, "Announce")
logger.Logger.Debug(utils.StructToFlatString(jobNames))
for _, jobName := range jobNames {
// 删除所有通知任务
scheduler.DeleteJob(jobName)
}
var announces []scheduler.AnnounceSetting
if err = json.Unmarshal([]byte(roomSetting.AnnounceSetting), &announces); err != nil {
logger.Logger.Errorf("获取定时通知设置失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "activate fail"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(announces))
for _, announce := range announces {
// 创建通知任务
if announce.Status {
// 注意,-为分隔符,需要删除uuid中的-
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-%s-Announce", room.ID, strings.ReplaceAll(announce.ID, "-", "")),
Func: scheduler.Announce,
Args: []any{game, announce.Content},
TimeType: scheduler.SecondType,
Interval: announce.Interval,
DayAt: "",
})
if err != nil {
logger.Logger.Errorf("定时通知定时任务处理失败, err: %v", err)
}
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "activate success"), "data": nil})
}
func (h *Handler) roomDelete(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasRoomPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
nonAdminUsers, err := h.userDao.GetNonAdminUsers()
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
room, err := h.roomDao.GetRoomByID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
worlds, err := h.worldDao.GetWorldsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
// 删除游戏相关
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.DeleteRoom()
if err != nil {
logger.Logger.Errorf("删除游戏相关文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
return
}
// 删除定时任务
jobNames := scheduler.GetJobsByRoomID(reqForm.RoomID)
logger.Logger.Debug(utils.StructToFlatString(jobNames))
for _, jobName := range jobNames {
scheduler.DeleteJob(jobName)
}
// 删除玩家统计
db.PlayersStatisticMutex.Lock()
delete(db.PlayersStatistic, reqForm.RoomID)
db.PlayersStatisticMutex.Unlock()
db.PlayersOnlineTimeMutex.Lock()
delete(db.PlayersOnlineTime, reqForm.RoomID)
db.PlayersOnlineTimeMutex.Unlock()
// 更新用户权限
roomIDStr := strconv.Itoa(reqForm.RoomID)
for _, user := range *nonAdminUsers {
if user.Rooms != "" {
roomParts := strings.Split(user.Rooms, ",")
var newRooms []string
for _, rid := range roomParts {
if rid != roomIDStr {
newRooms = append(newRooms, rid)
}
}
user.Rooms = strings.Join(newRooms, ",")
err = h.userDao.UpdateUser(&user)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
}
// 更新数据库
err = h.roomDao.Delete(room)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
err = h.roomSettingDao.Delete(roomSetting)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
for _, world := range *worlds {
err = h.worldDao.Delete(&world)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
}
err = h.uidMapDao.DeleteUidMapByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
================================================
FILE: app/room/i18n.go
================================================
package room
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["room name exist"] = "房间名重复"
i.ZH["upload save fail"] = "上传文件保存失败"
i.ZH["unzip fail"] = "解压失败"
i.ZH["find cluster home fail"] = "查询存档主目录失败"
i.ZH["cluster.ini file not found"] = "cluster.ini文件不存在"
i.ZH["read cluster.ini file fail"] = "读取cluster.ini文件失败"
i.ZH["cluster.ini cluster_name not found"] = "cluster.ini中未发现[cluster_name]字段"
i.ZH["cluster.ini game_mode not found"] = "cluster.ini中未发现[game_mode]字段"
i.ZH["get worlds path fail"] = "获取世界目录失败"
i.ZH["server.ini file not found"] = "server.ini文件不存在"
i.ZH["read server.ini file fail"] = "读取server.ini文件失败"
i.ZH["server.ini is_master not found"] = "server.ini中未发现[is_master]字段"
i.ZH["read is_master from server.ini fail"] = "读取server.ini[is_master]字段失败"
i.ZH["server.ini name not found"] = "server.ini中未发现[name]字段"
i.ZH["level data not found"] = "未发现世界配置"
i.ZH["no available worlds found"] = "存档文件中没有发现可用的世界"
i.ZH["number of worlds does not match"] = "上传存档世界个数与当前房间世界个数不相等"
i.ZH["write file fail"] = "写入文件失败"
i.ZH["upload success"] = "上传成功"
i.ZH["deactivate success"] = "关闭成功"
i.ZH["activate fail"] = "激活成功"
i.ZH["activate success"] = "激活成功"
i.EN["room name exist"] = "Room Name Already Existed"
i.EN["upload save fail"] = "file save fail"
i.EN["unzip fail"] = "unzip file fail"
i.EN["find cluster home fail"] = "find DST main path fail"
i.EN["cluster.ini file not found"] = "cluster.ini file not found"
i.EN["read cluster.ini file fail"] = "read cluster.ini file fail"
i.EN["cluster.ini cluster_name not found"] = "cluster_name not found in cluster.ini"
i.EN["cluster.ini game_mode not found"] = "game_mode not found in cluster.ini"
i.EN["get worlds path fail"] = "get worlds path fail"
i.EN["server.ini file not found"] = "server.ini file not found"
i.EN["read server.ini file fail"] = "read server.ini file fail"
i.EN["server.ini is_master not found"] = "is_master not found in server.ini"
i.EN["read is_master from server.ini fail"] = "read server.ini[is_master] fail"
i.EN["server.ini name not found"] = "name not found in server.ini"
i.EN["level data not found"] = "world level data not found"
i.EN["no available worlds found"] = "no available worlds found"
i.EN["number of worlds does not match"] = "the number of worlds does not match"
i.EN["write file fail"] = "write file fail"
i.EN["upload success"] = "upload success"
i.EN["deactivate success"] = "Deactivate Success"
i.EN["activate fail"] = "Activate Fail"
i.EN["activate success"] = "Activate Success"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/room/router.go
================================================
package room
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
room := v.Group("room")
room.Use(middleware.TokenCheck())
{
room.POST("", h.roomPost)
room.PUT("", h.roomPut)
room.GET("", h.roomGet)
room.GET("/list", h.listGet)
room.GET("/factor", h.factorGet)
room.GET("/basic", h.allRoomBasicGet)
room.GET("/worlds", h.roomWorldsGet)
room.POST("/upload", h.uploadPost)
room.POST("/activate", h.activatePost)
room.POST("/deactivate", h.deactivatePost)
room.DELETE("", middleware.AdminOnly(), h.roomDelete)
}
}
}
================================================
FILE: app/room/utils.go
================================================
package room
import (
"bufio"
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/scheduler"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
type Handler struct {
roomDao *dao.RoomDAO
userDao *dao.UserDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
globalSettingDao *dao.GlobalSettingDAO
uidMapDao *dao.UidMapDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO, globalSettingDao *dao.GlobalSettingDAO, uidMapDao *dao.UidMapDAO) *Handler {
return &Handler{
roomDao: roomDao,
userDao: userDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
globalSettingDao: globalSettingDao,
uidMapDao: uidMapDao,
}
}
type Partition struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
}
type XRoomWorld struct {
models.Room
Worlds []models.World `json:"worlds"`
Players []db.Players `json:"players"`
}
type XRoomTotalInfo struct {
RoomData models.Room `json:"roomData"`
WorldData []models.World `json:"worldData"`
RoomSettingData models.RoomSetting `json:"roomSettingData"`
}
// 是否拥有房间创建权限
func (h *Handler) hasCreatePermission(c *gin.Context) (bool, error) {
role, _ := c.Get("role")
username, _ := c.Get("username")
var (
has bool
err error
dbUser *models.User
)
// 管理员直接返回true
if role.(string) == "admin" {
has = true
} else {
dbUser, err = h.userDao.GetUserByUsername(username.(string))
if err != nil {
return has, err
}
if dbUser.RoomCreation {
has = true
}
}
return has, err
}
// 是否拥有对应房间权限
func (h *Handler) hasRoomPermission(c *gin.Context, roomID string) bool {
role, _ := c.Get("role")
username, _ := c.Get("username")
// 管理员直接返回true
if role.(string) == "admin" {
return true
} else {
dbUser, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Error("查询数据库失败")
return false
}
roomIDs := strings.Split(dbUser.Rooms, ",")
for _, id := range roomIDs {
if id == roomID {
return true
}
}
}
return false
}
// 处理定时任务
func processJobs(game *dst.Game, roomID int, roomSetting models.RoomSetting) {
// 备份 //
backupNames := scheduler.GetJobsByType(roomID, "Backup")
type BackupSetting struct {
Time string `json:"time"`
}
var backupSettings []BackupSetting
if err := json.Unmarshal([]byte(roomSetting.BackupSetting), &backupSettings); err != nil {
logger.Logger.Errorf("获取房间备份设置失败, err: %v", err)
}
if roomSetting.BackupEnable {
if len(backupSettings) >= len(backupNames) {
// 新设置长度大于旧设置,直接更新
for i, s := range backupSettings {
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-%d-Backup", roomID, i),
Func: scheduler.Backup,
Args: []any{game},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: s.Time,
})
if err != nil {
logger.Logger.Errorf("备份定时任务处理失败, err: %v", err)
}
}
} else {
// 新设置长度小于旧设置,超出的删除
for i, jobName := range backupNames {
if i >= len(backupSettings) {
scheduler.DeleteJob(jobName)
} else {
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-%d-Backup", roomID, i),
Func: scheduler.Backup,
Args: []any{game},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: backupSettings[i].Time,
})
if err != nil {
logger.Logger.Errorf("备份定时任务处理失败, err: %v", err)
}
}
}
}
} else {
// 删除所有备份任务
for _, jobName := range backupNames {
scheduler.DeleteJob(jobName)
}
}
// 备份清理 //
if roomSetting.BackupCleanEnable {
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-BackupClean", roomID),
Func: scheduler.BackupClean,
Args: []any{roomID, roomSetting.BackupCleanSetting},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: "05:16:27",
})
if err != nil {
logger.Logger.Errorf("备份清理定时任务处理失败, err: %v", err)
}
} else {
scheduler.DeleteJob(fmt.Sprintf("%d-BackupClean", roomID))
}
// 重启 //
if roomSetting.RestartEnable {
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-Restart", roomID),
Func: scheduler.Restart,
Args: []any{game},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: roomSetting.RestartSetting,
})
if err != nil {
logger.Logger.Errorf("重启定时任务处理失败, err: %v", err)
}
} else {
scheduler.DeleteJob(fmt.Sprintf("%d-Restart", roomID))
}
// 自动开启关闭游戏
if roomSetting.ScheduledStartStopEnable {
type ScheduledStartStopSetting struct {
Start string `json:"start"`
Stop string `json:"stop"`
}
var scheduledStartStopSetting ScheduledStartStopSetting
if err := json.Unmarshal([]byte(roomSetting.ScheduledStartStopSetting), &scheduledStartStopSetting); err != nil {
logger.Logger.Errorf("获取自动开启关闭游戏设置失败, err: %v", err)
}
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-ScheduledStart", roomID),
Func: scheduler.ScheduledStart,
Args: []any{game},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: scheduledStartStopSetting.Start,
})
if err != nil {
logger.Logger.Errorf("自动开启游戏任务处理失败, err: %v", err)
}
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-ScheduledStop", roomID),
Func: scheduler.ScheduledStop,
Args: []any{game},
TimeType: scheduler.DayType,
Interval: 0,
DayAt: scheduledStartStopSetting.Stop,
})
if err != nil {
logger.Logger.Errorf("自动关闭游戏任务处理失败, err: %v", err)
}
} else {
scheduler.DeleteJob(fmt.Sprintf("%d-ScheduledStart", roomID))
scheduler.DeleteJob(fmt.Sprintf("%d-ScheduledStop", roomID))
}
// 自动保活 //
if roomSetting.KeepaliveEnable {
err := scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-Keepalive", roomID),
Func: scheduler.Keepalive,
Args: []any{game, roomID},
TimeType: scheduler.MinuteType,
Interval: roomSetting.KeepaliveSetting,
DayAt: "",
})
if err != nil {
logger.Logger.Errorf("自动保活定时任务处理失败, err: %v", err)
}
} else {
scheduler.DeleteJob(fmt.Sprintf("%d-Keepalive", roomID))
}
}
func handleUpload(savePath, unzipPath string, room *models.Room, worlds *[]models.World, roomSetting *models.RoomSetting, uploadExtraInfo *UploadExtraInfo) (string, error) {
// 1. 解压上传的zip压缩包
err := utils.Unzip(savePath, unzipPath)
if err != nil {
return "unzip fail", err
}
// 2. 查找存档的home路径
clusterDir, err := findClusterDir(unzipPath)
if err != nil {
return "find cluster home fail", err
}
// 3. 获取token
clusterToken, err := utils.GetFileAllContent(fmt.Sprintf("%s/cluster_token.txt", clusterDir))
if err != nil || clusterToken == "" {
logger.Logger.Info("未发现饥荒令牌文件,使用默认令牌")
room.Token = utils.GetDstToken()
} else {
room.Token = clusterToken
}
// 4. 读取adminlist.txt blocklist.txt whitelist.txt的路径
adminlistPath := fmt.Sprintf("%s/adminlist.txt", clusterDir)
// whitelist_slots 会在dst.save时设置
if utils.FileDirectoryExists(adminlistPath) {
adminlist, err := utils.GetFileAllContent(adminlistPath)
if err == nil {
uploadExtraInfo.adminlist = adminlist
}
}
blocklistPath := fmt.Sprintf("%s/blocklist.txt", clusterDir)
if utils.FileDirectoryExists(blocklistPath) {
blocklist, err := utils.GetFileAllContent(blocklistPath)
if err == nil {
uploadExtraInfo.blocklist = blocklist
}
}
whitelistPath := fmt.Sprintf("%s/whitelist.txt", clusterDir)
if utils.FileDirectoryExists(whitelistPath) {
whitelist, err := utils.GetFileAllContent(whitelistPath)
if err == nil {
uploadExtraInfo.whitelist = whitelist
}
}
// 5. 读取cluster.ini
clusterIniPath := fmt.Sprintf("%s/cluster.ini", clusterDir)
if !utils.FileDirectoryExists(clusterIniPath) {
return "cluster.ini file not found", err
}
clusterIni, err := parseIniToMap(clusterIniPath)
if err != nil {
return "read cluster.ini file fail", err
}
if clusterIni["cluster_name"] == "" {
return "cluster.ini cluster_name not found", fmt.Errorf("未发现房间名")
}
room.GameName = clusterIni["cluster_name"]
room.Description = clusterIni["cluster_description"]
if clusterIni["game_mode"] == "" {
return "cluster.ini game_mode not found", fmt.Errorf("未发现游戏模式")
}
room.GameMode = clusterIni["game_mode"]
maxPlayer, err := strconv.Atoi(clusterIni["max_players"])
if err != nil {
logger.Logger.Info("玩家个数获取异常,设置为默认值6")
room.MaxPlayer = 6
} else {
room.MaxPlayer = maxPlayer
}
if clusterIni["pvp"] == "" {
logger.Logger.Info("玩家对战获取异常,设置为默认值关闭")
room.Pvp = false
} else {
pvp, err := strconv.ParseBool(clusterIni["pvp"])
if err != nil {
logger.Logger.Info("玩家对战获取异常,设置为默认值关闭")
room.Pvp = false
} else {
room.Pvp = pvp
}
}
if clusterIni["vote_enabled"] == "" {
logger.Logger.Info("玩家投票获取异常,设置为默认值关闭")
room.Vote = false
} else {
vote, err := strconv.ParseBool(clusterIni["vote_enabled"])
if err != nil {
logger.Logger.Info("玩家投票获取异常,设置为默认值关闭")
room.Vote = false
} else {
room.Vote = vote
}
}
if clusterIni["pause_when_empty"] == "" {
logger.Logger.Info("自动暂停获取异常,设置为默认值开启")
room.PauseEmpty = true
} else {
pauseEmpty, err := strconv.ParseBool(clusterIni["pause_when_empty"])
if err != nil {
logger.Logger.Info("自动暂停获取异常,设置为默认值开启")
room.PauseEmpty = false
} else {
room.PauseEmpty = pauseEmpty
}
}
maxRollBack, err := strconv.Atoi(clusterIni["max_snapshots"])
if err != nil {
logger.Logger.Info("回档天数获取异常,设置为默认值10")
room.MaxRollBack = 10
} else {
room.MaxRollBack = maxRollBack
}
room.Password = clusterIni["cluster_password"]
if clusterIni["master_ip"] == "" {
logger.Logger.Info("主世界IP获取异常,设置为默认值127.0.0.1")
room.MasterIP = "127.0.0.1"
} else {
room.MasterIP = clusterIni["master_ip"]
}
if clusterIni["cluster_key"] == "" {
logger.Logger.Info("世界认证密码获取异常,设置随机密码")
room.ClusterKey = utils.RandomString(14)
} else {
room.ClusterKey = clusterIni["cluster_key"]
}
tickRate, err := strconv.Atoi(clusterIni["tick_rate"])
if err != nil {
logger.Logger.Info("tick rate获取异常,设置为默认值15")
roomSetting.TickRate = 15
} else {
roomSetting.TickRate = tickRate
}
// 6. 读取世界目录
allWorldsPath, err := utils.GetDirs(clusterDir, false)
if err != nil {
return "get worlds path fail", err
}
utils.ReverseSlice(allWorldsPath) // 让Master在Caves前面
for _, i := range allWorldsPath {
// 判断是否含有奇奇怪怪的目录,MacOS真是狗屎啊
if strings.HasPrefix(i, "__") {
continue
}
var (
world models.World
worldPath WorldPath
)
worldPath.path = fmt.Sprintf("%s/%s", clusterDir, i)
// 读取server.ini
serverIniPath := fmt.Sprintf("%s/%s/server.ini", clusterDir, i)
if !utils.FileDirectoryExists(serverIniPath) {
return "server.ini file not found", err
}
serverIni, err := parseIniToMap(serverIniPath)
if err != nil {
return "read server.ini file fail", err
}
worldID, err := strconv.Atoi(serverIni["id"])
if err != nil {
logger.Logger.Info("世界ID获取异常,设置为默认值101")
world.GameID = 101
} else {
world.GameID = worldID
}
if serverIni["is_master"] == "" {
return "server.ini is_master not found", fmt.Errorf("未发现是否为主节点")
}
isMaster, err := strconv.ParseBool(serverIni["is_master"])
if err != nil {
return "read is_master from server.ini fail", err
}
world.IsMaster = isMaster
if serverIni["name"] == "" {
if isMaster {
logger.Logger.Info("世界名获取异常,设置为默认值Master")
world.WorldName = "Master"
worldPath.name = "Master"
} else {
logger.Logger.Info("世界名获取异常,设置为默认值Caves")
world.WorldName = "Caves"
worldPath.name = "Caves"
}
} else {
world.WorldName = serverIni["name"]
worldPath.name = serverIni["name"]
}
encodeUserPath, err := strconv.ParseBool(serverIni["encode_user_path"])
if err != nil {
logger.Logger.Info("获取encode_user_path失败,设置为默认值true")
world.EncodeUserPath = true
} else {
world.EncodeUserPath = encodeUserPath
}
// 读取世界配置 leveldataoverride.lua(worldgenoverride.lua)
levelDataPath := fmt.Sprintf("%s/%s/leveldataoverride.lua", clusterDir, i)
levelData, err := utils.GetFileAllContent(levelDataPath)
if err != nil {
levelDataPath = fmt.Sprintf("%s/%s/worldgenoverride.lua", clusterDir, i)
levelData, err = utils.GetFileAllContent(levelDataPath)
if err != nil {
return "level data not found", fmt.Errorf("未发现世界配置")
}
}
world.LevelData = levelData
// 读取mod配置 modoverrides.lua
modDataPath := fmt.Sprintf("%s/%s/modoverrides.lua", clusterDir, i)
modData, err := utils.GetFileAllContent(modDataPath)
if err == nil {
world.ModData = modData
}
uploadExtraInfo.worldPath = append(uploadExtraInfo.worldPath, worldPath)
*worlds = append(*worlds, world)
}
return "", nil
}
// 查找包含 cluster.ini 文件的目录
func findClusterDir(path string) (string, error) {
// 检查当前目录是否包含 cluster.ini
clusterFile := filepath.Join(path, "cluster.ini")
if _, err := os.Stat(clusterFile); err == nil {
return path, nil
}
// 读取当前目录
entries, err := os.ReadDir(path)
if err != nil {
return "", fmt.Errorf("读取目录失败: %v", err)
}
// 遍历子目录
for _, entry := range entries {
logger.Logger.Debug(entry.Name())
if entry.IsDir() {
subPath := filepath.Join(path, entry.Name())
// 递归查找
if result, err := findClusterDir(subPath); err == nil {
return result, nil
}
}
}
return "", fmt.Errorf("未找到包含 cluster.ini 的目录")
}
// 将ini文件读取为map
func parseIniToMap(filePath string) (map[string]string, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
configMap := make(map[string]string)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// 跳过空行和注释
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
continue
}
// 检查是否是节标题
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
continue
}
// 解析键值对
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
configMap[key] = value
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
return configMap, nil
}
type WorldPath struct {
name string
path string
}
type UploadExtraInfo struct {
adminlist string
blocklist string
whitelist string
worldPath []WorldPath
}
================================================
FILE: app/tools/handler.go
================================================
package tools
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/dst"
"dst-management-platform-api/logger"
"dst-management-platform-api/scheduler"
"dst-management-platform-api/utils"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
func (h *Handler) backupGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
backups, err := game.GetBackups()
if err != nil {
logger.Logger.Errorf("获取备份文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "get backup fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": backups})
}
func (h *Handler) backupPost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
err = game.Backup()
if err != nil {
logger.Logger.Errorf("创建备份文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "create backup fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "create backup success"), "data": nil})
}
func (h *Handler) backupDelete(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
Filenames []string `json:"filenames"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
count := game.DeleteBackups(reqForm.Filenames)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "?", "data": count})
}
func (h *Handler) backupRestorePost(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
Filename string `json:"filename"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !utils.IsSafeString(reqForm.Filename) {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
saveData, err := game.Restore(reqForm.Filename)
if err != nil {
logger.Logger.Errorf("恢复失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "restore fail"), "data": nil})
return
}
err = h.roomDao.UpdateRoom(&saveData.Room)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "restore fail"), "data": nil})
return
}
err = h.worldDao.UpdateWorlds(&saveData.Worlds)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "restore fail"), "data": nil})
return
}
err = h.roomSettingDao.UpdateRoomSetting(&saveData.RoomSetting)
if err != nil {
logger.Logger.Errorf("更新房间失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "restore fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "restore success"), "data": nil})
}
func (h *Handler) backupDownloadGet(c *gin.Context) {
// 1. 获取路径参数
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
Filename string `json:"filename" form:"filename"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// 2. 参数验证
if reqForm.RoomID == 0 || reqForm.Filename == "" {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// 3. 安全验证(防止路径遍历)
if strings.Contains(reqForm.Filename, "..") {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// 4. 构建文件路径
filePath := fmt.Sprintf("dmp_files/backup/%d/%s", reqForm.RoomID, reqForm.Filename)
c.File(filePath)
}
func (h *Handler) announceGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
roomSetting, err := h.roomSettingDao.GetRoomSettingsByRoomID(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": roomSetting.AnnounceSetting})
}
func (h *Handler) announcePut(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
Setting string `json:"setting"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
roomSetting.AnnounceSetting = reqForm.Setting
err = h.roomSettingDao.UpdateRoomSetting(roomSetting)
if err != nil {
logger.Logger.Errorf("更新通知设置失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "update fail"), "data": nil})
return
}
if room.Status {
// 更新定时任务
jobNames := scheduler.GetJobsByType(reqForm.RoomID, "Announce")
logger.Logger.Debug(utils.StructToFlatString(jobNames))
for _, jobName := range jobNames {
// 删除所有通知任务
scheduler.DeleteJob(jobName)
}
var announces []scheduler.AnnounceSetting
if err = json.Unmarshal([]byte(roomSetting.AnnounceSetting), &announces); err != nil {
logger.Logger.Errorf("获取定时通知设置失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "update fail"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(announces))
for _, announce := range announces {
// 创建通知任务
if announce.Status {
// 注意,-为分隔符,需要删除uuid中的-
err = scheduler.UpdateJob(&scheduler.JobConfig{
Name: fmt.Sprintf("%d-%s-Announce", room.ID, strings.ReplaceAll(announce.ID, "-", "")),
Func: scheduler.Announce,
Args: []any{game, announce.Content},
TimeType: scheduler.SecondType,
Interval: announce.Interval,
DayAt: "",
})
if err != nil {
logger.Logger.Errorf("定时通知定时任务处理失败, err: %v", err)
}
}
}
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "update success"), "data": nil})
}
func (h *Handler) mapGet(c *gin.Context) {
type ReqForm struct {
RoomID int `form:"roomID"`
WorldID int `form:"worldID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
mapData, err := game.GenerateBackgroundMap(reqForm.WorldID)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "generate map fail"), "data": nil})
return
}
type Prefab struct {
Name string `json:"name"`
X int `json:"x"`
Y int `json:"y"`
}
var Prefabs []Prefab
// 猪王 出生门 月台 岩浆池 绿洲 蚁狮 旋涡 巨大蜂窝
var prefabs = []string{
"pigking", "multiplayer_portal", "moonbase", "lava_pond",
"oasislake", "antlion", "oceanwhirlbigportal", "beequeenhivegrown",
}
for _, prefab := range prefabs {
cmd := fmt.Sprintf("print(c_findnext('%s').Transform:GetWorldPosition())", prefab)
x, y, err := game.GetCoordinate(cmd, reqForm.WorldID)
if err != nil {
logger.Logger.Warnf("坐标获取失败,跳过, err: %v, prefab: %s", err, prefab)
continue
}
X, Y := game.CoordinateToPx(mapData.Height, x, y)
Prefabs = append(Prefabs, Prefab{
Name: prefab,
X: X,
Y: Y,
})
}
count := game.CountPrefabs(reqForm.WorldID)
players := game.PlayerPosition(reqForm.WorldID)
for index := range players {
players[index].Coordinate.X, players[index].Coordinate.Y = game.CoordinateToPx(mapData.Height, players[index].Coordinate.X, players[index].Coordinate.Y)
}
type Data struct {
Image dst.MapData `json:"image"`
Prefabs []Prefab `json:"prefabs"`
Count []dst.PrefabItem `json:"count"`
Players []dst.PlayerPosition `json:"players"`
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": Data{
Image: mapData,
Prefabs: Prefabs,
Count: count,
Players: players,
}})
}
func tokenPost(c *gin.Context) {
type ReqForm struct {
Expiration int `json:"expiration"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.Expiration < 0 {
logger.Logger.Infof("请求参数错误, api: %s", c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
username, _ := c.Get("username")
nickname, _ := c.Get("nickname")
user := models.User{
Username: username.(string),
Nickname: nickname.(string),
Role: "admin",
}
var expiration int
if reqForm.Expiration == 0 {
// 生成永久token 99年
expiration = 99 * 365 * 24
} else {
expiration = reqForm.Expiration
}
token, err := utils.GenerateJWT(user, []byte(db.JwtSecret), expiration)
if err != nil {
logger.Logger.Errorf("创建token失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "create fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "create success"), "data": token})
}
func (h *Handler) snapshotGet(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID" form:"roomID"`
}
var reqForm ReqForm
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
snapshot, err := game.GetSnapshot()
if err != nil {
logger.Logger.Errorf("获取游戏存档文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": "get snapshot fail", "data": snapshot})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": snapshot})
}
func (h *Handler) snapshotDelete(c *gin.Context) {
type ReqForm struct {
RoomID int `json:"roomID"`
Name string `json:"name"`
}
var reqForm ReqForm
if err := c.ShouldBindJSON(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if reqForm.RoomID == 0 {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !utils.IsSafePath(reqForm.Name) {
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
if !h.hasPermission(c, strconv.Itoa(reqForm.RoomID)) {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": nil})
return
}
room, worlds, roomSetting, err := dao.FetchGameInfo(reqForm.RoomID)
if err != nil {
logger.Logger.Errorf("获取基本信息失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
game := dst.NewGameController(room, worlds, roomSetting, c.Request.Header.Get("X-I18n-Lang"))
// 关闭游戏
err = game.StopAllWorld()
if err != nil {
logger.Logger.Warnf("关闭游戏失败:%v,可能是游戏未运行,跳过", err)
}
// 删除存档文件
err = game.DeleteSnapshot(reqForm.Name)
if err != nil {
logger.Logger.Errorf("删除游戏存档文件失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete fail"), "data": nil})
return
}
// 启动游戏
err = game.StartAllWorld()
if err != nil {
logger.Logger.Errorf("启动游戏失败:%v", err)
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
================================================
FILE: app/tools/i18n.go
================================================
package tools
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
i.ZH["get backup fail"] = "获取备份文件失败"
i.ZH["create backup fail"] = "创建备份文件失败"
i.ZH["create backup success"] = "创建成功"
i.ZH["restore fail"] = "恢复失败"
i.ZH["restore success"] = "恢复成功"
i.ZH["get setting fail"] = "获取定时通知设置失败"
i.ZH["generate map fail"] = "生成地图失败"
i.ZH["get snapshot fail"] = "获取备份文件失败"
i.EN["get backup fail"] = "get backup fail"
i.EN["create backup fail"] = "create backup fail"
i.EN["create backup success"] = "create success"
i.EN["restore fail"] = "restore fail"
i.EN["restore success"] = "restore success"
i.EN["get setting fail"] = "Get Announce Settings Fail"
i.EN["generate map fail"] = "generate map fail"
i.EN["get snapshot fail"] = "get snapshot fail"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/tools/router.go
================================================
package tools
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
tools := v.Group("tools")
tools.Use(middleware.TokenCheck())
{
tools.GET("/backup", h.backupGet)
tools.POST("/backup", h.backupPost)
tools.DELETE("/backup", h.backupDelete)
tools.POST("/backup/restore", h.backupRestorePost)
tools.GET("/backup/download", h.backupDownloadGet)
tools.GET("/announce", h.announceGet)
tools.PUT("/announce", h.announcePut)
tools.GET("/map", h.mapGet)
tools.POST("/token", middleware.AdminOnly(), tokenPost)
tools.GET("/snapshot", h.snapshotGet)
tools.DELETE("/snapshot", h.snapshotDelete)
}
}
}
================================================
FILE: app/tools/utils.go
================================================
package tools
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/logger"
"strings"
"github.com/gin-gonic/gin"
)
type Handler struct {
roomDao *dao.RoomDAO
userDao *dao.UserDAO
worldDao *dao.WorldDAO
roomSettingDao *dao.RoomSettingDAO
}
func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettingDao *dao.RoomSettingDAO) *Handler {
return &Handler{
roomDao: roomDao,
userDao: userDao,
worldDao: worldDao,
roomSettingDao: roomSettingDao,
}
}
func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
role, _ := c.Get("role")
username, _ := c.Get("username")
// 管理员直接返回true
if role.(string) == "admin" {
return true
} else {
dbUser, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Error("查询数据库失败")
return false
}
roomIDs := strings.Split(dbUser.Rooms, ",")
for _, id := range roomIDs {
if id == roomID {
return true
}
}
}
return false
}
================================================
FILE: app/user/handler.go
================================================
package user
import (
"dst-management-platform-api/database/dao"
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
"dst-management-platform-api/logger"
"dst-management-platform-api/utils"
"net/http"
"github.com/gin-gonic/gin"
)
func (h *Handler) registerGet(c *gin.Context) {
var registered bool
num, err := h.userDao.Count(nil)
if err != nil {
registered = false
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "fail", "data": registered})
return
}
if num != 0 {
registered = false
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": registered})
return
} else {
registered = true
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": registered})
return
}
}
func (h *Handler) registerPost(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(user))
num, err := h.userDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if num != 0 {
logger.Logger.Info("创建用户失败,用户已存在")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "user exist"), "data": nil})
return
}
// 注册的用户默认拥有最高权限
user.Disabled = false
user.Role = "admin"
if errCreate := h.userDao.Create(&user); errCreate != nil {
logger.Logger.Errorf("创建用户失败, err: %v", errCreate)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "register success"), "data": nil})
return
}
func (h *Handler) loginPost(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(user))
if user.Username == "" || user.Password == "" {
logger.Logger.Infof("请求参数缺失, api: %s", c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
dbUser, err := h.userDao.GetUserByUsername(user.Username)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if dbUser.Username == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "user not exist"), "data": nil})
return
}
if dbUser.Disabled {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "disabled"), "data": nil})
return
}
if dbUser.Password != user.Password {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "wrong password"), "data": nil})
return
}
token, err := utils.GenerateJWT(*dbUser, []byte(db.JwtSecret), utils.JwtExpirationHours)
if err != nil {
logger.Logger.Errorf("生成jwt失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "login fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "login success"), "data": token})
}
func (h *Handler) menuGet(c *gin.Context) {
role, _ := c.Get("role")
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data []menuItem `json:"data"`
}
response := Response{
Code: 200,
Message: "success",
Data: nil,
}
if role.(string) == "admin" {
response.Data = []menuItem{
rooms,
dashboard,
game,
tools,
logs,
upload,
install,
platform,
}
} else {
toolsLess := tools
toolsLess.Links = []menuItem{
tools.Links[0],
tools.Links[1],
tools.Links[2],
tools.Links[4],
}
logsLess := logs
logsLess.Links = []menuItem{
logs.Links[0],
logs.Links[1],
logs.Links[2],
}
response.Data = []menuItem{
rooms,
dashboard,
game,
toolsLess,
logsLess,
upload,
}
}
c.JSON(http.StatusOK, response)
}
func (h *Handler) basePost(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(user))
dbUser, err := h.userDao.GetUserByUsername(user.Username)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if dbUser.Username != "" {
logger.Logger.Info("创建用户失败,用户已存在")
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "user exist"), "data": nil})
return
}
if errCreate := h.userDao.Create(&user); errCreate != nil {
logger.Logger.Errorf("创建用户失败, err: %v", errCreate)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "create success"), "data": nil})
return
}
func (h *Handler) baseGet(c *gin.Context) {
username, _ := c.Get("username")
dbUser, err := h.userDao.GetUserByUsername(username.(string))
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
dbUser.Password = ""
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": dbUser})
}
func (h *Handler) basePut(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(user))
dbUser, err := h.userDao.GetUserByUsername(user.Username)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if dbUser.Username == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "user not exist"), "data": nil})
return
}
user.Password = dbUser.Password
err = h.userDao.UpdateUser(&user)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "update fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "update success"), "data": nil})
}
func (h *Handler) baseDelete(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
logger.Logger.Debug(utils.StructToFlatString(user))
// 用户数小于等于1时,禁止删除
num, err := h.userDao.Count(nil)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if num <= 1 {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "delete all users"), "data": nil})
return
}
// 查询用户是否存在
dbUser, err := h.userDao.GetUserByUsername(user.Username)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
if dbUser.Username == "" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "user not exist"), "data": nil})
return
}
// 执行删除
err = h.userDao.Delete(dbUser)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "delete fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "delete success"), "data": nil})
}
func (h *Handler) userListGet(c *gin.Context) {
type ReqForm struct {
Partition
Q string `json:"q" form:"q"`
}
var (
reqForm ReqForm
data dao.PaginatedResult[models.User]
)
if err := c.ShouldBindQuery(&reqForm); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": data})
return
}
role, _ := c.Get("role")
if role.(string) != "admin" {
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "permission needed"), "data": data})
return
}
users, err := h.userDao.ListUsers(reqForm.Q, reqForm.Page, reqForm.PageSize)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": data})
return
}
data.Data = []models.User{} // 防止Data为nil
for _, user := range users.Data {
user.Password = ""
data.Data = append(data.Data, user)
}
data.Page = users.Page
data.PageSize = users.PageSize
data.TotalCount = users.TotalCount
c.JSON(http.StatusOK, gin.H{"code": 200, "message": "success", "data": data})
}
func (h *Handler) myselfPut(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Logger.Infof("请求参数错误: %v, api: %s", err, c.Request.URL.Path)
c.JSON(http.StatusOK, gin.H{"code": 400, "message": message.Get(c, "bad request"), "data": nil})
return
}
// 使用 JWT token 中的用户名,防止越权修改
username, _ := c.Get("username")
user.Username = username.(string)
logger.Logger.Debug(utils.StructToFlatString(user))
dbUser, err := h.userDao.GetUserByUsername(user.Username)
if err != nil {
logger.Logger.Errorf("查询数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message.Get(c, "database error"), "data": nil})
return
}
dbUser.Password = user.Password
dbUser.Nickname = user.Nickname
dbUser.Avatar = user.Avatar
err = h.userDao.UpdateUser(dbUser)
if err != nil {
logger.Logger.Errorf("更新数据库失败, err: %v", err)
c.JSON(http.StatusOK, gin.H{"code": 201, "message": message.Get(c, "update fail"), "data": nil})
return
}
c.JSON(http.StatusOK, gin.H{"code": 200, "message": message.Get(c, "myself update success"), "data": nil})
}
================================================
FILE: app/user/i18n.go
================================================
package user
import "dst-management-platform-api/utils"
type ExtendedI18n struct {
utils.BaseI18n
}
func NewExtendedI18n() *ExtendedI18n {
i := &ExtendedI18n{
BaseI18n: utils.BaseI18n{
ZH: make(map[string]string),
EN: make(map[string]string),
},
}
utils.I18nMutex.Lock()
defer utils.I18nMutex.Unlock()
// 复制基础翻译
for k, v := range utils.I18n.ZH {
i.ZH[k] = v
}
for k, v := range utils.I18n.EN {
i.EN[k] = v
}
// 添加扩展翻译
i.ZH["register success"] = "注册成功"
i.ZH["register fail"] = "注册失败"
i.ZH["user exist"] = "请勿重复注册"
i.ZH["login fail"] = "登录失败"
i.ZH["login success"] = "登录成功"
i.ZH["wrong password"] = "密码错误"
i.ZH["user not exist"] = "用户不存在"
i.ZH["disabled"] = "用户已被禁用"
i.ZH["myself update success"] = "修改成功,请重新登录"
i.ZH["delete all users"] = "禁止删除所有用户"
i.EN["register success"] = "Register Success"
i.EN["register fail"] = "Register Fail"
i.EN["user exist"] = "User Existed"
i.EN["login fail"] = "Login Fail"
i.EN["login success"] = "Login Success"
i.EN["wrong password"] = "Wrong Password"
i.EN["user not exist"] = "User Not Exist"
i.EN["disabled"] = "User is Disabled"
i.EN["myself update success"] = "Update success, please re-login"
i.EN["delete all users"] = "Prohibit deletion of all users"
return i
}
var message = NewExtendedI18n()
================================================
FILE: app/user/router.go
================================================
package user
import (
"dst-management-platform-api/middleware"
"dst-management-platform-api/utils"
"github.com/gin-gonic/gin"
)
func (h *Handler) RegisterRoutes(r *gin.Engine) {
v := r.Group(utils.ApiVersion)
{
user := v.Group("user")
{
user.GET("/register", h.registerGet)
user.POST("/register", h.registerPost)
user.POST("/login", middleware.LoginRateLimit(), h.loginPost)
user.GET("/base", middleware.TokenCheck(), h.baseGet)
user.POST("/base", middleware.TokenCheck(), middleware.AdminOnly(), h.basePost)
user.PUT("/base", middleware.TokenCheck(), middleware.AdminOnly(), h.basePut)
user.DELETE("/base", middleware.TokenCheck(), middleware.AdminOnly(), h.baseDelete)
user.GET("/menu", middleware.TokenCheck(), h.menuGet)
user.GET("/list", middleware.TokenCheck(), middleware.AdminOnly(), h.userListGet)
user.PUT("/myself", middleware.TokenCheck(), h.myselfPut)
}
}
}
================================================
FILE: app/user/utils.go
================================================
package user
import "dst-management-platform-api/database/dao"
type Handler struct {
userDao *dao.UserDAO
}
func NewHandler(userDao *dao.UserDAO) *Handler {
return &Handler{
userDao: userDao,
}
}
type menuItem struct {
ID int `json:"id"`
Type string `json:"type"`
Section string `json:"section"`
Title string `json:"title"`
To string `json:"to"`
Component string `json:"component"`
Icon string `json:"icon"`
Links []menuItem `json:"links"`
}
var rooms = menuItem{
ID: 1,
Type: "link",
Section: "",
Title: "rooms",
To: "/rooms",
Component: "rooms/index",
Icon: "ri-instance-line",
Links: nil,
}
var dashboard = menuItem{
ID: 2,
Type: "link",
Section: "",
Title: "dashboard",
To: "/dashboard",
Component: "dashboard/index",
Icon: "ri-function-ai-line",
Links: nil,
}
var game = menuItem{
ID: 3,
Type: "group",
Section: "",
Title: "game",
To: "/game",
Component: "",
Icon: "ri-gamepad-line",
Links: []menuItem{
{
ID: 301,
Type: "link",
Section: "",
Title: "gameBase",
To: "/game/base",
Component: "game/base",
Icon: "ri-sword-line",
Links: nil,
},
{
ID: 302,
Type: "link",
Section: "",
Title: "gameMod",
To: "/game/mod",
Component: "game/mod",
Icon: "ri-rocket-2-line",
Links: nil,
},
{
ID: 303,
Type: "link",
Section: "",
Title: "gamePlayer",
To: "/game/player",
Component: "game/player",
Icon: "ri-ghost-line",
Links: nil,
},
},
}
var upload = menuItem{
ID: 4,
Type: "link",
Section: "",
Title: "upload",
To: "/upload",
Component: "upload/index",
Icon: "ri-contacts-book-upload-line",
Links: nil,
}
var install = menuItem{
ID: 5,
Type: "link",
Section: "",
Title: "install",
To: "/install",
Component: "install/index",
Icon: "ri-import-line",
Links: nil,
}
var tools = menuItem{
ID: 6,
Type: "group",
Section: "",
Title: "tools",
To: "/tools",
Component: "",
Icon: "ri-wrench-line",
Links: []menuItem{
{
ID: 601,
Type: "link",
Section: "",
Title: "toolsBackup",
To: "/tools/backup",
Component: "tools/backup",
Icon: "ri-save-2-line",
Links: nil,
},
{
ID: 602,
Type: "link",
Section: "",
Title: "toolsAnnounce",
To: "/tools/announce",
Component: "tools/announce",
Icon: "ri-chat-smile-ai-3-line",
Links: nil,
},
{
ID: 603,
Type: "link",
Section: "",
Title: "toolsMap",
To: "/tools/map",
Component: "tools/map",
Icon: "ri-road-map-line",
Links: nil,
},
{
ID: 604,
Type: "link",
Section: "",
Title: "toolsToken",
To: "/tools/token",
Component: "tools/token",
Icon: "ri-coupon-line",
Links: nil,
},
{
ID: 605,
Type: "link",
Section: "",
Title: "toolsSnapshot",
To: "/tools/snapshot",
Component: "tools/snapshot",
Icon: "ri-flip-horizontal-line",
Links: nil,
},
},
}
var logs = menuItem{
ID: 7,
Type: "group",
Section: "",
Title: "logs",
To: "/logs",
Component: "",
Icon: "ri-blogger-line",
Links: []menuItem{
{
ID: 701,
Type: "link",
Section: "",
Title: "logsGame",
To: "/logs/game",
Component: "logs/game",
Icon: "ri-game-line",
Links: nil,
},
{
ID: 702,
Type: "link",
Section: "",
Title: "logsChat",
To: "/logs/chat",
Component: "logs/chat",
Icon: "ri-chat-smile-3-line",
Links: nil,
},
{
ID: 703,
Type: "link",
Section: "",
Title: "logsDownload",
To: "/logs/download",
Component: "logs/download",
Icon: "ri-download-2-line",
Links: nil,
},
{
ID: 704,
Type: "link",
Section: "",
Title: "logsSteam",
To: "/logs/steam",
Component: "logs/steam",
Icon: "ri-steam-line",
Links: nil,
},
{
ID: 705,
Type: "link",
Section: "",
Title: "logsAccess",
To: "/logs/access",
Component: "logs/access",
Icon: "ri-code-box-line",
Links: nil,
},
{
ID: 706,
Type: "link",
Section: "",
Title: "logsRuntime",
To: "/logs/runtime",
Component: "logs/runtime",
Icon: "ri-terminal-box-line",
Links: nil,
},
{
ID: 707,
Type: "link",
Section: "",
Title: "logsClean",
To: "/logs/clean",
Component: "logs/clean",
Icon: "ri-file-shred-line",
Links: nil,
},
},
}
var platform = menuItem{
ID: 8,
Type: "link",
Section: "",
Title: "platform",
To: "/platform",
Component: "platform/index",
Icon: "ri-vip-crown-2-line",
Links: nil,
}
type Partition struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
}
================================================
FILE: database/dao/base.go
================================================
package dao
import (
"gorm.io/gorm"
)
type BaseDAO[T any] struct {
db *gorm.DB
}
type PaginatedResult[T any] struct {
Data []T `json:"rows"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
TotalCount int64 `json:"total"`
}
func NewBaseDAO[T any](db *gorm.DB) *BaseDAO[T] {
return &BaseDAO[T]{db: db}
}
func (d *BaseDAO[T]) Create(model *T) error {
return d.db.Create(model).Error
}
func (d *BaseDAO[T]) Update(model *T) error {
return d.db.Save(model).Error
}
func (d *BaseDAO[T]) Delete(model *T) error {
return d.db.Delete(model).Error
}
func (d *BaseDAO[T]) Query(page, pageSize int, condition any, args ...any) (*PaginatedResult[T], error) {
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 10
}
if pageSize > 100 {
pageSize = 100
}
var models []T
var total int64
query := d.db.Model(new(T))
if condition != nil {
query = query.Where(condition, args...)
}
if err := query.Count(&total).Error; err != nil {
return nil, err
}
offset := (page - 1) * pageSize
err := query.Offset(offset).Limit(pageSize).Find(&models).Error
return &PaginatedResult[T]{
Data: models,
Page: page,
PageSize: pageSize,
TotalCount: total,
}, err
}
func (d *BaseDAO[T]) Count(condition any, args ...any) (int64, error) {
var count int64
err := d.db.Model(new(T)).Where(condition, args...).Count(&count).Error
return count, err
}
================================================
FILE: database/dao/composite.go
================================================
package dao
import (
"dst-management-platform-api/database/db"
"dst-management-platform-api/database/models"
)
// FetchGameInfo 传入房间ID,返回房间、世界、房间设置信息
func FetchGameInfo(roomID int) (*models.Room, *[]models.World, *models.RoomSetting, error) {
roomDAO := NewRoomDAO(db.DB)
worldDAO := NewWorldDAO(db.DB)
roomSettingDAO := NewRoomSettingDAO(db.DB)
room, err := roomDAO.GetRoomByID(roomID)
if err != nil {
return &models.Room{}, &[]models.World{}, &models.RoomSetting{}, err
}
worlds, err := worldDAO.GetWorldsByRoomID(roomID)
if err != nil {
return &models.Room{}, &[]models.World{}, &models.RoomSetting{}, err
}
roomSetting, err := roomSettingDAO.GetRoomSettingsByRoomID(roomID)
if err != nil {
return &models.Room{}, &[]models.World{}, &models.RoomSetting{}, err
}
return room, worlds, roomSetting, nil
}
================================================
FILE: database/dao/globalSetting.go
================================================
package dao
import (
"dst-management-platform-api/database/models"
"gorm.io/gorm"
)
type GlobalSettingDAO struct {
BaseDAO[models.GlobalSetting]
}
func NewGlobalSettingDAO(db *gorm.DB) *GlobalSettingDAO {
dao := &GlobalSettingDAO{
BaseDAO: *NewBaseDAO[models.GlobalSetting](db),
}
dao.initGlobalSetting()
return dao
}
func (d *GlobalSettingDAO) GetGlobalSetting(setting *models.GlobalSetting) error {
return d.db.First(setting).Error
}
func (d *GlobalSettingDAO) UpdateGlobalSetting(setting *models.GlobalSetting) error {
return d.db.Save(setting).Error
}
func (d *GlobalSettingDAO) initGlobalSetting() {
count, err := d.Count(nil)
if err != nil {
panic("数据库初始化失败: " + err.Error())
}
if count == 0 {
globalSetting := models.GlobalSetting{
PlayerGetFrequency: 60,
PlayerInfoSaveTime: 1,
UIDMaintainEnable: true,
SysMetricsEnable: true,
SysMetricsSetting: 6,
AutoUpdateEnable: true,
AutoUpdateSetting: "06:41:38",
AutoUpdateRestart: false,
}
err = d.db.Create(&globalSetting).Error
if err != nil {
panic("数据库初始化失败: " + err.Error())
}
}
}
================================================
FILE: database/dao/room.go
================================================
package dao
import (
"dst-management-platform-api/database/models"
"gorm.io/gorm"
)
type RoomDAO struct {
BaseDAO[models.Room]
}
func NewRoomDAO(db *gorm.DB) *RoomDAO {
return &RoomDAO{
BaseDAO: *NewBaseDAO[models.Room](db),
}
}
func (d *RoomDAO) CreateRoom(room *models.Room) (*models.Room, error) {
err := d.db.Create(room).Error
return room, err
}
func (d *RoomDAO) UpdateRoom(room *models.Room) error {
err := d.db.Save(room).Error
return err
}
func (d *RoomDAO) GetRoomByID(id int) (*models.Room, error) {
var room models.Room
err := d.db.Where("id = ?", id).First(&room).Error
return &room, err
}
func (d *RoomDAO) ListRooms(roomIDs []int, gameName string, page, pageSize int) (*PaginatedResult[models.Room], error) {
var (
condition string
args []any
)
switch {
case len(roomIDs) == 0 && gameName == "":
// 无条件查询(返回所有记录)
condition = "1 = 1" // 或者直接使用 "",但部分数据库可能不支持空 WHERE
case len(roomIDs) == 0 && gameName != "":
// 仅模糊查询 gameName
searchPattern := "%" + gameName + "%"
condition = "game_name LIKE ?"
args = []any{searchPattern}
case len(roomIDs) != 0 && gameName == "":
// 仅查询 name 在 roomNames 列表中的记录
condition = "id IN (?)"
args = []any{roomIDs}
case len(roomIDs) != 0 && gameName != "":
// 查询 name 在 roomNames 列表中,并且 name 或 display_name 匹配模糊搜索
searchPattern := "%" + gameName + "%"
condition = "id IN (?) AND (game_name LIKE ?)"
args = []any{roomIDs, searchPattern}
}
rooms, err := d.Query(page, pageSize, condition, args...)
return rooms, err
}
type RoomBasic struct {
RoomName string `json:"roomName"`
RoomID int `json:"roomID"`
Status bool `json:"status"`
}
func (d *RoomDAO) GetRoomBasic() (*[]RoomBasic, error) {
var rooms []models.Room
var roomBasics []RoomBasic
err := d.db.Find(&rooms).Error
if err != nil {
return &roomBasics, err
}
for _, room := range rooms {
roomBasics = append(roomBasics, RoomBasic{
RoomName: room.GameName,
RoomID: room.ID,
Status: room.Status,
})
}
return &roomBasics, nil
}
================================================
FILE: database/dao/roomSetting.go
================================================
package dao
import (
"dst-management-platform-api/database/models"
"gorm.io/gorm"
)
type RoomSettingDAO struct {
BaseDAO[models.RoomSetting]
}
func NewRoomSettingDAO(db *gorm.DB) *RoomSettingDAO {
return &RoomSettingDAO{
BaseDAO: *NewBaseDAO[models.RoomSetting](db),
}
}
func (d *RoomSettingDAO) GetRoomSettingsByRoomID(id int) (*models.RoomSetting, error) {
var roomSettings models.RoomSetting
err := d.db.Where("room_id = ?", id).First(&roomSettings).Error
return &roomSettings, err
}
func (d *RoomSettingDAO) UpdateRoomSetting(roomSetting *models.RoomSetting) error {
err := d.db.Save(roomSetting).Error
return err
}
================================================
FILE: database/dao/system.go
================================================
package dao
import (
"dst-mana
gitextract_wjok4e68/
├── .claude/
│ └── skills/
│ └── no-build/
│ └── SKILL.md
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows/
│ └── go.yml
├── .gitignore
├── CLAUDE.md
├── LICENSE
├── README.md
├── app/
│ ├── dashboard/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── logs/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── mod/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── platform/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── player/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── room/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ ├── tools/
│ │ ├── handler.go
│ │ ├── i18n.go
│ │ ├── router.go
│ │ └── utils.go
│ └── user/
│ ├── handler.go
│ ├── i18n.go
│ ├── router.go
│ └── utils.go
├── database/
│ ├── dao/
│ │ ├── base.go
│ │ ├── composite.go
│ │ ├── globalSetting.go
│ │ ├── room.go
│ │ ├── roomSetting.go
│ │ ├── system.go
│ │ ├── uidMap.go
│ │ ├── user.go
│ │ └── world.go
│ ├── db/
│ │ ├── cache.go
│ │ └── database.go
│ └── models/
│ ├── globalSetting.go
│ ├── room.go
│ ├── roomSetting.go
│ ├── system.go
│ ├── uidMap.go
│ ├── user.go
│ └── world.go
├── docker/
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── entry-point.sh
├── docs/
│ └── README.md
├── dst/
│ ├── dst.go
│ ├── logs.go
│ ├── map.go
│ ├── mod.go
│ ├── player.go
│ ├── room.go
│ ├── utils.go
│ └── world.go
├── embedFS/
│ ├── dist/
│ │ ├── assets/
│ │ │ ├── CodeEditor-D4vpmGPS.css
│ │ │ ├── CodeEditor-D9V3e-IU.js
│ │ │ ├── ConfirmBox-Dl9KDCIE.css
│ │ │ ├── ConfirmBox-UdDnLY_r.js
│ │ │ ├── NavbarThemeSwitcher-DznimRXG.js
│ │ │ ├── NavbarThemeSwitcher-HlaVvm-i.css
│ │ │ ├── Result-DI11lCXq.js
│ │ │ ├── TimeRunning-BnGoYIbA.js
│ │ │ ├── VAlert-DhdDkcOZ.css
│ │ │ ├── VAlert-hOsVftqi.js
│ │ │ ├── VBadge-D7oDPA4h.js
│ │ │ ├── VBadge-Di9QvBJE.css
│ │ │ ├── VChip-DNNqEGWs.css
│ │ │ ├── VChip-pydKSkvr.js
│ │ │ ├── VCol-BCVfdzjO.js
│ │ │ ├── VContainer-DLwbkp_b.css
│ │ │ ├── VContainer-Dy0K6N_V.js
│ │ │ ├── VDataTable-BKhtqv_h.css
│ │ │ ├── VDataTable-BRoXfM0Y.js
│ │ │ ├── VDataTableServer-DQucjpu4.js
│ │ │ ├── VDialog-BRzUh5G-.css
│ │ │ ├── VDialog-BetymGlx.js
│ │ │ ├── VExpansionPanels-B0znoeId.js
│ │ │ ├── VExpansionPanels-Cp9PmzBO.css
│ │ │ ├── VForm-D9iQptwo.js
│ │ │ ├── VInput-D4WPJeZb.js
│ │ │ ├── VInput-DAKtY42E.css
│ │ │ ├── VList-Cjh13LKl.css
│ │ │ ├── VList-D_MCDEbx.js
│ │ │ ├── VListItemAction-1-e96EHC.js
│ │ │ ├── VMenu-CXNpVDF7.js
│ │ │ ├── VMenu-Ceirw4NI.css
│ │ │ ├── VNumberInput-C-YY_5Ww.css
│ │ │ ├── VNumberInput-uP7vEYUF.js
│ │ │ ├── VPagination-BEBHkR5c.js
│ │ │ ├── VPagination-DFfzvyji.css
│ │ │ ├── VRadioGroup-C71Ml1om.css
│ │ │ ├── VRadioGroup-DDJeM_aY.js
│ │ │ ├── VRow-CL8Cm54n.css
│ │ │ ├── VRow-DtJTiJKO.js
│ │ │ ├── VSelect-CxsUdWRP.js
│ │ │ ├── VSelect-D5ZZgyw5.css
│ │ │ ├── VSelectionControl-CJfnRnc-.js
│ │ │ ├── VSelectionControl-Cp4tgg3l.css
│ │ │ ├── VSkeletonLoader-ChbR7Nc0.js
│ │ │ ├── VSkeletonLoader-sthcA-pB.css
│ │ │ ├── VSlider-CGi8T5rU.js
│ │ │ ├── VSlider-D2HQrba8.css
│ │ │ ├── VSpacer-63T2Dgcr.css
│ │ │ ├── VSpacer-D758VKS2.js
│ │ │ ├── VSparkline-CxEGkfC3.js
│ │ │ ├── VSwitch-CJSeLO2L.js
│ │ │ ├── VSwitch-DCpyu0YQ.css
│ │ │ ├── VTable-BIlReic3.css
│ │ │ ├── VTable-BLAHjZfY.js
│ │ │ ├── VTabs-B4m-OW3A.js
│ │ │ ├── VTabs-CArruFCC.css
│ │ │ ├── VTextField-C23ADfJX.js
│ │ │ ├── VTextField-DflQVYjD.css
│ │ │ ├── VTimePicker-BiJhf8Qg.js
│ │ │ ├── VTimePicker-D-Nx0mCo.css
│ │ │ ├── VToolbar-BhFwV7qm.js
│ │ │ ├── VToolbar-CJ08_fms.css
│ │ │ ├── VTooltip-BV7Wxv--.js
│ │ │ ├── VTooltip-CZlyHzm0.css
│ │ │ ├── _..-D12iSKdb.css
│ │ │ ├── _...error_-C0r044-X.js
│ │ │ ├── access-C8mK6Ha9.js
│ │ │ ├── add-qw2GdfGL.js
│ │ │ ├── announce-aAv7G3Cd.js
│ │ │ ├── axios-DNCpvJag.js
│ │ │ ├── backup-D4Jzm4dn.js
│ │ │ ├── base-Dq3aguG-.js
│ │ │ ├── base-DzlHa8yA.css
│ │ │ ├── blank-CExWZhJD.css
│ │ │ ├── blank-CdkSWbzI.js
│ │ │ ├── chat-CR9ZeRZy.js
│ │ │ ├── chat-DNbpOz0I.js
│ │ │ ├── clean-C3opSXJz.js
│ │ │ ├── clean-gJusj0Uj.css
│ │ │ ├── colors-DoZdCu1D.js
│ │ │ ├── default-BQziqBIh.js
│ │ │ ├── default-CDmOr08l.css
│ │ │ ├── dialog-transition-B2t_G0u1.js
│ │ │ ├── download-BZbCclQJ.js
│ │ │ ├── download-CEIvObtd.css
│ │ │ ├── download-InxnfqWA.js
│ │ │ ├── eventBus-B8tShFcG.js
│ │ │ ├── filter-Dycv7ILy.js
│ │ │ ├── game-OoVHjMuc.js
│ │ │ ├── history-DUBuZMd3.js
│ │ │ ├── index-BRyNS1GG.js
│ │ │ ├── index-BSTHz1Bk.css
│ │ │ ├── index-BhZX0V6g.js
│ │ │ ├── index-Bt3JTD_C.js
│ │ │ ├── index-But_YWe_.js
│ │ │ ├── index-CBy8Y7rp.js
│ │ │ ├── index-CYxihrrz.css
│ │ │ ├── index-CnNpN6vG.js
│ │ │ ├── index-CxuhU37S.css
│ │ │ ├── index-D9Rb-nie.css
│ │ │ ├── index-Dkfp6G7x.js
│ │ │ ├── index-H_F1Plkt.js
│ │ │ ├── index-Nr3m5Q0F.js
│ │ │ ├── index-Vik-EOmM.js
│ │ │ ├── index-c6y4pbVT.js
│ │ │ ├── index-nsLxUtLe.css
│ │ │ ├── index-roZNrXH_.js
│ │ │ ├── levelDataSetting-1LIRScEY.js
│ │ │ ├── list-Ch7Aeo7N.js
│ │ │ ├── log-B7kZm_Kd.js
│ │ │ ├── log-DWr6Dcj-.css
│ │ │ ├── logs-N8GO9ZBg.js
│ │ │ ├── map-Co9ruaLK.js
│ │ │ ├── metrics-BLfS8pVr.js
│ │ │ ├── mod-3aGhFcVM.js
│ │ │ ├── mod-DUnttE3W.js
│ │ │ ├── mod-y4ETdFdW.js
│ │ │ ├── modInfo-B5E3tm9p.css
│ │ │ ├── modInfo-BFWwrZKU.js
│ │ │ ├── online-Dk4H0O4k.js
│ │ │ ├── overview-CXTE8d7i.js
│ │ │ ├── platform-DXQOU806.js
│ │ │ ├── player-CpbcBwUI.js
│ │ │ ├── player-DzdX72KE.js
│ │ │ ├── preview-B1dPoyad.js
│ │ │ ├── preview-B9nDbvgA.css
│ │ │ ├── room-BLsFZNUm.js
│ │ │ ├── room-BV8LbqUm.js
│ │ │ ├── room-CNrLW54E.css
│ │ │ ├── roomSetting-BQQnBtEO.js
│ │ │ ├── rooms-CPzS3Hz9.js
│ │ │ ├── runtime-CAq17H-b.js
│ │ │ ├── setting-BelOsTLn.css
│ │ │ ├── setting-CRpOCMwz.js
│ │ │ ├── settingForm-Dzr9qn6f.js
│ │ │ ├── settingForm-_XZZJq3p.css
│ │ │ ├── settings-9AVqaQTs.js
│ │ │ ├── singleLog-CUnXBn4A.js
│ │ │ ├── snapshot-BCtcS9MN.js
│ │ │ ├── ssrBoot-BL3GCGCL.js
│ │ │ ├── statistics-DfYUbrAR.js
│ │ │ ├── steam-D68KmYFv.js
│ │ │ ├── token-BDX2Jc5X.css
│ │ │ ├── token-HaL7aTBf.js
│ │ │ ├── tools-BtEQwbyL.js
│ │ │ ├── user-CRly-xJV.js
│ │ │ ├── user-u2SlWTrk.js
│ │ │ ├── vue3-count-to.esm-1l4iYIqi.js
│ │ │ ├── world-Dt9MkwG_.js
│ │ │ └── world-OOh0Ytp7.css
│ │ ├── index.html
│ │ └── loader.css
│ ├── embed.go
│ └── shell/
│ ├── manual_install.sh
│ └── manual_update.sh
├── go.mod
├── go.sum
├── logger/
│ └── logger.go
├── main.go
├── middleware/
│ └── middleware.go
├── run.sh
├── scheduler/
│ ├── global.go
│ ├── init.go
│ ├── jobs.go
│ ├── room.go
│ └── utils.go
├── server/
│ ├── console.go
│ ├── flags.go
│ └── server.go
└── utils/
├── acf.go
├── constants.go
├── crypto.go
├── getter.go
├── i18n.go
├── jwt.go
├── security.go
├── system.go
└── tools.go
Showing preview only (410K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4400 symbols across 173 files)
FILE: app/dashboard/handler.go
method execGamePost (line 19) | func (h *Handler) execGamePost(c *gin.Context) {
method infoBaseGet (line 235) | func (h *Handler) infoBaseGet(c *gin.Context) {
method infoSysGet (line 309) | func (h *Handler) infoSysGet(c *gin.Context) {
method connectionCodeGet (line 323) | func (h *Handler) connectionCodeGet(c *gin.Context) {
method connectionCodePut (line 405) | func (h *Handler) connectionCodePut(c *gin.Context) {
function checkLobbyPost (line 443) | func checkLobbyPost(c *gin.Context) {
FILE: app/dashboard/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/dashboard/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/dashboard/utils.go
type Handler (line 17) | type Handler struct
method hasPermission (line 35) | func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
function NewHandler (line 25) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
function getDSTRoomsApi (line 59) | func getDSTRoomsApi(region string) string {
type Room (line 63) | type Room struct
type NeededResponse (line 68) | type NeededResponse struct
function checkDstLobbyRoom (line 72) | func checkDstLobbyRoom(urls []string, clusterName string) ([]Room, error) {
FILE: app/logs/handler.go
method contentGet (line 14) | func (h *Handler) contentGet(c *gin.Context) {
method historyListGet (line 62) | func (h *Handler) historyListGet(c *gin.Context) {
method historyContentGet (line 88) | func (h *Handler) historyContentGet(c *gin.Context) {
method cleanInfoGet (line 115) | func (h *Handler) cleanInfoGet(c *gin.Context) {
method cleanDelete (line 138) | func (h *Handler) cleanDelete(c *gin.Context) {
method downloadGet (line 173) | func (h *Handler) downloadGet(c *gin.Context) {
FILE: app/logs/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/logs/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/logs/utils.go
type Handler (line 7) | type Handler struct
function NewHandler (line 14) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
FILE: app/mod/handler.go
function modSearchGet (line 14) | func modSearchGet(c *gin.Context) {
method downloadPost (line 67) | func (h *Handler) downloadPost(c *gin.Context) {
method downloadedModsGet (line 119) | func (h *Handler) downloadedModsGet(c *gin.Context) {
method settingModConfigStructGet (line 148) | func (h *Handler) settingModConfigStructGet(c *gin.Context) {
method settingModConfigValueGet (line 180) | func (h *Handler) settingModConfigValueGet(c *gin.Context) {
method settingModConfigValuePut (line 212) | func (h *Handler) settingModConfigValuePut(c *gin.Context) {
method addEnablePost (line 258) | func (h *Handler) addEnablePost(c *gin.Context) {
method addDisablePost (line 304) | func (h *Handler) addDisablePost(c *gin.Context) {
method getEnabledModsGet (line 349) | func (h *Handler) getEnabledModsGet(c *gin.Context) {
method deletePost (line 384) | func (h *Handler) deletePost(c *gin.Context) {
method acfDelete (line 415) | func (h *Handler) acfDelete(c *gin.Context) {
FILE: app/mod/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/mod/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/mod/utils.go
type Handler (line 16) | type Handler struct
function NewHandler (line 22) | func NewHandler(roomDao *dao.RoomDAO, worldDao *dao.WorldDAO, roomSettin...
type JSONResponse (line 30) | type JSONResponse struct
type Response (line 33) | type Response struct
type PublishedFileDetails (line 37) | type PublishedFileDetails struct
type Data (line 50) | type Data struct
type ModInfo (line 56) | type ModInfo struct
type Tags (line 70) | type Tags struct
type VoteData (line 74) | type VoteData struct
function SearchMod (line 80) | func SearchMod(page int, pageSize int, searchText string, lang string) (...
function SearchModById (line 160) | func SearchModById(id int, lang string) (Data, error) {
function addDownloadedModInfo (line 222) | func addDownloadedModInfo(mods *[]dst.DownloadedMod, lang string) error {
FILE: app/platform/handler.go
method overviewGet (line 25) | func (h *Handler) overviewGet(c *gin.Context) {
function gameVersionGet (line 112) | func gameVersionGet(c *gin.Context) {
function websshWS (line 116) | func websshWS(c *gin.Context) {
function osInfoGet (line 262) | func osInfoGet(c *gin.Context) {
function metricsGet (line 273) | func metricsGet(c *gin.Context) {
method globalSettingsGet (line 299) | func (h *Handler) globalSettingsGet(c *gin.Context) {
method globalSettingsPost (line 312) | func (h *Handler) globalSettingsPost(c *gin.Context) {
method screenRunningGet (line 415) | func (h *Handler) screenRunningGet(c *gin.Context) {
function screenKillPost (line 448) | func screenKillPost(c *gin.Context) {
FILE: app/platform/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/platform/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/platform/utils.go
type Handler (line 14) | type Handler struct
function NewHandler (line 24) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
function getRES (line 36) | func getRES() uint64 {
type OSInfo (line 50) | type OSInfo struct
function getOSInfo (line 61) | func getOSInfo() (*OSInfo, error) {
FILE: app/player/handler.go
method onlineGet (line 16) | func (h *Handler) onlineGet(c *gin.Context) {
method listPost (line 51) | func (h *Handler) listPost(c *gin.Context) {
method listGet (line 106) | func (h *Handler) listGet(c *gin.Context) {
method uidMapGet (line 142) | func (h *Handler) uidMapGet(c *gin.Context) {
method statisticsOnlineTimeGet (line 173) | func (h *Handler) statisticsOnlineTimeGet(c *gin.Context) {
method statisticsPlayerCountGet (line 200) | func (h *Handler) statisticsPlayerCountGet(c *gin.Context) {
method chatGet (line 248) | func (h *Handler) chatGet(c *gin.Context) {
FILE: app/player/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/player/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/player/utils.go
type Handler (line 11) | type Handler struct
method hasPermission (line 31) | func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
function NewHandler (line 20) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
FILE: app/room/handler.go
method roomPost (line 21) | func (h *Handler) roomPost(c *gin.Context) {
method roomPut (line 112) | func (h *Handler) roomPut(c *gin.Context) {
method listGet (line 173) | func (h *Handler) listGet(c *gin.Context) {
method roomGet (line 280) | func (h *Handler) roomGet(c *gin.Context) {
method factorGet (line 326) | func (h *Handler) factorGet(c *gin.Context) {
method allRoomBasicGet (line 353) | func (h *Handler) allRoomBasicGet(c *gin.Context) {
method roomWorldsGet (line 364) | func (h *Handler) roomWorldsGet(c *gin.Context) {
method uploadPost (line 404) | func (h *Handler) uploadPost(c *gin.Context) {
method deactivatePost (line 681) | func (h *Handler) deactivatePost(c *gin.Context) {
method activatePost (line 746) | func (h *Handler) activatePost(c *gin.Context) {
method roomDelete (line 834) | func (h *Handler) roomDelete(c *gin.Context) {
FILE: app/room/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/room/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/room/utils.go
type Handler (line 22) | type Handler struct
method hasCreatePermission (line 60) | func (h *Handler) hasCreatePermission(c *gin.Context) (bool, error) {
method hasRoomPermission (line 86) | func (h *Handler) hasRoomPermission(c *gin.Context, roomID string) bool {
function NewHandler (line 31) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
type Partition (line 42) | type Partition struct
type XRoomWorld (line 47) | type XRoomWorld struct
type XRoomTotalInfo (line 53) | type XRoomTotalInfo struct
function processJobs (line 111) | func processJobs(game *dst.Game, roomID int, roomSetting models.RoomSett...
function handleUpload (line 249) | func handleUpload(savePath, unzipPath string, room *models.Room, worlds ...
function findClusterDir (line 473) | func findClusterDir(path string) (string, error) {
function parseIniToMap (line 502) | func parseIniToMap(filePath string) (map[string]string, error) {
type WorldPath (line 542) | type WorldPath struct
type UploadExtraInfo (line 547) | type UploadExtraInfo struct
FILE: app/tools/handler.go
method backupGet (line 20) | func (h *Handler) backupGet(c *gin.Context) {
method backupPost (line 59) | func (h *Handler) backupPost(c *gin.Context) {
method backupDelete (line 98) | func (h *Handler) backupDelete(c *gin.Context) {
method backupRestorePost (line 133) | func (h *Handler) backupRestorePost(c *gin.Context) {
method backupDownloadGet (line 199) | func (h *Handler) backupDownloadGet(c *gin.Context) {
method announceGet (line 228) | func (h *Handler) announceGet(c *gin.Context) {
method announcePut (line 259) | func (h *Handler) announcePut(c *gin.Context) {
method mapGet (line 338) | func (h *Handler) mapGet(c *gin.Context) {
function tokenPost (line 424) | func tokenPost(c *gin.Context) {
method snapshotGet (line 469) | func (h *Handler) snapshotGet(c *gin.Context) {
method snapshotDelete (line 508) | func (h *Handler) snapshotDelete(c *gin.Context) {
FILE: app/tools/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/tools/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/tools/utils.go
type Handler (line 11) | type Handler struct
method hasPermission (line 27) | func (h *Handler) hasPermission(c *gin.Context, roomID string) bool {
function NewHandler (line 18) | func NewHandler(userDao *dao.UserDAO, roomDao *dao.RoomDAO, worldDao *da...
FILE: app/user/handler.go
method registerGet (line 14) | func (h *Handler) registerGet(c *gin.Context) {
method registerPost (line 36) | func (h *Handler) registerPost(c *gin.Context) {
method loginPost (line 72) | func (h *Handler) loginPost(c *gin.Context) {
method menuGet (line 119) | func (h *Handler) menuGet(c *gin.Context) {
method basePost (line 172) | func (h *Handler) basePost(c *gin.Context) {
method baseGet (line 204) | func (h *Handler) baseGet(c *gin.Context) {
method basePut (line 217) | func (h *Handler) basePut(c *gin.Context) {
method baseDelete (line 248) | func (h *Handler) baseDelete(c *gin.Context) {
method userListGet (line 294) | func (h *Handler) userListGet(c *gin.Context) {
method myselfPut (line 335) | func (h *Handler) myselfPut(c *gin.Context) {
FILE: app/user/i18n.go
type ExtendedI18n (line 5) | type ExtendedI18n struct
function NewExtendedI18n (line 9) | func NewExtendedI18n() *ExtendedI18n {
FILE: app/user/router.go
method RegisterRoutes (line 10) | func (h *Handler) RegisterRoutes(r *gin.Engine) {
FILE: app/user/utils.go
type Handler (line 5) | type Handler struct
function NewHandler (line 9) | func NewHandler(userDao *dao.UserDAO) *Handler {
type menuItem (line 15) | type menuItem struct
type Partition (line 267) | type Partition struct
FILE: database/dao/base.go
type BaseDAO (line 7) | type BaseDAO struct
type PaginatedResult (line 11) | type PaginatedResult struct
function NewBaseDAO (line 18) | func NewBaseDAO[T any](db *gorm.DB) *BaseDAO[T] {
method Create (line 22) | func (d *BaseDAO[T]) Create(model *T) error {
method Update (line 26) | func (d *BaseDAO[T]) Update(model *T) error {
method Delete (line 30) | func (d *BaseDAO[T]) Delete(model *T) error {
method Query (line 34) | func (d *BaseDAO[T]) Query(page, pageSize int, condition any, args ...an...
method Count (line 67) | func (d *BaseDAO[T]) Count(condition any, args ...any) (int64, error) {
FILE: database/dao/composite.go
function FetchGameInfo (line 9) | func FetchGameInfo(roomID int) (*models.Room, *[]models.World, *models.R...
FILE: database/dao/globalSetting.go
type GlobalSettingDAO (line 9) | type GlobalSettingDAO struct
method GetGlobalSetting (line 21) | func (d *GlobalSettingDAO) GetGlobalSetting(setting *models.GlobalSett...
method UpdateGlobalSetting (line 25) | func (d *GlobalSettingDAO) UpdateGlobalSetting(setting *models.GlobalS...
method initGlobalSetting (line 29) | func (d *GlobalSettingDAO) initGlobalSetting() {
function NewGlobalSettingDAO (line 13) | func NewGlobalSettingDAO(db *gorm.DB) *GlobalSettingDAO {
FILE: database/dao/room.go
type RoomDAO (line 9) | type RoomDAO struct
method CreateRoom (line 19) | func (d *RoomDAO) CreateRoom(room *models.Room) (*models.Room, error) {
method UpdateRoom (line 24) | func (d *RoomDAO) UpdateRoom(room *models.Room) error {
method GetRoomByID (line 29) | func (d *RoomDAO) GetRoomByID(id int) (*models.Room, error) {
method ListRooms (line 35) | func (d *RoomDAO) ListRooms(roomIDs []int, gameName string, page, page...
method GetRoomBasic (line 73) | func (d *RoomDAO) GetRoomBasic() (*[]RoomBasic, error) {
function NewRoomDAO (line 13) | func NewRoomDAO(db *gorm.DB) *RoomDAO {
type RoomBasic (line 67) | type RoomBasic struct
FILE: database/dao/roomSetting.go
type RoomSettingDAO (line 9) | type RoomSettingDAO struct
method GetRoomSettingsByRoomID (line 19) | func (d *RoomSettingDAO) GetRoomSettingsByRoomID(id int) (*models.Room...
method UpdateRoomSetting (line 25) | func (d *RoomSettingDAO) UpdateRoomSetting(roomSetting *models.RoomSet...
function NewRoomSettingDAO (line 13) | func NewRoomSettingDAO(db *gorm.DB) *RoomSettingDAO {
FILE: database/dao/system.go
type SystemDAO (line 12) | type SystemDAO struct
method Get (line 25) | func (d *SystemDAO) Get(key string) (*models.System, error) {
method Set (line 31) | func (d *SystemDAO) Set(systems []models.System) error {
method initSystem (line 36) | func (d *SystemDAO) initSystem() {
function NewSystemDAO (line 16) | func NewSystemDAO(db *gorm.DB) *SystemDAO {
FILE: database/dao/uidMap.go
type UidMapDAO (line 10) | type UidMapDAO struct
method GetUidMapByRoomID (line 20) | func (d *UidMapDAO) GetUidMapByRoomID(roomID int) (*[]models.UidMap, e...
method UpdateUidMap (line 27) | func (d *UidMapDAO) UpdateUidMap(uidMap *models.UidMap) error {
method DeleteUidMapByRoomID (line 35) | func (d *UidMapDAO) DeleteUidMapByRoomID(roomID int) error {
function NewUidMapDAO (line 14) | func NewUidMapDAO(db *gorm.DB) *UidMapDAO {
FILE: database/dao/user.go
type UserDAO (line 10) | type UserDAO struct
method GetUserByUsername (line 20) | func (d *UserDAO) GetUserByUsername(username string) (*models.User, er...
method ListUsers (line 29) | func (d *UserDAO) ListUsers(q string, page, pageSize int) (*PaginatedR...
method UpdateUser (line 45) | func (d *UserDAO) UpdateUser(user *models.User) error {
method GetNonAdminUsers (line 50) | func (d *UserDAO) GetNonAdminUsers() (*[]models.User, error) {
function NewUserDAO (line 14) | func NewUserDAO(db *gorm.DB) *UserDAO {
FILE: database/dao/world.go
type WorldDAO (line 10) | type WorldDAO struct
method UpdateWorlds (line 20) | func (d *WorldDAO) UpdateWorlds(worlds *[]models.World) error {
method GetWorldsByRoomIDWthPage (line 51) | func (d *WorldDAO) GetWorldsByRoomIDWthPage(id int) (*PaginatedResult[...
method GetWorldsByRoomID (line 61) | func (d *WorldDAO) GetWorldsByRoomID(id int) (*[]models.World, error) {
function NewWorldDAO (line 14) | func NewWorldDAO(db *gorm.DB) *WorldDAO {
FILE: database/db/cache.go
type PlayerInfo (line 33) | type PlayerInfo struct
type Players (line 39) | type Players struct
type SysMetrics (line 44) | type SysMetrics struct
function init (line 53) | func init() {
function setCurrentDir (line 57) | func setCurrentDir() {
FILE: database/db/database.go
function InitDB (line 16) | func InitDB(dbPath string) {
function CheckTables (line 51) | func CheckTables() {
FILE: database/models/globalSetting.go
type GlobalSetting (line 3) | type GlobalSetting struct
method TableName (line 15) | func (GlobalSetting) TableName() string {
FILE: database/models/room.go
type Room (line 3) | type Room struct
method TableName (line 29) | func (Room) TableName() string {
FILE: database/models/roomSetting.go
type RoomSetting (line 3) | type RoomSetting struct
method TableName (line 22) | func (RoomSetting) TableName() string {
FILE: database/models/system.go
type System (line 3) | type System struct
method TableName (line 8) | func (System) TableName() string {
FILE: database/models/uidMap.go
type UidMap (line 3) | type UidMap struct
method TableName (line 9) | func (UidMap) TableName() string {
FILE: database/models/user.go
type User (line 3) | type User struct
method TableName (line 17) | func (User) TableName() string {
FILE: database/models/world.go
type World (line 3) | type World struct
method TableName (line 18) | func (World) TableName() string {
FILE: dst/dst.go
method SaveAll (line 9) | func (g *Game) SaveAll() error {
method StartWorld (line 28) | func (g *Game) StartWorld(id int) error {
method StartAllWorld (line 33) | func (g *Game) StartAllWorld() error {
method StopWorld (line 38) | func (g *Game) StopWorld(id int) error {
method StopAllWorld (line 43) | func (g *Game) StopAllWorld() error {
method WorldUpStatus (line 47) | func (g *Game) WorldUpStatus(id int) bool {
method WorldPerformanceStatus (line 51) | func (g *Game) WorldPerformanceStatus(id int) PerformanceStatus {
method DeleteWorld (line 56) | func (g *Game) DeleteWorld(id int) error {
method Reset (line 61) | func (g *Game) Reset(force bool) error {
method Announce (line 66) | func (g *Game) Announce(message string) error {
method SystemMsg (line 70) | func (g *Game) SystemMsg(message string) error {
method ConsoleCmd (line 75) | func (g *Game) ConsoleCmd(cmd string, worldID int) error {
method SessionInfo (line 80) | func (g *Game) SessionInfo() *RoomSessionInfo {
method DownloadMod (line 85) | func (g *Game) DownloadMod(id int, fileURL string) (error, int64) {
method GetDownloadedMods (line 90) | func (g *Game) GetDownloadedMods() *[]DownloadedMod {
method GetModConfigureOptions (line 95) | func (g *Game) GetModConfigureOptions(worldID, modID int, ugc bool) (*[]...
method GetModConfigureOptionsValues (line 100) | func (g *Game) GetModConfigureOptionsValues(worldID, modID int, ugc bool...
method ModConfigureOptionsValuesChange (line 105) | func (g *Game) ModConfigureOptionsValuesChange(worldID, modID int, modCo...
method ModEnable (line 110) | func (g *Game) ModEnable(worldID, modID int, ugc bool) error {
method GetEnabledMods (line 115) | func (g *Game) GetEnabledMods(worldID int) ([]DownloadedMod, error) {
method ModDisable (line 120) | func (g *Game) ModDisable(modID int) error {
method ModDelete (line 125) | func (g *Game) ModDelete(modID int, fileURL string) error {
method LogContent (line 130) | func (g *Game) LogContent(logType string, id, lines int) []string {
method HistoryFileList (line 135) | func (g *Game) HistoryFileList(logType string, id int) []string {
method HistoryFileContent (line 140) | func (g *Game) HistoryFileContent(logType, logfileName string, id int) s...
method LogsInfo (line 145) | func (g *Game) LogsInfo() LogInfo {
method LogsClean (line 150) | func (g *Game) LogsClean(cleanLogs *CleanLogs) bool {
method LogsList (line 155) | func (g *Game) LogsList(admin bool) []string {
method GetOnlinePlayerList (line 160) | func (g *Game) GetOnlinePlayerList(id int) ([]string, error) {
method GetLastAliveTime (line 165) | func (g *Game) GetLastAliveTime(id int) (string, error) {
method Backup (line 170) | func (g *Game) Backup() error {
method Restore (line 175) | func (g *Game) Restore(filename string) (*SaveJson, error) {
method GetBackups (line 180) | func (g *Game) GetBackups() ([]BackupFile, error) {
method DeleteBackups (line 185) | func (g *Game) DeleteBackups(filenames []string) int {
method RunningScreens (line 190) | func (g *Game) RunningScreens() ([]string, error) {
method DeleteRoom (line 195) | func (g *Game) DeleteRoom() error {
method AddPlayerList (line 200) | func (g *Game) AddPlayerList(uids []string, listType string) error {
method RemovePlayerList (line 205) | func (g *Game) RemovePlayerList(uid, listType string) error {
method GetPlayerList (line 210) | func (g *Game) GetPlayerList(listType string) []string {
method GenerateBackgroundMap (line 225) | func (g *Game) GenerateBackgroundMap(worldID int) (MapData, error) {
method CoordinateToPx (line 230) | func (g *Game) CoordinateToPx(size, a, b int) (int, int) {
method GetCoordinate (line 235) | func (g *Game) GetCoordinate(cmd string, worldID int) (int, int, error) {
method CountPrefabs (line 240) | func (g *Game) CountPrefabs(worldID int) []PrefabItem {
method PlayerPosition (line 245) | func (g *Game) PlayerPosition(worldID int) []PlayerPosition {
method GetSnapshot (line 250) | func (g *Game) GetSnapshot() ([]SnapshotFile, error) {
method DeleteSnapshot (line 255) | func (g *Game) DeleteSnapshot(filename string) error {
method DeleteAcf (line 260) | func (g *Game) DeleteAcf() error {
method ChatMessages (line 264) | func (g *Game) ChatMessages(lines int, needTime bool) ([]ChatMessage, er...
FILE: dst/logs.go
method getLogContent (line 9) | func (g *Game) getLogContent(logType string, id, lines int) []string {
method historyFileList (line 39) | func (g *Game) historyFileList(logType string, id int) []string {
method historyFileContent (line 69) | func (g *Game) historyFileContent(logType, logfileName string, id int) s...
type LogInfo (line 99) | type LogInfo struct
method logsInfo (line 107) | func (g *Game) logsInfo() LogInfo {
type CleanLogs (line 135) | type CleanLogs struct
method logsClean (line 143) | func (g *Game) logsClean(cleanLogs *CleanLogs) bool {
method logsList (line 189) | func (g *Game) logsList(admin bool) []string {
FILE: dst/map.go
type MapData (line 22) | type MapData struct
function tileID2Color (line 28) | func tileID2Color(tileID int) string {
function parseHexColor (line 74) | func parseHexColor(s string) color.RGBA {
method generateBackgroundMap (line 97) | func (g *Game) generateBackgroundMap(worldID int) (MapData, error) {
function coordinateToPx (line 209) | func coordinateToPx(size, a, b int) (int, int) {
method getCoordinate (line 216) | func (g *Game) getCoordinate(cmd string, worldID int) (int, int, error) {
type PrefabItem (line 298) | type PrefabItem struct
method countPrefabs (line 303) | func (g *Game) countPrefabs(worldID int) []PrefabItem {
type PlayerPosition (line 419) | type PlayerPosition struct
method playerPosition (line 429) | func (g *Game) playerPosition(worldID int) []PlayerPosition {
FILE: dst/mod.go
type modSaveData (line 17) | type modSaveData struct
method dsModsSetup (line 21) | func (g *Game) dsModsSetup() error {
method downloadMod (line 71) | func (g *Game) downloadMod(id int, fileURL string) (error, int64) {
method generateModDownloadCmd (line 164) | func (g *Game) generateModDownloadCmd(id int) string {
method removeGameOldMod (line 168) | func (g *Game) removeGameOldMod(id int) error {
method generateModCopyCmd (line 179) | func (g *Game) generateModCopyCmd(id int) string {
method processAcf (line 200) | func (g *Game) processAcf(id int) error {
type DownloadedMod (line 262) | type DownloadedMod struct
method getDownloadedMods (line 271) | func (g *Game) getDownloadedMods() *[]DownloadedMod {
method getModConfigureOptions (line 322) | func (g *Game) getModConfigureOptions(worldID, modID int, ugc bool) (*[]...
method getModConfigureOptionsValues (line 360) | func (g *Game) getModConfigureOptionsValues(worldID, modID int, ugc bool...
method modEnable (line 394) | func (g *Game) modEnable(worldID, modID int, ugc bool) error {
method saveMods (line 472) | func (g *Game) saveMods() error {
method modConfigureOptionsValuesChange (line 490) | func (g *Game) modConfigureOptionsValuesChange(worldID, modID int, modCo...
method getEnabledMods (line 535) | func (g *Game) getEnabledMods(worldID int) ([]DownloadedMod, error) {
method modDisable (line 582) | func (g *Game) modDisable(modID int) error {
method deleteMod (line 631) | func (g *Game) deleteMod(modID int, fileURL string) error {
method deleteAcf (line 687) | func (g *Game) deleteAcf() error {
FILE: dst/player.go
type playerSaveData (line 12) | type playerSaveData struct
function getPlayerList (line 21) | func getPlayerList(filepath string) []string {
method savePlayerList (line 46) | func (g *Game) savePlayerList() error {
method addPlayerList (line 69) | func (g *Game) addPlayerList(uids []string, listType string) error {
method removePlayerList (line 89) | func (g *Game) removePlayerList(uid, listType string) error {
type ChatMessage (line 118) | type ChatMessage struct
type ChatLogLine (line 127) | type ChatLogLine struct
function getDstStartTime (line 133) | func getDstStartTime(filepath string) (time.Time, error) {
function parseChatLogLine (line 157) | func parseChatLogLine(line string) (*ChatLogLine, error) {
function timeToSeconds (line 189) | func timeToSeconds(timeStr string) (int64, error) {
method chatMessages (line 214) | func (g *Game) chatMessages(lines int, needTime bool) ([]ChatMessage, er...
FILE: dst/room.go
type roomSaveData (line 17) | type roomSaveData struct
type SeasonLength (line 26) | type SeasonLength struct
type RoomSessionInfo (line 33) | type RoomSessionInfo struct
method createRoom (line 41) | func (g *Game) createRoom() error {
method getClusterIni (line 68) | func (g *Game) getClusterIni() string {
method reset (line 141) | func (g *Game) reset(force bool) error {
method announce (line 174) | func (g *Game) announce(message string) error {
method systemMsg (line 188) | func (g *Game) systemMsg(message string) error {
method sessionInfo (line 202) | func (g *Game) sessionInfo() *RoomSessionInfo {
type SaveJson (line 300) | type SaveJson struct
method backup (line 306) | func (g *Game) backup() error {
method restore (line 341) | func (g *Game) restore(filename string) (*SaveJson, error) {
type BackupFile (line 379) | type BackupFile struct
method getBackups (line 387) | func (g *Game) getBackups() ([]BackupFile, error) {
method deleteBackups (line 442) | func (g *Game) deleteBackups(filenames []string) int {
function findLatestMetaFile (line 456) | func findLatestMetaFile(directory string) (string, error) {
method runningScreen (line 514) | func (g *Game) runningScreen() ([]string, error) {
method deleteRoom (line 522) | func (g *Game) deleteRoom() error {
method getSnapshot (line 544) | func (g *Game) getSnapshot() ([]SnapshotFile, error) {
method deleteSnapshot (line 555) | func (g *Game) deleteSnapshot(filename string) error {
FILE: dst/utils.go
type Game (line 23) | type Game struct
method initInfo (line 59) | func (g *Game) initInfo() {
function NewGameController (line 46) | func NewGameController(room *models.Room, worlds *[]models.World, settin...
type Option (line 121) | type Option struct
type ConfigurationOption (line 127) | type ConfigurationOption struct
type ModInfoParser (line 135) | type ModInfoParser struct
method Parse (line 221) | func (mf *ModInfoParser) Parse(lang string) error {
function NewModInfoParser (line 141) | func NewModInfoParser(luaPath string, modID int) (*ModInfoParser, error) {
function convertLuaValue (line 156) | func convertLuaValue(lv lua.LValue) any {
type ModORConfig (line 311) | type ModORConfig struct
type ModORCollection (line 317) | type ModORCollection
method GetModConfig (line 516) | func (mc ModORCollection) GetModConfig(workshopID string) *ModORConfig {
method IsModEnabled (line 521) | func (mc ModORCollection) IsModEnabled(workshopID string) bool {
method GetConfigValue (line 529) | func (mc ModORCollection) GetConfigValue(workshopID, configKey string)...
method GetNestedConfig (line 537) | func (mc ModORCollection) GetNestedConfig(workshopID, parentKey, child...
method AddModConfig (line 547) | func (mc ModORCollection) AddModConfig(workshopID string, config *ModO...
method ToLuaCode (line 552) | func (mc ModORCollection) ToLuaCode() string {
type ModORParser (line 320) | type ModORParser struct
method close (line 332) | func (p *ModORParser) close() {
method Parse (line 339) | func (p *ModORParser) Parse(luaContent, lang string) (ModORCollection,...
method convertLuaTableToGo (line 354) | func (p *ModORParser) convertLuaTableToGo(lv lua.LValue) (ModORCollect...
method parseModConfig (line 375) | func (p *ModORParser) parseModConfig(table *lua.LTable) *ModORConfig {
method parseConfigurationOptions (line 399) | func (p *ModORParser) parseConfigurationOptions(table *lua.LTable) map...
method convertLuaValue (line 411) | func (p *ModORParser) convertLuaValue(lv lua.LValue) any {
method isArray (line 431) | func (p *ModORParser) isArray(table *lua.LTable) bool {
method convertLuaArray (line 472) | func (p *ModORParser) convertLuaArray(table *lua.LTable) []any {
method convertLuaMap (line 504) | func (p *ModORParser) convertLuaMap(table *lua.LTable) map[string]any {
function NewModORParser (line 325) | func NewModORParser() *ModORParser {
function formatLuaValue (line 609) | func formatLuaValue(value any) string {
function isValidLuaIdentifier (line 658) | func isValidLuaIdentifier(s string) bool {
function formatLuaKey (line 679) | func formatLuaKey(s string) string {
function downloadNotUGCMod (line 699) | func downloadNotUGCMod(url string, id int) (error, int64) {
function uniqueSliceKeepOrderString (line 754) | func uniqueSliceKeepOrderString(slice []string) []string {
function replaceDSTSOFile (line 768) | func replaceDSTSOFile() {
function getSessionID (line 788) | func getSessionID(savePath string) (string, error) {
function getSnapshotFiles (line 809) | func getSnapshotFiles(dir string) ([]SnapshotFile, error) {
type SnapshotFile (line 844) | type SnapshotFile struct
FILE: dst/world.go
type worldSaveData (line 19) | type worldSaveData struct
method createWorlds (line 31) | func (g *Game) createWorlds() error {
method worldUpStatus (line 98) | func (g *Game) worldUpStatus(id int) bool {
type PerformanceStatus (line 121) | type PerformanceStatus struct
method worldPerformanceStatus (line 128) | func (g *Game) worldPerformanceStatus(id int) PerformanceStatus {
method startWorld (line 199) | func (g *Game) startWorld(id int) error {
method startAllWorld (line 245) | func (g *Game) startAllWorld() error {
method stopWorld (line 278) | func (g *Game) stopWorld(id int) error {
method stopAllWorld (line 300) | func (g *Game) stopAllWorld() error {
method deleteWorld (line 311) | func (g *Game) deleteWorld(id int) error {
method consoleCmd (line 320) | func (g *Game) consoleCmd(cmd string, id int) error {
method getWorldByID (line 330) | func (g *Game) getWorldByID(id int) (*worldSaveData, error) {
function getServerIni (line 340) | func getServerIni(world *models.World) string {
method getOnlinePlayerList (line 358) | func (g *Game) getOnlinePlayerList(id int) ([]string, error) {
function readPlayerListFromEnd (line 385) | func readPlayerListFromEnd(logPath string) ([]string, error) {
method getLastAliveTime (line 472) | func (g *Game) getLastAliveTime(id int) (string, error) {
function getWorldLastTime (line 484) | func getWorldLastTime(logfile string) (string, error) {
FILE: embedFS/dist/assets/CodeEditor-D9V3e-IU.js
function cr (line 1) | function cr(){return hl||(hl=1,(function(De,Lt){(function(A,be){De.expor...
function Pu (line 24) | function Pu(){return gl||(gl=1,(function(De,Lt){(function(A){A(cr())})(f...
function Ru (line 24) | function Ru(){return ml||(ml=1,(function(De,Lt){(function(A){A(cr())})(f...
function qu (line 24) | function qu(){return bl||(bl=1,(function(De,Lt){(function(A){A(cr())})(f...
function Bu (line 24) | function Bu(){return wl||(wl=1,(function(De,Lt){(function(A){A(cr())})(f...
function Uu (line 24) | function Uu(){return kl||(kl=1,(function(De,Lt){(function(A){A(cr())})(f...
function Gu (line 24) | function Gu(){return Cl||(Cl=1,(function(De,Lt){(function(A){A(cr())})(f...
function ju (line 24) | function ju(){return Tl||(Tl=1,(function(De,Lt){(function(A){A(cr())})(f...
method data (line 24) | data(){return{contentValue:this.modelValue,coder:null,opt:{autoRefresh:!...
method _height (line 24) | _height(){return Number(this.height)?Number(this.height)+"px":this.height}
method modelValue (line 24) | modelValue(De){this.contentValue=De,De!==this.coder.getValue()&&this.cod...
method theme (line 24) | theme(De){this.toggleTheme(De)}
method mounted (line 24) | mounted(){this.init()}
method init (line 24) | init(){this.coder=Eu(Iu.fromTextArea(this.$refs.textarea,this.opt)),this...
method formatStrInJson (line 24) | formatStrInJson(De){return JSON.stringify(JSON.parse(De),null,4)}
method toggleTheme (line 24) | toggleTheme(De){this.coder.setOption("theme",De)}
method refresh (line 24) | refresh(){this.coder.refresh()}
function Xu (line 24) | function Xu(De,Lt,A,be,te,V){return Nu(),Au("div",{class:"sc-code-editor...
FILE: embedFS/dist/assets/ConfirmBox-UdDnLY_r.js
method setup (line 1) | setup(e,{emit:V}){const n=e,l=V,d=c(()=>{const o={success:{icon:"mdi-che...
FILE: embedFS/dist/assets/NavbarThemeSwitcher-DznimRXG.js
method setup (line 1) | setup(t){const e=H();return(n,a)=>(p(),F(R,null,[k(P,{width:"3em",height...
method setup (line 1) | setup(t){const{t:e}=x();return(n,a)=>V((p(),g(w,{icon:"ri-github-line",c...
method setup (line 1) | setup(t){const e=x(),{current:n}=W(),a=S(),s=$(()=>a.language),r=y([]);z...
method setup (line 1) | setup(t){const{t:e}=x();return(n,a)=>V((p(),g(w,{icon:"ri-book-marked-li...
function b (line 1) | function b(t){return typeof t=="function"?t():f(t)}
function te (line 1) | function te(t,e){function n(...a){return new Promise((s,r)=>{Promise.res...
function ne (line 1) | function ne(t=C){const e=y(!0);function n(){e.value=!1}function a(){e.va...
function ae (line 1) | function ae(...t){if(t.length!==1)return U(...t);const e=t[0];return typ...
function oe (line 1) | function oe(t,e,n={}){const{eventFilter:a=C,...s}=n;return T(t,te(a,e),s)}
function N (line 1) | function N(t,e,n={}){const{eventFilter:a,...s}=n,{eventFilter:r,pause:u,...
function de (line 1) | function de(t,e,...[n]){const{flush:a="sync",deep:s=!1,immediate:r=!0,di...
function ve (line 1) | function ve(t=!1,e={}){const{truthyValue:n=!0,falsyValue:a=!1}=e,s=q(t),...
function re (line 1) | function re(t,e){const n=K(d()),a=ae(t),s=$({get(){var o;const i=a.value...
method setup (line 1) | setup(t){const e=t,n=S(),{t:a}=x(),{name:s,global:r}=L(),u=L(),{state:m,...
method setup (line 1) | setup(t){const e=[{name:"light",icon:"ri-sun-line"},{name:"dark",icon:"r...
FILE: embedFS/dist/assets/Result-DI11lCXq.js
method setup (line 1) | setup(c){const e=c;return(n,g)=>(i(),t(p,{height:e.height,style:{"overfl...
FILE: embedFS/dist/assets/TimeRunning-BnGoYIbA.js
method setup (line 1) | setup(d){const i=d,{t:a}=v(),u=g(i.seconds);let c=null;const s=f(()=>{le...
FILE: embedFS/dist/assets/VAlert-hOsVftqi.js
function re (line 1) | function re(e,n){return{iconSize:w(()=>{const a=new Map(e.iconSizes),t=e...
method setup (line 1) | setup(e,n){let{emit:c,slots:a}=n;const t=E(e,"modelValue"),o=d(()=>{if(e...
FILE: embedFS/dist/assets/VBadge-D7oDPA4h.js
method setup (line 1) | setup(a,t){const{backgroundColorClasses:i,backgroundColorStyles:r}=y(()=...
FILE: embedFS/dist/assets/VChip-pydKSkvr.js
function tl (line 1) | function tl(e){let{selectedElement:o,containerElement:c,isRtl:s,isHorizo...
function al (line 1) | function al(e){let{selectedElement:o,containerElement:c,isHorizontal:s}=...
function pe (line 1) | function pe(e,o){return o?.[e?"scrollWidth":"scrollHeight"]||0}
function nl (line 1) | function nl(e,o){return o?.[e?"clientWidth":"clientHeight"]||0}
function xe (line 1) | function xe(e,o,c){if(!c)return 0;const{scrollLeft:s,offsetWidth:a,scrol...
function _ (line 1) | function _(e,o){return o?.[e?"offsetWidth":"offsetHeight"]||0}
function ze (line 1) | function ze(e,o){return o?.[e?"offsetLeft":"offsetTop"]||0}
method setup (line 1) | setup(e,o){let{slots:c}=o;const{isRtl:s}=Pe(),{displayClasses:a,mobile:b...
method setup (line 1) | setup(e,o){let{slots:c}=o;const{themeClasses:s}=Ce(e),{isSelected:a,sele...
method setup (line 1) | setup(e,o){let{attrs:c,emit:s,slots:a}=o;const{t:b}=Oe(),{borderClasses:...
FILE: embedFS/dist/assets/VCol-BCVfdzjO.js
function L (line 1) | function L(e,s,t){let o=e;if(!(t==null||t===!1)){if(s){const r=s.replace...
method setup (line 1) | setup(e,s){let{slots:t}=s;const o=C(()=>{const r=[];let a;for(a in u)u[a...
FILE: embedFS/dist/assets/VContainer-Dy0K6N_V.js
method setup (line 1) | setup(a,e){let{slots:s}=e;const{rtlClasses:n}=i(),{dimensionStyles:o}=r(...
FILE: embedFS/dist/assets/VDataTable-BRoXfM0Y.js
function Ve (line 1) | function Ve(e,t,l){return Object.keys(e).filter(a=>ft(a)&&a.endsWith(t))...
function At (line 1) | function At(e){const t=M(e,"page",void 0,a=>Number(a??1)),l=M(e,"itemsPe...
function Rt (line 1) | function Rt(e){const{page:t,itemsPerPage:l,itemsLength:a}=e,n=k(()=>l.va...
function Nt (line 1) | function Nt(){const e=K(Re);if(!e)throw new Error("Missing pagination!")...
function $t (line 1) | function $t(e){const t=Oe("usePaginatedItems"),{items:l,startIndex:a,sto...
function Gt (line 1) | function Gt(e){const{sortedItems:t,paginate:l,group:a}=e,n=$(e.pageBy);i...
method setup (line 1) | setup(e,t){let{slots:l}=t;const{t:a}=oe(),{page:n,pageCount:r,startIndex...
function jt (line 1) | function jt(){const t=(arguments.length>0&&arguments[0]!==void 0?argumen...
function he (line 1) | function he(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
function He (line 1) | function He(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
function Mt (line 1) | function Mt(e){if(e.key){if(e.key==="data-table-group")return Ge;if(["da...
function we (line 1) | function we(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
function Wt (line 1) | function Wt(e){let t=!1;function l(r,o){let u=arguments.length>2&&argume...
function Le (line 1) | function Le(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
function je (line 1) | function je(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
function Kt (line 1) | function Kt(e,t){const l=[];let a=0;const n=jt(e);for(;n.size()>0;){let ...
function Me (line 1) | function Me(e){const t=[];for(const l of e){const a={...Mt(l),...l},n=a....
function zt (line 1) | function zt(e,t){const l=j([]),a=j([]),n=j({}),r=j({}),o=j({});vt(()=>{c...
function se (line 1) | function se(){const e=K($e);if(!e)throw new Error("Missing headers!");re...
function Qt (line 1) | function Qt(e,t){let{allItems:l,currentPage:a}=t;const n=M(e,"modelValue...
function ue (line 1) | function ue(){const e=K(Ke);if(!e)throw new Error("Missing selection!");...
function Xt (line 1) | function Xt(e){const t=C(()=>e.initialSortOrder),l=M(e,"sortBy"),a=C(()=...
function Zt (line 1) | function Zt(e,t){if(!xt(e))return{active:!!e};const{key:l,mode:a,modifie...
function ea (line 1) | function ea(e){const{initialSortOrder:t,sortBy:l,mustSort:a,multiSort:n,...
function qe (line 1) | function qe(){const e=K(ze);if(!e)throw new Error("Missing sort!");retur...
function ta (line 1) | function ta(e,t,l,a){const n=oe();return{sortedItems:k(()=>l.value.lengt...
function aa (line 1) | function aa(e,t,l,a){const n=new Intl.Collator(l,{sensitivity:"accent",u...
method setup (line 1) | setup(e,t){let{slots:l}=t;const{t:a}=oe(),{toggleSort:n,sortBy:r,isSorte...
function na (line 1) | function na(e){return{groupBy:M(e,"groupBy")}}
function ra (line 1) | function ra(e){const{disableSort:t,groupBy:l,sortBy:a}=e,n=j(new Set),r=...
function Qe (line 1) | function Qe(){const e=K(Je);if(!e)throw new Error("Missing group!");retu...
function oa (line 1) | function oa(e,t){if(!e.length)return[];const l=new Map;for(const a of e)...
function Ye (line 1) | function Ye(e,t){let l=arguments.length>2&&arguments[2]!==void 0?argumen...
function Xe (line 1) | function Xe(e,t,l){const a=[];for(const n of e)"type"in n&&n.type==="gro...
function sa (line 1) | function sa(e,t,l,a){const n=k(()=>t.value.length?Ye($(e),t.value.map(o=...
method setup (line 1) | setup(e,t){let{slots:l}=t;const{isGroupOpen:a,toggleGroup:n,extractRows:...
function da (line 1) | function da(e){const t=C(()=>e.expandOnClick),l=M(e,"expanded",e.expande...
function tt (line 1) | function tt(){const e=K(et);if(!e)throw new Error("foo");return e}
method setup (line 1) | setup(e,t){let{slots:l}=t;const{displayClasses:a,mobile:n}=xe(e,"v-data-...
method setup (line 1) | setup(e,t){let{attrs:l,slots:a}=t;const{columns:n}=se(),{expandOnClick:r...
function ga (line 1) | function ga(e,t,l,a){const n=e.returnObject?t:ae(t,e.itemValue),r=ae(t,e...
function ma (line 1) | function ma(e,t,l){return t.map((a,n)=>ga(e,a,n,l))}
function va (line 1) | function va(e,t){return{items:k(()=>ma(e,e.items,t.value))}}
function ya (line 1) | function ya(e){let{page:t,itemsPerPage:l,sortBy:a,groupBy:n,search:r}=e;...
method setup (line 1) | setup(e,t){let{attrs:l,slots:a}=t;const{groupBy:n}=na(e),{initialSortOrd...
FILE: embedFS/dist/assets/VDataTableServer-DQucjpu4.js
method setup (line 1) | setup(e,T){let{attrs:V,slots:a}=T;const{groupBy:r}=M(e),{initialSortOrde...
FILE: embedFS/dist/assets/VDialog-BetymGlx.js
method setup (line 1) | setup(e,f){let{emit:i,slots:s}=f;const n=b(e,"modelValue"),{scopeId:v}=x...
FILE: embedFS/dist/assets/VExpansionPanels-B0znoeId.js
method setup (line 1) | setup(e,c){let{slots:n}=c;const a=h(x);if(!a)throw new Error("[Vuetify] ...
method setup (line 1) | setup(e,c){let{slots:n}=c;const a=h(x);if(!a)throw new Error("[Vuetify] ...
method setup (line 1) | setup(e,c){let{slots:n}=c;const a=O(e,x),{backgroundColorClasses:s,backg...
method setup (line 1) | setup(e,c){let{slots:n}=c;const{next:a,prev:s}=Y(e,x),{themeClasses:d}=Z...
FILE: embedFS/dist/assets/VForm-D9iQptwo.js
method setup (line 1) | setup(a,n){let{slots:m,emit:l}=n;const o=R(a),s=v();function i(r){r.prev...
FILE: embedFS/dist/assets/VInput-D4WPJeZb.js
function Te (line 1) | function Te(e){const t=F(e,"modelValue"),r=V(()=>e.disabled),l=V(()=>e.r...
function Se (line 1) | function Se(e){const t=Q(Y,null);return{...t,isReadonly:g(()=>!!(e?.read...
method setup (line 1) | setup(e,t){let{slots:r}=t;return O(()=>C("label",{class:N(["v-label",{"v...
function we (line 1) | function we(e){const{t}=te();function r(l){let{name:u,color:n,...v}=l;co...
function Le (line 1) | function Le(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
method setup (line 1) | setup(e,t){let{slots:r}=t;const l=g(()=>$(e.messages)),{textColorClasses...
function $e (line 1) | function $e(e){const t=Q(xe,null);if(!e){if(!t)throw new Error("Could no...
function Ae (line 1) | function Ae(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments...
method setup (line 1) | setup(e,t){let{attrs:r,slots:l,emit:u}=t;const{densityClasses:n}=ye(e),{...
FILE: embedFS/dist/assets/VList-D_MCDEbx.js
method setup (line 1) | setup(e,t){let{slots:a}=t;return Ve(),()=>a.default?.()}
method setup (line 1) | setup(e,t){let{slots:a}=t;const{isOpen:s,open:c,id:v}=ge(()=>e.value,()=...
method setup (line 1) | setup(e,t){let{slots:a}=t;const{textColorClasses:s,textColorStyles:c}=xe...
method setup (line 1) | setup(e,t){let{slots:a}=t;return ne(),()=>a.default?.()??e.items?.map((s...
function x (line 1) | function x(e,t){const a=I(t,e.itemTitle,t),s=I(t,e.itemValue,a),c=I(t,e....
function oe (line 1) | function oe(e,t){const a=se(e,x.neededProps),s=[];for(const c of t)s.pus...
function ft (line 1) | function ft(e){const t=P(()=>oe(e,e.items)),a=P(()=>t.value.some(i=>i.va...
function rt (line 1) | function rt(e,t){const a=R(t)?t:I(t,e.itemTitle),s=R(t)?t:I(t,e.itemValu...
function re (line 1) | function re(e,t){const a=[];for(const s of t)a.push(rt(e,s));return a}
function ut (line 1) | function ut(e){return{items:P(()=>re(e,e.items))}}
method setup (line 1) | setup(e,t){let{attrs:a,slots:s,emit:c}=t;const{items:v}=ut(e),{themeClas...
FILE: embedFS/dist/assets/VListItemAction-1-e96EHC.js
method setup (line 1) | setup(t,a){let{slots:e}=a;return n(()=>i(t.tag,{class:l(["v-list-item-ac...
FILE: embedFS/dist/assets/VMenu-CXNpVDF7.js
method setup (line 1) | setup(t,p){let{slots:v}=p;const a=C(t,"modelValue"),{scopeId:V}=D(),{isR...
FILE: embedFS/dist/assets/VNumberInput-uP7vEYUF.js
function Ee (line 1) | function Ee(n){let{toggleUpDown:$}=n,i=-1,w=-1;Q(s);function y(r){s(),V(...
method setup (line 1) | setup(n,$){let{slots:i}=$;const w=ge(),{holdStart:y,holdStop:s}=Ee({togg...
FILE: embedFS/dist/assets/VPagination-BEBHkR5c.js
function va (line 1) | function va(){const a=K([]);W(()=>a.value=[]);function _(n,p){a.value[p]...
method setup (line 1) | setup(a,_){let{slots:n,emit:p}=_;const i=X(a,"modelValue"),{t:f,n:$}=j()...
FILE: embedFS/dist/assets/VRadioGroup-DDJeM_aY.js
method setup (line 1) | setup(e,l){let{slots:s}=l;return v(()=>{const a=d.filterProps(e);return ...
method setup (line 1) | setup(e,l){let{attrs:s,slots:a}=l;const I=h(),c=F(()=>e.id||`radio-group...
FILE: embedFS/dist/assets/VRow-DtJTiJKO.js
function i (line 1) | function i(t,s){return L.reduce((n,e)=>{const a=t+N(e);return n[a]=s(),n...
function T (line 1) | function T(t,s,n){let e=h[t];if(n!=null){if(s){const a=s.replace(t,"");e...
method setup (line 1) | setup(t,s){let{slots:n}=s;const e=V(()=>{const a=[];let l;for(l in u)u[l...
FILE: embedFS/dist/assets/VSelect-CxsUdWRP.js
method setup (line 1) | setup(e,u){let{attrs:n,emit:c,slots:a}=u;const{resizeRef:v,contentRect:g...
function Dt (line 1) | function Dt(e,u){const n=Ue(),c=U(0);Pe(()=>{c.value=parseFloat(e.itemHe...
function Ft (line 1) | function Ft(e,u){let n=e.length-1,c=0,a=0,v=null,g=-1;if(e[n]<u)return n...
method setup (line 1) | setup(e,u){let{slots:n}=u;const c=je("VVirtualScroll"),{dimensionStyles:...
function Lt (line 1) | function Lt(e,u){const n=U(!1);let c;function a(l){cancelAnimationFrame(...
function _t (line 1) | function _t(e){let{groups:u,onLeave:n}=e;function c(l){return l.type==="...
function Mt (line 1) | function Mt(e,u){const n=lt(),c=K(()=>`menu-${n}`),a=ce(()=>be(u)),v=ce(...
method setup (line 1) | setup(e,u){let{slots:n}=u;const{t:c}=nt(),a=q(),v=q(),g=q(),l=q(),o=q(),...
FILE: embedFS/dist/assets/VSelectionControl-CJfnRnc-.js
method setup (line 1) | setup(e,n){let{slots:y}=n;const t=z(e,"modelValue"),o=U(),f=a(()=>e.id||...
function ve (line 1) | function ve(e){const n=te(M,void 0),{densityClasses:y}=oe(e),t=z(e,"mode...
method setup (line 1) | setup(e,n){let{attrs:y,slots:t}=n;const{group:o,densityClasses:f,icon:i,...
FILE: embedFS/dist/assets/VSkeletonLoader-ChbR7Nc0.js
function j (line 1) | function j(e){let a=arguments.length>1&&arguments[1]!==void 0?arguments[...
function o (line 1) | function o(e){const[a,t]=e.split("@");return Array.from({length:t}).map(...
function i (line 1) | function i(e){let a=[];if(!e)return a;const t=E[e];if(e!==t){if(e.includ...
function l (line 1) | function l(e){return e.replace(/\s/g,"").split(",").map(i)}
method setup (line 1) | setup(e,a){let{attrs:t,slots:r}=a;const{backgroundColorClasses:d,backgro...
FILE: embedFS/dist/assets/VSlider-CGi8T5rU.js
function je (line 1) | function je(e,t,r){const n=r==="vertical",c=t.getBoundingClientRect(),m=...
function Ue (line 1) | function Ue(e,t){return"touches"in e&&e.touches.length?e.touches[0][t]:"...
function m (line 1) | function m(k){if(k=parseFloat(k),n.value<=0)return k;const u=ne(k,t.valu...
function x (line 1) | function x(l){const a=S.value?.$el;if(!a)return;const N=t.direction==="v...
function _ (line 1) | function _(l){const a=x(l);a!=null&&c({value:a})}
function j (line 1) | function j(l){l.stopPropagation(),l.preventDefault(),g(l),window.removeE...
function q (line 1) | function q(l){g(l),window.removeEventListener("touchmove",_,z),l.target?...
function U (line 1) | function U(l){O(l),window.addEventListener("touchmove",_,z),l.target?.ad...
function H (line 1) | function H(l){l.button===0&&(l.preventDefault(),O(l),window.addEventList...
method setup (line 1) | setup(e,t){let{slots:r,emit:n}=t;const c=pe(ve),{isRtl:m,rtlClasses:k}=i...
method setup (line 1) | setup(e,t){let{slots:r}=t;const n=pe(ve);if(!n)throw new Error("[Vuetify...
method setup (line 1) | setup(e,t){let{slots:r,emit:n,attrs:c}=t;const m=ae(),k=ae(),{rtlClasses...
FILE: embedFS/dist/assets/VSparkline-CxEGkfC3.js
method setup (line 1) | setup(e,r){let{slots:u}=r;const c=O(),n=i(()=>e.id||`barline-${c}`),g=i(...
function te (line 1) | function te(e,r){let u=arguments.length>2&&arguments[2]!==void 0?argumen...
function P (line 1) | function P(e){return parseInt(e,10)}
function ae (line 1) | function ae(e,r,u){return P(e.x+u.x)===P(2*r.x)&&P(e.y+u.y)===P(2*r.y)}
function W (line 1) | function W(e,r){return Math.sqrt(Math.pow(r.x-e.x,2)+Math.pow(r.y-e.y,2))}
function E (line 1) | function E(e,r,u){const c={x:e.x-r.x,y:e.y-r.y},n=Math.sqrt(c.x*c.x+c.y*...
method setup (line 1) | setup(e,r){let{slots:u}=r;const c=O(),n=i(()=>e.id||`trendline-${c}`),g=...
method setup (line 1) | setup(e,r){let{slots:u}=r;const{textColorClasses:c,textColorStyles:n}=Q(...
FILE: embedFS/dist/assets/VSwitch-CJSeLO2L.js
method setup (line 1) | setup(e,_){let{attrs:A,slots:a}=_;const i=V(e,"indeterminate"),s=V(e,"mo...
FILE: embedFS/dist/assets/VTable-BLAHjZfY.js
method setup (line 1) | setup(e,s){let{slots:a,emit:x}=s;const{themeClasses:l}=r(e),{densityClas...
FILE: embedFS/dist/assets/VTabs-B4m-OW3A.js
function Oe (line 1) | function Oe(e,n){const o=e.changedTouches[0];n.touchstartX=o.clientX,n.t...
function je (line 1) | function je(e,n){const o=e.changedTouches[0];n.touchendX=o.clientX,n.tou...
function Ge (line 1) | function Ge(e,n){const o=e.changedTouches[0];n.touchmoveX=o.clientX,n.to...
function Ne (line 1) | function Ne(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[...
function Fe (line 1) | function Fe(e,n){const o=n.value,t=o?.parent?e.parentElement:e,s=o?.opti...
function Ue (line 1) | function Ue(e,n){const o=n.value?.parent?e.parentElement:e,t=n.instance?...
method setup (line 1) | setup(e,n){let{slots:o,attrs:t}=n;const{textColorClasses:s,textColorStyl...
method setup (line 1) | setup(e,n){let{slots:o}=n;const{themeClasses:t}=we(e),{isRtl:s}=ye(),{t:...
method setup (line 1) | setup(e,n){let{slots:o}=n;const t=ae(G,null),s=se(e,"modelValue"),u=C({g...
method setup (line 1) | setup(e,n){let{slots:o}=n;const t=ae(le),s=Pe(e,re),{isBooted:u}=ze();if...
method setup (line 1) | setup(e,n){let{slots:o}=n;return Y(()=>{const t=J.filterProps(e);return ...
function pe (line 1) | function pe(e){return e?e.map(n=>He(n)?n:{text:n,value:n}):[]}
method setup (line 1) | setup(e,n){let{attrs:o,slots:t}=n;const s=se(e,"modelValue"),u=C(()=>pe(...
FILE: embedFS/dist/assets/VTextField-C23ADfJX.js
method setup (line 1) | setup(e,c){let{slots:n}=c;const r=g(()=>e.max?`${e.value} / ${e.max}`:St...
method setup (line 1) | setup(e,c){let{slots:n}=c;return J(()=>o(Ke,{class:Z(["v-field-label",{"...
method setup (line 1) | setup(e,c){let{attrs:n,emit:r,slots:l}=c;const{themeClasses:d}=_e(e),{lo...
function tl (line 1) | function tl(e){const c=ve(),n=Oe(0),r=g(()=>e.autocomplete==="suppress")...
function al (line 1) | function al(e){function c(n,r){if(!e.autofocus||!n)return;const l=r[0].t...
method setup (line 1) | setup(e,c){let{attrs:n,emit:r,slots:l}=c;const d=Ue(e,"modelValue",void ...
FILE: embedFS/dist/assets/VTimePicker-BiJhf8Qg.js
method setup (line 1) | setup(e,s){let{emit:c}=s;const k=y(null),C=y(null),f=y(void 0),a=y(!1),o...
method setup (line 1) | setup(e,s){let{emit:c}=s;const{textColorClasses:k,textColorStyles:C}=be(...
function N (line 1) | function N(e){let s=arguments.length>1&&arguments[1]!==void 0?arguments[...
function Ve (line 1) | function Ve(e){return e?(e-1)%12+1:12}
function Q (line 1) | function Q(e,s){return e%12+(s==="pm"?12:0)}
function G (line 1) | function G(e){const s=e.replaceAll(/\D/g,"");return s.length>0?Number(s)...
function Oe (line 1) | function Oe(e,s,c){{if(e===23&&s)return{value:0};if(e===0&&!s)return{val...
function We (line 1) | function We(e,s){return e===59&&s?0:e===0&&!s?59:e+(s?1:-1)}
function Se (line 1) | function Se(e){const s=V(()=>{const f=e.min?Number(e.min.split(":")[0]):...
method setup (line 1) | setup(e,s){let{emit:c}=s;const{t:k}=ye(),{isAllowedHour:C,isAllowedMinut...
method setup (line 1) | setup(e,s){let{slots:c}=s;const{backgroundColorClasses:k,backgroundColor...
method setup (line 1) | setup(e,s){let{emit:c,slots:k}=s;const{t:C}=ye(),{densityClasses:f}=$e(e...
FILE: embedFS/dist/assets/VToolbar-BhFwV7qm.js
method setup (line 1) | setup(e,s){let{slots:t}=s;return v(()=>{const n=!!(t.default||t.text||e....
method setup (line 1) | setup(e,s){let{slots:t}=s;const{backgroundColorClasses:n,backgroundColor...
FILE: embedFS/dist/assets/VTooltip-BV7Wxv--.js
method setup (line 1) | setup(t,v){let{slots:n}=v;const o=O(t,"modelValue"),{scopeId:g}=x(),f=F(...
FILE: embedFS/dist/assets/_...error_-C0r044-X.js
method setup (line 1) | setup(o){const A=o;return(N,O)=>(Q(),g("div",p,[A.statusCode?(Q(),g("h1"...
method setup (line 1) | setup(o){const A=r(),N=c(()=>A.global.name.value==="light"?k:K);return(O...
FILE: embedFS/dist/assets/access-C8mK6Ha9.js
method setup (line 1) | setup(p){return(m,i)=>(t(),r(o,{type:"access"}))}
FILE: embedFS/dist/assets/add-qw2GdfGL.js
method setup (line 1) | setup(Ie){const c=se(),{mobile:G}=ne(),{t}=ie(),W=i([]),D=i(!1),k=()=>{c...
FILE: embedFS/dist/assets/announce-aAv7G3Cd.js
method setup (line 1) | setup(Te){const V=P(),J=Q(),{t:n}=X(),{mobile:A}=Y(),L=c(window.innerHei...
FILE: embedFS/dist/assets/axios-DNCpvJag.js
function Qe (line 1) | function Qe(e,t){return function(){return e.apply(t,arguments)}}
function K (line 1) | function K(e){return e!==null&&!$(e)&&e.constructor!==null&&!$(e.constru...
function Ot (line 1) | function Ot(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t...
function Lt (line 1) | function Lt(){return typeof globalThis<"u"?globalThis:typeof self<"u"?se...
function X (line 1) | function X(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;le...
function tt (line 1) | function tt(e,t){if(K(e))return null;t=t.toLowerCase();const n=Object.ke...
function we (line 1) | function we(){const{caseless:e,skipUndefined:t}=nt(this)&&this||{},n={},...
function sn (line 1) | function sn(e){return!!(e&&x(e.append)&&e[Ze]==="FormData"&&e[ce])}
method from (line 1) | static from(t,n,r,s,i,o){const c=new ot(t.message,n||t.code,r,s,i);retur...
method constructor (line 1) | constructor(t,n,r,s,i){super(t),Object.defineProperty(this,"message",{va...
method toJSON (line 1) | toJSON(){return{message:this.message,name:this.name,description:this.des...
function Ee (line 1) | function Ee(e){return a.isPlainObject(e)||a.isArray(e)}
function it (line 1) | function it(e){return a.endsWith(e,"[]")?e.slice(0,-2):e}
function he (line 1) | function he(e,t,n){return e?e.concat(t).map(function(s,i){return s=it(s)...
function dn (line 1) | function dn(e){return a.isArray(e)&&!e.some(Ee)}
function fe (line 1) | function fe(e,t,n){if(!a.isObject(e))throw new TypeError("target must be...
function ke (line 1) | function ke(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E...
function Se (line 1) | function Se(e,t){this._pairs=[],e&&fe(e,this,t)}
function hn (line 1) | function hn(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace...
function ct (line 1) | function ct(e,t,n){if(!t)return e;const r=n&&n.encode||hn,s=a.isFunction...
class je (line 1) | class je{constructor(){this.handlers=[]}use(t,n,r){return this.handlers....
method constructor (line 1) | constructor(){this.handlers=[]}
method use (line 1) | use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchrono...
method eject (line 1) | eject(t){this.handlers[t]&&(this.handlers[t]=null)}
method clear (line 1) | clear(){this.handlers&&(this.handlers=[])}
method forEach (line 1) | forEach(t){a.forEach(this.handlers,function(r){r!==null&&t(r)})}
function On (line 1) | function On(e,t){return fe(e,new O.classes.URLSearchParams,{visitor:func...
function Tn (line 1) | function Tn(e){return a.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"...
function An (line 1) | function An(e){const t={},n=Object.keys(e);let r;const s=n.length;let i;...
function lt (line 1) | function lt(e){function t(n,r,s,i){let o=n[i++];if(o==="__proto__")retur...
function xn (line 1) | function xn(e,t,n){if(a.isString(e))try{return(t||JSON.parse)(e),a.trim(...
function Pn (line 2) | function Pn(e){let t=0,n=e.length;for(;t<n;){const r=e.charCodeAt(t);if(...
function W (line 2) | function W(e){return e&&String(e).trim().toLowerCase()}
function Dn (line 2) | function Dn(e){return Pn(e.replace(Nn,""))}
function se (line 2) | function se(e){return e===!1||e==null?e:a.isArray(e)?e.map(se):Dn(String...
function Fn (line 2) | function Fn(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;...
function me (line 2) | function me(e,t,n,r,s){if(a.isFunction(r))return r.call(this,t,n);if(s&&...
function Ln (line 2) | function Ln(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(...
function Bn (line 2) | function Bn(e,t){const n=a.toCamelCase(" "+t);["get","set","has"].forEac...
method constructor (line 2) | constructor(t){t&&this.set(t)}
method set (line 2) | set(t,n,r){const s=this;function i(c,d,u){const l=W(d);if(!l)throw new E...
method get (line 2) | get(t,n){if(t=W(t),t){const r=a.findKey(this,t);if(r){const s=this[r];if...
method has (line 2) | has(t,n){if(t=W(t),t){const r=a.findKey(this,t);return!!(r&&this[r]!==vo...
method delete (line 2) | delete(t,n){const r=this;let s=!1;function i(o){if(o=W(o),o){const c=a.f...
method clear (line 2) | clear(t){const n=Object.keys(this);let r=n.length,s=!1;for(;r--;){const ...
method normalize (line 2) | normalize(t){const n=this,r={};return a.forEach(this,(s,i)=>{const o=a.f...
method concat (line 2) | concat(...t){return this.constructor.concat(this,...t)}
method toJSON (line 2) | toJSON(t){const n=Object.create(null);return a.forEach(this,(r,s)=>{r!=n...
method [Symbol.iterator] (line 2) | [Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator...
method toString (line 2) | toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n).j...
method getSetCookie (line 3) | getSetCookie(){return this.get("set-cookie")||[]}
method [Symbol.toStringTag] (line 3) | get[Symbol.toStringTag](){return"AxiosHeaders"}
method from (line 3) | static from(t){return t instanceof this?t:new this(t)}
method concat (line 3) | static concat(t,...n){const r=new this(t);return n.forEach(s=>r.set(s)),r}
method accessor (line 3) | static accessor(t){const r=(this[Ie]=this[Ie]={accessors:{}}).accessors,...
method set (line 3) | set(r){this[n]=r}
function be (line 3) | function be(e,t){const n=this||G,r=t||n,s=C.from(r.headers);let i=r.data...
function ut (line 3) | function ut(e){return!!(e&&e.__CANCEL__)}
method constructor (line 3) | constructor(t,n,r){super(t??"canceled",b.ERR_CANCELED,n,r),this.name="Ca...
function ft (line 3) | function ft(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.st...
function kn (line 3) | function kn(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1...
function jn (line 3) | function jn(e,t){e=e||10;const n=new Array(e),r=new Array(e);let s=0,i=0...
function In (line 3) | function In(e,t){let n=0,r=1e3/t,s,i;const o=(u,l=Date.now())=>{n=l,s=nu...
method write (line 3) | write(e,t,n,r,s,i,o){if(typeof document>"u")return;const c=[`${e}=${enco...
method read (line 3) | read(e){if(typeof document>"u")return null;const t=document.cookie.match...
method remove (line 3) | remove(e){this.write(e,"",Date.now()-864e5,"/")}
method write (line 3) | write(){}
method read (line 3) | read(){return null}
method remove (line 3) | remove(){}
function Mn (line 3) | function Mn(e){return typeof e!="string"?!1:/^([a-z][a-z\d+\-.]*:)?\/\//...
function $n (line 3) | function $n(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""...
function dt (line 3) | function dt(e,t,n){let r=!Mn(t);return e&&(r||n===!1)?$n(e,t):t}
function q (line 3) | function q(e,t){t=t||{};const n={};function r(u,l,m,y){return a.isPlainO...
function p (line 3) | function p(){S&&S(),h&&h(),s.cancelToken&&s.cancelToken.unsubscribe(l),s...
function w (line 3) | function w(){if(!f)return;const g=C.from("getAllResponseHeaders"in f&&f....
method pull (line 3) | async pull(d){try{const{done:u,value:l}=await s.next();if(u){c(),d.close...
method cancel (line 3) | cancel(d){return c(d),s.return()}
method duplex (line 3) | get duplex(){return h=!0,"half"}
function Yn (line 3) | function Yn(e,t){e=a.isArray(e)?e:[e];const{length:n}=e;let r,s;const i=...
function ye (line 5) | function ye(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.sign...
function ve (line 5) | function ve(e){return ye(e),e.headers=C.from(e.headers),e.data=be.call(e...
function s (line 5) | function s(i,o){return"[Axios v"+bt+"] Transitional option '"+i+"'"+o+(r...
function er (line 5) | function er(e,t,n){if(typeof e!="object")throw new b("options must be an...
method constructor (line 5) | constructor(t){this.defaults=t||{},this.interceptors={request:new je,res...
method request (line 5) | async request(t,n){try{return await this._request(t,n)}catch(r){if(r ins...
method _request (line 9) | _request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=q(this.defa...
method getUri (line 9) | getUri(t){t=q(this.defaults,t);const n=dt(t.baseURL,t.url,t.allowAbsolut...
function n (line 9) | function n(r){return function(i,o,c){return this.request(q(c||{},{method...
method constructor (line 9) | constructor(t){if(typeof t!="function")throw new TypeError("executor mus...
method throwIfRequested (line 9) | throwIfRequested(){if(this.reason)throw this.reason}
method subscribe (line 9) | subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this....
method unsubscribe (line 9) | unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexO...
method toAbortSignal (line 9) | toAbortSignal(){const t=new AbortController,n=r=>{t.abort(r)};return thi...
method source (line 9) | static source(){let t;return{token:new yt(function(s){t=s}),cancel:t}}
function nr (line 9) | function nr(e){return function(n){return e.apply(null,n)}}
function rr (line 9) | function rr(e){return a.isObject(e)&&e.isAxiosError===!0}
function wt (line 9) | function wt(e){const t=new I(e),n=Qe(I.prototype.request,t);return a.ext...
FILE: embedFS/dist/assets/backup-D4Jzm4dn.js
method setup (line 1) | setup(ue){const u=J(),G=K(),{t:o}=W(),{mobile:z}=O(),I=m(window.innerHei...
FILE: embedFS/dist/assets/base-Dq3aguG-.js
method setup (line 1) | setup(e,h){let{emit:r,slots:i}=h;const{t:n}=Se();function m(){r("click:p...
method setup (line 1) | setup(e,h){let{slots:r}=h;const i=we(e,ae,!0),n=k(()=>i?.value.value??e....
method setup (line 1) | setup(e,h){let{slots:r}=h;const i=me(ae,null),n=Ce(e,"modelValue"),m=k({...
method setup (line 1) | setup(e,h){let{slots:r}=h;return M(()=>{const i=ce.filterProps(e);return...
method setup (line 1) | setup(e,h){let{slots:r}=h;const{items:i,next:n,prev:m,selected:g}=Te(e,a...
method setup (line 1) | setup(e){const h=b(window.innerHeight),r=ze(),i=Oe();pe(),$e();const{t:n...
FILE: embedFS/dist/assets/blank-CdkSWbzI.js
function _ (line 1) | function _(l,i){const e=c("router-view");return a(),t("div",s,[r(e)])}
FILE: embedFS/dist/assets/chat-CR9ZeRZy.js
method setup (line 1) | setup(Y){const c=R(),N=A();U();const{t}=$(),n=d("current"),T=d([]),B=()=...
FILE: embedFS/dist/assets/chat-DNbpOz0I.js
method setup (line 1) | setup(E){const X=E,p=R(),{mobile:P}=Z(),{t:a}=W(),B=z([]),D=z(20),g=z(!1...
FILE: embedFS/dist/assets/clean-C3opSXJz.js
method setup (line 1) | setup(f,_){let{slots:b}=_;const{themeClasses:C}=U(f),{isSelected:a,selec...
method setup (line 1) | setup(f,_){let{slots:b}=_;const{isSelected:C,select:a,toggle:g,selectedC...
method setup (line 1) | setup(f){const _=Z(),b=ee(),{mobile:C}=le(),{t:a}=ae(),g=I({game:0,chat:...
FILE: embedFS/dist/assets/default-BQziqBIh.js
method setup (line 1) | setup(t){return(e,r)=>(b(),M("li",Zt,[_("div",te,[_("span",{class:"title...
method setup (line 1) | setup(t,{emit:e}){const r=t,l=e,n=St(),a=()=>{l("toggle"),r.item.to==="/...
method setup (line 1) | setup(t){return(e,r)=>(b(),M("li",{class:O(["nav-link",{disabled:t.item....
method setup (line 1) | setup(t){const e=wt(),{t:r}=lt(),l=ot(),n=e.menus,a=H(null),i=c=>{for(co...
function k (line 1) | function k(t){return getComputedStyle(t)}
function S (line 1) | function S(t,e){for(var r in e){var l=e[r];typeof l=="number"&&(l=l+"px"...
function U (line 1) | function U(t){var e=document.createElement("div");return e.className=t,e}
function V (line 1) | function V(t,e){if(!vt)throw new Error("No element matching method suppo...
function $ (line 1) | function $(t){t.remove?t.remove():t.parentNode&&t.parentNode.removeChild...
function mt (line 1) | function mt(t,e){return Array.prototype.filter.call(t.children,function(...
function Tt (line 1) | function Tt(t,e){var r=t.element.classList,l=w.state.scrolling(e);r.cont...
function Xt (line 1) | function Xt(t,e){Yt[e]=setTimeout(function(){return t.isAlive&&t.element...
function ce (line 1) | function ce(t,e){Tt(t,e),Xt(t,e)}
function q (line 1) | function q(t){if(typeof window.CustomEvent=="function")return new Custom...
function z (line 1) | function z(t,e,r,l,n){l===void 0&&(l=!0),n===void 0&&(n=!1);var a;if(e==...
function ue (line 1) | function ue(t,e,r,l,n){var a=r[0],i=r[1],u=r[2],o=r[3],c=r[4],h=r[5];l==...
function y (line 1) | function y(t){return parseInt(t,10)||0}
function de (line 1) | function de(t){return V(t,"input,[contenteditable]")||V(t,"select,[conte...
function he (line 1) | function he(t){var e=k(t);return y(e.width)+y(e.paddingLeft)+y(e.padding...
function C (line 1) | function C(t){var e=t.element,r=Math.floor(e.scrollTop),l=e.getBoundingC...
function gt (line 1) | function gt(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.s...
function fe (line 1) | function fe(t,e){var r={width:e.railXWidth},l=Math.floor(t.scrollTop);e....
function pe (line 1) | function pe(t){t.event.bind(t.scrollbarY,"mousedown",function(e){return ...
function ve (line 1) | function ve(t){bt(t,["containerHeight","contentHeight","pageY","railYHei...
function bt (line 1) | function bt(t,e){var r=e[0],l=e[1],n=e[2],a=e[3],i=e[4],u=e[5],o=e[6],c=...
function me (line 1) | function me(t){var e=t.element,r=function(){return V(e,":hover")},l=func...
function ge (line 1) | function ge(t){var e=t.element;function r(i,u){var o=Math.floor(e.scroll...
function be (line 1) | function be(t){if(!P.supportsTouch&&!P.supportsIePointer)return;var e=t....
method setup (line 1) | setup(t,{expose:e,emit:r}){const l=t,n=r,a=H(null),i=H(null);nt(()=>l.op...
method setup (line 1) | setup(t){const e=t,{mdAndDown:r}=F(),l=H(),n=ot();nt(()=>n.path,()=>{e.t...
method setup (line 1) | setup(t,{slots:e}){const r=H(!1),l=H(!1),n=Vt(r),a=ot(),{mdAndDown:i}=F(...
method setup (line 1) | setup(t){const{t:e}=lt(),r=wt(),l=r.userInfo,n=H();switch(l.avatar){case...
method setup (line 1) | setup(t){const{t:e}=lt(),{mobile:r}=F(),l=Ct(),n=H({local:0,server:0}),a...
method setup (line 1) | setup(t){const{mobile:e}=F();return(r,l)=>{const n=at("router-link");ret...
method setup (line 1) | setup(t){const e=H(!0);return At("refresh",l=>e.value=l),(l,n)=>{const a...
FILE: embedFS/dist/assets/dialog-transition-B2t_G0u1.js
method setup (line 1) | setup(a,o){let{slots:e}=o;const r={onBeforeEnter(t){t.style.pointerEvent...
function b (line 1) | function b(a){const o=a.querySelector(":scope > .v-card, :scope > .v-she...
function A (line 1) | function A(a,o){const e=B(a),r=O(o),[t,g]=getComputedStyle(o).transformO...
FILE: embedFS/dist/assets/download-BZbCclQJ.js
method setup (line 1) | setup(E){const S=I(),{t:s}=N(),{mobile:D}=P(),g=i([{title:s("game.mod.do...
FILE: embedFS/dist/assets/download-InxnfqWA.js
method setup (line 1) | setup(H){const r=S(),w=V();x();const{t}=T(),d=f(!1),y=()=>{d.value=!0;co...
FILE: embedFS/dist/assets/eventBus-B8tShFcG.js
method on (line 1) | on(s,t){this.events[s]||(this.events[s]=[]),this.events[s].push(t)}
method emit (line 1) | emit(s,t){this.events[s]&&this.events[s].forEach(e=>e(t))}
method off (line 1) | off(s,t){this.events[s]&&(this.events[s]=this.events[s].filter(e=>e!==t))}
FILE: embedFS/dist/assets/filter-Dycv7ILy.js
method setup (line 1) | setup(e,t){let{slots:l}=t;const n=K(e,"indeterminate"),s=K(e,"modelValue...
function V (line 1) | function V(e,t){if(!(e==null||typeof e=="boolean"||e===-1))return typeof...
function D (line 1) | function D(e,t,l){const n=[],s=l?.default??U,u=l?.filterKeys?_(l.filterK...
function W (line 1) | function W(e,t,l,n){const s=O([]),u=O(new Map),a=R(()=>n?.transform?y(t)...
function X (line 1) | function X(e,t,l){return l==null||!l.length?t:l.map((n,s)=>{const u=s===...
FILE: embedFS/dist/assets/game-OoVHjMuc.js
method setup (line 1) | setup(we){const f=le(),Q=oe(),{mobile:J}=ae(),{t:l}=se(),v=i("current"),...
FILE: embedFS/dist/assets/history-DUBuZMd3.js
method setup (line 1) | setup(V){const k=V,h=v(),{mobile:b}=w(),{t:o}=x(),g=[{key:"uid",title:o(...
FILE: embedFS/dist/assets/index-BRyNS1GG.js
method setup (line 1) | setup(ga){const f=$(),H=j(),{mobile:p}=G(),{t:l}=K(),s=u({username:f.use...
FILE: embedFS/dist/assets/index-BhZX0V6g.js
function k (line 1) | function k(r,e){if(typeof e!="function"&&e!==null)throw new TypeError("C...
function r (line 1) | function r(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,thi...
function r (line 1) | function r(){this.browser=new TL,this.node=!1,this.wxa=!1,this.worker=!1...
function CL (line 1) | function CL(r,e){var t=e.browser,a=r.match(/Firefox\/([\d.]+)/),n=r.matc...
function LL (line 1) | function LL(r){var e={};if(typeof JSON>"u")return e;for(var t=0;t<r.leng...
function Rw (line 1) | function Rw(r){for(var e in Mr)r[e]&&(Mr[e]=r[e])}
function dg (line 1) | function dg(){return OL++}
function fh (line 1) | function fh(){for(var r=[],e=0;e<arguments.length;e++)r[e]=arguments[e];...
function rt (line 1) | function rt(r){if(r==null||typeof r!="object")return r;var e=r,t=So.call...
function ut (line 1) | function ut(r,e,t){if(!tt(e)||!tt(r))return t?rt(e):r;for(var a in e)if(...
function hh (line 1) | function hh(r,e){for(var t=r[0],a=1,n=r.length;a<n;a++)t=ut(t,r[a],e);re...
function V (line 1) | function V(r,e){if(Object.assign)Object.assign(r,e);else for(var t in e)...
function J (line 1) | function J(r,e,t){for(var a=_t(e),n=0,i=a.length;n<i;n++){var o=a[n];(t?...
function vt (line 1) | function vt(r,e){if(r){if(r.indexOf)return r.indexOf(e);for(var t=0,a=r....
function gg (line 1) | function gg(r,e){var t=r.prototype;function a(){}a.prototype=e.prototype...
function $t (line 1) | function $t(r,e,t){if(r="prototype"in r?r.prototype:r,e="prototype"in e?...
function he (line 1) | function he(r){return!r||typeof r=="string"?!1:typeof r.length=="number"}
function D (line 1) | function D(r,e,t){if(r&&e)if(r.forEach&&r.forEach===RL)r.forEach(e,t);el...
function G (line 1) | function G(r,e,t){if(!r)return[];if(!e)return vh(r);if(r.map&&r.map===kL...
function ur (line 1) | function ur(r,e,t,a){if(r&&e){for(var n=0,i=r.length;n<i;n++)t=e.call(a,...
function At (line 1) | function At(r,e,t){if(!r)return[];if(!e)return vh(r);if(r.filter&&r.filt...
function Ow (line 1) | function Ow(r,e,t){if(r&&e){for(var a=0,n=r.length;a<n;a++)if(e.call(t,r...
function _t (line 1) | function _t(r){if(!r)return[];if(Object.keys)return Object.keys(r);var e...
function BL (line 1) | function BL(r,e){for(var t=[],a=2;a<arguments.length;a++)t[a-2]=argument...
function lt (line 1) | function lt(r){for(var e=[],t=1;t<arguments.length;t++)e[t-1]=arguments[...
function z (line 1) | function z(r){return Array.isArray?Array.isArray(r):So.call(r)==="[objec...
function K (line 1) | function K(r){return typeof r=="function"}
function Y (line 1) | function Y(r){return typeof r=="string"}
function hf (line 1) | function hf(r){return So.call(r)==="[object String]"}
function wt (line 1) | function wt(r){return typeof r=="number"}
function tt (line 1) | function tt(r){var e=typeof r;return e==="function"||!!r&&e==="object"}
function wp (line 1) | function wp(r){return!!Ew[So.call(r)]}
function Re (line 1) | function Re(r){return!!kw[So.call(r)]}
function ni (line 1) | function ni(r){return typeof r=="object"&&typeof r.nodeType=="number"&&t...
function Sl (line 1) | function Sl(r){return r.colorStops!=null}
function Nw (line 1) | function Nw(r){return r.image!=null}
function Bw (line 1) | function Bw(r){return So.call(r)==="[object RegExp]"}
function so (line 1) | function so(r){return r!==r}
function re (line 1) | function re(){for(var r=[],e=0;e<arguments.length;e++)r[e]=arguments[e];...
function st (line 1) | function st(r,e){return r??e}
function Tr (line 1) | function Tr(r,e,t){return r??e??t}
function vh (line 1) | function vh(r){for(var e=[],t=1;t<arguments.length;t++)e[t-1]=arguments[...
function ch (line 1) | function ch(r){if(typeof r=="number")return[r,r,r,r];var e=r.length;retu...
function xe (line 1) | function xe(r,e){if(!r)throw new Error(e)}
function sr (line 1) | function sr(r){return r==null?null:typeof r.trim=="function"?r.trim():r....
function Us (line 1) | function Us(r){r[Vw]=!0}
function Qi (line 1) | function Qi(r){return r[Vw]}
function r (line 1) | function r(){this.data={}}
function zL (line 1) | function zL(){return zw?new Map:new VL}
function r (line 1) | function r(e){var t=z(e);this.data=zL();var a=this;e instanceof r?e.each...
function Z (line 1) | function Z(r){return new Gw(r)}
function lo (line 1) | function lo(r,e){for(var t=new r.constructor(r.length+e.length),a=0;a<r....
function xl (line 1) | function xl(r,e){var t;if(Object.create)t=Object.create(r);else{var a=fu...
function yg (line 1) | function yg(r){var e=r.style;e.webkitUserSelect="none",e.userSelect="non...
function X (line 1) | function X(r,e){return r.hasOwnProperty(e)}
function Yt (line 1) | function Yt(){}
function rn (line 1) | function rn(r,e){return r==null&&(r=0),e==null&&(e=0),[r,e]}
function ye (line 1) | function ye(r,e){return r[0]=e[0],r[1]=e[1],r}
function $r (line 1) | function $r(r){return[r[0],r[1]]}
function Fw (line 1) | function Fw(r,e,t){return r[0]=e,r[1]=t,r}
function Tp (line 1) | function Tp(r,e,t){return r[0]=e[0]+t[0],r[1]=e[1]+t[1],r}
function vf (line 1) | function vf(r,e,t,a){return r[0]=e[0]+t[0]*a,r[1]=e[1]+t[1]*a,r}
function Na (line 1) | function Na(r,e,t){return r[0]=e[0]-t[0],r[1]=e[1]-t[1],r}
function Ys (line 1) | function Ys(r){return Math.sqrt(mg(r))}
function mg (line 1) | function mg(r){return r[0]*r[0]+r[1]*r[1]}
function WL (line 1) | function WL(r,e,t){return r[0]=e[0]*t[0],r[1]=e[1]*t[1],r}
function UL (line 1) | function UL(r,e,t){return r[0]=e[0]/t[0],r[1]=e[1]/t[1],r}
function YL (line 1) | function YL(r,e){return r[0]*e[0]+r[1]*e[1]}
function Ms (line 1) | function Ms(r,e,t){return r[0]=e[0]*t,r[1]=e[1]*t,r}
function ci (line 1) | function ci(r,e){var t=Ys(e);return t===0?(r[0]=0,r[1]=0):(r[0]=e[0]/t,r...
function cf (line 1) | function cf(r,e){return Math.sqrt((r[0]-e[0])*(r[0]-e[0])+(r[1]-e[1])*(r...
function Hw (line 1) | function Hw(r,e){return(r[0]-e[0])*(r[0]-e[0])+(r[1]-e[1])*(r[1]-e[1])}
function $L (line 1) | function $L(r,e){return r[0]=-e[0],r[1]=-e[1],r}
function Is (line 1) | function Is(r,e,t,a){return r[0]=e[0]+a*(t[0]-e[0]),r[1]=e[1]+a*(t[1]-e[...
function ue (line 1) | function ue(r,e,t){var a=e[0],n=e[1];return r[0]=t[0]*a+t[2]*n+t[4],r[1]...
function la (line 1) | function la(r,e,t){return r[0]=Math.min(e[0],t[0]),r[1]=Math.min(e[1],t[...
function ua (line 1) | function ua(r,e,t){return r[0]=Math.max(e[0],t[0]),r[1]=Math.max(e[1],t[...
function r (line 1) | function r(e,t){this.target=e,this.topTarget=t&&t.topTarget}
function r (line 1) | function r(e){this.handler=e,e.on("mousedown",this._dragStart,this),e.on...
function r (line 1) | function r(e){e&&(this._$eventProcessor=e)}
function Ap (line 1) | function Ap(r,e,t,a,n,i){var o=a+"-"+n,s=r.length;if(i.hasOwnProperty(o)...
function km (line 1) | function km(r,e){var t=[[r[0],r[1],1,0,0,0,-e[0]*r[0],-e[0]*r[1]],[0,0,0...
function KL (line 1) | function KL(r,e,t,a,n){return Cp(ev,e,a,n,!0)&&Cp(r,t,ev[0],ev[1])}
function Cp (line 1) | function Cp(r,e,t,a,n){if(e.getBoundingClientRect&&yt.domSupported&&!Ww(...
function jL (line 1) | function jL(r,e){var t=e.markers;if(t)return t;t=e.markers=[];for(var a=...
function JL (line 1) | function JL(r,e,t){for(var a=t?"invTrans":"trans",n=e[a],i=e.srcCoords,o...
function Ww (line 1) | function Ww(r){return r.nodeName.toUpperCase()==="CANVAS"}
function _e (line 1) | function _e(r){return r==null?"":(r+"").replace(QL,function(e,t){return ...
function Dp (line 1) | function Dp(r,e,t,a){return t=t||{},a?Nm(r,e,t):r2&&e.layerX!=null&&e.la...
function Nm (line 1) | function Nm(r,e,t){if(yt.domSupported&&r.getBoundingClientRect){var a=e....
function _g (line 1) | function _g(r){return r||window.event}
function tr (line 1) | function tr(r,e,t){if(e=_g(e),e.zrX!=null)return e;var a=e.type,n=a&&a.i...
function a2 (line 1) | function a2(r){var e=r.wheelDelta;if(e)return e;var t=r.deltaX,a=r.delta...
function Mp (line 1) | function Mp(r,e,t,a){r.addEventListener(e,t,a)}
function n2 (line 1) | function n2(r,e,t,a){r.removeEventListener(e,t,a)}
function Bm (line 1) | function Bm(r){return r.which===2||r.which===3}
function r (line 1) | function r(){this._track=[]}
function Vm (line 1) | function Vm(r){var e=r[1][0]-r[0][0],t=r[1][1]-r[0][1];return Math.sqrt(...
function o2 (line 1) | function o2(r){return[(r[0][0]+r[1][0])/2,(r[0][1]+r[1][1])/2]}
function Ge (line 1) | function Ge(){return[1,0,0,1,0,0]}
function bl (line 1) | function bl(r){return r[0]=1,r[1]=0,r[2]=0,r[3]=1,r[4]=0,r[5]=0,r}
function ph (line 1) | function ph(r,e){return r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=e[3],r[4]=e[4...
function Xr (line 1) | function Xr(r,e,t){var a=e[0]*t[0]+e[2]*t[1],n=e[1]*t[0]+e[3]*t[1],i=e[0...
function Ir (line 1) | function Ir(r,e,t){return r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=e[3],r[4]=e...
function an (line 1) | function an(r,e,t,a){a===void 0&&(a=[0,0]);var n=e[0],i=e[2],o=e[4],s=e[...
function dh (line 1) | function dh(r,e,t){var a=t[0],n=t[1];return r[0]=e[0]*a,r[1]=e[1]*n,r[2]...
function pi (line 1) | function pi(r,e){var t=e[0],a=e[2],n=e[4],i=e[1],o=e[3],s=e[5],l=t*o-i*a...
function Uw (line 1) | function Uw(r){var e=Ge();return ph(e,r),e}
function r (line 1) | function r(e,t){this.x=e||0,this.y=t||0}
function r (line 1) | function r(e,t,a,n){a<0&&(e=e+a,a=-a),n<0&&(t=t+n,n=-n),this.x=e,this.y=...
function l2 (line 1) | function l2(r,e,t){return{type:r,event:t,target:e.target,topTarget:e.top...
function u2 (line 1) | function u2(){pa(this.event)}
function e (line 1) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.hand...
function r (line 1) | function r(e,t){this.x=e,this.y=t}
function e (line 1) | function e(t,a,n,i,o){var s=r.call(this)||this;return s._hovered=new Ho(...
function v2 (line 1) | function v2(r,e,t){if(r[r.rectHover?"rectContain":"contain"](e,t)){for(v...
function zm (line 1) | function zm(r,e,t,a,n){for(var i=r.length-1;i>=0;i--){var o=r[i],s=void ...
function Xw (line 1) | function Xw(r,e,t){var a=r.painter;return e<0||e>a.getWidth()||t<0||t>a....
function c2 (line 1) | function c2(r){for(var e=0;r>=Zw;)e|=r&1,r>>=1;return r+e}
function Gm (line 1) | function Gm(r,e,t,a){var n=e+1;if(n===t)return 1;if(a(r[n++],r[e])<0){fo...
function p2 (line 1) | function p2(r,e,t){for(t--;e<t;){var a=r[e];r[e++]=r[t],r[t--]=a}}
function Fm (line 1) | function Fm(r,e,t,a,n){for(a===e&&a++;a<t;a++){for(var i=r[a],o=e,s=a,l;...
function iv (line 1) | function iv(r,e,t,a,n,i){var o=0,s=0,l=1;if(i(r,e[t+n])>0){for(s=a-n;l<s...
function ov (line 1) | function ov(r,e,t,a,n,i){var o=0,s=0,l=1;if(i(r,e[t+n])<0){for(s=n+1;l<s...
function d2 (line 1) | function d2(r,e){var t=Wo,a,n,i=0,o=[];a=[],n=[];function s(c,p){a[i]=c,...
function Yu (line 1) | function Yu(r,e,t,a){t||(t=0),a||(a=r.length);var n=a-t;if(!(n<2)){var i...
function sv (line 1) | function sv(){Hm||(Hm=!0,console.warn("z / z2 / zlevel of displayable is...
function Wm (line 1) | function Wm(r,e){return r.zlevel===e.zlevel?r.z===e.z?r.z2-e.z2:r.z-e.z:...
function r (line 1) | function r(){this._roots=[],this._displayList=[],this._displayListLen=0,...
function Ba (line 1) | function Ba(r){return r>-df&&r<df}
function Kw (line 1) | function Kw(r){return r>df||r<-df}
function ee (line 1) | function ee(r,e,t,a,n){var i=1-n;return i*i*(i*r+3*n*e)+n*n*(n*a+3*i*t)}
function Ym (line 1) | function Ym(r,e,t,a,n){var i=1-n;return 3*(((e-r)*i+2*(t-e)*n)*i+(a-t)*n...
function gf (line 1) | function gf(r,e,t,a,n,i){var o=a+3*(e-t)-r,s=3*(t-e*2+r),l=3*(e-r),u=r-n...
function jw (line 1) | function jw(r,e,t,a,n){var i=6*t-12*e+6*r,o=9*e+3*a-3*r-9*t,s=3*e-3*r,l=...
function ja (line 1) | function ja(r,e,t,a,n,i){var o=(e-r)*n+r,s=(t-e)*n+e,l=(a-t)*n+t,u=(s-o)...
function Jw (line 1) | function Jw(r,e,t,a,n,i,o,s,l,u,f){var h,v=.005,c=1/0,p,d,g,y;Fr[0]=l,Fr...
function y2 (line 1) | function y2(r,e,t,a,n,i,o,s,l){for(var u=r,f=e,h=0,v=1/l,c=1;c<=l;c++){v...
function le (line 1) | function le(r,e,t,a){var n=1-a;return n*(n*r+2*a*e)+a*a*t}
function Ip (line 1) | function Ip(r,e,t,a){return 2*((1-a)*(e-r)+a*(t-e))}
function m2 (line 1) | function m2(r,e,t,a,n){var i=r-2*e+t,o=2*(e-r),s=r-a,l=0;if(Ba(i)){if(Kw...
function Qw (line 1) | function Qw(r,e,t){var a=r+t-2*e;return a===0?.5:(r-e)/a}
function $s (line 1) | function $s(r,e,t,a,n){var i=(e-r)*a+r,o=(t-e)*a+e,s=(o-i)*a+i;n[0]=r,n[...
function tT (line 1) | function tT(r,e,t,a,n,i,o,s,l){var u,f=.005,h=1/0;Fr[0]=o,Fr[1]=s;for(va...
function _2 (line 1) | function _2(r,e,t,a,n,i,o){for(var s=r,l=e,u=0,f=1/o,h=1;h<=o;h++){var v...
function Sg (line 1) | function Sg(r){var e=r&&S2.exec(r);if(e){var t=e[1].split(","),a=+sr(t[0...
function r (line 1) | function r(e){this._inited=!1,this._startTime=0,this._pausedTime=0,this....
function r (line 1) | function r(e){this.value=e}
function r (line 1) | function r(){this._len=0}
function r (line 1) | function r(e){this._list=new b2,this._maxSize=10,this._map={},this._maxS...
function Ar (line 1) | function Ar(r){return r=Math.round(r),r<0?0:r>255?255:r}
function w2 (line 1) | function w2(r){return r=Math.round(r),r<0?0:r>360?360:r}
function Xs (line 1) | function Xs(r){return r<0?0:r>1?1:r}
function lv (line 1) | function lv(r){var e=r;return e.length&&e.charAt(e.length-1)==="%"?Ar(pa...
function jn (line 1) | function jn(r){var e=r;return e.length&&e.charAt(e.length-1)==="%"?Xs(pa...
function uv (line 1) | function uv(r,e,t){return t<0?t+=1:t>1&&(t-=1),t*6<1?r+(e-r)*t*6:t*2<1?e...
function Va (line 1) | function Va(r,e,t){return r+(e-r)*t}
function Qe (line 1) | function Qe(r,e,t,a,n){return r[0]=e,r[1]=t,r[2]=a,r[3]=n,r}
function Lp (line 1) | function Lp(r,e){return r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=e[3],r}
function wi (line 1) | function wi(r,e){Zl&&Lp(Zl,e),Zl=rT.put(r,Zl||e.slice())}
function Le (line 1) | function Le(r,e){if(r){e=e||[];var t=rT.get(r);if(t)return Lp(e,t);r=r+"...
function Pp (line 1) | function Pp(r,e){var t=(parseFloat(r[0])%360+360)%360/360,a=jn(r[1]),n=j...
function T2 (line 1) | function T2(r){if(r){var e=r[0]/255,t=r[1]/255,a=r[2]/255,n=Math.min(e,t...
function yf (line 1) | function yf(r,e){var t=Le(r);if(t){for(var a=0;a<3;a++)e<0?t[a]=t[a]*(1-...
function A2 (line 1) | function A2(r){var e=Le(r);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[...
function Ps (line 1) | function Ps(r,e,t){if(!(!(e&&e.length)||!(r>=0&&r<=1))){t=t||[];var a=r*...
function xg (line 1) | function xg(r,e,t){if(!(!(e&&e.length)||!(r>=0&&r<=1))){var a=r*(e.lengt...
function eo (line 1) | function eo(r,e,t,a){var n=Le(r);if(r)return n=T2(n),e!=null&&(n[0]=w2(e...
function Zs (line 1) | function Zs(r,e){var t=Le(r);if(t&&e!=null)return t[3]=Xs(e),Cr(t,"rgba")}
function Cr (line 1) | function Cr(r,e){if(!(!r||!r.length)){var t=r[0]+","+r[1]+","+r[2];retur...
function qs (line 1) | function qs(r,e){var t=Le(r);return t?(.299*t[0]+.587*t[1]+.114*t[2])*t[...
function M2 (line 1) | function M2(){return Cr([Math.round(Math.random()*255),Math.round(Math.r...
function mf (line 1) | function mf(r){if(Y(r)){var e=Xm.get(r);return e||(e=yf(r,-.1),Xm.put(r,...
function Ks (line 1) | function Ks(r){var e;if(!r||r==="transparent")r="none";else if(typeof r=...
function za (line 1) | function za(r){return r<Zm&&r>-Zm}
function ql (line 1) | function ql(r){return _f(r*1e3)/1e3}
function Rp (line 1) | function Rp(r){return _f(r*1e4)/1e4}
function L2 (line 1) | function L2(r){return"matrix("+ql(r[0])+","+ql(r[1])+","+ql(r[2])+","+ql...
function R2 (line 1) | function R2(r,e,t){return t==="top"?r+=e/2:t==="bottom"&&(r-=e/2),r}
function E2 (line 1) | function E2(r){return r&&(r.shadowBlur||r.shadowOffsetX||r.shadowOffsetY)}
function k2 (line 1) | function k2(r){var e=r.style,t=r.getGlobalScale();return[e.shadowColor,(...
function aT (line 1) | function aT(r){return r&&!!r.image}
function O2 (line 1) | function O2(r){return r&&!!r.svgElement}
function bg (line 1) | function bg(r){return aT(r)||O2(r)}
function nT (line 1) | function nT(r){return r.type==="linear"}
function iT (line 1) | function iT(r){return r.type==="radial"}
function oT (line 1) | function oT(r){return r&&(r.type==="linear"||r.type==="radial")}
function gh (line 1) | function gh(r){return"url(#"+r+")"}
function sT (line 1) | function sT(r){var e=r.getGlobalScale(),t=Math.max(e[0],e[1]);return Mat...
function lT (line 1) | function lT(r){var e=r.x||0,t=r.y||0,a=(r.rotation||0)*Ds,n=st(r.scaleX,...
function ia (line 1) | function ia(r,e,t){return(e-r)*t+r}
function fv (line 1) | function fv(r,e,t,a){for(var n=e.length,i=0;i<n;i++)r[i]=ia(e[i],t[i],a)...
function B2 (line 1) | function B2(r,e,t,a){for(var n=e.length,i=n&&e[0].length,o=0;o<n;o++){r[...
function Kl (line 1) | function Kl(r,e,t,a){for(var n=e.length,i=0;i<n;i++)r[i]=e[i]+t[i]*a;ret...
function qm (line 1) | function qm(r,e,t,a){for(var n=e.length,i=n&&e[0].length,o=0;o<n;o++){r[...
function V2 (line 1) | function V2(r,e){for(var t=r.length,a=e.length,n=t>a?e:r,i=Math.min(t,a)...
function z2 (line 1) | function z2(r,e,t){var a=r,n=e;if(!(!a.push||!n.push)){var i=a.length,o=...
function Rs (line 1) | function Rs(r){if(he(r)){var e=r.length;if(he(r[0])){for(var t=[],a=0;a<...
function $u (line 1) | function $u(r){return r[0]=Math.floor(r[0])||0,r[1]=Math.floor(r[1])||0,...
function G2 (line 1) | function G2(r){return he(r&&r[0])?2:1}
function jm (line 1) | function jm(r){return r===kp||r===Op}
function Jl (line 1) | function Jl(r){return r===Xu||r===uT}
function r (line 1) | function r(e){this.keyframes=[],this.discrete=!1,this._invalid=!1,this._...
function r (line 1) | function r(e,t,a,n){if(this._tracks={},this._trackKeys=[],this._maxTime=...
function qi (line 1) | function qi(){return new Date().getTime()}
function e (line 1) | function e(t){var a=r.call(this)||this;return a._running=!1,a._time=0,a....
function a (line 1) | function a(){t._running&&(pf(a),!t._paused&&t.update())}
function Np (line 1) | function Np(r){var e=r.pointerType;return e==="pen"||e==="touch"}
function U2 (line 1) | function U2(r){r.touching=!0,r.touchTimer!=null&&(clearTimeout(r.touchTi...
function cv (line 1) | function cv(r){r&&(r.zrByTouch=!0)}
function Y2 (line 1) | function Y2(r,e){return tr(r.dom,new $2(r,e),!0)}
function fT (line 1) | function fT(r,e){for(var t=e,a=!1;t&&t.nodeType!==9&&!(a=t.domBelongToZr...
function r (line 1) | function r(e,t){this.stopPropagation=Yt,this.stopImmediatePropagation=Yt...
function X2 (line 1) | function X2(r,e){var t=e.domHandlers;yt.pointerEventsSupported?D(vv.poin...
function Z2 (line 1) | function Z2(r,e){yt.pointerEventsSupported?D(Jm.pointer,t):yt.touchEvent...
function Zu (line 1) | function Zu(r,e,t,a){r.mounted[e]=t,r.listenerOpts[e]=a,Mp(r.domTarget,e...
function pv (line 1) | function pv(r){var e=r.mounted;for(var t in e)e.hasOwnProperty(t)&&n2(r....
function r (line 1) | function r(e,t){this.mounted={},this.listenerOpts={},this.touching=!1,th...
function e (line 1) | function e(t,a){var n=r.call(this)||this;return n.__pointerCapturing=!1,...
function cn (line 1) | function cn(r){return r>r0||r<-r0}
function r (line 1) | function r(){}
function vT (line 1) | function vT(r,e){for(var t=0;t<qr.length;t++){var a=qr[t];r[a]=e[a]}}
function Xe (line 1) | function Xe(r,e){e=e||Ka;var t=a0[e];t||(t=a0[e]=new wl(500));var a=t.ge...
function n0 (line 1) | function n0(r,e,t,a){var n=Xe(r,e),i=yh(e),o=ys(0,n,t),s=Yi(0,i,a),l=new...
function Tl (line 1) | function Tl(r,e,t,a){var n=((r||"")+"").split(`
function ys (line 2) | function ys(r,e,t){return t==="right"?r-=e:t==="center"&&(r-=e/2),r}
function Yi (line 2) | function Yi(r,e,t){return t==="middle"?r-=e/2:t==="bottom"&&(r-=e),r}
function yh (line 2) | function yh(r){return Xe("国",r)}
function Lr (line 2) | function Lr(r,e){return typeof r=="string"?r.lastIndexOf("%")>=0?parseFl...
function xf (line 2) | function xf(r,e,t){var a=e.position||"inside",n=e.distance!=null?e.dista...
function r (line 2) | function r(e){this.id=dg(),this.animators=[],this.currentStates=[],this....
function t (line 2) | function t(a,n,i,o){Object.defineProperty(e,a,{get:function(){if(!this[n...
function _v (line 2) | function _v(r,e,t,a,n){t=t||{};var i=[];cT(r,"",r,e,t,a,i,n);var o=i.len...
function Sv (line 2) | function Sv(r,e,t){for(var a=0;a<t;a++)r[a]=e[a]}
function Q2 (line 2) | function Q2(r){return he(r[0])}
function tP (line 2) | function tP(r,e,t){if(he(e[t]))if(he(r[t])||(r[t]=[]),Re(e[t])){var a=e[...
function eP (line 2) | function eP(r,e){return r===e||he(r)&&he(e)&&rP(r,e)}
function rP (line 2) | function rP(r,e){var t=r.length;if(t!==e.length)return!1;for(var a=0;a<t...
function cT (line 2) | function cT(r,e,t,a,n,i,o,s){for(var l=_t(a),u=n.duration,f=n.delay,h=n....
function e (line 2) | function e(t){var a=r.call(this)||this;return a.isGroup=!0,a._children=[...
function aP (line 2) | function aP(r){delete Wn[r]}
function nP (line 2) | function nP(r){if(!r)return!1;if(typeof r=="string")return qs(r,1)<Vp;if...
function r (line 2) | function r(e,t,a){var n=this;this._sleepAfterStill=10,this._stillFrameAc...
function Fp (line 2) | function Fp(r,e){var t=new iP(dg(),r,e);return Wn[t.id]=t,t}
function oP (line 2) | function oP(r){r.dispose()}
function sP (line 2) | function sP(){for(var r in Wn)Wn.hasOwnProperty(r)&&Wn[r].dispose();Wn={}}
function lP (line 2) | function lP(r){return Wn[r]}
function pT (line 2) | function pT(r,e){qu[r]=e}
function dT (line 2) | function dT(r){if(typeof Hp=="function")return Hp(r)}
function gT (line 2) | function gT(r){Hp=r}
function hP (line 2) | function hP(r){return r.replace(/^\s+|\s+$/g,"")}
function Dt (line 2) | function Dt(r,e,t,a){var n=e[0],i=e[1],o=t[0],s=t[1],l=i-n,u=s-o;if(l===...
function W (line 2) | function W(r,e){switch(r){case"center":case"middle":r="50%";break;case"l...
function Wt (line 2) | function Wt(r,e,t){return e==null&&(e=10),e=Math.min(Math.max(0,e),yT),r...
function Ye (line 2) | function Ye(r){return r.sort(function(e,t){return e-t}),r}
function br (line 2) | function br(r){if(r=+r,isNaN(r))return 0;if(r>1e-14){for(var e=1,t=0;t<1...
function mT (line 2) | function mT(r){var e=r.toString().toLowerCase(),t=e.indexOf("e"),a=t>0?+...
function Tg (line 2) | function Tg(r,e){var t=Math.log,a=Math.LN10,n=Math.floor(t(r[1]-r[0])/a)...
function vP (line 2) | function vP(r,e,t){if(!r[e])return 0;var a=_T(r,t);return a[e]||0}
function _T (line 2) | function _T(r,e){var t=ur(r,function(c,p){return c+(isNaN(p)?0:p)},0);if...
function cP (line 2) | function cP(r,e){var t=Math.max(br(r),br(e)),a=r+e;return t>yT?a:Wt(a,t)}
function Ag (line 2) | function Ag(r){var e=Math.PI*2;return(r%e+e)%e}
function uo (line 2) | function uo(r){return r>-i0&&r<i0}
function Ze (line 2) | function Ze(r){if(r instanceof Date)return r;if(Y(r)){var e=pP.exec(r);i...
function ST (line 2) | function ST(r){return Math.pow(10,_h(r))}
function _h (line 2) | function _h(r){if(r===0)return 0;var e=Math.floor(Math.log(r)/Math.LN10)...
function Cg (line 2) | function Cg(r,e){var t=_h(r),a=Math.pow(10,t),n=r/a,i;return e?n<1.5?i=1...
function Ku (line 2) | function Ku(r,e){var t=(r.length-1)*e+1,a=Math.floor(t),n=+r[a-1],i=t-a;...
function Up (line 2) | function Up(r){r.sort(function(l,u){return s(l,u,0)?-1:1});for(var e=-1/...
function Kr (line 2) | function Kr(r){var e=parseFloat(r);return e==r&&(e!==0||!Y(r)||r.indexOf...
function Dg (line 2) | function Dg(r){return!isNaN(Kr(r))}
function xT (line 2) | function xT(){return Math.round(Math.random()*9)}
function bT (line 2) | function bT(r,e){return e===0?r:bT(e,r%e)}
function o0 (line 2) | function o0(r,e){return r==null?e:e==null?r:r*e/bT(r,e)}
function Mt (line 2) | function Mt(r){throw new Error(r)}
function s0 (line 2) | function s0(r,e,t){return(e-r)*t+r}
function Rt (line 2) | function Rt(r){return r instanceof Array?r:r==null?[]:[r]}
function ii (line 2) | function ii(r,e,t){if(r){r[e]=r[e]||{},r.emphasis=r.emphasis||{},r.empha...
function xo (line 2) | function xo(r){return tt(r)&&!z(r)&&!(r instanceof Date)?r.value:r}
function dP (line 2) | function dP(r){return tt(r)&&!(r instanceof Array)}
function AT (line 2) | function AT(r,e,t){var a=t==="normalMerge",n=t==="replaceMerge",i=t==="r...
function gP (line 2) | function gP(r,e,t){var a=[];if(t==="replaceAll")return a;for(var n=0;n<r...
function yP (line 2) | function yP(r,e,t,a){D(a,function(n,i){if(!(!n||n.id==null)){var o=Es(n....
function mP (line 2) | function mP(r,e){D(e,function(t,a){if(!(!t||t.name==null))for(var n=0;n<...
function _P (line 2) | function _P(r,e,t){D(e,function(a){if(a){for(var n,i=0;(n=r[i])&&(n.newO...
function SP (line 2) | function SP(r,e){D(e,function(t){r.push({newOption:t,brandNew:!0,existin...
function xP (line 2) | function xP(r){var e=Z();D(r,function(t){var a=t.existing;a&&e.set(a.id,...
function CT (line 2) | function CT(r,e,t){var a=Jt(e[r],null),n=Jt(t[r],null);return a!=null&&n...
function Es (line 2) | function Es(r){return Jt(r,"")}
function Jt (line 2) | function Jt(r,e){return r==null?e:Y(r)?r:wt(r)||hf(r)?r+"":e}
function Mg (line 2) | function Mg(r){var e=r.name;return!!(e&&e.indexOf(wT))}
function js (line 2) | function js(r){return r&&r.id!=null&&Es(r.id).indexOf(TT)===0}
function bP (line 2) | function bP(r){return TT+r}
function wP (line 2) | function wP(r,e,t){D(r,function(a){var n=a.newOption;tt(n)&&(a.keyInfo.m...
function TP (line 2) | function TP(r,e,t,a){var n=e.type?e.type:t?t.subType:a.determineSubType(...
function AP (line 2) | function AP(r,e){var t={},a={};return n(r||[],t),n(e||[],a,t),[i(t),i(a)...
function oi (line 2) | function oi(r,e){if(e.dataIndexInside!=null)return e.dataIndexInside;if(...
function bt (line 2) | function bt(){var r="__ec_inner_"+CP++;return function(e){return e[r]||(...
function ks (line 2) | function ks(r,e,t){var a=Ig(e,t),n=a.mainTypeSpecified,i=a.queryOptionMa...
function Ig (line 2) | function Ig(r,e){var t;if(Y(r)){var a={};a[r+"Index"]=0,t=a}else t=r;var...
function Al (line 2) | function Al(r,e,t,a){a=a||Kt;var n=t.index,i=t.id,o=t.name,s={models:nul...
function DT (line 2) | function DT(r,e,t){r.setAttribute?r.setAttribute(e,t):r[e]=t}
function MP (line 2) | function MP(r,e){return r.getAttribute?r.getAttribute(e):r[e]}
function IP (line 2) | function IP(r){return r==="auto"?yt.domSupported?"html":"richText":r||"h...
function Yp (line 2) | function Yp(r,e){var t=Z(),a=[];return D(r,function(n){var i=e(n);(t.get...
function MT (line 2) | function MT(r,e,t,a,n){var i=e==null||e==="auto";if(a==null)return a;if(...
function Wr (line 2) | function Wr(r){var e={main:"",sub:""};if(r){var t=r.split(LP);e.main=t[0...
function PP (line 2) | function PP(r){xe(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(r),'componen...
function RP (line 2) | function RP(r){return!!(r&&r[IT])}
function Lg (line 2) | function Lg(r,e){r.$constructor=r,r.extend=function(t){var a=this,n;retu...
function EP (line 2) | function EP(r){return K(r)&&/^class\s/.test(Function.prototype.toString....
function LT (line 2) | function LT(r,e){r.extend=e.extend}
function OP (line 2) | function OP(r){var e=["__\0is_clz",kP++].join("_");r.prototype[e]=!0,r.i...
function NP (line 2) | function NP(r,e){for(var t=[],a=2;a<arguments.length;a++)t[a-2]=argument...
function BP (line 2) | function BP(r,e,t){return this.superClass.prototype[e].apply(r,t)}
function Sh (line 2) | function Sh(r){var e={};r.registerClass=function(a){var n=a.type||a.prot...
function si (line 2) | function si(r,e){for(var t=0;t<r.length;t++)r[t][1]||(r[t][1]=r[t][0]);r...
function r (line 2) | function r(){}
function FP (line 2) | function FP(r){if(typeof r=="string"){var e=$p.get(r);return e&&e.image}...
function Pg (line 2) | function Pg(r,e,t,a,n){if(r)if(typeof r=="string"){if(e&&e.__zrImageSrc=...
function u0 (line 2) | function u0(){var r=this.__cachedImgObj;this.onload=this.onerror=this.__...
function xh (line 2) | function xh(r){return r&&r.width&&r.height}
function HP (line 2) | function HP(r,e,t,a,n){var i={};return PT(i,r,e,t,a,n),i.text}
function PT (line 2) | function PT(r,e,t,a,n,i){if(!t){r.text="",r.isTruncated=!1;return}var o=...
function RT (line 4) | function RT(r,e,t,a){a=a||{};var n=V({},a);n.font=e,t=st(t,"..."),n.maxI...
function ET (line 4) | function ET(r,e,t){var a=t.containerWidth,n=t.font,i=t.contentWidth;if(!...
function WP (line 4) | function WP(r,e,t,a){for(var n=0,i=0,o=r.length;i<o&&n<e;i++){var s=r.ch...
function UP (line 4) | function UP(r,e){r!=null&&(r+="");var t=e.overflow,a=e.padding,n=e.font,...
function r (line 5) | function r(){}
function r (line 5) | function r(e){this.tokens=[],e&&(this.tokens=e)}
function r (line 5) | function r(){this.width=0,this.height=0,this.contentWidth=0,this.content...
function XP (line 5) | function XP(r,e){var t=new $P;if(r!=null&&(r+=""),!r)return t;for(var a=...
function bv (line 5) | function bv(r,e,t,a,n){var i=e==="",o=n&&t.rich[n]||{},s=r.lines,l=o.fon...
function ZP (line 7) | function ZP(r){var e=r.charCodeAt(0);return e>=32&&e<=591||e>=880&&e<=43...
function KP (line 7) | function KP(r){return ZP(r)?!!qP[r]:!0}
function kT (line 7) | function kT(r,e,t,a,n){for(var i=[],o=[],s="",l="",u=0,f=0,h=0;h<r.lengt...
function e (line 8) | function e(t){return r.call(this,t)||this}
function JP (line 8) | function JP(r,e,t){return wv.copy(r.getBoundingRect()),r.transform&&wv.a...
function wh (line 8) | function wh(r,e,t){if(r.length!==0){for(var a=r[0],n=a[0],i=a[0],o=a[1],...
function v0 (line 8) | function v0(r,e,t,a,n,i){n[0]=Be(r,t),n[1]=Be(e,a),i[0]=Ve(r,t),i[1]=Ve(...
function QP (line 8) | function QP(r,e,t,a,n,i,o,s,l,u){var f=jw,h=ee,v=f(r,t,n,o,c0);l[0]=1/0,...
function tR (line 8) | function tR(r,e,t,a,n,i,o,s){var l=Qw,u=le,f=Ve(Be(l(r,t,n),1),0),h=Ve(B...
function eR (line 8) | function eR(r,e,t,a,n,i,o,s,l){var u=la,f=ua,h=Math.abs(n-i);if(h%gn<1e-...
function Lv (line 8) | function Lv(r){var e=Math.round(r/Zp*1e8)/1e8;return e%2*Zp}
function Rg (line 8) | function Rg(r,e){var t=Lv(r[0]);t<0&&(t+=Ra);var a=t-r[0],n=r[1];n+=a,!e...
function r (line 8) | function r(e){this.dpr=1,this._xi=0,this._yi=0,this._x0=0,this._y0=0,thi...
function ka (line 8) | function ka(r,e,t,a,n,i,o){if(n===0)return!1;var s=n,l=0,u=r;if(o>e+s&&o...
function rR (line 8) | function rR(r,e,t,a,n,i,o,s,l,u,f){if(l===0)return!1;var h=l;if(f>e+h&&f...
function OT (line 8) | function OT(r,e,t,a,n,i,o,s,l){if(o===0)return!1;var u=o;if(l>e+u&&l>a+u...
function $e (line 8) | function $e(r){return r%=d0,r<0&&(r+=d0),r}
function aR (line 8) | function aR(r,e,t,a,n,i,o,s,l){if(o===0)return!1;var u=o;s-=r,l-=e;var f...
function oa (line 8) | function oa(r,e,t,a,n,i){if(i>e&&i>a||i<e&&i<a||a===e)return 0;var o=(i-...
function iR (line 8) | function iR(r,e){return Math.abs(r-e)<nR}
function oR (line 8) | function oR(){var r=rr[0];rr[0]=rr[1],rr[1]=r}
function sR (line 8) | function sR(r,e,t,a,n,i,o,s,l,u){if(u>e&&u>a&&u>i&&u>s||u<e&&u<a&&u<i&&u...
function lR (line 8) | function lR(r,e,t,a,n,i,o,s){if(s>e&&s>a&&s>i||s<e&&s<a&&s<i)return 0;va...
function uR (line 8) | function uR(r,e,t,a,n,i,o,s){if(s-=e,s>t||s<-t)return 0;var l=Math.sqrt(...
function NT (line 8) | function NT(r,e,t,a,n){for(var i=r.data,o=r.len(),s=0,l=0,u=0,f=0,h=0,v,...
function fR (line 8) | function fR(r,e,t){return NT(r,0,!1,e,t)}
function hR (line 8) | function hR(r,e,t,a){return NT(r,e,!0,t,a)}
function e (line 8) | function e(t){return r.call(this,t)||this}
function o (line 8) | function o(s){var l=i.call(this,s)||this;return t.init&&t.init.call(l,s),l}
function e (line 8) | function e(){return r!==null&&r.apply(this,arguments)||this}
function gR (line 8) | function gR(r){return!!(r&&typeof r!="string"&&r.width&&r.height)}
function e (line 8) | function e(){return r!==null&&r.apply(this,arguments)||this}
function yR (line 8) | function yR(r,e){var t=e.x,a=e.y,n=e.width,i=e.height,o=e.r,s,l,u,f;n<0&...
function BT (line 8) | function BT(r,e,t){if(e){var a=e.x1,n=e.x2,i=e.y1,o=e.y2;r.x1=a,r.x2=n,r...
function VT (line 8) | function VT(r,e,t){if(e){var a=e.x,n=e.y,i=e.width,o=e.height;r.x=a,r.y=...
function Un (line 8) | function Un(r,e,t){if(!e)return r;var a=Ki(r*2);return(a+Ki(e))%2===0?a/...
function r (line 8) | function r(){this.x=0,this.y=0,this.width=0,this.height=0}
function e (line 8) | function e(t){return r.call(this,t)||this}
function e (line 8) | function e(t){var a=r.call(this)||this;return a.type="text",a._children=...
function zT (line 8) | function zT(r){return typeof r=="string"&&(r.indexOf("px")!==-1||r.index...
function _0 (line 8) | function _0(r,e){for(var t=0;t<m0.length;t++){var a=m0[t],n=e[a];n!=null...
function GT (line 8) | function GT(r){return r.fontSize!=null||r.fontFamily||r.fontWeight}
function wR (line 8) | function wR(r){return S0(r),D(r.rich,S0),r}
function S0 (line 8) | function S0(r){if(r){r.font=St.makeFont(r);var e=r.align;e==="middle"&&(...
function x0 (line 8) | function x0(r,e){return r==null||e<=0||r==="transparent"||r==="none"?nul...
function b0 (line 8) | function b0(r){return r==null||r==="none"?null:r.image||r.colorStops?"#0...
function w0 (line 8) | function w0(r,e,t){return e==="right"?r-t[1]:e==="center"?r+t[3]/2-t[1]/...
function T0 (line 8) | function T0(r){var e=r.text;return e!=null&&(e+=""),e}
function Rv (line 8) | function Rv(r){return!!(r.backgroundColor||r.lineHeight||r.borderWidth&&...
function Ci (line 8) | function Ci(r){return r!=null&&r!=="none"}
function Ah (line 8) | function Ah(r,e,t){r.onHoverStateChange&&(r.hoverState||0)!==t&&r.onHove...
function HT (line 8) | function HT(r){Ah(r,"emphasis",Th)}
function WT (line 8) | function WT(r){r.hoverState===Th&&Ah(r,"normal",kg)}
function Og (line 8) | function Og(r){Ah(r,"blur",Cl)}
function UT (line 8) | function UT(r){r.hoverState===Cl&&Ah(r,"normal",kg)}
function AR (line 8) | function AR(r){r.selected=!0}
function CR (line 8) | function CR(r){r.selected=!1}
function D0 (line 8) | function D0(r,e,t){e(r,t)}
function ba (line 8) | function ba(r,e,t){D0(r,e,t),r.isGroup&&r.traverse(function(a){D0(a,e,t)})}
function wf (line 8) | function wf(r,e){switch(e){case"emphasis":r.hoverState=Th;break;case"nor...
function DR (line 8) | function DR(r,e,t,a){for(var n=r.style,i={},o=0;o<e.length;o++){var s=e[...
function MR (line 8) | function MR(r,e,t,a){var n=t&&vt(t,"select")>=0,i=!1;if(r instanceof mt)...
function IR (line 8) | function IR(r,e,t){if(t&&t.z2==null){t=V({},t);var a=r.z2SelectLift;t.z2...
function LR (line 8) | function LR(r,e,t){var a=vt(r.currentStates,e)>=0,n=r.style.opacity,i=a?...
function Ev (line 8) | function Ev(r,e){var t=this.states[r];if(this.style){if(r==="emphasis")r...
function li (line 8) | function li(r){r.stateProxy=Ev;var e=r.getTextContent(),t=r.getTextGuide...
function M0 (line 8) | function M0(r,e){!ZT(r,e)&&!r.__highByOuter&&ba(r,HT)}
function I0 (line 8) | function I0(r,e){!ZT(r,e)&&!r.__highByOuter&&ba(r,WT)}
function da (line 8) | function da(r,e){r.__highByOuter|=1<<(e||0),ba(r,HT)}
function ga (line 8) | function ga(r,e){!(r.__highByOuter&=~(1<<(e||0)))&&ba(r,WT)}
function YT (line 8) | function YT(r){ba(r,Og)}
function Ng (line 8) | function Ng(r){ba(r,UT)}
function $T (line 8) | function $T(r){ba(r,AR)}
function XT (line 8) | function XT(r){ba(r,CR)}
function ZT (line 8) | function ZT(r,e){return r.__highDownSilentOnTouch&&e.zrByTouch}
function qT (line 8) | function qT(r){var e=r.getModel(),t=[],a=[];e.eachComponent(function(n,i...
function Kp (line 8) | function Kp(r,e,t,a){var n=a.getModel();t=t||"coordinateSystem";function...
function jp (line 8) | function jp(r,e,t){if(!(r==null||e==null)){var a=t.getModel().getCompone...
function PR (line 8) | function PR(r,e,t){var a=r.seriesIndex,n=r.getData(e.dataType);if(n){var...
function Bg (line 8) | function Bg(r,e,t,a){var n={focusSelf:!1,dispatchers:null};if(r==null||r...
function RR (line 8) | function RR(r,e,t){var a=nt(r),n=Bg(a.componentMainType,a.componentIndex...
function ER (line 8) | function ER(r,e,t){qT(t);var a=nt(r),n=Bg(a.componentMainType,a.componen...
function kR (line 8) | function kR(r,e,t){if(Qp(e)){var a=e.dataType,n=r.getData(a),i=oi(n,e);z...
function L0 (line 8) | function L0(r){var e=r.getAllData();D(e,function(t){var a=t.data,n=t.typ...
function OR (line 8) | function OR(r){var e=[];return r.eachSeries(function(t){var a=t.getAllDa...
function Ya (line 8) | function Ya(r,e,t){Yn(r,!0),ba(r,li),Jp(r,e,t)}
function NR (line 8) | function NR(r){Yn(r,!1)}
function Ht (line 8) | function Ht(r,e,t,a){a?NR(r):Ya(r,e,t)}
function Jp (line 8) | function Jp(r,e,t){var a=nt(r);e!=null?(a.focus=e,a.blurScope=t):a.focus...
function ve (line 8) | function ve(r,e,t,a){t=t||"itemStyle";for(var n=0;n<P0.length;n++){var i...
function Yn (line 8) | function Yn(r,e){var t=e===!1,a=r;r.highDownSilentOnTouch&&(a.__highDown...
function Qs (line 8) | function Qs(r){return!!(r&&r.__highDownDispatcher)}
function VR (line 8) | function VR(r,e,t){var a=nt(r);a.componentMainType=e.mainType,a.componen...
function zR (line 8) | function zR(r){var e=C0[r];return e==null&&A0<=32&&(e=C0[r]=A0++),e}
function Qp (line 8) | function Qp(r){var e=r.type;return e===Os||e===Ju||e===Ns}
function R0 (line 8) | function R0(r){var e=r.type;return e===Qn||e===ju}
function GR (line 8) | function GR(r){var e=FT(r);e.normalFill=r.style.fill,e.normalStroke=r.st...
function KT (line 8) | function KT(r,e){if(e){var t=r.data,a=r.len(),n,i,o,s,l,u,f=Di.M,h=Di.C,...
function k0 (line 8) | function k0(r){return Math.sqrt(r[0]*r[0]+r[1]*r[1])}
function td (line 8) | function td(r,e){return(r[0]*e[0]+r[1]*e[1])/(k0(r)*k0(e))}
function O0 (line 8) | function O0(r,e){return(r[0]*e[1]<r[1]*e[0]?-1:1)*Math.acos(td(r,e))}
function N0 (line 8) | function N0(r,e,t,a,n,i,o,s,l,u,f){var h=l*(Xo/180),v=au(h)*(r-t)/2+ru(h...
function YR (line 8) | function YR(r){var e=new jr;if(!r)return e;var t=0,a=0,n=t,i=a,o,s=jr.CM...
function e (line 8) | function e(){return r!==null&&r.apply(this,arguments)||this}
function JT (line 8) | function JT(r){return r.setData!=null}
function QT (line 8) | function QT(r,e){var t=YR(r),a=V({},e);return a.buildPath=function(n){if...
function tA (line 8) | function tA(r,e){return new jT(QT(r,e))}
function $R (line 8) | function $R(r,e){var t=QT(r,e),a=(function(n){k(i,n);function i(o){var s...
function XR (line 8) | function XR(r,e){for(var t=[],a=r.length,n=0;n<a;n++){var i=r[n];t.push(...
function Vg (line 8) | function Vg(r,e){e=e||{};var t=new mt;return r.shape&&t.setShape(r.shape...
function r (line 8) | function r(){this.cx=0,this.cy=0,this.r=0}
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.cx=0,this.cy=0,this.rx=0,this.ry=0}
function e (line 8) | function e(t){return r.call(this,t)||this}
function jR (line 8) | function jR(r,e,t,a,n,i,o,s){var l=t-r,u=a-e,f=o-n,h=s-i,v=h*l-f*u;if(!(...
function nu (line 8) | function nu(r,e,t,a,n,i,o){var s=r-t,l=e-a,u=(o?i:-i)/Bs(s*s+l*l),f=u*l,...
function JR (line 8) | function JR(r){var e;if(z(r)){var t=r.length;if(!t)return r;t===1?e=[r[0...
function QR (line 8) | function QR(r,e){var t,a=ms(e.r,0),n=ms(e.r0||0,0),i=a>0,o=n>0;if(!(!i&&...
function r (line 8) | function r(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,th...
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.cx=0,this.cy=0,this.r=0,this.r0=0}
function e (line 8) | function e(t){return r.call(this,t)||this}
function rE (line 8) | function rE(r,e,t,a){var n=[],i=[],o=[],s=[],l,u,f,h;if(a){f=[1/0,1/0],h...
function rA (line 8) | function rA(r,e,t){var a=e.smooth,n=e.points;if(n&&n.length>=2){if(a){va...
function r (line 8) | function r(){this.points=null,this.smooth=0,this.smoothConstraint=null}
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.points=null,this.percent=1,this.smooth=0,this.smoothCo...
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.x1=0,this.y1=0,this.x2=0,this.y2=0,this.percent=1}
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.x1=0,this.y1=0,this.x2=0,this.y2=0,this.cpx1=0,this.cp...
function V0 (line 8) | function V0(r,e,t){var a=r.cpx2,n=r.cpy2;return a!=null||n!=null?[(t?Ym:...
function e (line 8) | function e(t){return r.call(this,t)||this}
function r (line 8) | function r(){this.cx=0,this.cy=0,this.r=0,this.startAngle=0,this.endAngl...
function e (line 8) | function e(t){return r.call(this,t)||this}
function e (line 8) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function r (line 8) | function r(e){this.colorStops=e||[]}
function e (line 8) | function e(t,a,n,i,o,s){var l=r.call(this,o)||this;return l.x=t??0,l.y=a...
function e (line 8) | function e(t,a,n,i,o){var s=r.call(this,i)||this;return s.x=t??.5,s.y=a?...
function r (line 8) | function r(e,t){this._corners=[],this._axes=[],this._origin=[0,0];for(va...
function e (line 8) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.notC...
function Co (line 8) | function Co(r,e,t,a,n){var i;if(e&&e.ecModel){var o=e.ecModel.getUpdateP...
function Gg (line 8) | function Gg(r,e,t,a,n,i,o){var s=!1,l;K(n)?(o=i,i=n,n=null):tt(n)&&(i=n....
function Tt (line 8) | function Tt(r,e,t,a,n,i){Gg("update",r,e,t,a,n,i)}
function Gt (line 8) | function Gt(r,e,t,a,n,i){Gg("enter",r,e,t,a,n,i)}
function ro (line 8) | function ro(r){if(!r.__zr)return!0;for(var e=0;e<r.animators.length;e++)...
function Ja (line 8) | function Ja(r,e,t,a,n,i){ro(r)||Gg("leave",r,e,t,a,n,i)}
function z0 (line 8) | function z0(r,e,t,a){r.removeTextContent(),r.removeTextGuideLine(),Ja(r,...
function tl (line 8) | function tl(r,e,t){function a(){r.parent&&r.parent.remove(r)}r.isGroup?r...
function Pr (line 8) | function Pr(r){iA(r).oldStyle=r.style}
function fE (line 8) | function fE(r){return iA(r).oldStyle}
function oA (line 8) | function oA(r){return mt.extend(r)}
function sA (line 8) | function sA(r,e){return hE(r,e)}
function vr (line 8) | function vr(r,e){ed[r]=e}
function Dh (line 8) | function Dh(r){if(ed.hasOwnProperty(r))return ed[r]}
function Il (line 8) | function Il(r,e,t,a){var n=tA(r,e);return t&&(a==="center"&&(t=lA(t,n.ge...
function Fg (line 8) | function Fg(r,e,t){var a=new se({style:{image:r,x:e.x,y:e.y,width:e.widt...
function lA (line 8) | function lA(r,e){var t=e.width/e.height,a=r.height*t,n;a<=r.width?n=r.he...
function Hg (line 8) | function Hg(r,e){if(r.applyTransform){var t=r.getBoundingRect(),a=t.calc...
function ho (line 8) | function ho(r,e){return BT(r,r,{lineWidth:e}),r}
function vE (line 8) | function vE(r){return VT(r.shape,r.shape,r.style),r}
function $a (line 8) | function $a(r,e){for(var t=bl([]);r&&r!==e;)Xr(t,r.getLocalTransform(),t...
function Dr (line 8) | function Dr(r,e,t){return e&&!he(e)&&(e=fa.getLocalTransform(e)),t&&(e=p...
function Mh (line 8) | function Mh(r,e,t){var a=e[4]===0||e[5]===0||e[0]===0?1:Math.abs(2*e[4]/...
function G0 (line 8) | function G0(r){return!r.isGroup}
function cE (line 8) | function cE(r){return r.shape!=null}
function Ll (line 8) | function Ll(r,e,t){if(!r||!e)return;function a(o){var s={};return o.trav...
function Wg (line 8) | function Wg(r,e){return G(r,function(t){var a=t[0];a=Af(a,e.x),a=Cf(a,e....
function uA (line 8) | function uA(r,e){var t=Af(r.x,e.x),a=Cf(r.x+r.width,e.x+e.width),n=Af(r....
function Do (line 8) | function Do(r,e,t){var a=V({rectHover:!0},e),n=a.style={strokeNoScale:!0...
function _s (line 8) | function _s(r,e,t,a,n){for(var i=0,o=n[n.length-1];i<n.length;i++){var s...
function fA (line 8) | function fA(r,e,t,a,n,i,o,s){var l=t-r,u=a-e,f=o-n,h=s-i,v=Nv(f,h,l,u);i...
function Nv (line 8) | function Nv(r,e,t,a){return r*a-t*e}
function pE (line 8) | function pE(r){return r<=1e-6&&r>=-1e-6}
function di (line 8) | function di(r){var e=r.itemTooltipOption,t=r.componentModel,a=r.itemName...
function F0 (line 8) | function F0(r,e){var t;r.isGroup&&(t=e(r)),t||r.traverse(e)}
function nn (line 8) | function nn(r,e){if(r)if(z(r))for(var t=0;t<r.length;t++)F0(r[t],e);else...
function hA (line 8) | function hA(r,e){for(var t=0;t<Ee.length;t++){var a=Ee[t],n=e[a],i=r.ens...
function rd (line 8) | function rd(r,e,t){var a=r.labelFetcher,n=r.labelDataIndex,i=r.labelDimI...
function ce (line 8) | function ce(r,e,t,a){t=t||Ih;for(var n=r instanceof St,i=!1,o=0;o<Js.len...
function ie (line 8) | function ie(r,e){e=e||"label";for(var t={normal:r.getModel(e)},a=0;a<Ee....
function Nt (line 8) | function Nt(r,e,t,a,n){var i={};return dE(i,r,t,a,n),e&&V(i,e),i}
function Df (line 8) | function Df(r,e,t){e=e||{};var a={},n,i=r.getShallow("rotate"),o=st(r.ge...
function dE (line 8) | function dE(r,e,t,a,n){t=t||Ih;var i=e.ecModel,o=i&&i.option.textStyle,s...
function gE (line 8) | function gE(r){for(var e;r&&r!==r.ecModel;){var t=(r.option||Ih).rich;if...
function Y0 (line 8) | function Y0(r,e,t,a,n,i,o,s){t=!n&&t||Ih;var l=a&&a.inheritColor,u=e.get...
function Ug (line 8) | function Ug(r,e){var t=e&&e.getModel("textStyle");return sr([r.fontStyle...
function vA (line 8) | function vA(r,e,t,a){if(r){var n=Mo(r);n.prevValue=n.value,n.value=t;var...
function cA (line 8) | function cA(r,e,t,a,n){var i=Mo(r);if(!i.valueAnimation||i.prevValue===i...
function r (line 8) | function r(){}
function r (line 8) | function r(){}
function r (line 8) | function r(){}
function r (line 8) | function r(e,t,a){this.parentModel=t,this.ecModel=a,this.option=e}
function Io (line 8) | function Io(r){return[r||"",wE++].join("_")}
function TE (line 8) | function TE(r){var e={};r.registerSubTypeDefaulter=function(t,a){var n=W...
function AE (line 8) | function AE(r,e){r.topologicalTravel=function(i,o,s,l){if(!i.length)retu...
function on (line 8) | function on(r,e){return ut(ut({},r,!0),e,!0)}
function Xg (line 8) | function Xg(r,e){r=r.toUpperCase(),$g[r]=new Ct(e),tf[r]=e}
function ME (line 8) | function ME(r){if(Y(r)){var e=tf[r.toUpperCase()]||{};return r===Mf||r==...
function ad (line 8) | function ad(r){return $g[r]}
function IE (line 8) | function IE(){return $g[ao]}
function Me (line 8) | function Me(r,e){return r+="","0000".substr(0,e-r.length)+r}
function no (line 8) | function no(r){switch(r){case"half-year":case"quarter":return"month";cas...
function LE (line 8) | function LE(r){return r===no(r)}
function PE (line 8) | function PE(r){switch(r){case"year":case"month":return"day";case"millise...
function Pl (line 8) | function Pl(r,e,t,a){var n=Ze(r),i=n[Kg(t)](),o=n[io(t)]()+1,s=Math.floo...
function RE (line 8) | function RE(r,e,t,a,n){var i=null;if(Y(t))i=t;else if(K(t))i=t(r.value,e...
function mA (line 8) | function mA(r,e){var t=Ze(r),a=t[io(e)]()+1,n=t[Lh(e)](),i=t[el(e)](),o=...
function Z0 (line 8) | function Z0(r,e,t){var a=wt(r)?Ze(r):r;switch(e=e||mA(r,t),e){case"year"...
function Kg (line 8) | function Kg(r){return r?"getUTCFullYear":"getFullYear"}
function io (line 8) | function io(r){return r?"getUTCMonth":"getMonth"}
function Lh (line 8) | function Lh(r){return r?"getUTCDate":"getDate"}
function el (line 8) | function el(r){return r?"getUTCHours":"getHours"}
function Ph (line 8) | function Ph(r){return r?"getUTCMinutes":"getMinutes"}
function Rh (line 8) | function Rh(r){return r?"getUTCSeconds":"getSeconds"}
function Eh (line 8) | function Eh(r){return r?"getUTCMilliseconds":"getMilliseconds"}
function EE (line 8) | function EE(r){return r?"setUTCFullYear":"setFullYear"}
function _A (line 8) | function _A(r){return r?"setUTCMonth":"setMonth"}
function SA (line 8) | function SA(r){return r?"setUTCDate":"setDate"}
function xA (line 8) | function xA(r){return r?"setUTCHours":"setHours"}
function bA (line 8) | function bA(r){return r?"setUTCMinutes":"setMinutes"}
function wA (line 8) | function wA(r){return r?"setUTCSeconds":"setSeconds"}
function TA (line 8) | function TA(r){return r?"setUTCMilliseconds":"setMilliseconds"}
function kE (line 8) | function kE(r,e,t,a,n,i,o,s){var l=new St({style:{text:r,font:e,align:t,...
function jg (line 8) | function jg(r){if(!Dg(r))return Y(r)?r:"-";var e=(r+"").split(".");retur...
function Jg (line 8) | function Jg(r,e){return r=(r||"").toLowerCase().replace(/-(.)/g,function...
function nd (line 8) | function nd(r,e,t){var a="{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}";function n(f)...
function Qg (line 8) | function Qg(r,e,t){z(e)||(e=[e]);var a=e.length;if(!a)return"";for(var n...
function OE (line 8) | function OE(r,e,t){return D(e,function(a,n){r=r.replace("{"+n+"}",a)}),r}
function AA (line 8) | function AA(r,e){var t=Y(r)?{color:r,extraCssText:e}:r||{},a=t.color,n=t...
function NE (line 8) | function NE(r,e,t){(r==="week"||r==="month"||r==="quarter"||r==="half-ye...
function BE (line 9) | function BE(r){return r&&r.charAt(0).toUpperCase()+r.substr(1)}
function ui (line 9) | function ui(r,e){return e=e||"transparent",Y(r)?r:tt(r)&&r.colorStops&&(...
function If (line 9) | function If(r,e){if(e==="_blank"||e==="blank"){var t=window.open();t.ope...
function ty (line 9) | function ty(r,e,t,a,n){var i=0,o=0;a==null&&(a=1/0),n==null&&(n=1/0);var...
function VE (line 9) | function VE(r,e,t){var a=e.width,n=e.height,i=W(r.left,a),o=W(r.top,n),s...
function jt (line 9) | function jt(r,e,t){t=yi(t||0);var a=e.width,n=e.height,i=W(r.left,a),o=W...
function kh (line 9) | function kh(r,e,t,a,n,i){var o=!n||!n.hv||n.hv[0],s=!n||!n.hv||n.hv[1],l...
function zE (line 9) | function zE(r,e){return r[$n[e][0]]!=null||r[$n[e][1]]!=null&&r[$n[e][2]...
function rl (line 9) | function rl(r){var e=r.layoutMode||r.constructor.layoutMode;return tt(e)...
function Qa (line 9) | function Qa(r,e,t){var a=t&&t.ignoreSize;!z(a)&&(a=[a,a]);var n=o($n[0],...
function Lo (line 9) | function Lo(r){return DA({},r)}
function DA (line 9) | function DA(r,e){return e&&r&&ef(CA,function(t){e.hasOwnProperty(t)&&(r[...
function e (line 9) | function e(t,a,n){var i=r.call(this,t,a,n)||this;return i.uid=Io("ec_cpt...
function FE (line 9) | function FE(r){var e=[];return D(gt.getClassesByMainType(r),function(t){...
function WE (line 9) | function WE(r){PA(r).datasetMap=Z()}
function RA (line 9) | function RA(r,e,t){var a={},n=ry(e);if(!n||!r)return a;var i=[],o=[],s=e...
function ey (line 9) | function ey(r,e,t){var a={},n=ry(r);if(!n)return a;var i=e.sourceFormat,...
function ry (line 9) | function ry(r){var e=r.get("data",!0);if(!e)return Al(r.ecModel,"dataset...
function UE (line 9) | function UE(r){return!r.get("transform",!0)&&!r.get("fromTransformResult...
function EA (line 9) | function EA(r,e){return kA(r.data,r.sourceFormat,r.seriesLayoutBy,r.dime...
function kA (line 9) | function kA(r,e,t,a,n,i){var o,s=5;if(Re(r))return fe.Not;var l,u;if(a){...
function YE (line 9) | function YE(r,e){xe(id.get(r)==null&&e),id.set(r,e)}
function $E (line 9) | function $E(r,e,t){var a=id.get(e);if(!a)return t;var n=a(r);return n?t....
function r (line 9) | function r(){}
function od (line 9) | function od(r,e,t,a){var n=Rt(r.get(["aria","decal","decals"]));return O...
function ZE (line 9) | function ZE(r,e){for(var t=r.length,a=0;a<t;a++)if(r[a].length>e)return ...
function OA (line 9) | function OA(r,e,t,a,n,i,o){i=i||r;var s=e(i),l=s.paletteIdx||0,u=s.palet...
function qE (line 9) | function qE(r,e){e(r).paletteIdx=0,e(r).paletteNameMap={}}
function e (line 9) | function e(){return r!==null&&r.apply(this,arguments)||this}
function f (line 9) | function f(h){var v=$E(this,h,Rt(t[h])),c=i.get(h),p=c?u&&u.get(h)?"repl...
function s (line 9) | function s(u){var f=n+"Index",h=n+"Id",v=n+"Name";return u&&(u[f]!=null|...
function l (line 9) | function l(u){return t.filter?At(u,t.filter):u}
function jE (line 9) | function jE(r,e){if(e){var t=e.seriesIndex,a=e.seriesId,n=e.seriesName;r...
function JE (line 9) | function JE(r,e){var t=r.color&&!r.colorLayer;D(e,function(a,n){n==="col...
function Q0 (line 9) | function Q0(r,e,t){if(z(e)){var a=Z();return D(e,function(i){if(i!=null)...
function t_ (line 9) | function t_(r,e){return e.hasOwnProperty("subType")?At(r,function(t){ret...
function e_ (line 9) | function e_(r){var e=Z();return r&&D(Rt(r.replaceMerge),function(t){e.se...
function r (line 9) | function r(e){D(QE,function(t){this[t]=$(e[t],e)},this)}
function r (line 9) | function r(){this._coordinateSystems=[]}
function r (line 9) | function r(e){this._timelineOptions=[],this._mediaList=[],this._currentM...
function rk (line 9) | function rk(r,e,t){var a=[],n,i,o=r.baseOption,s=r.timeline,l=r.options,...
function ak (line 9) | function ak(r,e,t){var a={width:e,height:t,aspectratio:e/t},n=!0;return ...
function nk (line 9) | function nk(r,e,t){return t==="min"?r>=e:t==="max"?r<=e:r===e}
function ik (line 9) | function ik(r,e){return r.join(",")===e.join(",")}
function Hv (line 9) | function Hv(r){var e=r&&r.itemStyle;if(e)for(var t=0,a=r_.length;t<a;t++...
function me (line 9) | function me(r,e,t){if(r&&r[e]&&(r[e].normal||r[e].emphasis)){var a=r[e]....
function xs (line 9) | function xs(r){me(r,"itemStyle"),me(r,"lineStyle"),me(r,"areaStyle"),me(...
function qt (line 9) | function qt(r,e){var t=al(r)&&r[e],a=al(t)&&t.textStyle;if(a)for(var n=0...
function er (line 9) | function er(r){r&&(xs(r),qt(r,"label"),r.emphasis&&qt(r.emphasis,"label"))}
function ok (line 9) | function ok(r){if(al(r)){Hv(r),xs(r),qt(r,"label"),qt(r,"upperLabel"),qt...
function aa (line 9) | function aa(r){return z(r)?r:r?[r]:[]}
function a_ (line 9) | function a_(r){return(z(r)?r[0]:r)||{}}
function sk (line 9) | function sk(r,e){gr(aa(r.series),function(a){al(a)&&ok(a)});var t=["xAxi...
function lk (line 9) | function lk(r,e){for(var t=e.split(","),a=r,n=0;n<t.length&&(a=a&&a[t[n]...
function uk (line 9) | function uk(r,e,t,a){for(var n=e.split(","),i=r,o,s=0;s<n.length-1;s++)o...
function n_ (line 9) | function n_(r){r&&D(fk,function(e){e[0]in r&&!(e[1]in r)&&(r[e[1]]=r[e[0...
function qo (line 9) | function qo(r){var e=r&&r.itemStyle;if(e)for(var t=0;t<Wv.length;t++){va...
function i_ (line 9) | function i_(r){r&&r.alignTo==="edge"&&r.margin!=null&&r.edgeDistance==nu...
function o_ (line 9) | function o_(r){r&&r.downplay&&!r.blur&&(r.blur=r.downplay)}
function vk (line 9) | function vk(r){r&&r.focusNodeAdjacency!=null&&(r.emphasis=r.emphasis||{}...
function BA (line 9) | function BA(r,e){if(r)for(var t=0;t<r.length;t++)e(r[t]),r[t]&&BA(r[t].c...
function VA (line 9) | function VA(r,e){sk(r,e),r.series=Rt(r.series),D(r.series,function(t){if...
function ck (line 9) | function ck(r){var e=Z();r.eachSeries(function(t){var a=t.get("stack");i...
function pk (line 9) | function pk(r){D(r,function(e,t){var a=[],n=[NaN,NaN],i=[e.stackResultDi...
function r (line 9) | function r(e){this.data=e.data||(e.sourceFormat===Qr?{}:[]),this.sourceF...
function iy (line 9) | function iy(r){return r instanceof Oh}
function sd (line 9) | function sd(r,e,t){t=t||zA(r);var a=e.seriesLayoutBy,n=gk(r,t,a,e.source...
function oy (line 9) | function oy(r){return new Oh({data:r,sourceFormat:Re(r)?Xa:cr})}
function dk (line 9) | function dk(r){return new Oh({data:r.data,sourceFormat:r.sourceFormat,se...
function zA (line 9) | function zA(r){var e=LA;if(Re(r))e=Xa;else if(z(r)){r.length===0&&(e=Te)...
function gk (line 9) | function gk(r,e,t,a,n){var i,o;if(!r)return{dimensionsDefine:s_(n),start...
function yk (line 9) | function yk(r){for(var e=0,t;e<r.length&&!(t=r[e++]););if(t)return _t(t)}
function s_ (line 9) | function s_(r){if(r){var e=Z();return G(r,function(t,a){t=tt(t)?t:{name:...
function l_ (line 9) | function l_(r,e,t,a){if(e===Po)for(var n=0;n<t.length&&n<a;n++)r(t[n]?t[...
function GA (line 9) | function GA(r){var e=r.sourceFormat;return e===pr||e===Qr}
function r (line 9) | function r(e,t){var a=iy(e)?e:oy(e);this._source=a;var n=this._data=a.da...
function i (line 9) | function i(o){for(var s=0;s<o.length;s++)this._data.push(o[s])}
function HA (line 9) | function HA(r,e){var t=mk[sy(r,e)];return t}
function WA (line 9) | function WA(r,e){var t=_k[sy(r,e)];return t}
function UA (line 9) | function UA(r){var e=Sk[r];return e}
function sy (line 9) | function sy(r,e){return r===Te?r+"_"+e:r}
function vo (line 9) | function vo(r,e,t){if(r){var a=r.getRawDataItem(e);if(a!=null){var n=r.g...
function r (line 9) | function r(){}
function c_ (line 9) | function c_(r){var e,t;return tt(r)?r.type&&(t=r):e=r,{text:e,frag:t}}
function zs (line 9) | function zs(r){return new bk(r)}
function r (line 9) | function r(e){e=e||{},this._reset=e.reset,this._plan=e.plan,this._count=...
function f (line 9) | function f(m){return!(m>=1)&&(m=1),m}
function o (line 9) | function o(){return e<r?e++:null}
function s (line 9) | function s(){var l=e%n*t+Math.ceil(e/n),u=e>=r?null:l<a?l:e;return e++,u}
function Za (line 9) | function Za(r,e){var t=e&&e.type;return t==="ordinal"?r:(t==="time"&&!wt...
function YA (line 9) | function YA(r){return wk.get(r)}
function r (line 9) | function r(e,t){if(!wt(t)){var a="";Mt(a)}this._opFn=$A[e],this._rvalFlo...
function r (line 9) | function r(e,t){var a=e==="desc";this._resultLT=a?1:-1,t==null&&(t=a?"mi...
function r (line 9) | function r(e,t){this._rval=t,this._isEQ=e,this._rvalTypeof=typeof t,this...
function Ck (line 9) | function Ck(r,e){return r==="eq"||r==="ne"?new Ak(r==="eq",e):X($A,r)?ne...
function r (line 9) | function r(){}
function Mk (line 9) | function Mk(r,e){var t=new Dk,a=r.data,n=t.sourceFormat=r.sourceFormat,i...
function Ik (line 9) | function Ik(r){var e=r.sourceFormat;if(!ly(e)){var t="";Mt(t)}return r.d...
function Lk (line 9) | function Lk(r){var e=r.sourceFormat,t=r.data;if(!ly(e)){var a="";Mt(a)}i...
function Pk (line 9) | function Pk(r,e,t){if(t!=null){if(wt(t)||!isNaN(t)&&!X(e,t))return r[t];...
function Rk (line 9) | function Rk(r){return rt(r)}
function Ek (line 9) | function Ek(r){r=rt(r);var e=r.type,t="";e||Mt(t);var a=e.split(":");a.l...
function kk (line 9) | function kk(r,e,t){var a=Rt(r),n=a.length,i="";n||Mt(i);for(var o=0,s=n;...
function Ok (line 9) | function Ok(r,e,t,a){var n="";e.length||Mt(n),tt(r)||Mt(n);var i=r.type,...
function ly (line 9) | function ly(r){return r===Te||r===pr}
function Li (line 9) | function Li(r){return r>65535?Nk:Bk}
function Pi (line 9) | function Pi(){return[1/0,-1/0]}
function Vk (line 9) | function Vk(r){var e=r.constructor;return e===Array?r.slice():new e(r)}
function g_ (line 9) | function g_(r,e,t,a,n){var i=KA[t||"float"];if(n){var o=r[e],s=o&&o.leng...
function r (line 9) | function r(){this._chunks=[],this._rawExtent=[],this._extent=[],this._co...
function e (line 9) | function e(t,a,n,i){return Za(t[i],this._dimensions[i])}
function r (line 9) | function r(e){this._sourceList=[],this._storeList=[],this._upstreamSignL...
function y_ (line 9) | function y_(r){var e=r.option.transform;e&&Us(r.option.transform)}
function uu (line 9) | function uu(r){return r.mainType==="series"}
function m_ (line 9) | function m_(r){throw new Error(r)}
function JA (line 9) | function JA(r){var e=r.lineHeight;return e==null?zk:"line-height:"+_e(e+...
function QA (line 9) | function QA(r,e){var t=r.color||"#6e7079",a=r.fontSize||12,n=r.fontWeigh...
function oe (line 15) | function oe(r,e){return e.type=r,e}
function ud (line 15) | function ud(r){return r.type==="section"}
function tC (line 15) | function tC(r){return ud(r)?Hk:Wk}
function eC (line 15) | function eC(r){if(ud(r)){var e=0,t=r.blocks.length,a=t>1||t>0&&!r.noHead...
function Hk (line 15) | function Hk(r,e,t,a){var n=e.noHeader,i=Uk(eC(e)),o=[],s=e.blocks||[];xe...
function Wk (line 15) | function Wk(r,e,t,a){var n=r.renderMode,i=e.noName,o=e.noValue,s=!e.mark...
function __ (line 15) | function __(r,e,t,a,n,i){if(r){var o=tC(r),s={useUTC:n,renderMode:t,orde...
function Uk (line 15) | function Uk(r){return{html:Gk[r],richText:Fk[r]}}
function fd (line 15) | function fd(r,e,t){var a='<div style="clear:both"></div>',n="margin: "+t...
function Yk (line 15) | function Yk(r,e,t){var a=e?"margin-left:2px":"";return'<span style="'+t+...
function $k (line 15) | function $k(r,e,t,a){var n=t?"10px":"20px",i=e?"float:right;margin-left:...
function rC (line 15) | function rC(r,e,t){return r.markupStyleCreator.wrapRichTextStyle(e,t)}
function Xk (line 15) | function Xk(r,e,t,a,n){var i=[n],o=a?10:20;return t&&i.push({padding:[0,...
function aC (line 15) | function aC(r,e){var t=r.getData().getItemVisual(e,"style"),a=t[r.visual...
function nC (line 15) | function nC(r,e){var t=r.get("padding");return t??(e==="richText"?[8,10]...
function r (line 15) | function r(){this.richTextStyles={},this._nextStyleNameId=xT()}
function iC (line 15) | function iC(r){var e=r.series,t=r.dataIndex,a=r.multipleSeries,n=e.getDa...
function Zk (line 15) | function Zk(r,e,t,a,n){var i=e.getData(),o=ur(r,function(h,v,c){var p=i....
function fu (line 15) | function fu(r,e){return r.getName(e)||r.getId(e)}
function e (line 15) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t._sel...
function S_ (line 15) | function S_(r){var e=r.name;Mg(r)||(r.name=qk(r)||e)}
function qk (line 15) | function qk(r){var e=r.getRawData(),t=e.mapDimensionsAll("seriesName"),a...
function Kk (line 15) | function Kk(r){return r.model.getRawData().count()}
function jk (line 15) | function jk(r){var e=r.model;return e.setData(e.getRawData().cloneShallo...
function Jk (line 15) | function Jk(r,e){e.outputData&&r.end>e.outputData.count()&&e.model.getRa...
function x_ (line 15) | function x_(r,e){D(lo(r.CHANGABLE_METHODS,r.DOWNSAMPLE_METHODS),function...
function Qk (line 15) | function Qk(r,e){var t=hd(r);return t&&t.setOutputEnd((e||this).count()),e}
function hd (line 15) | function hd(r){var e=(r.ecModel||{}).scheduler,t=e&&e.getPipeline(r.uid)...
function r (line 15) | function r(){this.group=new at,this.uid=Io("viewComponent")}
function Eo (line 15) | function Eo(){var r=bt();return function(e){var t=r(e),a=e.pipelineConte...
function r (line 15) | function r(){this.group=new at,this.uid=Io("viewChart"),this.renderTask=...
function b_ (line 15) | function b_(r,e,t){r&&Qs(r)&&(e==="emphasis"?da:ga)(r,t)}
function w_ (line 15) | function w_(r,e,t){var a=oi(r,e),n=e&&e.highlightKey!=null?zR(e.highligh...
function eO (line 15) | function eO(r){return tO(r.model)}
function rO (line 15) | function rO(r){var e=r.model,t=r.ecModel,a=r.api,n=r.payload,i=e.pipelin...
function Vh (line 15) | function Vh(r,e,t){var a,n=0,i=0,o=null,s,l,u,f;e=e||0;function h(){i=ne...
function ko (line 15) | function ko(r,e,t,a){var n=r[e];if(n){var i=n[Lf]||n,o=n[A_],s=n[T_];if(...
function nl (line 15) | function nl(r,e){var t=r[e];t&&t[Lf]&&(t.clear&&t.clear(),r[e]=t[Lf])}
function sC (line 15) | function sC(r,e){var t=r.visualStyleMapper||D_[e];return t||(console.war...
function lC (line 15) | function lC(r,e){var t=r.visualDrawType||nO[e];return t||(console.warn("...
function lO (line 15) | function lO(r,e){e=e||{},J(e,{text:"loading",textColor:"#000",fontSize:1...
function r (line 15) | function r(e,t,a,n){this._stageTaskMap=Z(),this.ecInstance=e,this.api=t,...
function s (line 15) | function s(l,u){return l.setDirty&&(!l.dirtyMap||l.dirtyMap.get(u.__pipe...
function f (line 15) | function f(h){var v=h.uid,c=s.set(v,o&&o.get(v)||zs({plan:cO,reset:pO,co...
function p (line 15) | function p(d){var g=d.uid,y=l.set(g,s&&s.get(g)||(v=!0,zs({reset:fO,onDi...
function uO (line 15) | function uO(r){r.overallReset(r.ecModel,r.api,r.payload)}
function fO (line 15) | function fO(r){return r.overallProgress&&hO}
function hO (line 15) | function hO(){this.agent.dirty(),this.getDownstream().dirty()}
function vO (line 15) | function vO(){this.agent&&this.agent.dirty()}
function cO (line 15) | function cO(r){return r.plan?r.plan(r.model,r.ecModel,r.api,r.payload):n...
function pO (line 15) | function pO(r){r.useClearVisual&&r.data.clearAllVisual();var e=r.resetDe...
function fC (line 15) | function fC(r){return function(e,t){var a=t.data,n=t.resetDefines[r];if(...
function gO (line 15) | function gO(r){return r.data.count()}
function yO (line 15) | function yO(r){Pf=null;try{r(il,hC)}catch{}return Pf}
function vC (line 15) | function vC(r,e){for(var t in e.prototype)r[t]=Yt}
function r (line 15) | function r(){}
function f (line 15) | function f(h,v,c,p){return h[c]==null||v[p||c]===h[c]}
function f (line 15) | function f(h,v){for(var c=r.getRawValue(v),p=r.getDataParams(v),d=0;d<u....
function a (line 15) | function a(n,i){for(var o=n.getItemModel(i),s=0;s<P_.length;s++){var l=P...
function uy (line 15) | function uy(r,e,t){switch(t){case"color":var a=r.getItemVisual(e,"style"...
function Rl (line 15) | function Rl(r,e){switch(e){case"color":var t=r.getVisual("style");return...
function pC (line 15) | function pC(r,e,t,a){switch(t){case"color":var n=r.ensureUniqueItemVisua...
function dC (line 15) | function dC(r,e){function t(a,n){var i=[];return a.eachComponent({mainTy...
function Ri (line 15) | function Ri(r,e,t,a,n){var i=r+e;t.isSilent(i)||a.eachComponent({mainTyp...
function bO (line 15) | function bO(r,e,t){r.on("selectchanged",function(a){var n=t.getModel();a...
function Xn (line 15) | function Xn(r,e,t){for(var a;r&&!(e(r)&&(a=r,t));)r=r.__hostTarget||r.pa...
function r (line 15) | function r(){this._id="__ec_inner_"+wO++}
function EO (line 15) | function EO(r,e){if(this.type!=="image"){var t=this.style;this.__isEmpty...
function Zt (line 15) | function Zt(r,e,t,a,n,i,o){var s=r.indexOf("empty")===0;s&&(r=r.substr(5...
function Oo (line 15) | function Oo(r){return z(r)||(r=[+r,+r]),[r[0]||0,r[1]||0]}
function mi (line 15) | function mi(r,e){if(r!=null)return z(r)||(r=[r,r]),[W(r[0],e[0])||0,W(st...
function Zn (line 15) | function Zn(r){return isFinite(r)}
function kO (line 15) | function kO(r,e,t){var a=e.x==null?0:e.x,n=e.x2==null?1:e.x2,i=e.y==null...
function OO (line 15) | function OO(r,e,t){var a=t.width,n=t.height,i=Math.min(a,n),o=e.x==null?...
function cd (line 15) | function cd(r,e,t){for(var a=e.type==="radial"?OO(r,e,t):kO(r,e,t),n=e.c...
function NO (line 15) | function NO(r,e){if(r===e||!r&&!e)return!1;if(!r||!e||r.length!==e.lengt...
function cu (line 15) | function cu(r){return parseInt(r,10)}
function ji (line 15) | function ji(r,e,t){var a=["width","height"][e],n=["clientWidth","clientH...
function BO (line 15) | function BO(r,e){return!r||r==="solid"||!(e>0)?null:r==="dashed"?[4*e,2*...
function fy (line 15) | function fy(r){var e=r.style,t=e.lineDash&&e.lineWidth>0&&BO(e.lineDash,...
function Ef (line 15) | function Ef(r){var e=r.stroke;return!(e==null||e==="none"||!(r.lineWidth...
function R_ (line 15) | function R_(r){return typeof r=="string"&&r!=="none"}
function kf (line 15) | function kf(r){var e=r.fill;return e!=null&&e!=="none"}
function E_ (line 15) | function E_(r,e){if(e.fillOpacity!=null&&e.fillOpacity!==1){var t=r.glob...
function k_ (line 15) | function k_(r,e){if(e.strokeOpacity!=null&&e.strokeOpacity!==1){var t=r....
function pd (line 15) | function pd(r,e,t){var a=Pg(e.image,e.__image,t);if(xh(a)){var n=r.creat...
function zO (line 15) | function zO(r,e,t,a){var n,i=Ef(t),o=kf(t),s=t.strokePercent,l=s<1,u=!e....
function GO (line 15) | function GO(r,e,t){var a=e.__image=Pg(t.image,e.__image,e,e.onload);if(!...
function FO (line 15) | function FO(r,e,t){var a,n=t.text;if(n!=null&&(n+=""),n){r.font=t.font||...
function gC (line 15) | function gC(r,e,t,a,n){var i=!1;if(!a&&(t=t||{},e===t))return!1;if(a||e....
function B_ (line 15) | function B_(r,e,t,a,n){var i=ol(e,n.inHover),o=a?null:t&&ol(t,n.inHover)...
function HO (line 15) | function HO(r,e,t,a,n){return gC(r,ol(e,n.inHover),t&&ol(t,n.inHover),a,n)}
function yC (line 15) | function yC(r,e){var t=e.transform,a=r.dpr||1;t?r.setTransform(a*t[0],a*...
function WO (line 15) | function WO(r,e,t){for(var a=!1,n=0;n<r.length;n++){var i=r[n];a=a||i.is...
function UO (line 15) | function UO(r,e){return r&&e?r[0]!==e[0]||r[1]!==e[1]||r[2]!==e[2]||r[3]...
function YO (line 15) | function YO(r){var e=kf(r),t=Ef(r);return!(r.lineDash||!(+e^+t)||e&&type...
function ze (line 15) | function ze(r,e){e.batchFill&&r.fill(),e.batchStroke&&r.stroke(),e.batch...
function ol (line 15) | function ol(r,e){return e&&r.__hoverStyle||r.style}
function hy (line 15) | function hy(r,e){qn(r,e,{inHover:!1,viewWidth:0,viewHeight:0},!0)}
function qn (line 15) | function qn(r,e,t,a){var n=e.transform;if(!e.shouldBePainted(t.viewWidth...
function $O (line 15) | function $O(r,e,t){var a=e.getDisplayables(),n=e.getTemporalDisplayables...
function co (line 15) | function co(r,e){if(r==="none")return null;var t=e.getDevicePixelRatio()...
function mC (line 15) | function mC(r){if(!r||r.length===0)return[["rect"]];if(Y(r))return[[r]];...
function _C (line 15) | function _C(r){if(!r||r.length===0)return[[0,0]];if(wt(r)){var e=Math.ce...
function XO (line 15) | function XO(r){if(!r||typeof r=="object"&&r.length===0)return[0,0];if(wt...
function ZO (line 15) | function ZO(r){return G(r,function(e){return SC(e)})}
function SC (line 15) | function SC(r){for(var e=0,t=0;t<r.length;++t)e+=r[t];return r.length%2=...
function qO (line 15) | function qO(r,e){r.eachRawSeries(function(t){if(!r.isSeriesFiltered(t)){...
function KO (line 15) | function KO(r,e){xC[r]=e}
function bC (line 15) | function bC(r){return xC[r]}
function DC (line 15) | function DC(r){return function(){for(var e=[],t=0;t<arguments.length;t++...
function MC (line 15) | function MC(r){return function(){for(var e=[],t=0;t<arguments.length;t++...
function IC (line 15) | function IC(r,e,t){return t[0]=t[0]&&t[0].toLowerCase(),hr.prototype[e]....
function e (line 15) | function e(){return r!==null&&r.apply(this,arguments)||this}
function e (line 15) | function e(t,a,n){var i=r.call(this,new _O)||this;i._chartsViews=[],i._c...
function S (line 15) | function S(x){var w=x.__requireNewView;x.__requireNewView=!1;var T="_ec_...
function b (line 15) | function b(x){x&&x.__alive&&x[v]&&x[v](x.__model,g,h._api,c)}
function t (line 15) | function t(h){h.clearColorPalette(),h.eachSeries(function(v){v.clearColo...
function a (line 15) | function a(h){var v=[],c=[],p=!1;if(h.eachComponent(function(m,_){var S=...
function n (line 15) | function n(h){for(var v=[],c=h.currentStates,p=0;p<c.length;p++){var d=c...
function i (line 15) | function i(h,v){var c=h._zr,p=c.storage,d=0;p.traverse(function(g){g.isG...
function o (line 15) | function o(h,v){var c=h.get("blendMode")||null;v.eachRendered(function(p...
function s (line 15) | function s(h,v){if(!h.preventAutoZ){var c=h.get("z")||0,p=h.get("zlevel"...
function l (line 15) | function l(h,v,c,p){var d=h.getTextContent(),g=h.getTextGuideLine(),y=h....
function u (line 15) | function u(h,v){v.eachRendered(function(c){if(!ro(c)){var p=c.getTextCon...
function f (line 15) | function f(h,v){var c=h.getModel("stateAnimation"),p=h.isAnimationEnable...
function c (line 15) | function c(){return v!==null&&v.apply(this,arguments)||this}
function v (line 15) | function v(c,p){for(var d=0;d<c.length;d++){var g=c[d];g[qv]=p}}
function n (line 15) | function n(){for(var i=[],o=0;o<arguments.length;o++)i[o]=arguments[o];e...
function md (line 15) | function md(r,e,t){var a=!(t&&t.ssr);if(a){var n=dy(r);if(n)return n}var...
function dN (line 15) | function dN(r){if(z(r)){var e=r;r=null,D(e,function(t){t.group!=null&&(r...
function kC (line 15) | function kC(r){Vf[r]=!1}
function yN (line 15) | function yN(r){Y(r)?r=ei[r]:r instanceof Of||(r=dy(r)),r instanceof Of&&...
function dy (line 15) | function dy(r){return ei[MP(r,py)]}
function mN (line 15) | function mN(r){return ei[r]}
function Gh (line 15) | function Gh(r,e){EC[r]=e}
function gy (line 15) | function gy(r){vt(gd,r)<0&&gd.push(r)}
function yy (line 15) | function yy(r,e){my(dd,r,e,aN)}
function OC (line 15) | function OC(r){Fh("afterinit",r)}
function NC (line 15) | function NC(r){Fh("afterupdate",r)}
function Fh (line 15) | function Fh(r,e){Sr.on(r,e)}
function Rr (line 15) | function Rr(r,e,t){K(e)&&(t=e,e="");var a=tt(r)?r.type:[r,r={event:e}][0...
function BC (line 15) | function BC(r,e){Ro.register(r,e)}
function _N (line 15) | function _N(r){var e=Ro.get(r);if(e)return e.getDimensionsInfo?e.getDime...
function VC (line 15) | function VC(r,e){my(Bf,r,e,wC,"layout")}
function sn (line 15) | function sn(r,e){my(Bf,r,e,TC,"visual")}
function my (line 15) | function my(r,e,t,a,n){if((K(e)||tt(e))&&(t=e,e=a),!(vt(j_,t)>=0)){j_.pu...
function _y (line 15) | function _y(r,e){yd[r]=e}
function SN (line 15) | function SN(r){Rw({createCanvas:r})}
function zC (line 15) | function zC(r,e,t){var a=bC("registerMap");a&&a(r,e,t)}
function xN (line 15) | function xN(r){var e=bC("getMap");return e&&e(r)}
function pt (line 15) | function pt(r){if(z(r)){D(r,function(e){pt(e)});return}vt(J_,r)>=0||(J_....
function Qo (line 15) | function Qo(r){return r==null?0:r.length||1}
function Q_ (line 15) | function Q_(r){return r}
function r (line 15) | function r(e,t,a,n,i,o){this._old=e,this._new=t,this._oldKeyGetter=a||Q_...
function r (line 15) | function r(e,t){this._encode=e,this._schema=t}
function AN (line 15) | function AN(r,e){var t={},a=t.encode={},n=Z(),i=[],o=[],s={};D(r.dimensi...
function ec (line 15) | function ec(r,e){return r.hasOwnProperty(e)||(r[e]=[]),r[e]}
function zf (line 15) | function zf(r){return r==="category"?"ordinal":r==="time"?"time":"float"}
function CN (line 15) | function CN(r){return!(r==="ordinal"||r==="time")}
function r (line 15) | function r(e){this.otherDims={},e!=null&&V(this,e)}
function r (line 15) | function r(e){this.dimensions=e.dimensions,this._dimOmitted=e.dimensionO...
function HC (line 15) | function HC(r){return r instanceof FC}
function WC (line 15) | function WC(r){for(var e=Z(),t=0;t<(r||[]).length;t++){var a=r[t],n=tt(a...
function UC (line 15) | function UC(r){var e=DN(r);return e.dimNameMap||(e.dimNameMap=WC(r.dimen...
function YC (line 15) | function YC(r){return r>30}
function r (line 15) | function r(e,t){this.type="list",this._dimOmitted=!1,this._nameList=[],t...
function EN (line 15) | function EN(r,e){return No(r,e).dimensions}
function No (line 15) | function No(r,e){iy(r)||(r=oy(r)),e=e||{};var t=e.coordDimensions||[],a=...
function kN (line 15) | function kN(r){for(var e=Z(),t=0;t<r.length;t++){var a=r[t],n=a.name,i=e...
function ON (line 15) | function ON(r,e,t,a){var n=Math.max(r.dimensionsDetectedCount||1,e.lengt...
function NN (line 15) | function NN(r,e,t){if(t||e.hasKey(r)){for(var a=0;e.hasKey(r+a);)a++;r+=...
function r (line 15) | function r(e){this.coordSysDims=[],this.axisMap=Z(),this.categoryAxisMap...
function VN (line 15) | function VN(r){var e=r.get("coordinateSystem"),t=new BN(e),a=zN[e];if(a)...
function ki (line 15) | function ki(r){return r.get("type")==="category"}
function $C (line 15) | function $C(r,e,t){t=t||{};var a=t.byIndex,n=t.stackedCoordDimension,i,o...
function GN (line 15) | function GN(r){return!HC(r.schema)}
function ma (line 15) | function ma(r,e){return!!e&&e===r.getCalculationInfo("stackedDimension")}
function Sy (line 15) | function Sy(r,e){return ma(r,e)?r.getCalculationInfo("stackResultDimensi...
function FN (line 15) | function FN(r,e){var t=r.get("coordinateSystem"),a=Ro.get(t),n;return e&...
function HN (line 15) | function HN(r,e,t){var a,n;return t&&D(r,function(i,o){var s=i.coordDim,...
function ta (line 15) | function ta(r,e,t){t=t||{};var a=e.getSourceManager(),n,i=!1;r?(i=!0,n=o...
function WN (line 15) | function WN(r){if(r.sourceFormat===cr){var e=UN(r.data||[]);return!z(xo(...
function UN (line 15) | function UN(r){for(var e=0;e<r.length&&r[e]==null;)e++;return r[e]}
function r (line 15) | function r(e){this._setting=e||{},this._extent=[1/0,-1/0]}
function r (line 15) | function r(e){this.categories=e.categories||[],this._needCollect=e.needC...
function $N (line 15) | function $N(r){return tt(r)&&r.value!=null?r.value:r+""}
function Sd (line 15) | function Sd(r){return r.type==="interval"||r.type==="log"}
function XN (line 15) | function XN(r,e,t,a){var n={},i=r[1]-r[0],o=n.interval=Cg(i/e,!0);t!=nul...
function nc (line 15) | function nc(r){var e=Math.pow(10,_h(r)),t=r/e;return t?t===2?t=3:t===3?t...
function XC (line 15) | function XC(r){return br(r)+2}
function r1 (line 15) | function r1(r,e,t){r[e]=Math.max(Math.min(r[e],t[1]),t[0])}
function ZN (line 15) | function ZN(r,e){!isFinite(r[0])&&(r[0]=e[0]),!isFinite(r[1])&&(r[1]=e[1...
function Hh (line 15) | function Hh(r,e){return r>=e[0]&&r<=e[1]}
function Wh (line 15) | function Wh(r,e){return e[1]===e[0]?.5:(r-e[0])/(e[1]-e[0])}
function Uh (line 15) | function Uh(r,e){return r*(e[1]-e[0])+e[0]}
function e (line 15) | function e(t){var a=r.call(this,t)||this;a.type="ordinal";var n=a.getSet...
function e (line 15) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function Ur (line 15) | function Ur(r){return z(r)?ZC?new Float32Array(r):r:new qN(r)}
function qC (line 15) | function qC(r){return r.get("stack")||xd+r.seriesIndex}
function xy (line 15) | function xy(r){return r.dim+r.index}
function KN (line 15) | function KN(r){var e=[],t=r.axis,a="axis0";if(t.type==="category"){for(v...
function KC (line 15) | function KC(r,e){var t=[];return e.eachSeriesByType(r,function(a){eD(a)&...
function jN (line 15) | function jN(r){var e={};D(r,function(l){var u=l.coordinateSystem,f=u.get...
function jC (line 15) | function jC(r){var e=jN(r),t=[];return D(r,function(a){var n=a.coordinat...
function JC (line 15) | function JC(r){var e={};D(r,function(a,n){var i=a.axisKey,o=a.bandWidth,...
function JN (line 15) | function JN(r,e,t){if(r&&e){var a=r[xy(e)];return a}}
function QC (line 15) | function QC(r,e){var t=KC(r,e),a=jC(t);D(t,function(n){var i=n.getData()...
function tD (line 15) | function tD(r){return{seriesType:r,plan:Eo(),reset:function(e){if(eD(e))...
function eD (line 15) | function eD(r){return r.coordinateSystem&&r.coordinateSystem.type==="car...
function rD (line 15) | function rD(r){return r.pipelineContext&&r.pipelineContext.large}
function QN (line 15) | function QN(r,e){var t=e.model.get("startValue");return t||(t=0),e.toGlo...
function e (line 15) | function e(t){var a=r.call(this,t)||this;return a.type="time",a}
function eB (line 15) | function eB(r,e,t,a){var n=Ze(e),i=Ze(t),o=function(p){return Z0(n,p,a)=...
function rB (line 15) | function rB(r,e){return r/=or,r>16?16:r>7.5?7:r>3.5?4:r>1.5?2:1}
function aB (line 15) | function aB(r){var e=30*or;return r/=e,r>6?6:r>3?3:r>2?2:1}
function nB (line 15) | function nB(r){return r/=Vs,r>12?12:r>6?6:r>3.5?4:r>2?2:1}
function a1 (line 15) | function a1(r,e){return r/=e?qg:Zg,r>30?30:r>20?20:r>15?15:r>10?10:r>5?5...
function iB (line 15) | function iB(r){return Cg(r,!0)}
function oB (line 15) | function oB(r,e,t){var a=new Date(r);switch(no(e)){case"year":case"month...
function sB (line 15) | function sB(r,e,t,a){var n=1e4,i=yA,o=0;function s(C,M,I,L,P,R,E){for(va...
function e (line 15) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function _u (line 15) | function _u(r,e){return lB(r,br(e))}
function r (line 15) | function r(e,t,a){this._prepareParams(e,t,a)}
function nD (line 15) | function nD(r,e,t){var a=r.rawExtentInfo;return a||(a=new hB(r,e,t),r.ra...
function Su (line 15) | function Su(r,e){return e==null?null:so(e)?NaN:r.parse(e)}
function iD (line 15) | function iD(r,e){var t=r.type,a=nD(r,e,r.getExtent()).calculate();r.setB...
function pB (line 15) | function pB(r,e,t,a){var n=t.axis.getExtent(),i=Math.abs(n[1]-n[0]),o=JN...
function fi (line 15) | function fi(r,e){var t=e,a=iD(r,t),n=a.extent,i=t.get("splitNumber");r i...
function El (line 15) | function El(r,e){if(e=e||r.get("type"),e)switch(e){case"category":return...
function dB (line 15) | function dB(r){var e=r.scale.getExtent(),t=e[0],a=e[1];return!(t>0&&a>0|...
function Bo (line 15) | function Bo(r){var e=r.getLabelModel().get("formatter"),t=r.type==="cate...
function Ty (line 15) | function Ty(r,e){return r.type==="category"?r.scale.getLabel(e):e.value}
function gB (line 15) | function gB(r){var e=r.model,t=r.scale;if(!(!e.get(["axisLabel","show"])...
function yB (line 15) | function yB(r,e){var t=e*Math.PI/180,a=r.width,n=r.height,i=a*Math.abs(M...
function Ay (line 15) | function Ay(r){var e=r.get("interval");return e??"auto"}
function oD (line 15) | function oD(r){return r.type==="category"&&Ay(r.getLabelModel())===0}
function Gf (line 15) | function Gf(r,e){var t={};return D(r.mapDimensionsAll(e),function(a){t[S...
function mB (line 15) | function mB(r,e,t){e&&D(Gf(e,t),function(a){var n=e.getApproximateExtent...
function r (line 15) | function r(){}
function _B (line 15) | function _B(r){return ta(null,r)}
function xB (line 15) | function xB(r,e){var t=e;e instanceof Ct||(t=new Ct(e));var a=El(t);retu...
function bB (line 15) | function bB(r){$t(r,Vo)}
function wB (line 15) | function wB(r,e){return e=e||{},Nt(r,null,null,e.state!=="normal")}
function i1 (line 15) | function i1(r,e){return Math.abs(r-e)<AB}
function zn (line 15) | function zn(r,e,t){var a=0,n=r[0];if(!n)return!1;for(var i=1;i<r.length;...
function ic (line 15) | function ic(r,e){for(var t=0;t<r.length;t++)ue(r[t],r[t],e)}
function o1 (line 15) | function o1(r,e,t,a){for(var n=0;n<r.length;n++){var i=r[n];a&&(i=a.proj...
function DB (line 15) | function DB(r){for(var e=0,t=0,a=0,n=r.length,i=r[n-1][0],o=r[n-1][1],s=...
function r (line 15) | function r(e){this.name=e}
function r (line 15) | function r(e,t){this.type="polygon",this.exterior=e,this.interiors=t}
function r (line 15) | function r(e){this.type="linestring",this.points=e}
function e (line 15) | function e(t,a,n){var i=r.call(this,t)||this;return i.type="geoJSON",i.g...
function e (line 15) | function e(t,a){var n=r.call(this,t)||this;return n.type="geoSVG",n._elO...
function IB (line 15) | function IB(r){if(!r.UTF8Encoding)return r;var e=r,t=e.UTF8Scale;t==null...
function oc (line 15) | function oc(r,e,t){for(var a=0;a<r.length;a++)r[a]=uD(r[a],e[a],t)}
function uD (line 15) | function uD(r,e,t){for(var a=[],n=e[0],i=e[1],o=0;o<r.length;o+=2){var s...
function bd (line 15) | function bd(r,e){return r=IB(r),G(At(r.features,function(t){return t.geo...
function fD (line 15) | function fD(r,e){var t=G(e,function(a){return r.scale.parse(a)});return ...
function OB (line 15) | function OB(r){var e=r.getLabelModel().get("customValues");if(e){var t=B...
function NB (line 15) | function NB(r,e){var t=r.getTickModel().get("customValues");if(t){var a=...
function BB (line 15) | function BB(r){var e=r.getLabelModel(),t=hD(r,e);return!e.get("show")||r...
function hD (line 15) | function hD(r,e){var t=vD(r,"labels"),a=Ay(e),n=cD(t,a);if(n)return n;va...
function VB (line 15) | function VB(r,e){var t=vD(r,"ticks"),a=Ay(e),n=cD(t,a);if(n)return n;var...
function zB (line 15) | function zB(r){var e=r.scale.getTicks(),t=Bo(r);return{labels:G(e,functi...
function vD (line 15) | function vD(r,e){return sl(r)[e]||(sl(r)[e]=[])}
function cD (line 15) | function cD(r,e){for(var t=0;t<r.length;t++)if(r[t].key===e)return r[t]....
function pD (line 15) | function pD(r,e,t){return r.push({key:e,value:t}),t}
function GB (line 15) | function GB(r){var e=sl(r).autoInterval;return e??(sl(r).autoInterval=r....
function FB (line 15) | function FB(r){var e=HB(r),t=Bo(r),a=(e.axisRotate-e.labelRotate)/180*Ma...
function HB (line 15) | function HB(r){var e=r.getLabelModel();return{axisRotate:r.getRotate?r.g...
function dD (line 15) | function dD(r,e,t){var a=Bo(r),n=r.scale,i=n.getExtent(),o=r.getLabelMod...
function gD (line 15) | function gD(r,e,t){var a=r.scale,n=Bo(r),i=[];return D(a.getTicks(),func...
function r (line 15) | function r(e,t,a){this.onBand=!1,this.inverse=!1,this.dim=e,this.scale=t...
function f1 (line 15) | function f1(r,e){var t=r[1]-r[0],a=e,n=t/a/2;r[0]+=n,r[1]-=n}
function WB (line 15) | function WB(r,e,t,a){var n=e.length;if(!r.onBand||t||!n)return;var i=r.g...
function UB (line 15) | function UB(r){var e=gt.extend(r);return gt.registerClass(e),e}
function YB (line 15) | function YB(r){var e=zt.extend(r);return zt.registerClass(e),e}
function $B (line 15) | function $B(r){var e=Et.extend(r);return Et.registerClass(e),e}
function XB (line 15) | function XB(r){var e=Lt.extend(r);return Lt.registerClass(e),e}
function qB (line 15) | function qB(r,e,t,a,n){var i=t.width,o=t.height;switch(r){case"top":a.se...
function KB (line 15) | function KB(r,e,t,a,n,i,o,s,l){o-=r,s-=e;var u=Math.sqrt(o*o+s*s);o/=u,s...
function Ff (line 15) | function Ff(r,e,t,a,n,i,o,s){var l=n-r,u=i-e,f=t-r,h=a-e,v=Math.sqrt(f*f...
function yD (line 15) | function yD(r,e,t,a,n,i,o){t<0&&(r=r+t,t=-t),a<0&&(e=e+a,a=-a);var s=r+t...
function jB (line 15) | function jB(r,e,t){var a=yD(e.x,e.y,e.width,e.height,r.x,r.y,xr);return ...
function JB (line 15) | function JB(r,e,t){for(var a=0,n=0,i=0,o=0,s,l,u=1/0,f=e.data,h=r.x,v=r....
function h1 (line 15) | function h1(r,e){if(r){var t=r.getTextGuideLine(),a=r.getTextContent();i...
function mD (line 15) | function mD(r,e){if(e<=180&&e>0){e=e/180*Math.PI,wr.fromArray(r[0]),Vt.f...
function QB (line 15) | function QB(r,e,t){if(t<=180&&t>0){t=t/180*Math.PI,wr.fromArray(r[0]),Vt...
function sc (line 15) | function sc(r,e,t,a){var n=t==="normal",i=n?r:r.ensureState(t);i.ignore=...
function tV (line 15) | function tV(r,e){var t=e.smooth,a=e.points;if(a)if(r.moveTo(a[0][0],a[0]...
function Cy (line 15) | function Cy(r,e,t){var a=r.getTextGuideLine(),n=r.getTextContent();if(!n...
function Dy (line 15) | function Dy(r,e){e=e||"labelLine";for(var t={normal:r.getModel(e)},a=0;a...
function _D (line 15) | function _D(r){for(var e=[],t=0;t<r.length;t++){var a=r[t];if(!a.default...
function SD (line 15) | function SD(r,e,t,a,n,i){var o=r.length;if(o<2)return;r.sort(function(x,...
function eV (line 15) | function eV(r,e,t,a){return SD(r,"x","width",e,t)}
function xD (line 15) | function xD(r,e,t,a){return SD(r,"y","height",e,t)}
function bD (line 15) | function bD(r){var e=[];r.sort(function(d,g){return g.priority-d.priorit...
function rV (line 15) | function rV(r){if(r){for(var e=[],t=0;t<r.length;t++)e.push(r[t].slice()...
function aV (line 15) | function aV(r,e){var t=r.label,a=e&&e.getTextGuideLine();return{dataInde...
function xu (line 15) | function xu(r,e,t){for(var a=0;a<t.length;a++){var n=t[a];e[n]!=null&&(r...
function r (line 15) | function r(){this._labelList=[],this._chartViewList=[]}
function n (line 15) | function n(_,S){return function(){h1(_,S)}}
function oV (line 15) | function oV(r){r.registerUpdateLifecycle("series:beforeupdate",function(...
function r (line 15) | function r(){}
function uV (line 15) | function uV(r){var e=r.fill;return e!=null&&e!==My}
function fV (line 15) | function fV(r){var e=r.stroke;return e!=null&&e!==My}
function vV (line 15) | function vV(r,e,t,a){var n=e.opacity==null?1:e.opacity;if(t instanceof s...
function DD (line 15) | function DD(r){return document.createElementNS(AD,r)}
function ae (line 15) | function ae(r,e,t,a,n){return{tag:r,attrs:t||{},children:a,text:n,key:e}}
function dV (line 15) | function dV(r,e){var t=[];if(e)for(var a in e){var n=e[a],i=a;n!==!1&&(n...
function gV (line 15) | function gV(r){return"</"+r+">"}
function Iy (line 15) | function Iy(r,e){e=e||{};var t=e.newline?`
function yV (line 16) | function yV(r,e,t){t=t||{};var a=t.newline?`
function Td (line 17) | function Td(r){return{zrId:r,shadowCache:{},patternCache:{},gradientCach...
function p1 (line 17) | function p1(r,e,t,a){return ae("svg","root",{width:r,height:e,xmlns:AD,"...
function MD (line 17) | function MD(){return mV++}
function _V (line 17) | function _V(r,e,t){var a=V({},r.shape);V(a,e),r.buildPath(t,a);var n=new...
function SV (line 17) | function SV(r,e){var t=e.originX,a=e.originY;(t||a)&&(r[En]=t+"px "+a+"p...
function ID (line 17) | function ID(r,e){var t=e.zrId+"-ani-"+e.cssAnimIdx++;return e.cssAnims[t...
function bV (line 17) | function bV(r,e,t){var a=r.shape.paths,n={},i,o;if(D(a,function(l){var u...
function g1 (line 17) | function g1(r){return Y(r)?d1[r]?"cubic-bezier("+d1[r]+")":Sg(r)?r:"":""}
function $h (line 17) | function $h(r,e,t,a){var n=r.animators,i=n.length,o=[];if(r instanceof C...
function wV (line 17) | function wV(r,e,t){if(!r.ignore)if(r.isSilent()){var a={"pointer-events"...
function y1 (line 17) | function y1(r,e,t,a){var n=JSON.stringify(r),i=t.cssStyleCache[n];i||(i=...
function LD (line 17) | function LD(r){return r&&Y(r.src)}
function PD (line 17) | function PD(r){return r&&K(r.toDataURL)}
function Ly (line 17) | function Ly(r,e,t,a){vV(function(n,i){var o=n==="fill"||n==="stroke";o&&...
function Py (line 17) | function Py(r,e){var t=dT(e);t&&(t.each(function(a,n){a!=null&&(r[(c1+n)...
function m1 (line 17) | function m1(r){return za(r[0]-1)&&za(r[1])&&za(r[2])&&za(r[3]-1)}
function TV (line 17) | function TV(r){return za(r[4])&&za(r[5])}
function Ry (line 17) | function Ry(r,e,t){if(e&&!(TV(e)&&m1(e))){var a=1e4;r.transform=m1(e)?"t...
function _1 (line 17) | function _1(r,e,t){for(var a=r.points,n=[],i=0;i<a.length;i++)n.push(ll(...
function S1 (line 17) | function S1(r){return!r.smooth}
function AV (line 17) | function AV(r){var e=G(r,function(t){return typeof t=="string"?[t,t]:t})...
function DV (line 17) | function DV(r){for(var e=r.animators,t=0;t<e.length;t++)if(e[t].targetNa...
function RD (line 17) | function RD(r,e){var t=r.style,a=r.shape,n=CV[r.type],i={},o=e.animation...
function MV (line 17) | function MV(r,e){var t=r.style,a=t.image;if(a&&!Y(a)&&(LD(a)?a=a.src:PD(...
function IV (line 17) | function IV(r,e){var t=r.style,a=t.text;if(a!=null&&(a+=""),!(!a||isNaN(...
function x1 (line 17) | function x1(r,e){if(r instanceof mt)return RD(r,e);if(r instanceof se)re...
function LV (line 17) | function LV(r,e,t){var a=r.style;if(E2(a)){var n=k2(r),i=t.shadowCache,o...
function ED (line 17) | function ED(r,e,t,a){var n=r[t],i,o={gradientUnits:n.global?"userSpaceOn...
function kD (line 17) | function kD(r,e,t,a){var n=r.style[t],i=r.getBoundingRect(),o={},s=n.rep...
function PV (line 17) | function PV(r,e,t){var a=t.clipPathCache,n=t.defs,i=a[r.id];if(!i){i=t.z...
function b1 (line 17) | function b1(r){return document.createTextNode(r)}
function Gn (line 17) | function Gn(r,e,t){r.insertBefore(e,t)}
function w1 (line 17) | function w1(r,e){r.removeChild(e)}
function T1 (line 17) | function T1(r,e){r.appendChild(e)}
function OD (line 17) | function OD(r){return r.parentNode}
function ND (line 17) | function ND(r){return r.nextSibling}
function vc (line 17) | function vc(r,e){r.textContent=e}
function Ad (line 17) | function Ad(r){return r===void 0}
function Gr (line 17) | function Gr(r){return r!==void 0}
function kV (line 17) | function kV(r,e,t){for(var a={},n=e;n<=t;++n){var i=r[n].key;i!==void 0&...
function bs (line 17) | function bs(r,e){var t=r.key===e.key,a=r.tag===e.tag;return a&&t}
function ul (line 17) | function ul(r){var e,t=r.children,a=r.tag;if(Gr(a)){var n=r.elm=DD(a);if...
function BD (line 17) | function BD(r,e,t,a,n){for(;a<=n;++a){var i=t[a];i!=null&&Gn(r,ul(i),e)}}
function Wf (line 17) | function Wf(r,e,t,a){for(;t<=a;++t){var n=e[t];if(n!=null)if(Gr(n.tag)){...
function Ey (line 17) | function Ey(r,e){var t,a=e.elm,n=r&&r.attrs||{},i=e.attrs||{};if(n!==i){...
function OV (line 17) | function OV(r,e,t){for(var a=0,n=0,i=e.length-1,o=e[0],s=e[i],l=t.length...
function $i (line 17) | function $i(r,e){var t=e.elm=r.elm,a=r.children,n=e.children;r!==e&&(Ey(...
function NV (line 17) | function NV(r,e){if(bs(r,e))$i(r,e);else{var t=r.elm,a=OD(t);ul(e),a!==n...
function r (line 17) | function r(e,t,a){if(this.type="svg",this.refreshHover=C1(),this.configL...
function C1 (line 17) | function C1(r){return function(){}}
function zV (line 17) | function zV(r,e,t,a){var n;if(t&&t!=="none")if(n=ae("rect","bg",{width:r...
function GV (line 17) | function GV(r){r.registerPainter("svg",VV)}
function D1 (line 17) | function D1(r,e,t){var a=Mr.createCanvas(),n=e.getWidth(),i=e.getHeight(...
function e (line 17) | function e(t,a,n){var i=r.call(this)||this;i.motionBlur=!1,i.lastFrameAl...
function f (line 17) | function f(m){if(!(!m.isFinite()||m.isZero()))if(o.length===0){var _=new...
function p (line 17) | function p(d,g,y,m){if(o.clearRect(d,g,y,m),a&&a!=="transparent"){var _=...
function HV (line 17) | function HV(r){return r?r.__builtin__?!0:!(typeof r.resize!="function"||...
function WV (line 17) | function WV(r,e){var t=document.createElement("div");return t.style.cssT...
function r (line 17) | function r(e,t,a,n){this.type="canvas",this._zlevelList=[],this._prevDis...
function t (line 17) | function t(h){i&&(i.__endIndex!==h&&(i.__dirty=!0),i.__endIndex=h)}
function YV (line 17) | function YV(r){r.registerPainter("canvas",UV)}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function po (line 17) | function po(r,e){var t=r.mapDimensionsAll("defaultedLabel"),a=t.length;i...
function VD (line 17) | function VD(r,e){var t=r.mapDimensionsAll("defaultedLabel");if(!z(e))ret...
function e (line 17) | function e(t,a,n,i){var o=r.call(this)||this;return o.updateData(t,a,n,i...
function I (line 17) | function I(R){return M?t.getName(R):po(t,R)}
function XV (line 17) | function XV(r,e){this.parent.drift(r,e)}
function pc (line 17) | function pc(r,e,t,a){return e&&!isNaN(e[0])&&!isNaN(e[1])&&!(a.isIgnore&...
function I1 (line 17) | function I1(r){return r!=null&&!tt(r)&&(r={isIgnore:r}),r||{}}
function L1 (line 17) | function L1(r){var e=r.hostModel,t=e.getModel("emphasis");return{emphasi...
function r (line 17) | function r(e){this.group=new at,this._SymbolCtor=e||kl}
function n (line 17) | function n(l){l.isGroup||(l.incremental=!0,l.ensureState("emphasis").hov...
function zD (line 17) | function zD(r,e,t){var a=r.getBaseAxis(),n=r.getOtherAxis(a),i=ZV(n,t),o...
function ZV (line 17) | function ZV(r,e){var t=0,a=r.scale.getExtent();return e==="start"?t=a[0]...
function GD (line 17) | function GD(r,e,t,a){var n=NaN;r.stacked&&(n=t.get(t.getCalculationInfo(...
function qV (line 17) | function qV(r,e){var t=[];return e.diff(r).add(function(a){t.push({cmd:"...
function KV (line 17) | function KV(r,e,t,a,n,i,o,s){for(var l=qV(r,e),u=[],f=[],h=[],v=[],c=[],...
function ri (line 17) | function ri(r,e){return isNaN(r)||isNaN(e)}
function Cd (line 17) | function Cd(r,e,t,a,n,i,o,s,l){for(var u,f,h,v,c,p,d=t,g=0;g<a;g++){var ...
function r (line 17) | function r(){this.smooth=0,this.smoothConstraint=!0}
function e (line 17) | function e(t){var a=r.call(this,t)||this;return a.type="ec-polyline",a}
function e (line 17) | function e(){return r!==null&&r.apply(this,arguments)||this}
function e (line 17) | function e(t){var a=r.call(this,t)||this;return a.type="ec-polygon",a}
function WD (line 17) | function WD(r,e,t,a,n){var i=r.getArea(),o=i.x,s=i.y,l=i.width,u=i.heigh...
function UD (line 17) | function UD(r,e,t){var a=r.getArea(),n=Wt(a.r0,1),i=Wt(a.r,1),o=new be({...
function Nl (line 17) | function Nl(r,e,t,a,n){if(r){if(r.type==="polar")return UD(r,e,t);if(r.t...
function _i (line 17) | function _i(r,e){return r.type===e}
function P1 (line 17) | function P1(r,e){if(r.length===e.length){for(var t=0;t<r.length;t++)if(r...
function R1 (line 17) | function R1(r){for(var e=1/0,t=1/0,a=-1/0,n=-1/0,i=0;i<r.length;){var o=...
function E1 (line 17) | function E1(r,e){var t=R1(r),a=t[0],n=t[1],i=R1(e),o=i[0],s=i[1];return ...
function k1 (line 17) | function k1(r){return wt(r)?r:r?.5:0}
function QV (line 17) | function QV(r,e,t){if(!t.valueDim)return[];for(var a=e.count(),n=Ur(a*2)...
function Pa (line 17) | function Pa(r,e,t,a,n){var i=t.getBaseAxis(),o=i.dim==="x"||i.dim==="rad...
function tz (line 17) | function tz(r,e){var t=[],a=r.length,n,i;function o(f,h,v){var c=f.coord...
function ez (line 17) | function ez(r,e,t){var a=r.getVisual("visualMeta");if(!(!a||!a.length||!...
function rz (line 17) | function rz(r,e,t){var a=r.get("showAllSymbol"),n=a==="auto";if(!(a&&!n)...
function az (line 17) | function az(r,e){var t=r.getExtent(),a=Math.abs(t[1]-t[0])/r.scale.count...
function nz (line 17) | function nz(r,e){return isNaN(r)||isNaN(e)}
function iz (line 17) | function iz(r){for(var e=r.length/2;e>0&&nz(r[e*2-2],r[e*2-1]);e--);retu...
function O1 (line 17) | function O1(r,e){return[r[e*2],r[e*2+1]]}
function oz (line 17) | function oz(r,e,t){for(var a=r.length/2,n=t==="x"?0:1,i,o,s=0,l=-1,u=0;u...
function YD (line 17) | function YD(r){if(r.get(["endLabel","show"]))return!0;for(var e=0;e<Ee.l...
function dc (line 17) | function dc(r,e,t,a){if(_i(e,"cartesian2d")){var n=a.getModel("endLabel"...
function sz (line 17) | function sz(r,e){var t=e.getBaseAxis(),a=t.isHorizontal(),n=t.inverse,i=...
function e (line 17) | function e(){return r!==null&&r.apply(this,arguments)||this}
function Bl (line 17) | function Bl(r,e){return{seriesType:r,plan:Eo(),reset:function(t){var a=t...
function $D (line 17) | function $D(r){return{seriesType:r,reset:function(e,t,a){var n=e.getData...
function hz (line 17) | function hz(r){r.registerChartView(lz),r.registerSeriesModel($V),r.regis...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function r (line 17) | function r(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,th...
function e (line 17) | function e(t){var a=r.call(this,t)||this;return a.type="sausage",a}
function pz (line 17) | function pz(r,e){e=e||{};var t=e.isRoundCap;return function(a,n,i){var o...
function dz (line 17) | function dz(r,e,t,a){if(wt(a)){r.setTextConfig({rotation:a});return}else...
function Tu (line 17) | function Tu(r,e,t){return e*Math.sin(r)*(t?-1:1)}
function Au (line 17) | function Au(r,e,t){return e*Math.cos(r)*(t?1:-1)}
function Kn (line 17) | function Kn(r,e,t){var a=r.get("borderRadius");if(a==null)return t?{corn...
function gz (line 17) | function gz(r,e){var t=r.getArea&&r.getArea();if(_i(r,"cartesian2d")){va...
function e (line 17) | function e(){var t=r.call(this)||this;return t.type=e.type,t._isFirstFra...
function T (line 17) | function T(M){var I=Cu[u.type](s,M),L=Az(u,h,I);return L.useStyle(m.getI...
function mz (line 17) | function mz(r,e){var t=r.get("realtimeSort",!0),a=e.getBaseAxis();if(t&&...
function V1 (line 17) | function V1(r,e,t,a,n,i,o,s){var l,u;i?(u={x:a.x,width:a.width},l={y:a.y...
function z1 (line 17) | function z1(r,e){for(var t=0;t<e.length;t++)if(!isFinite(r[e[t]]))return...
function xz (line 17) | function xz(r){return r.startAngle!=null&&r.endAngle!=null&&r.startAngle...
function XD (line 17) | function XD(r){return(function(e){var t=e?"Arc":"Angle";return function(...
function F1 (line 17) | function F1(r,e,t,a,n,i,o,s){var l=e.getItemVisual(t,"style");if(s){if(!...
function bz (line 17) | function bz(r,e){var t=r.get(["itemStyle","borderColor"]);if(!t||t==="no...
function r (line 17) | function r(){}
function e (line 17) | function e(t){var a=r.call(this,t)||this;return a.type="largeBar",a}
function W1 (line 17) | function W1(r,e,t,a){var n=r.getData(),i=n.getLayout("valueAxisHorizonta...
function Tz (line 17) | function Tz(r,e,t){for(var a=r.baseDimIdx,n=1-a,i=r.shape.points,o=r.lar...
function ZD (line 17) | function ZD(r,e,t){if(_i(t,"cartesian2d")){var a=e,n=t.getArea();return{...
function Az (line 17) | function Az(r,e,t){var a=r.type==="polar"?be:xt;return new a({shape:ZD(e...
function Cz (line 17) | function Cz(r){r.registerChartView(yz),r.registerSeriesModel(vz),r.regis...
function qD (line 17) | function qD(r,e){return jt(r.getBoxLayoutParams(),{width:e.getWidth(),he...
function KD (line 17) | function KD(r,e){var t=qD(r,e),a=r.get("center"),n=r.get("radius");z(n)|...
function Dz (line 17) | function Dz(r,e,t){e.eachSeriesByType(r,function(a){var n=a.getData(),i=...
function Vl (line 17) | function Vl(r){return{seriesType:r,reset:function(e,t){var a=t.findCompo...
function $1 (line 17) | function $1(r,e,t,a,n,i,o,s,l,u){if(r.length<2)return;function f(d){for(...
function Iz (line 17) | function Iz(r,e,t,a,n,i,o,s){for(var l=[],u=[],f=Number.MAX_VALUE,h=-Num...
function JD (line 17) | function JD(r,e,t){if(t===void 0&&(t=!1),r.labelStyleWidth==null){var a=...
function mc (line 17) | function mc(r){return r.position==="center"}
function Lz (line 17) | function Lz(r){var e=r.getData(),t=[],a,n,i=!1,o=(r.get("minShowLabelAng...
function e (line 17) | function e(t,a,n){var i=r.call(this)||this;i.z2=2;var o=new St;return i....
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.igno...
function zo (line 17) | function zo(r,e,t){e=z(e)&&{coordDimensions:e}||V({encodeDefine:r.getEnc...
function r (line 17) | function r(e,t){this._getDataWithEncodedVisual=e,this._getRawData=t}
function e (line 17) | function e(){return r!==null&&r.apply(this,arguments)||this}
function Oz (line 17) | function Oz(r){return{seriesType:r,reset:function(e,t){var a=e.getData()...
function Nz (line 17) | function Nz(r){r.registerChartView(Rz),r.registerSeriesModel(kz),dC("pie...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function r (line 17) | function r(){}
function e (line 17) | function e(t){var a=r.call(this,t)||this;return a._off=0,a.hoverDataIdx=...
function r (line 17) | function r(){this.group=new at}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){return r!==null&&r.apply(this,arguments)||this}
function e (line 17) | function e(){return r!==null&&r.apply(this,arguments)||this}
function go (line 17) | function go(r,e,t,a){D($z,function(n,i){var o=ut(ut({},eM[i],!0),a,!0),s...
function X1 (line 17) | function X1(r){return r.type||(r.data?"category":"value")}
function r (line 17) | function r(e){this.type="cartesian",this._dimList=[],this._axes={},this....
function Z1 (line 17) | function Z1(r){return r.type==="interval"||r.type==="time"}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(t,a,n,i,o){var s=r.call(this,t,a,n)||this;return s.index=0,s....
function Id (line 17) | function Id(r,e,t){t=t||{};var a=r.coordinateSystem,n=e.axis,i={},o=n.ge...
function q1 (line 17) | function q1(r){return r.get("coordinateSystem")==="cartesian2d"}
function K1 (line 17) | function K1(r){var e={xAxisModel:null,yAxisModel:null};return D(e,functi...
function rM (line 17) | function rM(r,e,t){var a=_a.prototype,n=a.getTicks.call(t),i=a.getTicks....
function r (line 17) | function r(e,t,a){this.type="grid",this._coordsMap={},this._coordsList=[...
function n (line 17) | function n(o){var s,l=_t(o),u=l.length;if(u){for(var f=[],h=u-1;h>=0;h--...
function l (line 17) | function l(){D(s,function(u){var f=u.isHorizontal(),h=f?[0,o.width]:[0,o...
function u (line 17) | function u(f){return function(h,v){if(Sc(h,e)){var c=h.get("position");f...
function a (line 17) | function a(n,i){D(Gf(n,i.dim),function(o){i.scale.unionExtentFromData(n,...
function Sc (line 17) | function Sc(r,e){return r.getCoordSysModel()===e}
function j1 (line 17) | function j1(r,e,t,a){t.getAxesOnZeroOf=function(){return i?[i]:[]};var n...
function J1 (line 17) | function J1(r){return r&&r.type!=="category"&&r.type!=="time"&&dB(r)}
function jz (line 17) | function jz(r,e){var t=r.getExtent(),a=t[0]+t[1];r.toGlobalCoord=r.dim==...
function r (line 17) | function r(e,t){this.group=new at,this.opt=t,this.axisModel=e,J(t,{label...
function Jz (line 17) | function Jz(r,e,t,a){var n=Ag(t-r),i,o,s=a[0]>a[1],l=e==="start"&&!s||e!...
function Qz (line 17) | function Qz(r,e,t){if(!oD(r.axis)){var a=r.get(["axisLabel","showMinLabe...
function Ke (line 17) | function Ke(r){r&&(r.ignore=!0)}
function tS (line 17) | function tS(r,e){var t=r&&r.getBoundingRect().clone(),a=e&&e.getBounding...
function eS (line 17) | function eS(r){return r==="middle"||r==="center"}
function aM (line 17) | function aM(r,e,t,a,n){for(var i=[],o=[],s=[],l=0;l<r.length;l++){var u=...
function t5 (line 17) | function t5(r,e,t,a){var n=t.axis,i=t.getModel("axisTick"),o=i.get("show...
function e5 (line 17) | function e5(r,e,t,a){var n=t.axis,i=t.getModel("minorTick");if(!(!i.get(...
function r5 (line 17) | function r5(r,e,t,a){var n=t.axis,i=re(a.axisLabelShow,t.get(["axisLabel...
function a5 (line 17) | function a5(r,e){var t={axesInfo:{},seriesInvolved:!1,coordSysAxesInfo:{...
function n5 (line 17) | function n5(r,e,t){var a=e.getComponent("tooltip"),n=e.getComponent("axi...
function i5 (line 17) | function i5(r,e,t,a,n,i){var o=e.getModel("axisPointer"),s=["type","snap...
function o5 (line 17) | function o5(r,e){e.eachSeries(function(t){var a=t.coordinateSystem,n=t.g...
function s5 (line 17) | function s5(r,e){for(var t=e.model,a=e.dim,n=0;n<r.length;n++){var i=r[n...
function xc (line 17) | function xc(r,e){return r==="all"||z(r)&&vt(r,e)>=0||r===e}
function l5 (line 17) | function l5(r){var e=Oy(r);if(e){var t=e.axisPointerModel,a=e.axis.scale...
function Oy (line 17) | function Oy(r){var e=(r.ecModel.getComponent("axisPointer")||{}).coordSy...
function u5 (line 17) | function u5(r){var e=Oy(r);return e&&e.axisPointerModel}
function Ld (line 17) | function Ld(r){return!!r.get(["handle","show"])}
function hl (line 17) | function hl(r){return r.type+"||"+r.id}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function nM (line 17) | function nM(r,e,t,a){var n=t.axis;if(!n.scale.isBlank()){var i=t.getMode...
function iM (line 17) | function iM(r){Pd(r).splitAreaColors=null}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function lM (line 17) | function lM(r){r.registerComponentView(p5),r.registerComponentModel(Hz),...
function d5 (line 17) | function d5(r){pt(lM),r.registerSeriesModel(Bz),r.registerChartView(Fz),...
function g5 (line 17) | function g5(r){r.eachSeriesByType("radar",function(e){var t=e.getData(),...
function nS (line 17) | function nS(r){return!isNaN(r[0])&&!isNaN(r[1])}
function iS (line 17) | function iS(r){return[r.cx,r.cy]}
function y5 (line 17) | function y5(r){var e=r.polar;if(e){z(e)||(e=[e]);var t=[];D(e,function(a...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function u (line 17) | function u(v,c){var p=v.getItemVisual(c,"symbol")||"circle";if(p!=="none...
function f (line 17) | function f(v,c,p,d,g,y){p.removeAll();for(var m=0;m<c.length-1;m++){var ...
function h (line 17) | function h(v){return G(v,function(c){return[i.cx,i.cy]})}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function Mu (line 17) | function Mu(r,e){return J({show:e},r)}
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 17) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function m (line 17) | function m(R,E,N){var O=N%E.length;return R[O]=R[O]||[],O}
function e (line 17) | function e(t,a,n){var i=r.call(this,t,a,n)||this;return i.type="value",i...
function r (line 17) | function r(e,t,a){this.dimensions=[],this._model=e,this._indicatorAxes=G...
function A5 (line 17) | function A5(r){r.registerCoordinateSystem("radar",T5),r.registerComponen...
function C5 (line 17) | function C5(r){pt(A5),r.registerChartView(m5),r.registerSeriesModel(_5),...
function D5 (line 17) | function D5(r,e,t){var a=Ny(r);a[e]=t}
function M5 (line 17) | function M5(r,e,t){var a=Ny(r),n=a[e];n===t&&(a[e]=null)}
function sS (line 17) | function sS(r,e){return!!Ny(r)[e]}
function Ny (line 17) | function Ny(r){return r[oS]||(r[oS]={})}
function e (line 17) | function e(t){var a=r.call(this)||this;a._zr=t;var n=$(a._mousedownHandl...
function bc (line 17) | function bc(r,e,t,a,n){r.pointerChecker&&r.pointerChecker(a,n.originX,n....
function uM (line 17) | function uM(r,e,t,a,n){n.isAvailableBehavior=$(nf,null,t,a),r.trigger(e,n)}
function nf (line 17) | function nf(r,e,t){var a=t[r];return!r||a&&(!Y(a)||e.event[a+"Key"])}
function By (line 17) | function By(r,e,t){var a=r.target;a.x+=e,a.y+=t,a.dirty()}
function Vy (line 17) | function Vy(r,e,t,a){var n=r.target,i=r.zoomLimit,o=r.zoom=r.zoom||1;if(...
function Xh (line 17) | function Xh(r,e,t){var a=e.getComponentByElement(r.topTarget),n=a&&a.coo...
function fM (line 17) | function fM(r){if(Y(r)){var e=new DOMParser;r=e.parseFromString(r,"text/...
function r (line 17) | function r(){this._defs={},this._root=null}
function hS (line 17) | function hS(r,e){var t=r.getAttribute("gradientUnits");t==="userSpaceOnU...
function vS (line 17) | function vS(r,e){for(var t=r.firstChild;t;){if(t.nodeType===1&&t.nodeNam...
function je (line 17) | function je(r,e){r&&r.__inheritedStyle&&(e.__inheritedStyle||(e.__inheri...
function cS (line 17) | function cS(r){for(var e=Zh(r),t=[],a=0;a<e.length;a+=2){var n=parseFloa...
function He (line 17) | function He(r,e,t,a,n){var i=e,o=i.__inheritedStyle=i.__inheritedStyle||...
function P5 (line 17) | function P5(r,e){var t=e.__selfStyle;if(t){var a=t.textBaseline,n=a;!a||...
function pS (line 17) | function pS(r,e,t,a){var n=t&&t.match(R5);if(n){var i=sr(n[1]);a.push([r...
function E5 (line 17) | function E5(r,e){for(var t=0;t<e.length;t++){var a=e[t];a[0].style[a[1]]...
function Zh (line 17) | function Zh(r){return r.match(k5)||[]}
function N5 (line 17) | function N5(r,e){var t=r.getAttribute("transform");if(t){t=t.replace(/,/...
function hM (line 17) | function hM(r,e,t){var a=r.getAttribute("style");if(a){dS.lastIndex=0;fo...
function B5 (line 17) | function B5(r,e,t){for(var a=0;a<lS.length;a++){var n=lS[a],i=r.getAttri...
function vM (line 17) | function vM(r,e){var t=e.width/r.width,a=e.height/r.height,n=Math.min(t,...
function V5 (line 17) | function V5(r,e){var t=new L5;return t.parse(r,e)}
function r (line 17) | function r(e,t){this.type="geoSVG",this._usedGraphicMap=Z(),this._freedG...
function F5 (line 18) | function F5(r){r.silent=!1,r.isGroup&&r.traverse(function(e){e.silent=!1})}
function H5 (line 18) | function H5(r){var e=[],t=Z();return D(r,function(a){if(a.namedFrom==nul...
function W5 (line 18) | function W5(r,e){if(r==="china"){for(var t=0;t<e.length;t++)if(e[t].name...
function Y5 (line 18) | function Y5(r,e){if(r==="china"){var t=U5[e.name];if(t){var a=e.getCente...
function X5 (line 18) | function X5(r,e){r==="china"&&e.name==="台湾"&&e.geometries.push({type:"po...
function r (line 18) | function r(e,t,a){this.type="geoJSON",this._parsedMap=Z(),this._mapName=...
function K5 (line 19) | function K5(r){for(var e,t=0;t<r.length;t++){var a=r[t].getBoundingRect(...
function j5 (line 19) | function j5(r){return Y(r)?typeof JSON<"u"&&JSON.parse?JSON.parse(r):new...
function Iu (line 19) | function Iu(r){var e=r.getItemStyle(),t=r.get("areaColor");return t!=nul...
function yS (line 19) | function yS(r){var e=r.style;e&&(e.stroke=e.stroke||e.fill,e.fill=null)}
function r (line 19) | function r(e){var t=new at;this.uid=Io("ec_map_draw"),this._controller=n...
function f (line 19) | function f(c,p){return p&&(c=p(c)),c&&[c[0]*i.scaleX+i.x,c[1]*i.scaleY+i...
function h (line 19) | function h(c){for(var p=[],d=!u&&l&&l.project,g=0;g<c.length;++g){var y=...
function v (line 19) | function v(c){return{shape:{points:h(c)}}}
function w (line 19) | function w(T,A){if(T.length){var C=new Ch({culling:!0,segmentIgnoreThres...
function l (line 19) | function l(){var u={type:"geoRoam",componentType:s};return u[s+"Id"]=e.i...
function mS (line 19) | function mS(r,e,t,a){var n=a.getModel("itemStyle"),i=a.getModel(["emphas...
function _S (line 19) | function _S(r,e,t,a,n,i,o){var s=r.data,l=r.isGeo,u=s&&isNaN(s.get(s.map...
function SS (line 19) | function SS(r,e,t,a,n,i){r.data?r.data.setItemGraphicEl(i,e):nt(e).event...
function xS (line 19) | function xS(r,e,t,a,n){r.data||di({el:e,componentModel:n,itemName:t,item...
function bS (line 19) | function bS(r,e,t,a,n){e.highDownSilentOnTouch=!!n.get("selectedMode");v...
function wS (line 19) | function wS(r,e,t){var a=[],n;function i(){n=[]}function o(){n.length&&(...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function aG (line 19) | function aG(r,e){var t={};return D(r,function(a){a.each(a.mapDimension("...
function nG (line 19) | function nG(r){var e={};r.eachSeriesByType("map",function(t){var a=t.get...
function iG (line 19) | function iG(r){var e={};r.eachSeriesByType("map",function(t){var a=t.get...
function e (line 19) | function e(t){var a=r.call(this)||this;return a.type="view",a.dimensions...
function AS (line 19) | function AS(r){var e=r.seriesModel;return e?e.coordinateSystem:null}
function e (line 19) | function e(t,a,n){var i=r.call(this,t)||this;i.dimensions=dM,i.type="geo...
function CS (line 19) | function CS(r){var e=r.geoModel,t=r.seriesModel;return e?e.coordinateSys...
function DS (line 19) | function DS(r,e){var t=r.get("boundingCoords");if(t!=null){var a=t[0],n=...
function sG (line 19) | function sG(r,e){D(e.get("geoCoord"),function(t,a){r.addGeoCoord(a,t)})}
function r (line 19) | function r(){this.dimensions=dM}
function n (line 19) | function n(o){return{nameProperty:o.get("nameProperty"),aspectScale:o.ge...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function MS (line 19) | function MS(r,e){return r.pointToProjected?r.pointToProjected(e):r.point...
function Gy (line 19) | function Gy(r,e,t,a){var n=r.getZoom(),i=r.getCenter(),o=e.zoom,s=r.proj...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function hG (line 19) | function hG(r,e,t){Sa.registerMap(r,e,t)}
function yM (line 19) | function yM(r){r.registerCoordinateSystem("geo",gM),r.registerComponentM...
function vG (line 19) | function vG(r){pt(yM),r.registerChartView(eG),r.registerSeriesModel(rG),...
function cG (line 19) | function cG(r){var e=r;e.hierNode={defaultAncestor:null,ancestor:e,preli...
function pG (line 19) | function pG(r,e){var t=r.isExpand?r.children:[],a=r.parentNode.children,...
function dG (line 19) | function dG(r){var e=r.hierNode.prelim+r.parentNode.hierNode.modifier;r....
function IS (line 19) | function IS(r){return arguments.length?r:xG}
function ws (line 19) | function ws(r,e){return r-=Math.PI/2,{x:e*Math.cos(r),y:e*Math.sin(r)}}
function gG (line 19) | function gG(r,e){return jt(r.getBoxLayoutParams(),{width:e.getWidth(),he...
function yG (line 19) | function yG(r){for(var e=r.children,t=e.length,a=0,n=0;--t>=0;){var i=e[...
function mG (line 19) | function mG(r,e,t,a){if(e){for(var n=r,i=r,o=i.parentNode.children[0],s=...
function Ac (line 19) | function Ac(r){var e=r.children;return e.length&&r.isExpand?e[e.length-1...
function Cc (line 19) | function Cc(r){var e=r.children;return e.length&&r.isExpand?e[0]:r.hierN...
function _G (line 19) | function _G(r,e,t){return r.hierNode.ancestor.parentNode===e.parentNode?...
function SG (line 19) | function SG(r,e,t){var a=t/(e.hierNode.i-r.hierNode.i);e.hierNode.change...
function xG (line 19) | function xG(r,e){return r.parentNode===e.parentNode?1:2}
function r (line 19) | function r(){this.parentPoint=[],this.childPoints=[]}
function e (line 19) | function e(t){return r.call(this,t)||this}
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function LS (line 19) | function LS(r,e){var t=r.getItemLayout(e);return t&&!isNaN(t.x)&&!isNaN(...
function PS (line 19) | function PS(r,e,t,a,n){var i=!t,o=r.tree.getNodeByDataIndex(e),s=o.getMo...
function AG (line 19) | function AG(r,e,t,a,n,i,o,s){var l=e.getModel(),u=r.get("edgeShape"),f=r...
function RS (line 19) | function RS(r,e,t,a,n){var i=e.tree.root,o=mM(i,r),s=o.source,l=o.source...
function mM (line 19) | function mM(r,e){for(var t=e.parentNode===r?e:e.parentNode||e,a;a=t.getL...
function ES (line 19) | function ES(r,e,t,a,n){var i=r.tree.getNodeByDataIndex(e),o=r.tree.root,...
function kd (line 19) | function kd(r,e,t,a,n){var i,o,s,l,u,f,h,v;if(r==="radial"){u=a.rawX,h=a...
function _M (line 19) | function _M(r){var e=r.mainData,t=r.datas;t||(t={main:e},r.datasAttr={ma...
function CG (line 19) | function CG(r,e){if(PG(this)){var t=V({},lr(this).datas);t[this.dataType...
function DG (line 19) | function DG(r,e){return r.struct&&r.struct.update(),e}
function MG (line 19) | function MG(r,e){return D(lr(e).datas,function(t,a){t!==e&&Fy(t.cloneSha...
function IG (line 19) | function IG(r){var e=lr(this).mainData;return r==null||e==null?e:lr(e).d...
function LG (line 19) | function LG(){var r=lr(this).mainData;return r==null?[{data:r}]:G(_t(lr(...
function PG (line 19) | function PG(r){return lr(r).mainData===r}
function SM (line 19) | function SM(r,e,t){lr(r).datas={},D(e,function(a,n){Fy(a,n,r,t)})}
function Fy (line 19) | function Fy(r,e,t,a){lr(t).datas[e]=r,lr(r).mainData=t,r.dataType=e,a.st...
function r (line 19) | function r(e,t){this.depth=0,this.height=0,this.dataIndex=-1,this.childr...
function r (line 19) | function r(e){this.type="tree",this._nodes=[],this.hostModel=e}
function s (line 19) | function s(f,h){var v=f.value;o=Math.max(o,z(v)?v.length:1),i.push(f);va...
function EG (line 19) | function EG(r,e){var t=e.children;r.parentNode!==e&&(t.push(r),r.parentN...
function vl (line 19) | function vl(r,e,t){if(r&&vt(e,r.type)>=0){var a=t.getData().tree.root,n=...
function xM (line 19) | function xM(r){for(var e=[];r;)r=r.parentNode,r&&e.push(r);return e.reve...
function Wy (line 19) | function Wy(r,e){var t=xM(r);return vt(t,e)>=0}
function qh (line 19) | function qh(r,e){for(var t=[];r;){var a=r.dataIndex;t.push({name:r.name,...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.hasS...
function s (line 19) | function s(h){h.wrapMethod("getItemModel",function(v,c){var p=o.getNodeB...
function OG (line 19) | function OG(r,e,t){for(var a=[r],n=[],i;i=a.pop();)if(n.push(i),i.isExpa...
function ss (line 19) | function ss(r,e){for(var t=[r],a;a=t.pop();)if(e(a),a.isExpand){var n=a....
function NG (line 19) | function NG(r,e){r.eachSeriesByType("tree",function(t){BG(t,e)})}
function BG (line 19) | function BG(r,e){var t=gG(r,e);r.layoutInfo=t;var a=r.get("layout"),n=0,...
function VG (line 19) | function VG(r){r.eachSeriesByType("tree",function(e){var t=e.getData(),a...
function zG (line 19) | function zG(r){r.registerAction({type:"treeExpandAndCollapse",event:"tre...
function GG (line 19) | function GG(r){r.registerChartView(TG),r.registerSeriesModel(kG),r.regis...
function FG (line 19) | function FG(r){for(var e=0;e<kS.length;e++)r.registerAction({type:kS[e],...
function bM (line 19) | function bM(r){var e=r.getData(),t=e.tree,a={};t.eachNode(function(n){fo...
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function f (line 19) | function f(h){h.wrapMethod("getItemModel",function(v,c){var p=u.getNodeB...
function wM (line 19) | function wM(r){var e=0;D(r.children,function(a){wM(a);var n=a.value;z(n)...
function WG (line 19) | function WG(r,e){var t=Rt(e.get("color")),a=Rt(e.get(["aria","decal","de...
function r (line 19) | function r(e){this.group=new at,e.add(this.group)}
function $G (line 19) | function $G(r,e,t,a,n,i){var o=[[n?r:r-Dc,e],[r+t,e],[r+t,e+a],[n?r:r-Dc...
function XG (line 19) | function XG(r,e,t){nt(r).eventData={componentType:"series",componentSubT...
function r (line 19) | function r(){this._storage=[],this._elExistsMap={}}
function qG (line 19) | function qG(){return new ZG}
function e (line 19) | function e(){var t=r!==null&&r.apply(this,arguments)||this;return t.type...
function h (line 19) | function h(m,_,S,b){return tF(a,l,u,n,s,f,m,_,S,b)}
function d (line 19) | function d(m,_,S,b,x){b?(_=m,D(m,function(A,C){!A.isRemoved()&&T(C,C)}))...
function g (line 19) | function g(m){var _=ls();return m&&D(m,function(S,b){var x=_[b];D(S,func...
function y (line 19) | function y(){D(v,function(m){D(m,function(_){_.parent&&_.parent.remove(_...
function ls (line 19) | function ls(){return{nodeGroup:[],background:[],content:[]}}
function tF (line 19) | function tF(r,e,t,a,n,i,o,s,l,u){if(!o)return;var f=o.getLayout(),h=r.ge...
function eF (line 19) | function eF(r,e){return r*KG+e}
function r (line 19) | function r(e){var t=e.mappingMethod,a=e.type,n=this.option=rt(e);this.ty...
function v (line 19) | function v(c,p){var d=Math.abs(c-e);d<i&&(i=d,n=p)}
function aF (line 19) | function aF(r){var e=r.pieceList;r.hasSpecialVisual=!1,D(e,function(t,a)...
function nF (line 19) | function nF(r){var e=r.categories,t=r.categoryMap={},a=r.visual;if(cl(e,...
function Mc (line 19) | function Mc(r,e){var t=r.visual,a=[];tt(t)?cl(t,function(i){a.push(i)}):...
function Lu (line 19) | function Lu(r){return{applyVisual:function(e,t,a){var n=this.mapValueToV...
function FS (line 19) | function FS(r){var e=this.option.visual;return e[Math.round(Dt(r,[0,1],[...
function us (line 19) | function us(r){return function(e,t,a){a(r,this.mapValueToVisual(e))}}
function Ts (line 19) | function Ts(r){var e=this.option.visual;return e[this.option.loop&&r!==Z...
function Nn (line 19) | function Nn(){return this.option.visual[0]}
function Nd (line 19) | function Nd(r){return{linear:function(e){return Dt(e,r,this.option.visua...
function Bd (line 19) | function Bd(r){var e=this.option,t=e.pieceList;if(e.hasSpecialVisual){va...
function TM (line 19) | function TM(r,e){return r.visual=e,r.type==="color"&&(r.parsedVisual=G(e...
function Pu (line 19) | function Pu(r,e,t){return r?e<=t:e<t}
function CM (line 19) | function CM(r,e,t,a){var n=r.getModel(),i=r.getLayout(),o=r.hostTree.dat...
function lF (line 19) | function lF(r,e,t){var a=V({},e),n=t.designatedVisualItemStyle;return D(...
function HS (line 19) | function HS(r){var e=Ic(r,"color");if(e){var t=Ic(r,"colorAlpha"),a=Ic(r...
function uF (line 19) | function uF(r,e){return e!=null?eo(e,null,null,r):null}
function Ic (line 19) | function Ic(r,e){var t=r[e];if(t!=null&&t!=="none")return t}
function fF (line 19) | function fF(r,e,t,a,n,i){if(!(!i||!i.length)){var o=Lc(e,"color")||n.col...
function Lc (line 19) | function Lc(r,e){var t=r.get(e);return z(t)&&t.length?{name:e,range:t}:n...
function hF (line 19) | function hF(r,e,t,a,n,i){var o=V({},e);if(n){var s=n.type,l=s==="color"&...
function MM (line 19) | function MM(r,e,t,a){var n,i;if(!r.isRemoved()){var o=r.getLayout();n=o....
function gF (line 19) | function gF(r,e,t,a,n,i){var o=r.children||[],s=a.sort;s!=="asc"&&s!=="d...
function yF (line 19) | function yF(r,e,t,a,n){if(!a)return t;for(var i=r.get("visibleMin"),o=n....
function mF (line 19) | function mF(r,e){return e&&r.sort(function(t,a){var n=e==="asc"?t.getVal...
function _F (line 19) | function _F(r,e,t){for(var a=0,n=0,i=e.length;n<i;n++)a+=e[n].getValue()...
function SF (line 19) | function SF(r,e,t){for(var a=0,n=1/0,i=0,o=void 0,s=r.length;i<s;i++)o=r...
function US (line 19) | function US(r,e,t,a,n){var i=e===t.width?0:1,o=1-i,s=["x","y"],l=["width...
function xF (line 19) | function xF(r,e,t,a,n){var i=(e||{}).node,o=[a,n];if(!i||i===t)return o;...
function bF (line 19) | function bF(r,e,t){if(e)return{x:e.x,y:e.y};var a={x:0,y:0};if(!t)return...
function IM (line 19) | function IM(r,e,t,a,n){var i=r.getLayout(),o=t[n],s=o&&o===r;if(!(o&&!s|...
function LM (line 19) | function LM(r){return r.get(cF)?r.get(pF):0}
function wF (line 19) | function wF(r){r.registerSeriesModel(HG),r.registerChartView(QG),r.regis...
function TF (line 19) | function TF(r){var e=r.findComponents({mainType:"legend"});!e||!e.length...
function AF (line 19) | function AF(r){var e={};r.eachSeriesByType("graph",function(t){var a=t.g...
function Ru (line 19) | function Ru(r){return r instanceof Array||(r=[r,r]),r}
function CF (line 19) | function CF(r){r.eachSeriesByType("graph",function(e){var t=e.getGraph()...
function IF (line 19) | function IF(r){Kh(r)&&(r.__curvenessList=[],r.__edgeMap={},PM(r))}
function LF (line 19) | function LF(r,e,t,a){if(Kh(t)){var n=dl(r,e,t),i=t.__edgeMap,o=i[RM(n)];...
function Yy (line 19) | function Yy(r,e,t,a){var n=Kh(e),i=z(n);if(!n)return null;var o=DF(r,e);...
function EM (line 19) | function EM(r){var e=r.coordinateSystem;if(!(e&&e.type!=="view")){var t=...
function $y (line 19) | function $y(r,e){r.eachEdge(function(t,a){var n=Tr(t.getModel().get(["li...
function PF (line 19) | function PF(r,e){r.eachSeriesByType("graph",function(t){var a=t.get("lay...
function As (line 19) | function As(r){var e=r.coordinateSystem;if(e.type!=="view")return 1;var ...
function Cs (line 19) | function Cs(r){var e=r.getVisual("symbolSize");return e instanceof Array...
function Xy (line 19) | function Xy(r,e,t,a){var n=r.coordinateSystem;if(!(n&&n.type!=="view")){...
function kM (line 19) | function kM(r,e,t,a){var n=r.getGraphicEl();if(n){var i=r.getModel(),o=i...
function EF (line 19) | function EF(r){r.eachSeriesByType("graph",function(e){e.get("layout")===...
function kF (line 19) | function kF(r,e,t){for(var a=r,n=e,i=t.rect,o=i.width,s=i.height,l=[i.x+...
fun
Condensed preview — 251 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,115K chars).
[
{
"path": ".claude/skills/no-build/SKILL.md",
"chars": 375,
"preview": "---\nname: no-build\ndescription: 修改代码后禁止执行 go build、go run 等编译/运行命令。本项目目标平台是 Linux 服务器,代码修改后由用户自行部署测试。\n---\n\n# 禁止本地编译/运行\n\n"
},
{
"path": ".editorconfig",
"chars": 619,
"preview": "# @see: http://editorconfig.org\n\nroot = true\n\n[*] # 表示所有文件适用\ncharset = utf-8 # 设置文件字符集为 utf-8\nend_of_line = lf # 控制换行类型("
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 671,
"preview": "name: Bug上报\ndescription: 遇到了问题,点我提交\ntitle: \"[Bug] \"\nlabels:\n - bug\nassignees: miracleEverywhere\nbody:\n - type: input\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 27,
"preview": "blank_issues_enabled: false"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 545,
"preview": "name: 添加新功能吧\ndescription: 说说你想要什么吧\ntitle: \"[Feature] \"\nlabels:\n - enhancement\nassignees: miracleEverywhere\nbody:\n - ty"
},
{
"path": ".github/workflows/go.yml",
"chars": 2019,
"preview": "name: release-packages\n\non:\n push:\n tags: [ \"v*\" ]\n\nenv:\n REGISTRY_SERVER_ADDRESS: ghcr.io/miracleeverywhere/dst-ma"
},
{
"path": ".gitignore",
"chars": 33,
"preview": ".idea\n.claude/settings.local.json"
},
{
"path": "CLAUDE.md",
"chars": 5844,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "LICENSE",
"chars": 1064,
"preview": "MIT License\n\nCopyright (c) 2024 Miracle\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "README.md",
"chars": 4292,
"preview": "### [中文文档] | [[README](docs/README.md)]\n\n# DMP 饥荒管理平台\n\n\n\ntype Handler struct {\n\tuserDao *dao.UserDAO"
},
{
"path": "app/mod/handler.go",
"chars": 15430,
"preview": "package mod\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/dst\"\n\t\"dst-management-pl"
},
{
"path": "app/mod/i18n.go",
"chars": 1638,
"preview": "package mod\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExtendedI"
},
{
"path": "app/mod/router.go",
"chars": 845,
"preview": "package mod\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/gin-g"
},
{
"path": "app/mod/utils.go",
"chars": 7214,
"preview": "package mod\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/dst\"\n\t\"dst-management-pl"
},
{
"path": "app/platform/handler.go",
"chars": 13778,
"preview": "package platform\n\nimport (\n\t\"context\"\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database"
},
{
"path": "app/platform/i18n.go",
"chars": 864,
"preview": "package platform\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExte"
},
{
"path": "app/platform/router.go",
"chars": 1043,
"preview": "package platform\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/"
},
{
"path": "app/platform/utils.go",
"chars": 2237,
"preview": "package platform\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/shirou/gopsutil/v3"
},
{
"path": "app/player/handler.go",
"chars": 9668,
"preview": "package player\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-ma"
},
{
"path": "app/player/i18n.go",
"chars": 571,
"preview": "package player\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExtend"
},
{
"path": "app/player/router.go",
"chars": 620,
"preview": "package player\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/gi"
},
{
"path": "app/player/utils.go",
"chars": 1275,
"preview": "package player\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/logger\"\n\t\"strings\"\n\n\t"
},
{
"path": "app/room/handler.go",
"chars": 30579,
"preview": "package room\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-mana"
},
{
"path": "app/room/i18n.go",
"chars": 2938,
"preview": "package room\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExtended"
},
{
"path": "app/room/router.go",
"chars": 709,
"preview": "package room\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/gin-"
},
{
"path": "app/room/utils.go",
"chars": 14684,
"preview": "package room\n\nimport (\n\t\"bufio\"\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database/db\"\n\t"
},
{
"path": "app/tools/handler.go",
"chars": 17982,
"preview": "package tools\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-man"
},
{
"path": "app/tools/i18n.go",
"chars": 1163,
"preview": "package tools\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExtende"
},
{
"path": "app/tools/router.go",
"chars": 785,
"preview": "package tools\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/gin"
},
{
"path": "app/tools/utils.go",
"chars": 1050,
"preview": "package tools\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/logger\"\n\t\"strings\"\n\n\t\""
},
{
"path": "app/user/handler.go",
"chars": 10746,
"preview": "package user\n\nimport (\n\t\"dst-management-platform-api/database/dao\"\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-mana"
},
{
"path": "app/user/i18n.go",
"chars": 1290,
"preview": "package user\n\nimport \"dst-management-platform-api/utils\"\n\ntype ExtendedI18n struct {\n\tutils.BaseI18n\n}\n\nfunc NewExtended"
},
{
"path": "app/user/router.go",
"chars": 916,
"preview": "package user\n\nimport (\n\t\"dst-management-platform-api/middleware\"\n\t\"dst-management-platform-api/utils\"\n\n\t\"github.com/gin-"
},
{
"path": "app/user/utils.go",
"chars": 5356,
"preview": "package user\n\nimport \"dst-management-platform-api/database/dao\"\n\ntype Handler struct {\n\tuserDao *dao.UserDAO\n}\n\nfunc New"
},
{
"path": "database/dao/base.go",
"chars": 1413,
"preview": "package dao\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\ntype BaseDAO[T any] struct {\n\tdb *gorm.DB\n}\n\ntype PaginatedResult[T any] struct"
},
{
"path": "database/dao/composite.go",
"chars": 830,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-management-platform-api/database/models\"\n)\n\n// Fe"
},
{
"path": "database/dao/globalSetting.go",
"chars": 1106,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype GlobalSettingDAO struct {\n"
},
{
"path": "database/dao/room.go",
"chars": 2032,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype RoomDAO struct {\n\tBaseDAO["
},
{
"path": "database/dao/roomSetting.go",
"chars": 638,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype RoomSettingDAO struct {\n\tB"
},
{
"path": "database/dao/system.go",
"chars": 1126,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-management-platform-api/database/models\"\n\t\"dst-ma"
},
{
"path": "database/dao/uidMap.go",
"chars": 797,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\t\"fmt\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype UidMapDAO struct {\n"
},
{
"path": "database/dao/user.go",
"chars": 1184,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\t\"errors\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype UserDAO struct {"
},
{
"path": "database/dao/world.go",
"chars": 1311,
"preview": "package dao\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\t\"errors\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype WorldDAO struct "
},
{
"path": "database/db/cache.go",
"chars": 1371,
"preview": "package db\n\nimport (\n\t\"os\"\n\t\"sync\"\n)\n\nvar (\n\t// JwtSecret jwt密钥\n\tJwtSecret string\n\t// CurrentDir 当前工作目录\n\tCurrentDir stri"
},
{
"path": "database/db/database.go",
"chars": 1482,
"preview": "package db\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\t\"dst-management-platform-api/logger\"\n\t\"fmt\"\n\t\"os\"\n\n"
},
{
"path": "database/models/globalSetting.go",
"chars": 959,
"preview": "package models\n\ntype GlobalSetting struct {\n\tID int `gorm:\"primaryKey;not null;column:id\" json:\"id\"`\n"
},
{
"path": "database/models/room.go",
"chars": 1824,
"preview": "package models\n\ntype Room struct {\n\tID int `gorm:\"primaryKey;autoIncrement;column:id\" json:\"id\"`\n\tStatu"
},
{
"path": "database/models/roomSetting.go",
"chars": 1543,
"preview": "package models\n\ntype RoomSetting struct {\n\tRoomID int `gorm:\"primaryKey;not null;column:room_id\" j"
},
{
"path": "database/models/system.go",
"chars": 169,
"preview": "package models\n\ntype System struct {\n\tKey string `gorm:\"primaryKey;not null\"`\n\tValue string `gorm:\"not null\"`\n}\n\nfunc "
},
{
"path": "database/models/uidMap.go",
"chars": 295,
"preview": "package models\n\ntype UidMap struct {\n\tUID string `gorm:\"primaryKey;not null;column:uid\" json:\"uid\"`\n\tNickname strin"
},
{
"path": "database/models/user.go",
"chars": 894,
"preview": "package models\n\ntype User struct {\n\tUsername string `gorm:\"primaryKey;not null;column:username\" json:\"username\" bin"
},
{
"path": "database/models/world.go",
"chars": 1018,
"preview": "package models\n\ntype World struct {\n\tID int `gorm:\"primaryKey;autoIncrement;column:id\" json:\"id\"` // "
},
{
"path": "docker/Dockerfile",
"chars": 843,
"preview": "# 第1阶段:构建应用程序\nFROM --platform=$BUILDPLATFORM golang:1.25 AS build\n\nARG GOPROXY\nARG GOSUMDB\nARG GOPRIVATE\nARG TARGETARCH\n"
},
{
"path": "docker/docker-compose.yml",
"chars": 1288,
"preview": "services:\n\n dmp:\n # image: ghcr.io/harelive/dst-management-platform-api:latest # 测试用\n # image: ghcr.io/miracleeve"
},
{
"path": "docker/entry-point.sh",
"chars": 788,
"preview": "#!/bin/bash\n\n# 定义变量\nDMP_HOME=\"/root\"\n\ncd $DMP_HOME || exit\n\n# 定义 SIGTERM 信号处理函数\ncleanup() {\n echo \"Received SIGTERM, "
},
{
"path": "docs/README.md",
"chars": 2526,
"preview": "### [[中文文档](../README.md)] | [README]\n\n# DMP\n\n\n\n// SaveAll 保存所有配置文件\n"
},
{
"path": "dst/logs.go",
"chars": 4563,
"preview": "package dst\n\nimport (\n\t\"dst-management-platform-api/logger\"\n\t\"dst-management-platform-api/utils\"\n\t\"fmt\"\n)\n\nfunc (g *Game"
},
{
"path": "dst/map.go",
"chars": 11452,
"preview": "package dst\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-management-platform-api/logger\""
},
{
"path": "dst/mod.go",
"chars": 17215,
"preview": "package dst\n\nimport (\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-management-platform-api/logger\"\n\t\"dst-management-"
},
{
"path": "dst/player.go",
"chars": 7308,
"preview": "package dst\n\nimport (\n\t\"dst-management-platform-api/logger\"\n\t\"dst-management-platform-api/utils\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"stri"
},
{
"path": "dst/room.go",
"chars": 13452,
"preview": "package dst\n\nimport (\n\t\"dst-management-platform-api/database/models\"\n\t\"dst-management-platform-api/logger\"\n\t\"dst-managem"
},
{
"path": "dst/utils.go",
"chars": 20007,
"preview": "package dst\n\nimport (\n\t\"dst-management-platform-api/database/db\"\n\t\"dst-management-platform-api/database/models\"\n\t\"dst-ma"
},
{
"path": "dst/world.go",
"chars": 11478,
"preview": "package dst\n\nimport (\n\t\"bufio\"\n\t\"dst-management-platform-api/database/models\"\n\t\"dst-management-platform-api/logger\"\n\t\"ds"
},
{
"path": "embedFS/dist/assets/CodeEditor-D4vpmGPS.css",
"chars": 8986,
"preview": ".CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre."
},
{
"path": "embedFS/dist/assets/CodeEditor-D9V3e-IU.js",
"chars": 256973,
"preview": "import{aH as Tu,_ as Mu,c as Au,o as Nu,z as Du,aI as zu,k as Ou,n as Wu,aJ as Eu}from\"./index-But_YWe_.js\";var pi={expo"
},
{
"path": "embedFS/dist/assets/ConfirmBox-Dl9KDCIE.css",
"chars": 159,
"preview": ".v-card-title[data-v-0609b185]{padding:20px 24px 0}.v-card-text[data-v-0609b185]{padding:16px 24px 8px}.v-card-actions[d"
},
{
"path": "embedFS/dist/assets/ConfirmBox-UdDnLY_r.js",
"chars": 2087,
"preview": "import{_ as x,i as c,A as r,o as i,w as t,a,V as v,ab as B,k as S,t as s,p as k,D as f,L as T,C as w,s as m,v as p}from\""
},
{
"path": "embedFS/dist/assets/NavbarThemeSwitcher-DznimRXG.js",
"chars": 5299,
"preview": "import{_ as O,c as F,o as p,a as k,k as B,l as f,a1 as H,m as P,t as I,F as R,b as x,z as V,A as g,s as w,a2 as W,g as S"
},
{
"path": "embedFS/dist/assets/NavbarThemeSwitcher-HlaVvm-i.css",
"chars": 191,
"preview": ".logo[data-v-bfb49ee7]{width:3em!important;height:2em!important}::view-transition-old(root),::view-transition-new(root){"
},
{
"path": "embedFS/dist/assets/Result-DI11lCXq.js",
"chars": 1360,
"preview": "import{A as t,o as i,w as a,k as l,n as u,D as r,L as f,J as s,t as o}from\"./index-But_YWe_.js\";import{V as p}from\"./VCo"
},
{
"path": "embedFS/dist/assets/TimeRunning-BnGoYIbA.js",
"chars": 1202,
"preview": "import{b as v,f as g,i as f,O as b,j as k,a9 as R,c as n,o as t,D as r,k as M,t as o,l,F as B}from\"./index-But_YWe_.js\";"
},
{
"path": "embedFS/dist/assets/VAlert-DhdDkcOZ.css",
"chars": 4530,
"preview": "@charset \"UTF-8\";.v-alert{display:grid;flex:1 1;grid-template-areas:\"prepend content append close\" \". content . .\";grid-"
},
{
"path": "embedFS/dist/assets/VAlert-hOsVftqi.js",
"chars": 3172,
"preview": "import{am as L,aj as f,i as w,ai as F,as as E,a4 as d,aQ as M,bD as N,bz as R,b2 as j,bA as O,b1 as q,c6 as J,a$ as K,b0"
},
{
"path": "embedFS/dist/assets/VBadge-D7oDPA4h.js",
"chars": 2035,
"preview": "import{ai as k,aj as B,a_ as y,a$ as N,a2 as S,b0 as V,h as x,b1 as P,b2 as h,ak as T,b3 as _,a as s,E as n,b4 as w,b5 a"
},
{
"path": "embedFS/dist/assets/VBadge-Di9QvBJE.css",
"chars": 1615,
"preview": "@charset \"UTF-8\";.v-badge{display:inline-block;line-height:1}.v-badge__badge{align-items:center;display:inline-flex;bord"
},
{
"path": "embedFS/dist/assets/VChip-DNNqEGWs.css",
"chars": 11853,
"preview": "@charset \"UTF-8\";.v-chip{align-items:center;display:inline-flex;font-weight:500;max-width:100%;min-width:0;overflow:hidd"
},
{
"path": "embedFS/dist/assets/VChip-pydKSkvr.js",
"chars": 10713,
"preview": "import{ai as le,aj as te,bd as Pe,e as we,au as be,a6 as T,i as S,bs as se,bt as Ee,bu as oe,O as ge,ak as me,a as f,k a"
},
{
"path": "embedFS/dist/assets/VCol-BCVfdzjO.js",
"chars": 1395,
"preview": "import\"./VRow-DtJTiJKO.js\";import{ai as y,aj as S,i as C,T as k,aU as N,aX as h,ba as n,bb as f}from\"./index-But_YWe_.js"
},
{
"path": "embedFS/dist/assets/VContainer-DLwbkp_b.css",
"chars": 419,
"preview": "@charset \"UTF-8\";.v-container{width:100%;padding:16px;margin-right:auto;margin-left:auto}@media(min-width:960px){.v-cont"
},
{
"path": "embedFS/dist/assets/VContainer-Dy0K6N_V.js",
"chars": 455,
"preview": "import{ai as t,aj as l,bd as i,b2 as r,ak as m,a as c,n as u,K as d,aU as C,b4 as f,aX as p}from\"./index-But_YWe_.js\";co"
},
{
"path": "embedFS/dist/assets/VDataTable-BKhtqv_h.css",
"chars": 7813,
"preview": "@charset \"UTF-8\";.v-data-table{width:100%}.v-data-table__table{width:100%;border-collapse:separate;border-spacing:0}.v-d"
},
{
"path": "embedFS/dist/assets/VDataTable-BRoXfM0Y.js",
"chars": 32303,
"preview": "import{cb as ft,bo as gt,aj as V,as as M,cc as $,i as k,O as be,b$ as Oe,ar as K,aR as me,a0 as z,ai as q,a2 as oe,ak as"
},
{
"path": "embedFS/dist/assets/VDataTableServer-DQucjpu4.js",
"chars": 2959,
"preview": "import{c as M,a as Q,b as U,d as X,u as Z,p as $,e as ee,f as ae,g as te,h as oe,i as se,j as le,k as S,l as y,m as f,n "
},
{
"path": "embedFS/dist/assets/VDialog-BRzUh5G-.css",
"chars": 2608,
"preview": "@charset \"UTF-8\";.v-dialog{align-items:center;justify-content:center;margin:auto}.v-dialog>.v-overlay__content{max-heigh"
},
{
"path": "embedFS/dist/assets/VDialog-BetymGlx.js",
"chars": 1649,
"preview": "import{ai as P,aj as E,as as b,bp as x,f as y,O as D,ak as w,bq as u,E as l,a as d,aN as A,at as F,br as I,aC as L,al as"
},
{
"path": "embedFS/dist/assets/VExpansionPanels-B0znoeId.js",
"chars": 4943,
"preview": "import{ai as m,aj as P,ar as h,cu as j,ak as b,a as i,z as A,b9 as F,k as u,n as y,K as v,cv as N,aX as E,R as I,a_ as w"
},
{
"path": "embedFS/dist/assets/VExpansionPanels-Cp9PmzBO.css",
"chars": 6394,
"preview": "@charset \"UTF-8\";.v-expansion-panel{background-color:rgb(var(--v-theme-surface));color:rgba(var(--v-theme-on-surface),va"
},
{
"path": "embedFS/dist/assets/VForm-D9iQptwo.js",
"chars": 726,
"preview": "import{ai as d,aj as p,f as v,ak as b,k as y,n as F,K as V,aN as h,aX as k}from\"./index-But_YWe_.js\";import{c as R,a as "
},
{
"path": "embedFS/dist/assets/VInput-D4WPJeZb.js",
"chars": 9039,
"preview": "import{aj as I,as as F,a4 as V,a6 as z,f as _,O as P,a0 as ae,ar as Q,i as g,aJ as ne,ai as T,ak as O,k as C,n as L,K as"
},
{
"path": "embedFS/dist/assets/VInput-DAKtY42E.css",
"chars": 4435,
"preview": "@charset \"UTF-8\";.v-label{align-items:center;color:inherit;display:inline-flex;font-size:.9375rem;letter-spacing:normal;"
},
{
"path": "embedFS/dist/assets/VList-Cjh13LKl.css",
"chars": 2084,
"preview": "@charset \"UTF-8\";.v-list{overflow:auto;padding:8px 0;position:relative;outline:none}.v-list{border-color:rgba(var(--v-bo"
},
{
"path": "embedFS/dist/assets/VList-D_MCDEbx.js",
"chars": 10201,
"preview": "import{ai as j,aj as L,bK as ge,i as P,bL as ye,ar as he,bM as Ce,a4 as d,ak as $,a as f,al as Ie,bN as Se,b8 as pe,z as"
},
{
"path": "embedFS/dist/assets/VListItemAction-1-e96EHC.js",
"chars": 407,
"preview": "import{ai as s,aj as o,ak as n,a as i,n as m,K as l,aU as r,aX as c}from\"./index-But_YWe_.js\";const V=o({start:Boolean,e"
},
{
"path": "embedFS/dist/assets/VMenu-CXNpVDF7.js",
"chars": 2810,
"preview": "import{ai as P,aj as h,as as C,bp as D,bd as x,aL as R,a4 as M,f as I,ar as T,bV as y,a6 as L,Q as S,bW as O,O as K,i as"
},
{
"path": "embedFS/dist/assets/VMenu-Ceirw4NI.css",
"chars": 551,
"preview": "@charset \"UTF-8\";.v-menu>.v-overlay__content{display:flex;flex-direction:column}.v-menu>.v-overlay__content{border-radiu"
},
{
"path": "embedFS/dist/assets/VNumberInput-C-YY_5Ww.css",
"chars": 2827,
"preview": "@charset \"UTF-8\";.v-number-input input[type=number]{-moz-appearance:textfield}.v-number-input input[type=number]::-webki"
},
{
"path": "embedFS/dist/assets/VNumberInput-uP7vEYUF.js",
"chars": 7491,
"preview": "import{V as J,m as me}from\"./VTextField-C23ADfJX.js\";import{c8 as Q,ai as fe,aj as pe,f as ge,i as h,a6 as H,a2 as be,as"
},
{
"path": "embedFS/dist/assets/VPagination-BEBHkR5c.js",
"chars": 5653,
"preview": "import{f as K,cp as W,ai as G,aj as U,as as X,a2 as j,bd as O,aQ as Q,e as H,a6 as J,ax as R,bs as Y,i as g,aS as k,a4 a"
},
{
"path": "embedFS/dist/assets/VPagination-DFfzvyji.css",
"chars": 230,
"preview": "@charset \"UTF-8\";.v-pagination__list{display:inline-flex;list-style-type:none;justify-content:center;width:100%}.v-pagin"
},
{
"path": "embedFS/dist/assets/VRadioGroup-C71Ml1om.css",
"chars": 300,
"preview": "@charset \"UTF-8\";.v-radio-group>.v-input__control{flex-direction:column}.v-radio-group>.v-input__control>.v-label{margin"
},
{
"path": "embedFS/dist/assets/VRadioGroup-DDJeM_aY.js",
"chars": 1641,
"preview": "import{V as d,m as A,a as N,b as O}from\"./VSelectionControl-CJfnRnc-.js\";import{ai as y,aj as b,ak as v,a as t,E as i,aL"
},
{
"path": "embedFS/dist/assets/VRow-CL8Cm54n.css",
"chars": 9122,
"preview": "@charset \"UTF-8\";.v-row{display:flex;flex-wrap:wrap;flex:1 1 auto;margin:-12px}.v-row+.v-row{margin-top:12px}.v-row+.v-r"
},
{
"path": "embedFS/dist/assets/VRow-DtJTiJKO.js",
"chars": 1498,
"preview": "import{ai as v,aj as S,i as V,T as k,aU as p,aX as w,ba as L,bb as N}from\"./index-But_YWe_.js\";const o=[\"start\",\"end\",\"c"
},
{
"path": "embedFS/dist/assets/VSelect-CxsUdWRP.js",
"chars": 16020,
"preview": "import{f as He,e as ze}from\"./VInput-D4WPJeZb.js\";import{u as Ne,V as Se,m as $e}from\"./VTextField-C23ADfJX.js\";import{a"
},
{
"path": "embedFS/dist/assets/VSelect-D5ZZgyw5.css",
"chars": 1859,
"preview": "@charset \"UTF-8\";.v-select--chips.v-input--density-compact .v-field--variant-solo .v-label.v-field-label--floating,.v-se"
},
{
"path": "embedFS/dist/assets/VSelectionControl-CJfnRnc-.js",
"chars": 4935,
"preview": "import{ai as D,aj as x,as as z,aL as U,a4 as a,ax as K,ak as j,k as m,n as E,K as h,aT as O,aW as W,aX as L,bx as X,aq a"
},
{
"path": "embedFS/dist/assets/VSelectionControl-Cp4tgg3l.css",
"chars": 2363,
"preview": "@charset \"UTF-8\";.v-selection-control{align-items:center;contain:layout;display:flex;flex:1 0;grid-area:control;position"
},
{
"path": "embedFS/dist/assets/VSkeletonLoader-ChbR7Nc0.js",
"chars": 2405,
"preview": "import{ai as h,aj as f,a_ as k,b2 as x,bA as y,aQ as S,a2 as C,i as L,b_ as w,ak as B,k as n,E as P,F as T,aT as V,bF as"
},
{
"path": "embedFS/dist/assets/VSkeletonLoader-sthcA-pB.css",
"chars": 5622,
"preview": "@charset \"UTF-8\";.v-skeleton-loader{align-items:center;background:rgb(var(--v-theme-surface));border-radius:6px;display:"
},
{
"path": "embedFS/dist/assets/VSlider-CGi8T5rU.js",
"chars": 11904,
"preview": "import{aj as se,i as o,cr as me,bd as ie,a4 as M,a6 as te,f as ae,c8 as we,aS as Ce,bF as Ve,b6 as _e,aR as ne,aC as Te,"
},
{
"path": "embedFS/dist/assets/VSlider-D2HQrba8.css",
"chars": 10278,
"preview": "@charset \"UTF-8\";.v-slider .v-slider__container input{cursor:default;padding:0;width:100%;display:none}.v-slider>.v-inpu"
},
{
"path": "embedFS/dist/assets/VSpacer-63T2Dgcr.css",
"chars": 40,
"preview": "@charset \"UTF-8\";.v-spacer{flex-grow:1}\n"
},
{
"path": "embedFS/dist/assets/VSpacer-D758VKS2.js",
"chars": 95,
"preview": "import{am as a}from\"./index-But_YWe_.js\";const r=a(\"v-spacer\",\"div\",\"VSpacer\");export{r as V};\n"
},
{
"path": "embedFS/dist/assets/VSparkline-CxEGkfC3.js",
"chars": 7977,
"preview": "import{h as V}from\"./axios-DNCpvJag.js\";import{aj as I,ai as X,aL as O,i,aw as U,ak as z,k as l,bh as j,F as Z,f as F,O "
},
{
"path": "embedFS/dist/assets/VSwitch-CJSeLO2L.js",
"chars": 2711,
"preview": "import{d as O}from\"./index-c6y4pbVT.js\";import{ai as $,aj as q,as as V,bc as G,f as g,co as Q,a4 as w,aL as W,ak as X,aM"
},
{
"path": "embedFS/dist/assets/VSwitch-DCpyu0YQ.css",
"chars": 4851,
"preview": "@charset \"UTF-8\";.v-switch .v-label{padding-inline-start:10px}.v-switch__loader{display:flex}.v-switch__loader .v-progre"
},
{
"path": "embedFS/dist/assets/VTable-BIlReic3.css",
"chars": 3997,
"preview": "@charset \"UTF-8\";.v-table{font-size:15px;transition-duration:.28s;transition-property:height;transition-timing-function:"
},
{
"path": "embedFS/dist/assets/VTable-BLAHjZfY.js",
"chars": 1009,
"preview": "import{ai as d,aj as i,aQ as r,bz as n,ak as v,a as b,k as t,bl as m,n as h,K as u,aT as c,aU as f,aW as g,aX as y}from\""
},
{
"path": "embedFS/dist/assets/VTabs-B4m-OW3A.js",
"chars": 12408,
"preview": "import{cs as Q,ai as R,aj as $,b0 as fe,a_ as Z,f as H,i as C,ak as Y,s as z,a as x,k as E,n as j,K as M,F as p,E as _,a"
},
{
"path": "embedFS/dist/assets/VTabs-CArruFCC.css",
"chars": 6970,
"preview": "@charset \"UTF-8\";.v-tab.v-tab.v-btn{height:var(--v-tabs-height);border-radius:0;min-width:60px}.v-slide-group--horizonta"
},
{
"path": "embedFS/dist/assets/VTextField-C23ADfJX.js",
"chars": 10722,
"preview": "import{ai as G,aj as D,a4 as g,ak as J,a as o,z as ie,b9 as fe,k as t,n as oe,K as Z,b8 as he,b5 as Ve,aX as ue,aQ as _e"
},
{
"path": "embedFS/dist/assets/VTextField-DflQVYjD.css",
"chars": 19898,
"preview": "@charset \"UTF-8\";.v-text-field input{color:inherit;flex:1;transition:.15s opacity cubic-bezier(.4,0,.2,1);min-width:0}.v"
},
{
"path": "embedFS/dist/assets/VTimePicker-BiJhf8Qg.js",
"chars": 16985,
"preview": "import{ai as Z,aj as j,f as y,b0 as be,a_ as ge,i as V,O as z,c8 as Ce,ak as p,k as _,n as ne,K as E,ci as Ae,a6 as He,a"
},
{
"path": "embedFS/dist/assets/VTimePicker-D-Nx0mCo.css",
"chars": 219874,
"preview": "@charset \"UTF-8\";.v-time-picker.v-picker{min-width:328px}.v-time-picker .v-picker__body,.v-time-picker-controls__field-l"
},
{
"path": "embedFS/dist/assets/VToolbar-BhFwV7qm.js",
"chars": 3011,
"preview": "import{ai as b,aj as g,ak as v,a,k as l,n as f,K as y,aU as h,aX as x,a_ as H,by as I,bA as R,b1 as E,a$ as D,aQ as $,bd"
},
{
"path": "embedFS/dist/assets/VToolbar-CJ08_fms.css",
"chars": 2688,
"preview": "@charset \"UTF-8\";.v-toolbar{align-items:flex-start;display:flex;flex:none;flex-direction:column;justify-content:space-be"
},
{
"path": "embedFS/dist/assets/VTooltip-BV7Wxv--.js",
"chars": 1523,
"preview": "import{ai as S,aj as h,as as O,bp as x,aL as F,a4 as c,f as I,i,E as u,ak as T,bq as d,a as k,aN as R,at as p,br as w}fr"
},
{
"path": "embedFS/dist/assets/VTooltip-CZlyHzm0.css",
"chars": 666,
"preview": "@charset \"UTF-8\";.v-tooltip>.v-overlay__content{background:rgb(var(--v-tooltip-background));color:rgb(var(--v-theme-surf"
},
{
"path": "embedFS/dist/assets/_..-D12iSKdb.css",
"chars": 788,
"preview": ".header-title[data-v-bdf026be]{font-size:clamp(3rem,5vw,6rem);line-height:clamp(3rem,5vw,6rem)}.layout-blank .misc-wrapp"
},
{
"path": "embedFS/dist/assets/_...error_-C0r044-X.js",
"chars": 9840,
"preview": "import{_ as C,c as g,o as Q,D as e,t as s,h as r,i as c,a as B,k as d,l as t,m as a,w,s as Y,v as H}from\"./index-But_YWe"
},
{
"path": "embedFS/dist/assets/access-C8mK6Ha9.js",
"chars": 664,
"preview": "import o from\"./singleLog-CUnXBn4A.js\";import{A as r,o as t}from\"./index-But_YWe_.js\";import\"./Result-DI11lCXq.js\";impor"
},
{
"path": "embedFS/dist/assets/add-qw2GdfGL.js",
"chars": 9552,
"preview": "import{_ as re}from\"./Result-DI11lCXq.js\";import{m as f}from\"./mod-DUnttE3W.js\";import{g as se,e as ne,b as ie,f as i,j "
},
{
"path": "embedFS/dist/assets/announce-aAv7G3Cd.js",
"chars": 7733,
"preview": "import{_ as K}from\"./Result-DI11lCXq.js\";import{g as P,d as Q,b as X,e as Y,f as c,j as ee,a8 as te,a9 as oe,c as y,o as"
},
{
"path": "embedFS/dist/assets/axios-DNCpvJag.js",
"chars": 39812,
"preview": "import{bH as Rt,aZ as Ge,g as gt,d as Fe,G as te}from\"./index-But_YWe_.js\";function Qe(e,t){return function(){return e.a"
},
{
"path": "embedFS/dist/assets/backup-D4Jzm4dn.js",
"chars": 5910,
"preview": "import{_ as j}from\"./Result-DI11lCXq.js\";import{t as b}from\"./tools-BtEQwbyL.js\";import{g as J,d as K,b as W,e as O,f as"
},
{
"path": "embedFS/dist/assets/base-Dq3aguG-.js",
"chars": 14907,
"preview": "import{_ as ye}from\"./Result-DI11lCXq.js\";import{ai as J,aj as B,a2 as Se,ak as M,k as z,a as l,s as O,al as oe,am as ke"
},
{
"path": "embedFS/dist/assets/base-DzlHa8yA.css",
"chars": 4613,
"preview": "@charset \"UTF-8\";.v-stepper.v-sheet{overflow:hidden}.v-stepper.v-sheet{box-shadow:0 2px 4px rgba(var(--v-shadow-key-umbr"
},
{
"path": "embedFS/dist/assets/blank-CExWZhJD.css",
"chars": 52,
"preview": ".layout-wrapper.layout-blank{flex-direction:column}\n"
},
{
"path": "embedFS/dist/assets/blank-CdkSWbzI.js",
"chars": 264,
"preview": "import{_ as o,c as t,o as a,a as r,r as c}from\"./index-But_YWe_.js\";const n={},s={class:\"layout-wrapper layout-blank\",\"d"
},
{
"path": "embedFS/dist/assets/chat-CR9ZeRZy.js",
"chars": 4133,
"preview": "import{_ as L}from\"./Result-DI11lCXq.js\";import G from\"./singleLog-CUnXBn4A.js\";import{g as R,d as A,e as U,b as $,f as "
},
{
"path": "embedFS/dist/assets/chat-DNbpOz0I.js",
"chars": 26085,
"preview": "import{p as Y}from\"./player-CpbcBwUI.js\";import{g as R,e as Z,b as W,f as z,O as I,aC as k,j as J,A as d,o as t,w as n,a"
},
{
"path": "embedFS/dist/assets/clean-C3opSXJz.js",
"chars": 6939,
"preview": "import{_ as E}from\"./Result-DI11lCXq.js\";import{ai as L,aj as M,aQ as U,au as j,a as t,n as q,K as Q,aT as K,aU as X,aA "
},
{
"path": "embedFS/dist/assets/clean-gJusj0Uj.css",
"chars": 118,
"preview": "@charset \"UTF-8\";.v-item-group{flex:0 1 auto;max-width:100%;position:relative;transition:.2s cubic-bezier(.4,0,.2,1)}\n"
},
{
"path": "embedFS/dist/assets/colors-DoZdCu1D.js",
"chars": 4951,
"preview": "const e={base:\"#f44336\",lighten5:\"#ffebee\",lighten4:\"#ffcdd2\",lighten3:\"#ef9a9a\",lighten2:\"#e57373\",lighten1:\"#ef5350\",d"
},
{
"path": "embedFS/dist/assets/default-BQziqBIh.js",
"chars": 31529,
"preview": "import{c as M,o as b,k as _,t as T,u as St,a as v,J as I,K as O,L as x,z as yt,R as Rt,A as L,w as p,M as rt,d as wt,b a"
},
{
"path": "embedFS/dist/assets/default-CDmOr08l.css",
"chars": 5028,
"preview": "@charset \"UTF-8\";.layout-vertical-nav .nav-group-label{display:flex;align-items:center;cursor:pointer}.layout-vertical-n"
},
{
"path": "embedFS/dist/assets/dialog-transition-B2t_G0u1.js",
"chars": 2417,
"preview": "import{ai as D,aj as F,a as u,E as V,aY as v,bh as w,bj as l,c2 as x,bk as E,c3 as P,c4 as B,bi as O}from\"./index-But_YW"
},
{
"path": "embedFS/dist/assets/download-BZbCclQJ.js",
"chars": 3452,
"preview": "import{_ as B,g as I,b as N,e as P,f as i,j as $,c as p,o as d,A as f,l as e,F as V,a as r,w as m,ah as v,s as w,v as x,"
},
{
"path": "embedFS/dist/assets/download-CEIvObtd.css",
"chars": 114,
"preview": ".item-container[data-v-81935ebd]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:20px}\n"
},
{
"path": "embedFS/dist/assets/download-InxnfqWA.js",
"chars": 1992,
"preview": "import{_ as k}from\"./Result-DI11lCXq.js\";import{l as v}from\"./logs-N8GO9ZBg.js\";import{g as S,d as V,e as x,b as T,f,j a"
},
{
"path": "embedFS/dist/assets/eventBus-B8tShFcG.js",
"chars": 238,
"preview": "const i={events:{},on(s,t){this.events[s]||(this.events[s]=[]),this.events[s].push(t)},emit(s,t){this.events[s]&&this.ev"
},
{
"path": "embedFS/dist/assets/filter-Dycv7ILy.js",
"chars": 3398,
"preview": "import{V as x,m as P}from\"./VSelectionControl-CJfnRnc-.js\";import{ai as j,aj as w,as as K,a4 as C,ak as B,at as L,a as S"
},
{
"path": "embedFS/dist/assets/game-OoVHjMuc.js",
"chars": 6616,
"preview": "import{_ as ee}from\"./Result-DI11lCXq.js\";import $ from\"./log-B7kZm_Kd.js\";import{l as R}from\"./logs-N8GO9ZBg.js\";import"
},
{
"path": "embedFS/dist/assets/history-DUBuZMd3.js",
"chars": 2902,
"preview": "import{g as v,e as w,b as x,f as T,A as L,o as y,w as e,a,s as S,l as i,v as l,t as r,U as m,W as p,J as n,c as D,D as I"
},
{
"path": "embedFS/dist/assets/index-BRyNS1GG.js",
"chars": 4564,
"preview": "import{d as h,a as R,b as B,c as D,V as M}from\"./VBadge-D7oDPA4h.js\";import{_ as O,d as $,u as j,e as G,b as K,f as u,j "
},
{
"path": "embedFS/dist/assets/index-BSTHz1Bk.css",
"chars": 2637,
"preview": "@charset \"UTF-8\";.v-treeview-item{--list-indent-size: 28px}.v-treeview-item.v-treeview-item--filtered{display:none}.v-tr"
},
{
"path": "embedFS/dist/assets/index-BhZX0V6g.js",
"chars": 1043727,
"preview": "import{_ as SL,c as xL,o as bL,n as wL}from\"./index-But_YWe_.js\";var bp=function(r,e){return bp=Object.setPrototypeOf||{"
},
{
"path": "embedFS/dist/assets/index-Bt3JTD_C.js",
"chars": 297174,
"preview": "import{_ as Ke}from\"./ConfirmBox-UdDnLY_r.js\";import{_ as Ve}from\"./Result-DI11lCXq.js\";import{_ as qe}from\"./TimeRunnin"
},
{
"path": "embedFS/dist/assets/index-But_YWe_.js",
"chars": 504906,
"preview": "const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=[\"assets/blank-CdkSWbzI.js\",\"assets/blank-CExWZhJD.css\",\"assets/"
},
{
"path": "embedFS/dist/assets/index-CBy8Y7rp.js",
"chars": 3523,
"preview": "import{b as g,f as c,j as b,a8 as V,a9 as y,c as x,o as T,a as t,w as e,v as l,t as m,l as o,x as _,F as M}from\"./index-"
},
{
"path": "embedFS/dist/assets/index-CYxihrrz.css",
"chars": 72,
"preview": ".form-container[data-v-accbc606]{width:100%;margin:0 auto;padding:20px}\n"
},
{
"path": "embedFS/dist/assets/index-CnNpN6vG.js",
"chars": 16264,
"preview": "import{_ as be}from\"./Result-DI11lCXq.js\";import{ai as M,aj as H,f as S,i as k,ak as Z,a,k as I,al as J,F as B,E as G,at"
},
{
"path": "embedFS/dist/assets/index-CxuhU37S.css",
"chars": 3535,
"preview": "@charset \"UTF-8\";.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xt"
},
{
"path": "embedFS/dist/assets/index-D9Rb-nie.css",
"chars": 1826953,
"preview": "@charset \"UTF-8\";.v-card{display:block;overflow:hidden;overflow-wrap:break-word;position:relative;padding:0;text-decorat"
},
{
"path": "embedFS/dist/assets/index-Dkfp6G7x.js",
"chars": 12057,
"preview": "import{_ as fe}from\"./ConfirmBox-UdDnLY_r.js\";import{_ as pe}from\"./Result-DI11lCXq.js\";import{e as ge,b as he,u as ve,d"
},
{
"path": "embedFS/dist/assets/index-H_F1Plkt.js",
"chars": 17684,
"preview": "import{_ as J,u as S,b as W,d as _,g as $,e as ee,f as i,h as ae,i as se,j as le,c as x,o as b,k as B,a as e,w as s,n as"
},
{
"path": "embedFS/dist/assets/index-Nr3m5Q0F.js",
"chars": 1185,
"preview": "import{r as m,bI as u,bJ as f,T as v,E as x}from\"./index-But_YWe_.js\";import{V as d}from\"./VTooltip-BV7Wxv--.js\";functio"
},
{
"path": "embedFS/dist/assets/index-Vik-EOmM.js",
"chars": 97,
"preview": "function t(e,s){return new Promise(a=>setTimeout(async()=>{await void 0,a()},e))}export{t as s};\n"
},
{
"path": "embedFS/dist/assets/index-c6y4pbVT.js",
"chars": 3761,
"preview": "import{ai as g,bh as x,c5 as p,aY as v,T as b,aj as w}from\"./index-But_YWe_.js\";const S=w({disabled:Boolean,group:Boolea"
},
{
"path": "embedFS/dist/assets/index-nsLxUtLe.css",
"chars": 1345,
"preview": ".layout-blank .auth-wrapper[data-v-6ec3c7a2]{min-block-size:100dvh}.layout-blank .auth-footer-mask[data-v-6ec3c7a2]{posi"
},
{
"path": "embedFS/dist/assets/index-roZNrXH_.js",
"chars": 35924,
"preview": "import{_ as gl}from\"./ConfirmBox-UdDnLY_r.js\";import{_ as hl}from\"./Result-DI11lCXq.js\";import{f as c,g as kl,d as Vl,e "
},
{
"path": "embedFS/dist/assets/levelDataSetting-1LIRScEY.js",
"chars": 69822,
"preview": "import{b as q,g as x,i as M,f as c,j as O,O as w,c as T,o as s,a as p,k as l,m as Y,z as A,A as r,l as N,w as i,v as _,t"
},
{
"path": "embedFS/dist/assets/list-Ch7Aeo7N.js",
"chars": 4120,
"preview": "import{g as M,e as O,b as H,f as n,j as J,r as Q,A as _,o as f,w as l,a,ab as R,k as C,t as u,l as s,s as w,v as y,x as "
},
{
"path": "embedFS/dist/assets/log-B7kZm_Kd.js",
"chars": 594,
"preview": "import{M as r}from\"./preview-B1dPoyad.js\";import{_ as g,g as c,i as a,a7 as p,A as u,o as m,n as i,l as e}from\"./index-B"
},
{
"path": "embedFS/dist/assets/log-DWr6Dcj-.css",
"chars": 77,
"preview": ".mdp[data-v-32641ba1] .md-editor-preview .md-editor-code{margin:0!important}\n"
},
{
"path": "embedFS/dist/assets/logs-N8GO9ZBg.js",
"chars": 608,
"preview": "import{h as n}from\"./axios-DNCpvJag.js\";const r={content:{url:\"/logs/content\",get:async function(t){return await n.get(t"
},
{
"path": "embedFS/dist/assets/map-Co9ruaLK.js",
"chars": 42236,
"preview": "import{r as Be}from\"./room-BLsFZNUm.js\";import{t as Ee}from\"./tools-BtEQwbyL.js\";import{g as Fe,d as Ve,e as De,b as Le,"
},
{
"path": "embedFS/dist/assets/metrics-BLfS8pVr.js",
"chars": 5228,
"preview": "import{_ as w}from\"./index-BhZX0V6g.js\";import{p as D}from\"./platform-DXQOU806.js\";import{f as o,e as B,b as $,j as E,A "
},
{
"path": "embedFS/dist/assets/mod-3aGhFcVM.js",
"chars": 3027,
"preview": "import{j as T,f as v,e as x,b as U,O as B,c as i,o as n,k as C,a as m,w as r,l as t,z as N,A as p,v as V,t as h,F as f,y"
},
{
"path": "embedFS/dist/assets/mod-DUnttE3W.js",
"chars": 1058,
"preview": "import{h as n}from\"./axios-DNCpvJag.js\";const a={download:{url:\"/mod/download\",post:async function(t){return await n.pos"
},
{
"path": "embedFS/dist/assets/mod-y4ETdFdW.js",
"chars": 3597,
"preview": "import{_ as x}from\"./Result-DI11lCXq.js\";import{j as A,a8 as D,a9 as G,b as R,g as $,d as z,f as V,c as p,l as t,o as r,"
},
{
"path": "embedFS/dist/assets/modInfo-B5E3tm9p.css",
"chars": 706,
"preview": ".precise-rating[data-v-82ef83df]{display:inline-flex;align-items:center;gap:8px}.rating-container[data-v-82ef83df]{displ"
},
{
"path": "embedFS/dist/assets/modInfo-BFWwrZKU.js",
"chars": 7785,
"preview": "import{_ as D,i as g,c as v,o as m,k as t,D as F,F as w,y as V,A as b,J as _,n as T,l as a,t as l,b as E,f as z,w as s,a"
},
{
"path": "embedFS/dist/assets/online-Dk4H0O4k.js",
"chars": 3459,
"preview": "import{g as D,e as L,b as S,f as g,j as I,A as N,o as V,w as e,a,l as t,s as v,v as n,t as i,U as y,W as _,J as m,aP as "
},
{
"path": "embedFS/dist/assets/overview-CXTE8d7i.js",
"chars": 5629,
"preview": "import{_ as V}from\"./TimeRunning-BnGoYIbA.js\";import{p as N}from\"./platform-DXQOU806.js\";import{g as j,b as z,f as C,j a"
},
{
"path": "embedFS/dist/assets/platform-DXQOU806.js",
"chars": 885,
"preview": "import{h as r}from\"./axios-DNCpvJag.js\";const e={overview:{url:\"/platform/overview\",get:async function(t){return await r"
},
{
"path": "embedFS/dist/assets/player-CpbcBwUI.js",
"chars": 673,
"preview": "import{h as a}from\"./axios-DNCpvJag.js\";const n={online:{url:\"/player/online\",get:async function(t){return await a.get(t"
},
{
"path": "embedFS/dist/assets/player-DzdX72KE.js",
"chars": 5353,
"preview": "import{_ as R}from\"./Result-DI11lCXq.js\";import z from\"./online-Dk4H0O4k.js\";import{g as A,d as B,e as D,b as I,f as b,j"
},
{
"path": "embedFS/dist/assets/preview-B1dPoyad.js",
"chars": 175824,
"preview": "import{P as eu,f as Fe,Q as Fu,a as ne,a0 as te,i as fe,ar as B,F as Fn,aL as Sn,O as U,a4 as vt,aC as ye,j as ce,a6 as "
},
{
"path": "embedFS/dist/assets/preview-B9nDbvgA.css",
"chars": 57231,
"preview": ".md-editor .md-editor-preview{--md-theme-color: var(--md-color);--md-theme-color-reverse: #eee;--md-theme-color-hover: #"
},
{
"path": "embedFS/dist/assets/room-BLsFZNUm.js",
"chars": 895,
"preview": "import{h as r}from\"./axios-DNCpvJag.js\";const o={base:{url:\"/room\",get:async function(t){return await r.get(this.url,t)}"
}
]
// ... and 51 more files (download for full content)
About this extraction
This page contains the full source code of the miracleEverywhere/dst-management-platform-api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 251 files (5.6 MB), approximately 1.5M tokens, and a symbol index with 4400 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.