Skip to content

Commit 2b3be6f

Browse files
[1.7] 优化项目代码
1 parent 87d472b commit 2b3be6f

20 files changed

Lines changed: 1579 additions & 1230 deletions

README.md

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Velocity 线路切换插件,让玩家在不退出服务器的情况下自动
1414
支持基于多种条件的自动转移:
1515
- **地理位置**: 国家、省份
1616
- **网络运营商**: 电信、联通、移动
17-
- **连接域名**: 不同域名进入不同线路(类似 PistonMOTD 的虚拟主机)
17+
- **连接域名**: 支持 SRV 解析(如 `test.2b2t.icu:10004`),支持端口匹配
1818
- **玩家 UUID**: VIP 专属线路
1919
- **历史记录**: 自动记住玩家上次使用的线路
2020

@@ -23,6 +23,10 @@ Velocity 线路切换插件,让玩家在不退出服务器的情况下自动
2323
- `!` 表示非(!中国 = 海外)
2424
- 多条件 AND 组合
2525

26+
### 智能防环路
27+
- **冗余转移检测**: 如果玩家已经连接到目标线路的 IP/端口,插件会自动跳过转移,防止连接中断或死循环。
28+
- **超时保护**: 如果转移失败,自动回退到安全服务器。
29+
2630
### 线路管理
2731
- 实时延迟监控
2832
- 手动切换线路
@@ -41,45 +45,57 @@ Velocity 线路切换插件,让玩家在不退出服务器的情况下自动
4145
外部只能看到 Velocity 的 IP,无法直接连接内部子服务器,防止 DDoS 绕过。
4246

4347
**单域名多入口**
44-
配置 `cn.example.com``us.example.com` 解析到同一个 Velocity,插件根据域名自动分流。
48+
配置 `cn.example.com``us.example.com` 解析到同一个 Velocity,插件根据域名自动分流。支持 SRV 记录解析,精准匹配 `host:port`
4549

4650
## 快速开始
4751

4852
### 安装
4953
1. 下载 `simpletransfer-xx.jar`
5054
2. 放入 Velocity 的 `plugins` 目录
5155
3. 重启 Velocity
52-
4. 编辑 `plugins/simpletransfer/config.toml` 配置线路
56+
4. 编辑 `plugins/simpletransfer/config.conf` 配置线路
5357
5. 编辑 `plugins/simpletransfer/rules.conf` 配置规则
5458

5559
### 基础配置
5660

