Initial commit

This commit is contained in:
2026-05-23 23:13:12 +02:00
commit e58680b78b
12 changed files with 621 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
# Created by https://www.toptal.com/developers/gitignore/api/java,maven
# Edit at https://www.toptal.com/developers/gitignore?templates=java,maven
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# End of https://www.toptal.com/developers/gitignore/api/java,maven
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="MCRun" />
</profile>
</annotationProcessing>
</component>
</project>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="github" />
<option name="name" value="github" />
<option name="url" value="https://maven.pkg.github.com/KartoffelChipss/LifeStealZ" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="central" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
</component>
</project>
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="ASK" />
<option name="description" value="" />
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK" />
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+71
View File
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="98c311c3-6130-4449-9bcb-07029b56b776" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/encodings.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/strassburger/mcrun/RunServerMojo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/strassburger/mcrun/downloader/PaperServerJarDownloader.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/strassburger/mcrun/downloader/ServerJarDownloadException.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/strassburger/mcrun/downloader/ServerJarDownloader.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="Exception" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 1
}</component>
<component name="ProjectId" id="3E8fdcRwutbLOHxizPneckuMJLe" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="98c311c3-6130-4449-9bcb-07029b56b776" name="Changes" comment="" />
<created>1779565206518</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1779565206518</updated>
<workItem from="1779565207730" duration="57000" />
<workItem from="1779565274563" duration="632000" />
<workItem from="1779565913273" duration="4857000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>
+180
View File
@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.strassburger</groupId>
<artifactId>mc-run</artifactId>
<version>0.0.7</version>
<packaging>maven-plugin</packaging>
<name>MCRun Maven Plugin</name>
<prerequisites>
<maven>${maven.version}</maven>
</prerequisites>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.version>3.3.9</maven.version>
<maven-plugin-plugin.version>3.13.1</maven-plugin-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${maven.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_maven-plugin_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven-plugin-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven-plugin-plugin.version}</version>
<configuration>
<goalPrefix>mcrun</goalPrefix>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<debug>true</debug>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<pomIncludes>
<pomInclude>*/pom.xml</pomInclude>
</pomIncludes>
<postBuildHookScript>verify</postBuildHookScript>
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
<settingsFile>src/it/settings.xml</settingsFile>
<goals>
<goal>clean</goal>
<goal>test-compile</goal>
</goals>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
@@ -0,0 +1,95 @@
package org.strassburger.mcrun;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.strassburger.mcrun.downloader.PaperServerJarDownloader;
import org.strassburger.mcrun.downloader.ServerJarDownloadException;
import org.strassburger.mcrun.downloader.ServerJarDownloader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@Mojo(name = "run", defaultPhase = LifecyclePhase.PACKAGE)
public class RunServerMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.jar")
private File pluginJar;
@Parameter(defaultValue = "${project.basedir}/server")
private File serverDirectory;
@Parameter(defaultValue = "server.jar")
private String serverJarName;
@Parameter(defaultValue = "paper")
private String serverSoftware;
@Parameter(required = true)
private String mcVersion;
@Parameter(defaultValue = "2G")
private String memory;
@Parameter(defaultValue = "true")
private boolean acceptEula;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("Server directory: " + serverDirectory.getAbsolutePath());
File pluginsDir = new File(serverDirectory, "plugins");
pluginsDir.mkdirs();
try {
Files.copy(pluginJar.toPath(),
new File(pluginsDir, pluginJar.getName()).toPath(),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new MojoExecutionException("Failed to copy plugin jar file: " + pluginJar.getAbsolutePath(), e);
}
try {
ServerJarDownloader jarDownloader = ServerJarDownloader.forServerSoftware(serverSoftware)
.withLog(getLog()).withServerDirectory(serverDirectory)
.withServerJarName(serverJarName).withMcVersion(mcVersion)
.build();
jarDownloader.run();
} catch (ServerJarDownloadException e) {
throw new MojoExecutionException("Failed to download server jar", e);
}
if (acceptEula) acceptEula();
ProcessBuilder pb = new ProcessBuilder(
"java", "-Xmx" + memory, "-Xms" + memory,
"-jar", serverJarName, "--nogui"
);
pb.directory(serverDirectory);
pb.inheritIO();
Process process;
try {
process = pb.start();
} catch (IOException e) {
throw new MojoExecutionException("Failed to start server: " + e.getMessage(), e);
}
try {
process.waitFor();
} catch (InterruptedException e) {
throw new MojoExecutionException("Failed to start server: " + e.getMessage(), e);
}
}
private void acceptEula() throws MojoExecutionException {
File eula = new File(serverDirectory, "eula.txt");
if (eula.exists()) return;
try {
Files.writeString(eula.toPath(), "eula=true\n");
getLog().info("eula.txt created and accepted.");
} catch (IOException e) {
throw new MojoExecutionException("Failed to write eula.txt: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,59 @@
package org.strassburger.mcrun.downloader;
import org.apache.maven.plugin.logging.Log;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class PaperServerJarDownloader extends ServerJarDownloader {
public PaperServerJarDownloader(Log log, File serverDirectory, String serverJarName, String mcVersion) {
super(log, serverDirectory, serverJarName, mcVersion);
}
@Override
protected void downloadServerJar() throws ServerJarDownloadException {
File jarFile = getServerJar();
try {
getLog().info("Fetching latest Paper build info for " + getMcVersion() + "...");
String apiUrl = "https://fill.papermc.io/v3/projects/paper/versions/" + getMcVersion() + "/builds/latest";
String json = fetchUrl(apiUrl);
String downloadUrl = parseDownloadUrl(json);
getLog().info("Downloading Paper from: " + downloadUrl);
try (var in = new java.net.URI(downloadUrl).toURL().openStream()) {
Files.copy(in, jarFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
getLog().info("Paper downloaded to: " + jarFile.getAbsolutePath());
} catch (Exception e) {
throw new ServerJarDownloadException("Failed to download Paper: " + e.getMessage(), e);
}
}
private String fetchUrl(String url) throws Exception {
var connection = new java.net.URI(url).toURL().openConnection();
connection.setRequestProperty("Accept", "application/json");
try (var in = connection.getInputStream()) {
return new String(in.readAllBytes());
}
}
private String parseDownloadUrl(String json) {
// Parse: .downloads["server:default"].url
int downloadsIdx = json.indexOf("\"downloads\"");
int serverDefaultIdx = json.indexOf("\"server:default\"", downloadsIdx);
int urlIdx = json.indexOf("\"url\"", serverDefaultIdx);
int urlStart = json.indexOf("\"", urlIdx + 5) + 1;
int urlEnd = json.indexOf("\"", urlStart);
if (urlStart <= 0 || urlEnd <= 0) {
throw new ServerJarDownloadException("Could not parse download URL from Paper API response");
}
return json.substring(urlStart, urlEnd);
}
}
@@ -0,0 +1,11 @@
package org.strassburger.mcrun.downloader;
public class ServerJarDownloadException extends RuntimeException {
public ServerJarDownloadException(String message) {
super(message);
}
public ServerJarDownloadException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,89 @@
package org.strassburger.mcrun.downloader;
import org.apache.maven.plugin.logging.Log;
import java.io.File;
import java.util.function.Function;
public abstract class ServerJarDownloader {
private final Log log;
private final File serverDirectory;
private final String serverJarName;
private final String mcVersion;
public ServerJarDownloader(Log log, File serverDirectory, String serverJarName, String mcVersion) {
this.log = log;
this.serverDirectory = serverDirectory;
this.serverJarName = serverJarName;
this.mcVersion = mcVersion;
}
public void run() throws ServerJarDownloadException {
if (getServerJar().exists()) {
log.info("Paper jar already exists, skipping download.");
return;
}
serverDirectory.mkdirs();
downloadServerJar();
}
protected Log getLog() {
return log;
}
protected String getMcVersion() {
return mcVersion;
}
protected File getServerJar() {
return new File(serverDirectory, serverJarName);
}
protected abstract void downloadServerJar() throws ServerJarDownloadException;
public static Builder forServerSoftware(String serverSoftware) throws ServerJarDownloadException {
return switch (serverSoftware) {
case "paper" -> new Builder(b ->
new PaperServerJarDownloader(b.log, b.serverDirectory, b.serverJarName, b.mcVersion));
default -> throw new ServerJarDownloadException("Invalid server software: " + serverSoftware);
};
}
public static class Builder {
private final Function<Builder, ServerJarDownloader> factory;
private Log log;
private File serverDirectory;
private String serverJarName;
private String mcVersion;
public Builder(Function<Builder, ServerJarDownloader> factory) {
this.factory = factory;
}
public Builder withLog(Log log) {
this.log = log;
return this;
}
public Builder withServerDirectory(File serverDirectory) {
this.serverDirectory = serverDirectory;
return this;
}
public Builder withServerJarName(String serverJarName) {
this.serverJarName = serverJarName;
return this;
}
public Builder withMcVersion(String mcVersion) {
this.mcVersion = mcVersion;
return this;
}
public ServerJarDownloader build() {
return factory.apply(this);
}
}
}