@dimzxzzx07/mc-headless 1.8.0 → 1.9.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 +317 -703
- package/dist/core/JavaChecker.d.ts +16 -3
- package/dist/core/JavaChecker.d.ts.map +1 -1
- package/dist/core/JavaChecker.js +171 -65
- package/dist/core/JavaChecker.js.map +1 -1
- package/dist/core/MinecraftServer.d.ts +15 -0
- package/dist/core/MinecraftServer.d.ts.map +1 -1
- package/dist/core/MinecraftServer.js +200 -110
- package/dist/core/MinecraftServer.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -16
- package/dist/index.js.map +1 -1
- package/dist/platforms/BedrockServer.d.ts.map +1 -1
- package/dist/platforms/BedrockServer.js +2 -0
- package/dist/platforms/BedrockServer.js.map +1 -1
- package/dist/platforms/JavaServer.d.ts.map +1 -1
- package/dist/platforms/JavaServer.js +2 -0
- package/dist/platforms/JavaServer.js.map +1 -1
- package/dist/platforms/SkinRestorer.d.ts +14 -0
- package/dist/platforms/SkinRestorer.d.ts.map +1 -0
- package/dist/platforms/SkinRestorer.js +145 -0
- package/dist/platforms/SkinRestorer.js.map +1 -0
- package/dist/platforms/SkinsRestorer.d.ts +14 -0
- package/dist/platforms/SkinsRestorer.d.ts.map +1 -0
- package/dist/platforms/SkinsRestorer.js +145 -0
- package/dist/platforms/SkinsRestorer.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/JavaChecker.ts +162 -61
- package/src/core/MinecraftServer.ts +233 -120
- package/src/index.ts +33 -17
- package/src/platforms/BedrockServer.ts +2 -0
- package/src/platforms/JavaServer.ts +2 -0
- package/src/platforms/SkinRestorer.ts +127 -0
- package/src/types/index.ts +2 -0
package/package.json
CHANGED
package/src/core/JavaChecker.ts
CHANGED
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
import { execSync, exec } from 'child_process';
|
|
2
|
+
import * as fs from 'fs-extra';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as https from 'https';
|
|
5
|
+
import * as tar from 'tar';
|
|
2
6
|
import { Logger } from '../utils/Logger';
|
|
3
|
-
|
|
7
|
+
|
|
8
|
+
export interface JavaInfo {
|
|
9
|
+
path: string;
|
|
10
|
+
version: string;
|
|
11
|
+
type: 'system' | 'portable';
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export class JavaChecker {
|
|
6
15
|
private static logger = Logger.getInstance();
|
|
16
|
+
private static readonly JAVA_DIR = path.join('/tmp', '.mc-headless-java');
|
|
17
|
+
private static readonly JAVA_17_URL = 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.10%2B7/OpenJDK17U-jre_x64_linux_hotspot_17.0.10_7.tar.gz';
|
|
18
|
+
private static readonly JAVA_21_URL = 'https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jre_x64_linux_hotspot_21.0.2_13.tar.gz';
|
|
19
|
+
private static readonly JAVA_11_URL = 'https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.22%2B7/OpenJDK11U-jre_x64_linux_hotspot_11.0.22_7.tar.gz';
|
|
20
|
+
private static readonly JAVA_8_URL = 'https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u402-b06/OpenJDK8U-jre_x64_linux_hotspot_8u402b06.tar.gz';
|
|
7
21
|
|
|
8
|
-
public static checkJava(): Promise<boolean> {
|
|
22
|
+
public static async checkJava(): Promise<boolean> {
|
|
9
23
|
return new Promise((resolve) => {
|
|
10
|
-
exec('java
|
|
11
|
-
if (error) {
|
|
12
|
-
this.logger.warning('Java is not installed');
|
|
24
|
+
exec('which java', (error, stdout) => {
|
|
25
|
+
if (error || !stdout.trim()) {
|
|
26
|
+
this.logger.warning('Java is not installed in system');
|
|
13
27
|
resolve(false);
|
|
14
28
|
} else {
|
|
15
|
-
|
|
29
|
+
const javaPath = stdout.trim();
|
|
30
|
+
this.logger.success(`System Java found at: ${javaPath}`);
|
|
16
31
|
resolve(true);
|
|
17
32
|
}
|
|
18
33
|
});
|
|
19
34
|
});
|
|
20
35
|
}
|
|
21
36
|
|
|
22
|
-
public static
|
|
37
|
+
public static getSystemJavaPath(): string | null {
|
|
38
|
+
try {
|
|
39
|
+
const stdout = execSync('which java').toString().trim();
|
|
40
|
+
return stdout || null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static getSystemJavaVersion(): string | null {
|
|
23
47
|
try {
|
|
24
48
|
const output = execSync('java -version 2>&1').toString();
|
|
25
49
|
if (output.includes('version "21')) return '21';
|
|
@@ -33,74 +57,151 @@ export class JavaChecker {
|
|
|
33
57
|
}
|
|
34
58
|
}
|
|
35
59
|
|
|
36
|
-
public static async
|
|
37
|
-
|
|
60
|
+
public static async getOrDownloadPortableJava(version: '17' | '21' | '11' | '8' = '17'): Promise<JavaInfo> {
|
|
61
|
+
await fs.ensureDir(this.JAVA_DIR);
|
|
62
|
+
|
|
63
|
+
const javaHome = path.join(this.JAVA_DIR, `jre-${version}`);
|
|
64
|
+
const javaBin = path.join(javaHome, 'bin', 'java');
|
|
38
65
|
|
|
39
|
-
if (
|
|
40
|
-
this.logger.
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
if (await fs.pathExists(javaBin)) {
|
|
67
|
+
this.logger.info(`Portable Java ${version} already exists at ${javaBin}`);
|
|
68
|
+
|
|
69
|
+
const versionOutput = execSync(`"${javaBin}" -version 2>&1`).toString();
|
|
70
|
+
this.logger.debug(`Portable Java version: ${versionOutput.split('\n')[0]}`);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
path: javaBin,
|
|
74
|
+
version: version,
|
|
75
|
+
type: 'portable'
|
|
76
|
+
};
|
|
43
77
|
}
|
|
44
78
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
79
|
+
this.logger.info(`Downloading portable Java ${version} JRE...`);
|
|
80
|
+
|
|
81
|
+
let downloadUrl = '';
|
|
82
|
+
switch (version) {
|
|
83
|
+
case '21':
|
|
84
|
+
downloadUrl = this.JAVA_21_URL;
|
|
85
|
+
break;
|
|
86
|
+
case '17':
|
|
87
|
+
downloadUrl = this.JAVA_17_URL;
|
|
88
|
+
break;
|
|
89
|
+
case '11':
|
|
90
|
+
downloadUrl = this.JAVA_11_URL;
|
|
91
|
+
break;
|
|
92
|
+
case '8':
|
|
93
|
+
downloadUrl = this.JAVA_8_URL;
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
downloadUrl = this.JAVA_17_URL;
|
|
50
97
|
}
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
const tarPath = path.join(this.JAVA_DIR, `jre-${version}.tar.gz`);
|
|
100
|
+
|
|
101
|
+
await this.downloadFile(downloadUrl, tarPath);
|
|
102
|
+
|
|
103
|
+
this.logger.info(`Extracting Java ${version}...`);
|
|
104
|
+
await fs.ensureDir(javaHome);
|
|
105
|
+
await tar.extract({
|
|
106
|
+
file: tarPath,
|
|
107
|
+
cwd: javaHome,
|
|
108
|
+
strip: 1
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await fs.remove(tarPath);
|
|
112
|
+
|
|
113
|
+
await fs.chmod(javaBin, 0o755);
|
|
114
|
+
|
|
115
|
+
this.logger.success(`Portable Java ${version} installed at ${javaBin}`);
|
|
116
|
+
|
|
117
|
+
const versionOutput = execSync(`"${javaBin}" -version 2>&1`).toString();
|
|
118
|
+
this.logger.debug(`Portable Java version: ${versionOutput.split('\n')[0]}`);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
path: javaBin,
|
|
122
|
+
version: version,
|
|
123
|
+
type: 'portable'
|
|
124
|
+
};
|
|
55
125
|
}
|
|
56
126
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
127
|
+
private static downloadFile(url: string, destPath: string): Promise<void> {
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
this.logger.info(`Downloading from ${url}`);
|
|
130
|
+
|
|
131
|
+
const file = fs.createWriteStream(destPath);
|
|
132
|
+
https.get(url, (response) => {
|
|
133
|
+
if (response.statusCode !== 200) {
|
|
134
|
+
reject(new Error(`Download failed: ${response.statusCode}`));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
61
137
|
|
|
62
|
-
|
|
63
|
-
|
|
138
|
+
const totalSize = parseInt(response.headers['content-length'] || '0');
|
|
139
|
+
let downloadedSize = 0;
|
|
140
|
+
|
|
141
|
+
response.on('data', (chunk) => {
|
|
142
|
+
downloadedSize += chunk.length;
|
|
143
|
+
if (totalSize > 0) {
|
|
144
|
+
const percent = ((downloadedSize / totalSize) * 100).toFixed(1);
|
|
145
|
+
process.stdout.write(`\rDownloading Java: ${percent}% (${(downloadedSize / 1024 / 1024).toFixed(1)} MB / ${(totalSize / 1024 / 1024).toFixed(1)} MB)`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
response.pipe(file);
|
|
150
|
+
|
|
151
|
+
file.on('finish', () => {
|
|
152
|
+
process.stdout.write('\n');
|
|
153
|
+
file.close();
|
|
154
|
+
resolve();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
file.on('error', (err) => {
|
|
158
|
+
fs.unlink(destPath).catch(() => {});
|
|
159
|
+
reject(err);
|
|
160
|
+
});
|
|
161
|
+
}).on('error', (err) => {
|
|
162
|
+
fs.unlink(destPath).catch(() => {});
|
|
163
|
+
reject(err);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public static async ensureJava(version: '17' | '21' | 'auto' = 'auto', usePortable: boolean = true): Promise<JavaInfo> {
|
|
169
|
+
const targetVersion = version === 'auto' ? '17' : version;
|
|
64
170
|
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
171
|
+
if (!usePortable) {
|
|
172
|
+
const systemJava = this.getSystemJavaPath();
|
|
173
|
+
if (systemJava) {
|
|
174
|
+
const systemVersion = this.getSystemJavaVersion();
|
|
175
|
+
if (systemVersion === targetVersion || (targetVersion === '17' && systemVersion === '21')) {
|
|
176
|
+
this.logger.success(`Using system Java ${systemVersion} at ${systemJava}`);
|
|
177
|
+
return {
|
|
178
|
+
path: systemJava,
|
|
179
|
+
version: systemVersion || targetVersion,
|
|
180
|
+
type: 'system'
|
|
181
|
+
};
|
|
182
|
+
}
|
|
77
183
|
}
|
|
78
|
-
} else if (osType === 'darwin') {
|
|
79
|
-
command = `brew install openjdk@${targetVersion}`;
|
|
80
|
-
} else {
|
|
81
|
-
this.logger.error('Automatic Java installation not supported on this OS');
|
|
82
|
-
throw new Error('Java not installed');
|
|
83
184
|
}
|
|
185
|
+
|
|
186
|
+
return this.getOrDownloadPortableJava(targetVersion as any);
|
|
187
|
+
}
|
|
84
188
|
|
|
85
|
-
|
|
189
|
+
public static cleanupOldJava(): void {
|
|
190
|
+
try {
|
|
191
|
+
const files = fs.readdirSync(this.JAVA_DIR);
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
const oneDay = 24 * 60 * 60 * 1000;
|
|
86
194
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
this.logger.success(`Java ${targetVersion} installed successfully`);
|
|
94
|
-
resolve();
|
|
195
|
+
files.forEach(file => {
|
|
196
|
+
const filePath = path.join(this.JAVA_DIR, file);
|
|
197
|
+
const stats = fs.statSync(filePath);
|
|
198
|
+
if (now - stats.mtimeMs > oneDay) {
|
|
199
|
+
fs.removeSync(filePath);
|
|
200
|
+
this.logger.debug(`Cleaned up old Java: ${file}`);
|
|
95
201
|
}
|
|
96
202
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
if (child.stderr) {
|
|
102
|
-
child.stderr.pipe(process.stderr);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
203
|
+
} catch (error) {
|
|
204
|
+
this.logger.debug('No old Java to cleanup');
|
|
205
|
+
}
|
|
105
206
|
}
|
|
106
207
|
}
|