57-
**config.toml** - 定义线路
58-
```toml
59-
[servers.bgp]
60-
name = "BGP线路"
61-
target = "bgp-server" # Velocity 中注册的服务器名
62-
description = "多线BGP"
63-
64-
[servers.dx]
65-
name = "电信线路"
66-
target = "dx-server"
67-
description = "电信专线"
61+
**config.conf** - 定义线路 (HOCON 格式)
62+
```hocon
63+
permission_groups {
64+
default {
65+
permission = ""
66+
servers {
67+
bgp {
68+
host = "bgp.example.com"
69+
port = 25565
70+
display_name = "BGP线路"
71+
description = "多线BGP"
72+
}
73+
dx {
74+
host = "dx.example.com"
75+
port = 25565
76+
display_name = "电信线路"
77+
description = "电信专线"
78+
}
79+
}
80+
}
81+
}
6882
```
6983

7084
**rules.conf** - 定义规则
7185
```hocon
72-
{
73-
enabled = true
74-
name = "电信用户优化"
75-
conditions {
76-
isp = "电信"
77-
has_history = false
78-
}
79-
action {
80-
transfer = "dx"
86+
[
87+
{
88+
enabled = true
89+
name = "电信用户优化"
90+
conditions {
91+
isp = "电信"
92+
has_history = false
93+
}
94+
action {
95+
transfer = "dx"
96+
}
8197
}
82-
}
98+
]
8399
```
84100

85101
### 命令
@@ -92,7 +108,7 @@ description = "电信专线"
92108
管理员命令:
93109
- `/st status <玩家>` - 查看玩家信息
94110
- `/st transfer <玩家> <线路>` - 强制转移玩家
95-
- `/st reload [config|rules|all]` - 重载配置
111+
- `/st reload` - 重载配置
96112
- `/st rules` - 查看规则列表
97113

98114
### 权限
@@ -105,8 +121,8 @@ description = "电信专线"
105121
玩家 -> Velocity (SimpleTransfer) -> 内部子服务器
106122
|
107123
+-- 检测 IP 地理位置
108-
+-- 匹配路由规则
109-
+-- 自动转移到最优线路
124+
+-- 匹配路由规则 (支持 SRV 域名)
125+
+-- 自动转移到最优线路 (智能防环路)
110126
+-- 记录历史选择
111127
```
112128

@@ -160,24 +176,24 @@ Velocity 作为中转层,所有玩家连接先到 Velocity,插件检测玩
160176
}
161177
```
162178

163-
### 场景3: 域名分流
179+
### 场景3: 域名分流 (支持 SRV)
164180
```hocon
165181
# cn.example.com 进入国内线路
166182
{
167183
enabled = true
168184
conditions {
169-
hostname = "cn.example.com"
185+
hostname = "cn.example.com" # 默认端口 25565
170186
}
171187
action { transfer = "bgp" }
172188
}
173189
174-
# us.example.com 进入国际线路
190+
# SRV 域名匹配 (需指定端口)
175191
{
176192
enabled = true
177193
conditions {
178-
hostname = "us.example.com"
194+
hostname = "play.example.com:10004"
179195
}
180-
action { transfer = "us" }
196+
action { transfer = "vip" }
181197
}
182198
```
183199

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>ict.minesunshineone</groupId>
77
<artifactId>simpletransfer</artifactId>
8-
<version>1.6</version>
8+
<version>1.7</version>
99
<packaging>jar</packaging>
1010

1111
<name>SimpleTransfer</name>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package ict.minesunshineone.simpleTransfer.command;
2+
3+
import com.velocitypowered.api.command.CommandSource;
4+
5+
import ict.minesunshineone.simpleTransfer.config.ConfigManager;
6+
import ict.minesunshineone.simpleTransfer.connection.PlayerConnectionManager;
7+
import net.kyori.adventure.text.Component;
8+
import net.kyori.adventure.text.format.NamedTextColor;
9+
import net.kyori.adventure.text.format.TextColor;
10+
11+
/**
12+
* 子命令基类
13+
* 提供通用工具方法
14+
*/
15+
public abstract class AbstractSubCommand implements SubCommand {
16+
17+
protected final ConfigManager configManager;
18+
protected final PlayerConnectionManager connectionManager;
19+
20+
// 常用颜色常量
21+
protected static final TextColor COLOR_PRIMARY = TextColor.color(0x00DDFF);
22+
protected static final TextColor COLOR_SUCCESS = TextColor.color(0x00FF88);
23+
protected static final TextColor COLOR_WARNING = TextColor.color(0xFFDD00);
24+
protected static final TextColor COLOR_ERROR = TextColor.color(0xFFAAAA);
25+
protected static final TextColor COLOR_INFO = TextColor.color(0xAAFFFF);
26+
protected static final TextColor COLOR_MUTED = TextColor.color(0xAAAAAA);
27+
protected static final TextColor COLOR_TITLE = TextColor.color(0x00FFFF);
28+
29+
protected AbstractSubCommand(ConfigManager configManager, PlayerConnectionManager connectionManager) {
30+
this.configManager = configManager;
31+
this.connectionManager = connectionManager;
32+
}
33+
34+
// ========== 消息构建工具方法 ==========
35+
36+
/**
37+
* 发送带箭头前缀的消息
38+
*/
39+
protected void sendPrefixed(CommandSource source, String text, TextColor color) {
40+
source.sendMessage(Component.text()
41+
.append(Component.text(" ➤ ", COLOR_PRIMARY))
42+
.append(Component.text(text, color))
43+
.build());
44+
}
45+
46+
/**
47+
* 发送带箭头前缀的键值对消息
48+
*/
49+
protected void sendKeyValue(CommandSource source, String key, String value, TextColor valueColor) {
50+
source.sendMessage(Component.text()
51+
.append(Component.text(" ➤ ", COLOR_PRIMARY))
52+
.append(Component.text(key, NamedTextColor.WHITE))
53+
.append(Component.text(value, valueColor))
54+
.build());
55+
}
56+
57+
/**
58+
* 发送错误消息
59+
*/
60+
protected void sendError(CommandSource source, String message) {
61+
source.sendMessage(Component.text()
62+
.append(Component.text(" ✘ ", NamedTextColor.RED))
63+
.append(Component.text(message, COLOR_ERROR))
64+
.build());
65+
}
66+
67+
/**
68+
* 发送成功消息
69+
*/
70+
protected void sendSuccess(CommandSource source, String message) {
71+
source.sendMessage(Component.text()
72+
.append(Component.text(" ✓ ", NamedTextColor.GREEN))
73+
.append(Component.text(message, COLOR_SUCCESS))
74+
.build());
75+
}
76+
77+
/**
78+
* 发送空行
79+
*/
80+
protected void sendEmpty(CommandSource source) {
81+
source.sendMessage(Component.text(" "));
82+
}
83+
84+
/**
85+
* 发送标题
86+
*/
87+
protected void sendTitle(CommandSource source, String title) {
88+
source.sendMessage(Component.text()
89+
.append(Component.text("✦ ", NamedTextColor.GOLD))
90+
.append(Component.text(title, COLOR_TITLE))
91+
.build());
92+
}
93+
94+
/**
95+
* 获取延迟颜色
96+
*/
97+
protected NamedTextColor getPingColor(long ping) {
98+
if (ping < 50) {
99+
return NamedTextColor.GREEN;
100+
} else if (ping < configManager.getPingThreshold()) {
101+
return NamedTextColor.YELLOW;
102+
} else {
103+
return NamedTextColor.RED;
104+
}
105+
}
106+
107+
/**
108+
* 检查是否为管理员
109+
*/
110+
protected boolean isAdmin(CommandSource source) {
111+
return source.hasPermission("simpletransfer.admin");
112+
}
113+
114+
@Override
115+
public boolean hasPermission(CommandSource source) {
116+
if (isAdminOnly()) {
117+
return isAdmin(source);
118+
}
119+
return true;
120+
}
121+
}

0 commit comments

Comments
 (0)