-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
183 lines (139 loc) · 5.49 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import si from 'systeminformation';
// import { Rcon } from "rcon-client";
import { exec } from 'child_process';
import fs from 'fs';
import archiver from 'archiver';
import path from 'path';
import moment from 'moment';
import fsExtra from 'fs-extra'; // 引入 fs-extra
import ps from 'ps-node';
import Rcon from 'rcon-srcds';
/**设置区域**************/
//游戏存档目录
const gamedataPath: string = String.raw`C:\Users\kiros\Desktop\steamcmd\steamapps\common\PalServer\Pal\Saved\SaveGames`;
//存档备份路径
const backupPath: string = path.join(__dirname, 'backup');
//存档备份周期
const backupInterval: number = 1800;
//监控服务端名称
//const processName: string = "Pal";
//服务端启动路径
const cmd: string = String.raw`C:\Users\kiros\Desktop\steamcmd\steamapps\common\PalServer\PalServer.exe`;
//内存占用百分比阈值(%)
const memTarget: number = 90;
//内存监控周期(秒)
const checkSecond: number = 40;
//关服延迟(秒) 默认内存监控周期一半(不应超过内存监控周期,避免重复触发指令)
const rebootSecond: number = checkSecond / 2;
//rcon地址 默认本季
const serverHost: string = '127.0.0.1';
//rcon端口
const serverPort: number = 25575;
//rcon密码
const rconPassword: string = 'admin';
/************************/
async function sendMsgandReboot(): Promise<void> {
try {
const server = new Rcon({ host: serverHost, port: serverPort ,encoding: 'ascii'});
await server.authenticate(rconPassword);
console.log(`[${moment().format('HH:mm:ss')}] Rcon authenticated`);
let status = await server.execute(`Shutdown ${rebootSecond} The_server_will_restart_in_${rebootSecond}_seconds.`);
console.log(`[${moment().format('HH:mm:ss')}] ${status}`)
server.execute('mp_autokick 0'); // no need to read the response
} catch (error) {
console.error(`[${moment().format('HH:mm:ss')}] Error sending RCON command:`, error);
}
}
function startProcess(): void {
exec(cmd, (err:Error | null, stdout: string, stderr: string) => {
if (err) {
console.error(`[${moment().format('HH:mm:ss')}] Error starting process: ${err}`);
return;
}
if (stderr) {
console.error(`[${moment().format('HH:mm:ss')}] Standard error output: ${stderr}`);
}
console.log(`[${moment().format('HH:mm:ss')}] Process started: ${stdout}`);
});
}
async function checkMemoryUsage(): Promise<number> {
try {
const mem: si.Systeminformation.MemData = await si.mem();
const memPercentage: number = Math.floor((mem.used / mem.total) * 100);
// console.log(`总内存: ${mem.total}`);
// console.log(`已用内存: ${mem.used}`);
// console.log(`空闲内存: ${mem.free}`);
console.log(`[${moment().format('HH:mm:ss')}] 当前内存占用百分比: ${memPercentage}%`);
if (memPercentage > memTarget) {
console.log(`负载过高,即将重启。`);
backupDirectory(gamedataPath, backupPath);//关服前备份一次
await sendMsgandReboot();
}
return memPercentage;
} catch (error) {
console.error(`[${moment().format('HH:mm:ss')}] 获取内存信息时出错:`, error);
return 0;
}
}
async function checkServerStatus(): Promise<void> {
try {
// 检查内存使用情况
checkMemoryUsage();
// 查找特定路径的进程
ps.lookup({}, (err, resultList) => {
if (err) {
throw err;
}
let isFound = false;
for (const process of resultList) {
if (process && process.command === cmd) {
isFound = true;
console.log(`[${moment().format('HH:mm:ss')}] 发现服务端正在安稳运行。`);
break;
}
}
if (!isFound) {
console.log(`[${moment().format('HH:mm:ss')}] 未发现服务端,尝试启动。`);
startProcess();
}
});
} catch (error) {
console.error(`错误: ${error}`);
}
}
async function backupDirectory(sourceDir: string, backupDir: string): Promise<void> {
try {
// 创建临时目录路径
const tempDir: string = path.join(backupDir, 'temp');
// 确保临时目录存在或创建它
await fsExtra.ensureDir(tempDir);
// 清空临时目录
await fsExtra.emptyDir(tempDir);
// 复制文件到临时目录
await fsExtra.copy(sourceDir, tempDir);
// 创建压缩文件的名称
const zipFileName: string = `backup-${moment().format('YYYYMMDDHHmm')}.zip`;
const output = fs.createWriteStream(path.join(backupDir, zipFileName));
const archive = archiver('zip', {
zlib: { level: 9 } // 设置压缩级别
});
output.on('close', function () {
console.log(`[${moment().format('HH:mm:ss')}] Backup of '${sourceDir}' has been saved as '${zipFileName}'`);
});
archive.on('error', function (err) {
throw err;
});
archive.pipe(output);
archive.directory(tempDir, false);
archive.finalize();
} catch (error) {
console.error(`[${moment().format('HH:mm:ss')}] Error during backup: `, error);
}
}
backupDirectory(gamedataPath, backupPath);
// 每隔20秒钟检查一次
setInterval(checkServerStatus, checkSecond*1000);
// 备份游戏目录
setInterval(() => {
backupDirectory(gamedataPath, backupPath);
}, backupInterval*1000);