@dimzxzzx07/mc-headless 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/core/ConfigHandler.d.ts +12 -0
- package/dist/core/ConfigHandler.d.ts.map +1 -0
- package/dist/core/ConfigHandler.js +118 -0
- package/dist/core/ConfigHandler.js.map +1 -0
- package/dist/core/JavaChecker.d.ts +8 -0
- package/dist/core/JavaChecker.d.ts.map +1 -0
- package/dist/core/JavaChecker.js +68 -0
- package/dist/core/JavaChecker.js.map +1 -0
- package/dist/core/MinecraftServer.d.ts +28 -0
- package/dist/core/MinecraftServer.d.ts.map +1 -0
- package/dist/core/MinecraftServer.js +312 -0
- package/dist/core/MinecraftServer.js.map +1 -0
- package/dist/core/ServerManager.d.ts +30 -0
- package/dist/core/ServerManager.d.ts.map +1 -0
- package/dist/core/ServerManager.js +203 -0
- package/dist/core/ServerManager.js.map +1 -0
- package/dist/engines/Downloader.d.ts +16 -0
- package/dist/engines/Downloader.d.ts.map +1 -0
- package/dist/engines/Downloader.js +159 -0
- package/dist/engines/Downloader.js.map +1 -0
- package/dist/engines/FabricEngine.d.ts +11 -0
- package/dist/engines/FabricEngine.d.ts.map +1 -0
- package/dist/engines/FabricEngine.js +70 -0
- package/dist/engines/FabricEngine.js.map +1 -0
- package/dist/engines/ForgeEngine.d.ts +11 -0
- package/dist/engines/ForgeEngine.d.ts.map +1 -0
- package/dist/engines/ForgeEngine.js +84 -0
- package/dist/engines/ForgeEngine.js.map +1 -0
- package/dist/engines/PaperEngine.d.ts +11 -0
- package/dist/engines/PaperEngine.d.ts.map +1 -0
- package/dist/engines/PaperEngine.js +88 -0
- package/dist/engines/PaperEngine.js.map +1 -0
- package/dist/engines/ServerEngine.d.ts +10 -0
- package/dist/engines/ServerEngine.d.ts.map +1 -0
- package/dist/engines/ServerEngine.js +3 -0
- package/dist/engines/ServerEngine.js.map +1 -0
- package/dist/engines/VanillaEngine.d.ts +11 -0
- package/dist/engines/VanillaEngine.d.ts.map +1 -0
- package/dist/engines/VanillaEngine.js +70 -0
- package/dist/engines/VanillaEngine.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/BedrockServer.d.ts +30 -0
- package/dist/platforms/BedrockServer.d.ts.map +1 -0
- package/dist/platforms/BedrockServer.js +301 -0
- package/dist/platforms/BedrockServer.js.map +1 -0
- package/dist/platforms/GeyserBridge.d.ts +9 -0
- package/dist/platforms/GeyserBridge.d.ts.map +1 -0
- package/dist/platforms/GeyserBridge.js +98 -0
- package/dist/platforms/GeyserBridge.js.map +1 -0
- package/dist/platforms/JavaServer.d.ts +27 -0
- package/dist/platforms/JavaServer.d.ts.map +1 -0
- package/dist/platforms/JavaServer.js +237 -0
- package/dist/platforms/JavaServer.js.map +1 -0
- package/dist/types/index.d.ts +80 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/FileUtils.d.ts +20 -0
- package/dist/utils/FileUtils.d.ts.map +1 -0
- package/dist/utils/FileUtils.js +172 -0
- package/dist/utils/FileUtils.js.map +1 -0
- package/dist/utils/Logger.d.ts +26 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +91 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/PropertiesParser.d.ts +7 -0
- package/dist/utils/PropertiesParser.d.ts.map +1 -0
- package/dist/utils/PropertiesParser.js +124 -0
- package/dist/utils/PropertiesParser.js.map +1 -0
- package/dist/utils/SystemDetector.d.ts +14 -0
- package/dist/utils/SystemDetector.d.ts.map +1 -0
- package/dist/utils/SystemDetector.js +152 -0
- package/dist/utils/SystemDetector.js.map +1 -0
- package/package.json +51 -0
- package/src/core/ConfigHandler.ts +136 -0
- package/src/core/JavaChecker.ts +71 -0
- package/src/core/MinecraftServer.ts +326 -0
- package/src/core/ServerManager.ts +196 -0
- package/src/engines/Downloader.ts +144 -0
- package/src/engines/FabricEngine.ts +49 -0
- package/src/engines/ForgeEngine.ts +65 -0
- package/src/engines/PaperEngine.ts +68 -0
- package/src/engines/ServerEngine.ts +10 -0
- package/src/engines/VanillaEngine.ts +49 -0
- package/src/index.ts +83 -0
- package/src/platforms/BedrockServer.ts +311 -0
- package/src/platforms/GeyserBridge.ts +83 -0
- package/src/platforms/JavaServer.ts +241 -0
- package/src/scripts/detect-os.sh +56 -0
- package/src/scripts/install-java.sh +38 -0
- package/src/types/index.ts +89 -0
- package/src/utils/FileUtils.ts +162 -0
- package/src/utils/Logger.ts +97 -0
- package/src/utils/PropertiesParser.ts +126 -0
- package/src/utils/SystemDetector.ts +127 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { MinecraftServer } from './MinecraftServer';
|
|
3
|
+
import { MinecraftConfig, ServerInfo } from '../types';
|
|
4
|
+
import { Logger } from '../utils/Logger';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs-extra';
|
|
7
|
+
|
|
8
|
+
export class ServerManager extends EventEmitter {
|
|
9
|
+
private servers: Map<string, MinecraftServer> = new Map();
|
|
10
|
+
private logger: Logger;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.logger = Logger.getInstance();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public createServer(name: string, config: Partial<MinecraftConfig>): MinecraftServer {
|
|
18
|
+
if (this.servers.has(name)) {
|
|
19
|
+
throw new Error(`Server with name ${name} already exists`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const server = new MinecraftServer(config);
|
|
23
|
+
this.servers.set(name, server);
|
|
24
|
+
|
|
25
|
+
server.on('ready', (info) => {
|
|
26
|
+
this.emit('server-ready', { name, info });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
server.on('stop', (data) => {
|
|
30
|
+
this.emit('server-stopped', { name, data });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
server.on('player-join', (player) => {
|
|
34
|
+
this.emit('player-joined', { name, player });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
server.on('player-leave', (playerName) => {
|
|
38
|
+
this.emit('player-left', { name, playerName });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
server.on('resource', (info) => {
|
|
42
|
+
this.emit('resource-update', { name, info });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.logger.success(`Server created: ${name}`);
|
|
46
|
+
return server;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public getServer(name: string): MinecraftServer | undefined {
|
|
50
|
+
return this.servers.get(name);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async startServer(name: string): Promise<ServerInfo> {
|
|
54
|
+
const server = this.servers.get(name);
|
|
55
|
+
if (!server) {
|
|
56
|
+
throw new Error(`Server not found: ${name}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.logger.info(`Starting server: ${name}`);
|
|
60
|
+
return server.start();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public async stopServer(name: string): Promise<void> {
|
|
64
|
+
const server = this.servers.get(name);
|
|
65
|
+
if (!server) {
|
|
66
|
+
throw new Error(`Server not found: ${name}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.logger.info(`Stopping server: ${name}`);
|
|
70
|
+
await server.stop();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async stopAll(): Promise<void> {
|
|
74
|
+
const promises = Array.from(this.servers.entries()).map(async ([name, server]) => {
|
|
75
|
+
this.logger.info(`Stopping server: ${name}`);
|
|
76
|
+
await server.stop();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await Promise.all(promises);
|
|
80
|
+
this.logger.success('All servers stopped');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public removeServer(name: string): boolean {
|
|
84
|
+
const server = this.servers.get(name);
|
|
85
|
+
if (server) {
|
|
86
|
+
if (server['serverInfo'].status === 'running') {
|
|
87
|
+
throw new Error(`Cannot remove running server: ${name}`);
|
|
88
|
+
}
|
|
89
|
+
this.servers.delete(name);
|
|
90
|
+
this.logger.info(`Server removed: ${name}`);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public listServers(): Array<{ name: string; info: ServerInfo }> {
|
|
97
|
+
return Array.from(this.servers.entries()).map(([name, server]) => ({
|
|
98
|
+
name,
|
|
99
|
+
info: server['serverInfo']
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public getServerCount(): number {
|
|
104
|
+
return this.servers.size;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public getRunningServers(): Array<{ name: string; info: ServerInfo }> {
|
|
108
|
+
return Array.from(this.servers.entries())
|
|
109
|
+
.filter(([_, server]) => server['serverInfo'].status === 'running')
|
|
110
|
+
.map(([name, server]) => ({ name, info: server['serverInfo'] }));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public async broadcastCommand(command: string): Promise<void> {
|
|
114
|
+
const promises = Array.from(this.servers.entries()).map(async ([name, server]) => {
|
|
115
|
+
try {
|
|
116
|
+
if (server['serverInfo'].status === 'running') {
|
|
117
|
+
server.sendCommand(command);
|
|
118
|
+
this.logger.debug(`Command sent to ${name}: ${command}`);
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.logger.error(`Failed to send command to ${name}:`, error);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await Promise.all(promises);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async saveAll(): Promise<void> {
|
|
129
|
+
await this.broadcastCommand('save-all');
|
|
130
|
+
this.logger.info('Save-all command sent to all servers');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public async backupAll(type: 'full' | 'world' | 'plugins' = 'world'): Promise<Map<string, string>> {
|
|
134
|
+
const results = new Map();
|
|
135
|
+
|
|
136
|
+
const promises = Array.from(this.servers.entries()).map(async ([name, server]) => {
|
|
137
|
+
try {
|
|
138
|
+
const backupPath = await server.backup(type);
|
|
139
|
+
results.set(name, backupPath);
|
|
140
|
+
this.logger.success(`Backup created for ${name}: ${backupPath}`);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.logger.error(`Failed to backup ${name}:`, error);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
await Promise.all(promises);
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public async importServersFromDirectory(dir: string): Promise<void> {
|
|
151
|
+
if (!await fs.pathExists(dir)) {
|
|
152
|
+
throw new Error(`Directory not found: ${dir}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const files = await fs.readdir(dir);
|
|
156
|
+
|
|
157
|
+
for (const file of files) {
|
|
158
|
+
if (file.endsWith('.json')) {
|
|
159
|
+
try {
|
|
160
|
+
const configPath = path.join(dir, file);
|
|
161
|
+
const configData = await fs.readFile(configPath, 'utf8');
|
|
162
|
+
const config = JSON.parse(configData);
|
|
163
|
+
const serverName = path.basename(file, '.json');
|
|
164
|
+
|
|
165
|
+
this.createServer(serverName, config);
|
|
166
|
+
this.logger.info(`Imported server: ${serverName}`);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
this.logger.error(`Failed to import ${file}:`, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public async exportServersToDirectory(dir: string): Promise<void> {
|
|
175
|
+
await fs.ensureDir(dir);
|
|
176
|
+
|
|
177
|
+
for (const [name, server] of this.servers) {
|
|
178
|
+
try {
|
|
179
|
+
const config = server['config'];
|
|
180
|
+
const configPath = path.join(dir, `${name}.json`);
|
|
181
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
182
|
+
this.logger.info(`Exported server: ${name}`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.logger.error(`Failed to export ${name}:`, error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public getStatus(): any {
|
|
190
|
+
return {
|
|
191
|
+
totalServers: this.getServerCount(),
|
|
192
|
+
runningServers: this.getRunningServers().length,
|
|
193
|
+
servers: this.listServers()
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import * as fs from 'fs-extra';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
import { Logger } from '../utils/Logger';
|
|
6
|
+
import { DownloadInfo } from '../types';
|
|
7
|
+
|
|
8
|
+
export class Downloader {
|
|
9
|
+
private static logger = Logger.getInstance();
|
|
10
|
+
|
|
11
|
+
public static async downloadFile(info: DownloadInfo, destDir: string): Promise<string> {
|
|
12
|
+
const destPath = path.join(destDir, info.fileName);
|
|
13
|
+
|
|
14
|
+
this.logger.info(`Downloading ${info.fileName}...`);
|
|
15
|
+
|
|
16
|
+
await fs.ensureDir(destDir);
|
|
17
|
+
|
|
18
|
+
const writer = fs.createWriteStream(destPath);
|
|
19
|
+
|
|
20
|
+
const response = await axios({
|
|
21
|
+
method: 'GET',
|
|
22
|
+
url: info.url,
|
|
23
|
+
responseType: 'stream'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const totalLength = response.headers['content-length'];
|
|
27
|
+
|
|
28
|
+
let downloadedLength = 0;
|
|
29
|
+
|
|
30
|
+
response.data.on('data', (chunk: Buffer) => {
|
|
31
|
+
downloadedLength += chunk.length;
|
|
32
|
+
if (totalLength) {
|
|
33
|
+
const progress = (downloadedLength / parseInt(totalLength) * 100).toFixed(2);
|
|
34
|
+
process.stdout.write(`\rDownloading: ${progress}%`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
response.data.pipe(writer);
|
|
39
|
+
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
writer.on('finish', async () => {
|
|
42
|
+
process.stdout.write('\n');
|
|
43
|
+
|
|
44
|
+
if (info.sha1) {
|
|
45
|
+
const hash = await this.calculateSHA1(destPath);
|
|
46
|
+
if (hash !== info.sha1) {
|
|
47
|
+
await fs.remove(destPath);
|
|
48
|
+
reject(new Error(`SHA1 mismatch for ${info.fileName}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.logger.success('SHA1 verified');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (info.size) {
|
|
55
|
+
const stats = await fs.stat(destPath);
|
|
56
|
+
if (stats.size !== info.size) {
|
|
57
|
+
await fs.remove(destPath);
|
|
58
|
+
reject(new Error(`Size mismatch for ${info.fileName}`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.logger.success(`Downloaded: ${info.fileName}`);
|
|
64
|
+
resolve(destPath);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
writer.on('error', reject);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public static getPaperURL(version: string, build?: string): DownloadInfo {
|
|
72
|
+
return {
|
|
73
|
+
url: `https://api.papermc.io/v2/projects/paper/versions/${version}/builds/${build || 'latest'}/downloads/paper-${version}-${build || 'latest'}.jar`,
|
|
74
|
+
fileName: `paper-${version}.jar`
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public static getPurpurURL(version: string): DownloadInfo {
|
|
79
|
+
return {
|
|
80
|
+
url: `https://api.purpurmc.org/v2/purpur/${version}/latest/download`,
|
|
81
|
+
fileName: `purpur-${version}.jar`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public static getVanillaURL(version: string): DownloadInfo {
|
|
86
|
+
return {
|
|
87
|
+
url: `https://piston-data.mojang.com/v1/objects/${this.getVanillaHash(version)}/server.jar`,
|
|
88
|
+
fileName: `vanilla-${version}.jar`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public static getSpigotURL(version: string): DownloadInfo {
|
|
93
|
+
return {
|
|
94
|
+
url: `https://download.getbukkit.org/spigot/spigot-${version}.jar`,
|
|
95
|
+
fileName: `spigot-${version}.jar`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public static getForgeURL(version: string): DownloadInfo {
|
|
100
|
+
return {
|
|
101
|
+
url: `https://maven.minecraftforge.net/net/minecraftforge/forge/${version}-${version}.0/forge-${version}-${version}.0-installer.jar`,
|
|
102
|
+
fileName: `forge-${version}-installer.jar`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public static getFabricURL(version: string): DownloadInfo {
|
|
107
|
+
return {
|
|
108
|
+
url: `https://meta.fabricmc.net/v2/versions/loader/${version}/0.15.11/1.0.1/server/jar`,
|
|
109
|
+
fileName: `fabric-${version}.jar`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public static getGeyserURL(): DownloadInfo {
|
|
114
|
+
return {
|
|
115
|
+
url: 'https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/standalone',
|
|
116
|
+
fileName: 'geyser.jar'
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public static getFloodgateURL(): DownloadInfo {
|
|
121
|
+
return {
|
|
122
|
+
url: 'https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot',
|
|
123
|
+
fileName: 'floodgate.jar'
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private static getVanillaHash(version: string): string {
|
|
128
|
+
const hashes: Record<string, string> = {
|
|
129
|
+
'1.20.4': '8dd1a28015f51b1803213892b75b7a20c5981f4d',
|
|
130
|
+
'1.20.2': '5b2aad530acbdcdcc875c1b2e2b469f498e37d2f',
|
|
131
|
+
'1.20.1': 'ae9f122b71bdab92d28fea6e27c53b0b28e25c1a',
|
|
132
|
+
'1.19.4': '8f3112a279976a0f4bcb8152d2a13015b0e2c1f5',
|
|
133
|
+
'1.18.2': 'c8f83c5655348435ec3f61fc9c6b83f3519e8a2c'
|
|
134
|
+
};
|
|
135
|
+
return hashes[version] || hashes['1.20.1'];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private static async calculateSHA1(filePath: string): Promise<string> {
|
|
139
|
+
const hash = createHash('sha1');
|
|
140
|
+
const data = await fs.readFile(filePath);
|
|
141
|
+
hash.update(data);
|
|
142
|
+
return hash.digest('hex');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ServerEngine } from './ServerEngine';
|
|
2
|
+
import { MinecraftConfig } from '../types';
|
|
3
|
+
import { Downloader } from './Downloader';
|
|
4
|
+
import { FileUtils } from '../utils/FileUtils';
|
|
5
|
+
import { PropertiesParser } from '../utils/PropertiesParser';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
|
|
8
|
+
export class FabricEngine implements ServerEngine {
|
|
9
|
+
public async download(config: MinecraftConfig, serverDir: string): Promise<string> {
|
|
10
|
+
const downloadInfo = Downloader.getFabricURL(config.version);
|
|
11
|
+
return Downloader.downloadFile(downloadInfo, serverDir);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async prepare(config: MinecraftConfig, serverDir: string): Promise<void> {
|
|
15
|
+
if (config.autoAcceptEula) {
|
|
16
|
+
await FileUtils.writeFile(
|
|
17
|
+
path.join(serverDir, 'eula.txt'),
|
|
18
|
+
'eula=true'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const properties = PropertiesParser.generateServerProperties(config);
|
|
23
|
+
await FileUtils.writeProperties(
|
|
24
|
+
path.join(serverDir, 'server.properties'),
|
|
25
|
+
properties
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getJavaArgs(config: MinecraftConfig): string[] {
|
|
30
|
+
const args: string[] = [];
|
|
31
|
+
|
|
32
|
+
args.push(`-Xms${config.memory.init}`);
|
|
33
|
+
args.push(`-Xmx${config.memory.max}`);
|
|
34
|
+
|
|
35
|
+
return args;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getServerJar(jarPath: string): string {
|
|
39
|
+
return jarPath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public getServerArgs(): string[] {
|
|
43
|
+
return ['nogui'];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public getServerType(): string {
|
|
47
|
+
return 'fabric';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ServerEngine } from './ServerEngine';
|
|
2
|
+
import { MinecraftConfig } from '../types';
|
|
3
|
+
import { Downloader } from './Downloader';
|
|
4
|
+
import { FileUtils } from '../utils/FileUtils';
|
|
5
|
+
import { PropertiesParser } from '../utils/PropertiesParser';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
|
|
10
|
+
export class ForgeEngine implements ServerEngine {
|
|
11
|
+
public async download(config: MinecraftConfig, serverDir: string): Promise<string> {
|
|
12
|
+
const downloadInfo = Downloader.getForgeURL(config.version);
|
|
13
|
+
return Downloader.downloadFile(downloadInfo, serverDir);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public async prepare(config: MinecraftConfig, serverDir: string, jarPath: string): Promise<void> {
|
|
17
|
+
if (config.autoAcceptEula) {
|
|
18
|
+
await FileUtils.writeFile(
|
|
19
|
+
path.join(serverDir, 'eula.txt'),
|
|
20
|
+
'eula=true'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const properties = PropertiesParser.generateServerProperties(config);
|
|
25
|
+
await FileUtils.writeProperties(
|
|
26
|
+
path.join(serverDir, 'server.properties'),
|
|
27
|
+
properties
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const installerPath = jarPath;
|
|
31
|
+
const forgeJar = path.join(serverDir, `forge-${config.version}-${config.version}.0-server.jar`);
|
|
32
|
+
|
|
33
|
+
if (!await FileUtils.fileExists(forgeJar)) {
|
|
34
|
+
console.log('Running Forge installer...');
|
|
35
|
+
execSync(`java -jar "${installerPath}" --installServer`, {
|
|
36
|
+
cwd: serverDir,
|
|
37
|
+
stdio: 'inherit'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public getJavaArgs(config: MinecraftConfig): string[] {
|
|
43
|
+
const args: string[] = [];
|
|
44
|
+
|
|
45
|
+
args.push(`-Xms${config.memory.init}`);
|
|
46
|
+
args.push(`-Xmx${config.memory.max}`);
|
|
47
|
+
|
|
48
|
+
return args;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public getServerJar(jarPath: string): string {
|
|
52
|
+
const dir = path.dirname(jarPath);
|
|
53
|
+
const files = fs.readdirSync(dir);
|
|
54
|
+
const forgeJar = files.find((f: string) => f.includes('forge') && f.endsWith('-server.jar'));
|
|
55
|
+
return forgeJar ? path.join(dir, forgeJar) : jarPath;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public getServerArgs(): string[] {
|
|
59
|
+
return ['nogui'];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public getServerType(): string {
|
|
63
|
+
return 'forge';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ServerEngine } from './ServerEngine';
|
|
2
|
+
import { MinecraftConfig } from '../types';
|
|
3
|
+
import { Downloader } from './Downloader';
|
|
4
|
+
import { FileUtils } from '../utils/FileUtils';
|
|
5
|
+
import { PropertiesParser } from '../utils/PropertiesParser';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
|
|
8
|
+
export class PaperEngine implements ServerEngine {
|
|
9
|
+
public async download(config: MinecraftConfig, serverDir: string): Promise<string> {
|
|
10
|
+
const downloadInfo = Downloader.getPaperURL(config.version);
|
|
11
|
+
return Downloader.downloadFile(downloadInfo, serverDir);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async prepare(config: MinecraftConfig, serverDir: string): Promise<void> {
|
|
15
|
+
if (config.autoAcceptEula) {
|
|
16
|
+
await FileUtils.writeFile(
|
|
17
|
+
path.join(serverDir, 'eula.txt'),
|
|
18
|
+
'eula=true'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const properties = PropertiesParser.generateServerProperties(config);
|
|
23
|
+
await FileUtils.writeProperties(
|
|
24
|
+
path.join(serverDir, 'server.properties'),
|
|
25
|
+
properties
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getJavaArgs(config: MinecraftConfig): string[] {
|
|
30
|
+
const args: string[] = [];
|
|
31
|
+
|
|
32
|
+
args.push(`-Xms${config.memory.init}`);
|
|
33
|
+
args.push(`-Xmx${config.memory.max}`);
|
|
34
|
+
|
|
35
|
+
if (config.memory.useAikarsFlags) {
|
|
36
|
+
args.push('-XX:+UseG1GC');
|
|
37
|
+
args.push('-XX:+ParallelRefProcEnabled');
|
|
38
|
+
args.push('-XX:MaxGCPauseMillis=200');
|
|
39
|
+
args.push('-XX:+UnlockExperimentalVMOptions');
|
|
40
|
+
args.push('-XX:+DisableExplicitGC');
|
|
41
|
+
args.push('-XX:+AlwaysPreTouch');
|
|
42
|
+
args.push('-XX:G1HeapWastePercent=5');
|
|
43
|
+
args.push('-XX:G1MixedGCCountTarget=4');
|
|
44
|
+
args.push('-XX:InitiatingHeapOccupancyPercent=15');
|
|
45
|
+
args.push('-XX:G1MixedGCLiveThresholdPercent=90');
|
|
46
|
+
args.push('-XX:G1RSetUpdatingPauseTimePercent=5');
|
|
47
|
+
args.push('-XX:SurvivorRatio=32');
|
|
48
|
+
args.push('-XX:+PerfDisableSharedMem');
|
|
49
|
+
args.push('-XX:MaxTenuringThreshold=1');
|
|
50
|
+
args.push('-Dusing.aikars.flags=https://mcflags.emc.gs');
|
|
51
|
+
args.push('-Daikars.new.flags=true');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return args;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public getServerJar(jarPath: string): string {
|
|
58
|
+
return jarPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public getServerArgs(): string[] {
|
|
62
|
+
return ['nogui'];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public getServerType(): string {
|
|
66
|
+
return 'paper';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MinecraftConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface ServerEngine {
|
|
4
|
+
download(config: MinecraftConfig, serverDir: string): Promise<string>;
|
|
5
|
+
prepare(config: MinecraftConfig, serverDir: string, jarPath?: string): Promise<void>;
|
|
6
|
+
getJavaArgs(config: MinecraftConfig): string[];
|
|
7
|
+
getServerJar(jarPath: string): string;
|
|
8
|
+
getServerArgs(): string[];
|
|
9
|
+
getServerType(): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ServerEngine } from './ServerEngine';
|
|
2
|
+
import { MinecraftConfig } from '../types';
|
|
3
|
+
import { Downloader } from './Downloader';
|
|
4
|
+
import { FileUtils } from '../utils/FileUtils';
|
|
5
|
+
import { PropertiesParser } from '../utils/PropertiesParser';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
|
|
8
|
+
export class VanillaEngine implements ServerEngine {
|
|
9
|
+
public async download(config: MinecraftConfig, serverDir: string): Promise<string> {
|
|
10
|
+
const downloadInfo = Downloader.getVanillaURL(config.version);
|
|
11
|
+
return Downloader.downloadFile(downloadInfo, serverDir);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async prepare(config: MinecraftConfig, serverDir: string): Promise<void> {
|
|
15
|
+
if (config.autoAcceptEula) {
|
|
16
|
+
await FileUtils.writeFile(
|
|
17
|
+
path.join(serverDir, 'eula.txt'),
|
|
18
|
+
'eula=true'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const properties = PropertiesParser.generateServerProperties(config);
|
|
23
|
+
await FileUtils.writeProperties(
|
|
24
|
+
path.join(serverDir, 'server.properties'),
|
|
25
|
+
properties
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getJavaArgs(config: MinecraftConfig): string[] {
|
|
30
|
+
const args: string[] = [];
|
|
31
|
+
|
|
32
|
+
args.push(`-Xms${config.memory.init}`);
|
|
33
|
+
args.push(`-Xmx${config.memory.max}`);
|
|
34
|
+
|
|
35
|
+
return args;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getServerJar(jarPath: string): string {
|
|
39
|
+
return jarPath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public getServerArgs(): string[] {
|
|
43
|
+
return ['nogui'];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public getServerType(): string {
|
|
47
|
+
return 'vanilla';
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { MinecraftServer } from './core/MinecraftServer';
|
|
2
|
+
import { Logger, LogLevel } from './utils/Logger';
|
|
3
|
+
import { SystemDetector } from './utils/SystemDetector';
|
|
4
|
+
|
|
5
|
+
export * from './core/MinecraftServer';
|
|
6
|
+
export * from './core/ConfigHandler';
|
|
7
|
+
export * from './core/JavaChecker';
|
|
8
|
+
export * from './core/ServerManager';
|
|
9
|
+
export * from './platforms/JavaServer';
|
|
10
|
+
export * from './platforms/BedrockServer';
|
|
11
|
+
export * from './platforms/GeyserBridge';
|
|
12
|
+
export * from './engines/Downloader';
|
|
13
|
+
export * from './engines/PaperEngine';
|
|
14
|
+
export * from './engines/VanillaEngine';
|
|
15
|
+
export * from './engines/ForgeEngine';
|
|
16
|
+
export * from './engines/FabricEngine';
|
|
17
|
+
export * from './utils/Logger';
|
|
18
|
+
export * from './utils/FileUtils';
|
|
19
|
+
export * from './utils/SystemDetector';
|
|
20
|
+
export * from './utils/PropertiesParser';
|
|
21
|
+
export * from './types';
|
|
22
|
+
|
|
23
|
+
export default MinecraftServer;
|
|
24
|
+
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
const logger = Logger.getInstance();
|
|
27
|
+
logger.setLogLevel(LogLevel.INFO);
|
|
28
|
+
|
|
29
|
+
const systemInfo = SystemDetector.getSystemInfo();
|
|
30
|
+
logger.info('System Information:', systemInfo);
|
|
31
|
+
|
|
32
|
+
const server = new MinecraftServer({
|
|
33
|
+
platform: 'java',
|
|
34
|
+
version: '1.20.1',
|
|
35
|
+
type: 'paper',
|
|
36
|
+
memory: {
|
|
37
|
+
init: '1G',
|
|
38
|
+
max: '2G',
|
|
39
|
+
useAikarsFlags: true
|
|
40
|
+
},
|
|
41
|
+
world: {
|
|
42
|
+
difficulty: 'normal',
|
|
43
|
+
hardcore: false,
|
|
44
|
+
gamemode: 'survival',
|
|
45
|
+
maxPlayers: 10,
|
|
46
|
+
viewDistance: 10,
|
|
47
|
+
levelName: 'world'
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
server.on('ready', (info) => {
|
|
52
|
+
logger.success(`
|
|
53
|
+
Mc Headless - Powered By Dimzxzzx07
|
|
54
|
+
Address: ${info.ip}:${info.port}
|
|
55
|
+
Version: ${info.version} ${info.type}
|
|
56
|
+
Players: 0/${info.maxPlayers}
|
|
57
|
+
`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
server.on('player-join', (player) => {
|
|
61
|
+
logger.info(`Player joined: ${player.name}`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
server.on('player-leave', (name) => {
|
|
65
|
+
logger.info(`Player left: ${name}`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
server.start().catch(error => {
|
|
69
|
+
logger.error('Failed to start server:', error);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
process.on('SIGINT', async () => {
|
|
73
|
+
logger.info('Received SIGINT, stopping server...');
|
|
74
|
+
await server.stop();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
process.on('SIGTERM', async () => {
|
|
79
|
+
logger.info('Received SIGTERM, stopping server...');
|
|
80
|
+
await server.stop();
|
|
81
|
+
process.exit(0);
|
|
82
|
+
});
|
|
83
|
+
}
|