commit 5c27681d1da2c05127575dbceb37013356ea40ef Author: Coldsmiles Date: Sun Sep 13 18:08:00 2026 +0800 Initialize Infstar Paper-based server core diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b94b480 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.gradle/ +build/ +paper-api/ +paper-server/ +paper/ +run/ +# 本地测试服务器的世界、日志、配置和 JAR 均不上传。 +test-server/ +.idea/ +*.iml diff --git a/README.md b/README.md new file mode 100644 index 0000000..816b552 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Infstar + +Infstar 是一个基于 Paper 的 Minecraft 服务端核心,优先保证原版机制、 +技术服和客户端 Mod 的协议兼容性,再逐步加入可控的性能优化。 + +Paper 是 Infstar 唯一的代码上游。Leaves、Leaf、Purpur 和其他项目只作为 +独立补丁的参考来源,不会成为 Infstar 的固定继承链或构建依赖。 + +## 当前基线 + +- Minecraft:`26.2` +- Paper commit:`e5fe71723e2ffde7cc9fafc085ac3bb73e63175e` +- Java:`25` +- 品牌标识:`infstarmc:infstar` +- Maven Group:`org.infstarmc.infstar` +- 开发版本:`-dev-` + +Paper commit 有意固定在 `gradle.properties` 中。更新基线时应更新 commit、 +重新应用补丁、运行兼容性测试,并审查生成的补丁变化后再合并。 + +## 构建 + +```bash +./gradlew applyAllPatches +./gradlew createPaperclipJar +``` + +Paperweight 会创建多层 Git 工作仓库,Linux 下应将项目放在 ext4、btrfs 等 +原生 Linux 文件系统中。NTFS、exFAT 或部分 FUSE 挂载可能无法可靠处理 Git +锁文件,不建议直接在这些文件系统中构建。 + +需要使用 Paper 本地镜像或单独的上游工作目录时,可通过 Gradle 属性覆盖: + +```bash +./gradlew applyAllPatches \ + -PpaperRepository=/path/to/Paper \ + -PpaperweightUpstreamWorkDir=/path/to/infstar-upstreams +``` + +在发布流程建立前,生成的服务端均视为开发版本。仓库还没有提交时,Git +短提交会临时显示为 `unknown`;首次提交后会自动使用七位短提交值。 + +## 上游策略 + +Infstar 直接跟进 Paper。其他下游项目不会作为 Git 子模块或构建必需依赖。 +从其他项目移植的补丁必须经过审查、注明来源、完成测试,并在移植后由 +Infstar 自行维护。 diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..3adfdc2 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,68 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent + +plugins { + `maven-publish` + id("io.papermc.paperweight.patcher") version "2.0.0-beta.21" +} + +val paperRepository = "https://repo.papermc.io/repository/maven-public/" + +subprojects { + apply(plugin = "java-library") + apply(plugin = "maven-publish") + + extensions.configure { + toolchain.languageVersion = JavaLanguageVersion.of(25) + } + + repositories { + mavenCentral() + maven(paperRepository) + } + + tasks.withType().configureEach { + options.encoding = Charsets.UTF_8.name() + options.release = 25 + options.isFork = true + options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal")) + } + tasks.withType().configureEach { + options.encoding = Charsets.UTF_8.name() + } + tasks.withType().configureEach { + filteringCharset = Charsets.UTF_8.name() + } + tasks.withType().configureEach { + testLogging { + showStackTraces = true + exceptionFormat = TestExceptionFormat.FULL + events(TestLogEvent.STANDARD_OUT) + } + } +} + +paperweight { + upstreams.paper { + repo = providers.gradleProperty("paperRepository") + .orElse("https://github.com/PaperMC/Paper.git") + ref = providers.gradleProperty("paperCommit") + + patchFile { + path = "paper-api/build.gradle.kts" + outputFile = file("infstar-api/build.gradle.kts") + patchFile = file("infstar-api/build.gradle.kts.patch") + } + patchFile { + path = "paper-server/build.gradle.kts" + outputFile = file("infstar-server/build.gradle.kts") + patchFile = file("infstar-server/build.gradle.kts.patch") + } + patchDir("paperApi") { + upstreamPath = "paper-api" + excludes = setOf("build.gradle.kts") + patchesDir = file("infstar-api/paper-patches") + outputDir = file("paper-api") + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..467438d --- /dev/null +++ b/gradle.properties @@ -0,0 +1,11 @@ +group=org.infstarmc.infstar +mcVersion=26.2 +apiVersion=26.2 +channel=ALPHA + +# Paper 是唯一代码上游。跟进 Paper 时应经过审查后再更新此 commit。 +paperCommit=e5fe71723e2ffde7cc9fafc085ac3bb73e63175e + +org.gradle.configuration-cache=true +org.gradle.caching=true +org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..b1b8ef5 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c61a118 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..249efbb --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# gradlew start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh gradlew +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8508ef6 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,82 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem gradlew startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:execute +@rem Setup the command line + + + +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel + +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/infstar-api/build.gradle.kts b/infstar-api/build.gradle.kts new file mode 100644 index 0000000..c1a2f83 --- /dev/null +++ b/infstar-api/build.gradle.kts @@ -0,0 +1,279 @@ +import io.papermc.paperweight.checkstyle.JavadocTag +import paper.libs.com.google.gson.Gson + +plugins { + `java-library` + `maven-publish` + // Infstar:API 模块基于 Paper API 源码生成。 + idea + // Paper Checkstyle 依赖上游复合构建中的 paper-checkstyle 模块,衍生核心暂不启用。 + // id("io.papermc.paperweight.paper-checkstyle") +} + +java { + withSourcesJar() + withJavadocJar() +} + +val projectCustomJavadocTags = setOf( + JavadocTag("apiNote", "a", "API Note:"), +) + +/* +paperCheckstyle { + customJavadocTags = projectCustomJavadocTags + directoriesToSkipFile = layout.projectDirectory.file(".checkstyle/ignored_directories.txt") +} +*/ + +val annotationsVersion = "26.0.2" +val adventureVersion = "5.2.0" +val bungeeCordChatVersion = "1.21-R0.2-deprecated+build.21" +val slf4jVersion = "2.0.17" +val log4jVersion = "2.26.0" + +val apiAndDocs: Configuration by configurations.creating +configurations.api { + extendsFrom(apiAndDocs) +} +val javadocSourcepath: Configuration by configurations.creating { + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + } +} + +// Configure mockito agent that is needed in newer Java versions +val mockitoAgent = configurations.register("mockitoAgent") +abstract class MockitoAgentProvider : CommandLineArgumentProvider { + @get:CompileClasspath + abstract val fileCollection: ConfigurableFileCollection + + override fun asArguments(): Iterable { + return listOf("-javaagent:" + fileCollection.files.single().absolutePath) + } +} + +dependencies { + // api dependencies are listed transitively to API consumers + api("com.google.guava:guava:33.6.0-jre") + api("com.google.code.gson:gson:2.14.0") + api("org.yaml:snakeyaml:2.2") + api("org.joml:joml:1.10.8") { + isTransitive = false // https://github.com/JOML-CI/JOML/issues/352 + } + api("it.unimi.dsi:fastutil:8.5.18") + api("org.apache.logging.log4j:log4j-api:$log4jVersion") + api("org.slf4j:slf4j-api:$slf4jVersion") + api("com.mojang:brigadier:1.3.10") + + // Deprecate bungeecord-chat in favor of adventure + api("net.md-5:bungeecord-chat:$bungeeCordChatVersion") { + exclude("com.google.guava", "guava") + } + + apiAndDocs(platform("net.kyori:adventure-bom:$adventureVersion")) + apiAndDocs("net.kyori:adventure-api") + apiAndDocs("net.kyori:adventure-key") + apiAndDocs("net.kyori:adventure-text-minimessage") + apiAndDocs("net.kyori:adventure-text-serializer-gson") + apiAndDocs("net.kyori:adventure-text-serializer-legacy") + apiAndDocs("net.kyori:adventure-text-serializer-plain") + apiAndDocs("net.kyori:adventure-text-logger-slf4j") + + api("org.apache.maven:maven-resolver-provider:3.9.6") // make API dependency for Paper Plugins + implementation("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") + implementation("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") + + // Annotations - Slowly migrate to jspecify + val annotations = "org.jetbrains:annotations:$annotationsVersion" + compileOnly(annotations) + testCompileOnly(annotations) + javadocSourcepath(annotations) // For adventure-api module requirements + + val checkerQual = "org.checkerframework:checker-qual:3.49.2" + compileOnlyApi(checkerQual) + testCompileOnly(checkerQual) + + apiAndDocs("org.jspecify:jspecify:1.0.0") + + // Test dependencies + testImplementation("org.apache.commons:commons-lang3:3.20.0") + testImplementation("org.junit.jupiter:junit-jupiter:6.0.3") + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.mockito:mockito-core:5.22.0") + testImplementation("org.ow2.asm:asm-tree:9.9.1") + mockitoAgent("org.mockito:mockito-core:5.22.0") { isTransitive = false } // configure mockito agent that is needed in newer java versions + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3") + + // checkstyle + // checkstyle(project(":paper-checkstyle")) +} + +val generatedDir: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() +idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) + } +} +sourceSets { + main { + java { + srcDir(generatedDir) + srcDir(file("../paper-api/src/main/java")) + } + resources { + srcDir(file("../paper-api/src/main/resources")) + } + } + test { + java { + srcDir(file("../paper-api/src/test/java")) + } + resources { + srcDir(file("../paper-api/src/test/resources")) + } + } +} + +val outgoingVariants = arrayOf("runtimeElements", "apiElements", "sourcesElements", "javadocElements") +val mainCapability = "${project.group}:${project.name}:${project.version}" +configurations { + val outgoing = outgoingVariants.map { named(it) } + for (config in outgoing) { + config { + attributes { + attribute(io.papermc.paperweight.util.mainCapabilityAttribute, mainCapability) + } + outgoing { + capability(mainCapability) + // Paper-MojangAPI has been merged into Paper-API + capability("io.papermc.paper:paper-mojangapi:${project.version}") + capability("com.destroystokyo.paper:paper-mojangapi:${project.version}") + // Conflict with old coordinates + capability("com.destroystokyo.paper:paper-api:${project.version}") + capability("org.spigotmc:spigot-api:${project.version}") + capability("org.bukkit:bukkit:${project.version}") + } + } + } +} + +configure { + publications.create("maven") { + // For Brigadier API + outgoingVariants.forEach { + suppressPomMetadataWarningsFor(it) + } + from(components["java"]) + } +} + +abstract class GenerateApiVersioningFile : DefaultTask() { + @get:OutputFile + abstract val outputFile: RegularFileProperty + + @get:Input + abstract val projectVersion: Property + + @get:Input + abstract val apiVersion: Property + + @TaskAction + fun generate() { + val file = outputFile.get().asFile + file.parentFile.mkdirs() + val map = mapOf( + "version" to projectVersion.get(), + "currentApiVersion" to apiVersion.get() + ) + file.writeText(Gson().toJson(map)) + } +} + +val generateApiVersioningFile = tasks.register("generateApiVersioningFile") { + outputFile.set(layout.buildDirectory.file("apiVersioning.json")) + projectVersion.set(project.version.toString()) + apiVersion.set(rootProject.providers.gradleProperty("apiVersion")) +} + +tasks.jar { + from(generateApiVersioningFile.flatMap { it.outputFile }) + manifest { + attributes( + "Automatic-Module-Name" to "org.bukkit" + ) + } +} + +abstract class Services { + @get:Inject + abstract val fileSystemOperations: FileSystemOperations +} +val services = objects.newInstance() + +tasks.withType().configureEach { + val options = options as StandardJavadocDocletOptions + options.overview = "src/main/javadoc/overview.html" + options.use() + options.isDocFilesSubDirs = true + options.links( + "https://guava.dev/releases/33.6.0-jre/api/docs/", + "https://www.javadocs.dev/org.yaml/snakeyaml/2.2/", + "https://www.javadocs.dev/org.jetbrains/annotations/$annotationsVersion/", + "https://www.javadocs.dev/org.joml/joml/1.10.8/", + "https://www.javadocs.dev/com.google.code.gson/gson/2.14.0", + "https://jspecify.dev/docs/api/", + "https://jd.papermc.io/adventure/$adventureVersion/", + "https://www.javadocs.dev/org.slf4j/slf4j-api/$slf4jVersion/", + "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", + "https://www.javadocs.dev/org.apache.maven.resolver/maven-resolver-api/1.7.3", + ) + options.tags(projectCustomJavadocTags.map { it.toOptionString() }) + + inputs.files(javadocSourcepath).ignoreEmptyDirectories().withPropertyName(javadocSourcepath.name + "-configuration") + val javadocSourcepathElements = javadocSourcepath.elements + doFirst { + options.addStringOption( + "sourcepath", + javadocSourcepathElements.get().map { it.asFile }.joinToString(separator = File.pathSeparator, transform = File::getPath) + ) + } + + // workaround for https://github.com/gradle/gradle/issues/4046 + inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset") + val fsOps = services.fileSystemOperations + doLast { + fsOps.copy { + from("src/main/javadoc") { + include("**/doc-files/**") + } + into("build/docs/javadoc") + } + } +} + +tasks.test { + useJUnitPlatform() + + // configure mockito agent that is needed in newer java versions + val provider = objects.newInstance() + provider.fileCollection.from(mockitoAgent) + jvmArgumentProviders.add(provider) +} + +// Compile tests with -parameters for better junit parameterized test names +tasks.compileTestJava { + options.compilerArgs.add("-parameters") +} + +val scanJarForBadCalls by tasks.registering(io.papermc.paperweight.tasks.ScanJarForBadCalls::class) { + badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;") + jarToScan.set(tasks.jar.flatMap { it.archiveFile }) + classpath.from(configurations.compileClasspath) +} +tasks.check { + dependsOn(scanJarForBadCalls) +} diff --git a/infstar-api/build.gradle.kts.patch b/infstar-api/build.gradle.kts.patch new file mode 100644 index 0000000..f8901b9 --- /dev/null +++ b/infstar-api/build.gradle.kts.patch @@ -0,0 +1,59 @@ +--- a/paper-api/build.gradle.kts ++++ b/paper-api/build.gradle.kts +@@ -4,8 +_,10 @@ + plugins { + `java-library` + `maven-publish` ++ // Infstar:API 模块基于 Paper API 源码生成。 + idea +- id("io.papermc.paperweight.paper-checkstyle") ++ // Paper Checkstyle 依赖上游复合构建中的 paper-checkstyle 模块,衍生核心暂不启用。 ++ // id("io.papermc.paperweight.paper-checkstyle") + } + + java { +@@ -17,10 +_,12 @@ + JavadocTag("apiNote", "a", "API Note:"), + ) + ++/* + paperCheckstyle { + customJavadocTags = projectCustomJavadocTags + directoriesToSkipFile = layout.projectDirectory.file(".checkstyle/ignored_directories.txt") + } ++*/ + + val annotationsVersion = "26.0.2" + val adventureVersion = "5.2.0" +@@ -105,10 +_,10 @@ + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3") + + // checkstyle +- checkstyle(project(":paper-checkstyle")) ++ // checkstyle(project(":paper-checkstyle")) + } + +-val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() ++val generatedDir: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() + idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) +@@ -118,6 +_,18 @@ + main { + java { + srcDir(generatedDir) ++ srcDir(file("../paper-api/src/main/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/main/resources")) ++ } ++ } ++ test { ++ java { ++ srcDir(file("../paper-api/src/test/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/test/resources")) + } + } + } diff --git a/infstar-api/paper-patches/.gitkeep b/infstar-api/paper-patches/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/infstar-api/paper-patches/features/0001-Add-Infstar-brand-identity.patch b/infstar-api/paper-patches/features/0001-Add-Infstar-brand-identity.patch new file mode 100644 index 0000000..88a49c1 --- /dev/null +++ b/infstar-api/paper-patches/features/0001-Add-Infstar-brand-identity.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Coldsmiles +Date: Sun, 6 Sep 2026 11:33:34 +0800 +Subject: [PATCH] Add Infstar brand identity + + +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java +index 4e099b584c3435ba1ebce492011d35ae1ac0cdf8..9e4fea24e65b43c0b7698adbb2d2e17f76336628 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfo.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java +@@ -12,10 +12,12 @@ import org.jetbrains.annotations.ApiStatus; + */ + @ApiStatus.NonExtendable + public interface ServerBuildInfo { +- /** +- * The brand id for Paper. +- */ +- Key BRAND_PAPER_ID = Key.key("papermc", "paper"); ++ /** Infstar 的稳定品牌标识。 */ ++ Key BRAND_INFSTAR_ID = Key.key("infstarmc", "infstar"); ++ ++ /** @deprecated 请使用 {@link #BRAND_INFSTAR_ID}。 */ ++ @Deprecated(forRemoval = false) ++ Key BRAND_PAPER_ID = BRAND_INFSTAR_ID; + + /** + * Gets the {@code ServerBuildInfo}. diff --git a/infstar-server/build.gradle.kts b/infstar-server/build.gradle.kts new file mode 100644 index 0000000..3b93e7c --- /dev/null +++ b/infstar-server/build.gradle.kts @@ -0,0 +1,370 @@ +import io.papermc.fill.model.BuildChannel +import io.papermc.paperweight.attribute.DevBundleOutput +import io.papermc.paperweight.util.* +import java.time.Instant + +plugins { + `java-library` + `maven-publish` + idea + id("io.papermc.paperweight.core") + id("io.papermc.fill.gradle") version "1.0.12" +} + +val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" + +dependencies { + mache("io.papermc:mache:26.2+build.1") + paperclip("io.papermc:paperclip:3.0.4") +} + +paperweight { + minecraftVersion = providers.gradleProperty("mcVersion") + gitFilePatches = false + + // Infstar:在 Paper 的服务端源码上应用自有补丁。 + val infstar = forks.register("infstar") { + upstream.patchDir("paperServer") { + upstreamPath = "paper-server" + excludes = setOf("src/minecraft", "patches", "build.gradle.kts") + patchesDir = rootDirectory.dir("infstar-server/paper-patches") + outputDir = rootDirectory.dir("paper-server") + } + } + activeFork = infstar + + updatingMinecraft { + // oldPaperCommit = "d4fe85375af18bfa88f44d7c1e6a61904ae550cc" + } +} + +tasks.generateDevelopmentBundle { + libraryRepositories.addAll( + "https://repo.maven.apache.org/maven2/", + paperMavenPublicUrl, + ) +} + +abstract class Services { + @get:Inject + abstract val archiveOperations: ArchiveOperations +} +val services = objects.newInstance() + +if (project.providers.gradleProperty("publishDevBundle").isPresent) { + val devBundleComponent = publishing.softwareComponentFactory.adhoc("devBundle") + components.add(devBundleComponent) + + val devBundle = configurations.consumable("devBundle") { + attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.ZIP)) + outgoing.artifact(tasks.generateDevelopmentBundle.flatMap { it.devBundleFile }) + } + devBundleComponent.addVariantsFromConfiguration(devBundle) {} + + val runtime = configurations.consumable("serverRuntimeClasspath") { + attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES)) + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + extendsFrom(configurations.runtimeClasspath.get()) + } + devBundleComponent.addVariantsFromConfiguration(runtime) { + mapToMavenScope("runtime") + } + + val compile = configurations.consumable("serverCompileClasspath") { + attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES)) + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API)) + extendsFrom(configurations.compileClasspath.get()) + } + devBundleComponent.addVariantsFromConfiguration(compile) { + mapToMavenScope("compile") + } + + tasks.withType(GenerateMavenPom::class).configureEach { + doLast { + val text = destination.readText() + // Remove dependencies from pom, dev bundle is designed for gradle module metadata consumers + destination.writeText( + text.substringBefore("") + text.substringAfter("") + ) + } + } + + publishing { + publications.create("devBundle") { + artifactId = "dev-bundle" + from(devBundleComponent) + } + } +} + +// Infstar:编译生成后的 Paper 源码,而不是空的模块目录。 +sourceSets { + main { + java { + srcDir("../paper-server/src/main/java") + srcDir("../paper-server/src/generated/java") + } + resources { srcDir("../paper-server/src/main/resources") } + } + test { + java { srcDir("../paper-server/src/test/java") } + resources { srcDir("../paper-server/src/test/resources") } + } +} + +val log4jPlugins = sourceSets.create("log4jPlugins") { + java { srcDir("../paper-server/src/log4jPlugins/java") } +} +configurations.named(log4jPlugins.compileClasspathConfigurationName) { + extendsFrom(configurations.compileClasspath.get()) +} +val alsoShade: Configuration by configurations.creating + +val runtimeConfiguration by configurations.consumable("runtimeConfiguration") { + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + extendsFrom(configurations.getByName(sourceSets.main.get().runtimeElementsConfigurationName)) +} + +// Configure mockito agent that is needed in newer java versions +val mockitoAgent = configurations.register("mockitoAgent") +abstract class MockitoAgentProvider : CommandLineArgumentProvider { + @get:CompileClasspath + abstract val fileCollection: ConfigurableFileCollection + + override fun asArguments(): Iterable { + return listOf("-javaagent:" + fileCollection.files.single().absolutePath) + } +} + +dependencies { + implementation(project(":infstar-api")) + implementation("ca.spottedleaf:leafpile:1.2.0") + implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ + implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 + implementation("net.minecrell:terminalconsoleappender:1.3.0") + implementation("net.kyori:adventure-text-serializer-ansi") + + /* + Required to add the missing Log4j2Plugins.dat file from log4j-core + which has been removed by Mojang. Without it, log4j has to classload + all its classes to check if they are plugins. + Scanning takes about 1-2 seconds so adding this speeds up the server start. + */ + implementation("org.apache.logging.log4j:log4j-core:2.26.0") + log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.26.0") // Needed to generate meta for our Log4j plugins + runtimeOnly(log4jPlugins.output) + alsoShade(log4jPlugins.output) + + implementation("com.velocitypowered:velocity-native:4.1.0") { + isTransitive = false + } + implementation("io.netty:netty-codec-haproxy:4.2.15.Final") // Add support for proxy protocol + implementation("org.apache.logging.log4j:log4j-iostreams:2.26.0") + implementation("org.ow2.asm:asm-commons:9.9.1") + implementation("org.spongepowered:configurate-yaml:4.2.0") + + // Deps that were previously in the API but have now been moved here for backwards compat, eventually to be removed + runtimeOnly("commons-lang:commons-lang:2.6") + runtimeOnly("org.xerial:sqlite-jdbc:3.49.1.0") + runtimeOnly("com.mysql:mysql-connector-j:9.2.0") + runtimeOnly("com.lmax:disruptor:3.4.4") + implementation("com.googlecode.json-simple:json-simple:1.1.1") { // change to runtimeOnly once Timings is removed + isTransitive = false // includes junit + } + + testImplementation("io.github.classgraph:classgraph:4.8.184") // For mob goal test + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3") + testImplementation("org.junit.jupiter:junit-jupiter:6.0.3") + testImplementation("org.junit.platform:junit-platform-suite-engine:6.0.3") + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.mockito:mockito-core:5.22.0") + mockitoAgent("org.mockito:mockito-core:5.22.0") { isTransitive = false } // Configure mockito agent that is needed in newer java versions + testImplementation("org.ow2.asm:asm-tree:9.9.1") + testImplementation("org.junit-pioneer:junit-pioneer:2.3.0") // CartesianTest + + // Spark + implementation("me.lucko:spark-api:0.1-20240720.200737-2") + implementation("me.lucko:spark-paper:1.10.180") +} + +tasks.jar { + manifest { + val git = Git(rootProject.layout.projectDirectory.path) + val mcVersion = rootProject.providers.gradleProperty("mcVersion").get() + val buildTime = providers.environmentVariable("BUILD_STARTED_AT").map(Instant::parse).orElse(Instant.EPOCH).get() + val buildVersion = project.version.toString() + val gitHash = buildVersion.substringAfterLast('-') + val gitBranch = runCatching { git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() }.getOrDefault("unknown") + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", + "Implementation-Title" to "Infstar", + "Implementation-Version" to buildVersion, + "Implementation-Vendor" to "Infstar", + "Specification-Title" to "Infstar", + "Specification-Version" to project.version, + "Specification-Vendor" to "Infstar", + "Brand-Id" to "infstarmc:infstar", + "Brand-Name" to "Infstar", + "Build-Number" to "", + "Build-Time" to buildTime.toString(), + "Git-Branch" to gitBranch, + "Git-Commit" to gitHash, + ) + for (tld in setOf("net", "com", "org")) { + attributes("$tld/bukkit", "Sealed" to true) + } + } +} + +// Compile tests with -parameters for better junit parameterized test names +tasks.compileTestJava { + options.compilerArgs.add("-parameters") +} + +tasks.named(log4jPlugins.compileJavaTaskName) { + options.compilerArgs.addAll( + listOf( + "-Alog4j.graalvm.groupId=${project.group}", + "-Alog4j.graalvm.artifactId=${project.name}" + ) + ) +} + +// Bump compile tasks to 1GB memory to avoid OOMs +tasks.withType().configureEach { + options.forkOptions.memoryMaximumSize = "1G" +} + +val scanJarForBadCalls by tasks.registering(io.papermc.paperweight.tasks.ScanJarForBadCalls::class) { + badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;") + jarToScan.set(tasks.jar.flatMap { it.archiveFile }) + classpath.from(configurations.compileClasspath) +} +tasks.check { + dependsOn(scanJarForBadCalls) +} + +// Use TCA for console improvements +tasks.jar { + val archiveOperations = services.archiveOperations + from(alsoShade.elements.map { + it.map { f -> + if (f.asFile.isFile) { + archiveOperations.zipTree(f.asFile) + } else { + f.asFile + } + } + }) +} + +tasks.test { + include("**/**TestSuite.class") + workingDir = temporaryDir + useJUnitPlatform { + forkEvery = 1 + excludeTags("Slow") + } + + // Configure mockito agent that is needed in newer java versions + val provider = objects.newInstance() + provider.fileCollection.from(mockitoAgent) + jvmArgumentProviders.add(provider) +} + +val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() +idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) + } +} +sourceSets { + main { + java { + srcDir(generatedDir) + } + } +} + +fun TaskContainer.registerRunTask( + name: String, + block: JavaExec.() -> Unit +): TaskProvider = register(name) { + group = "runs" + mainClass.set("org.bukkit.craftbukkit.Main") + standardInput = System.`in` + workingDir = rootProject.layout.projectDirectory + .dir(providers.gradleProperty("paper.runWorkDir").getOrElse("run")) + .asFile + javaLauncher.set(project.javaToolchains.launcherFor { + languageVersion.set(JavaLanguageVersion.of(25)) + // TODO - JB runtime 25 has issues with spark rn + // vendor.set(JvmVendorSpec.JETBRAINS) + }) + jvmArgs(/*"-XX:+AllowEnhancedClassRedefinition", */"--enable-native-access=ALL-UNNAMED") + + if (rootProject.childProjects["test-plugin"] != null) { + val testPluginJar = rootProject.project(":test-plugin").tasks.jar.flatMap { it.archiveFile } + inputs.file(testPluginJar) + args("-add-plugin=${testPluginJar.get().asFile.absolutePath}") + } + + args("--nogui") + systemProperty("net.kyori.adventure.text.warnWhenLegacyFormattingDetected", true) + if (providers.gradleProperty("paper.runDisableWatchdog").getOrElse("false") == "true") { + systemProperty("disable.watchdog", true) + } + systemProperty("io.papermc.paper.suppress.sout.nags", true) + systemProperty("paper.maxChatCommandInputSize", 32767) + systemProperty("paper.disableMigrationDelay", true) + systemProperty("paper.updatingMinecraft", providers.gradleProperty("updatingMinecraft").getOrElse("false").toBoolean()) + + val memoryGb = providers.gradleProperty("paper.runMemoryGb").getOrElse("2") + minHeapSize = "${memoryGb}G" + maxHeapSize = "${memoryGb}G" + + doFirst { + workingDir.mkdirs() + } + + block(this) +} + +tasks.registerRunTask("runServer") { + description = "Spin up a test server from the Mojang mapped server jar" + classpath(tasks.jar) + classpath(configurations.runtimeClasspath) +} + +tasks.registerRunTask("runDevServer") { + description = "Spin up a test server without assembling a jar" + classpath(sourceSets.main.map { it.runtimeClasspath }) +} + +tasks.registerRunTask("runBundler") { + description = "Spin up a test server from the Mojang mapped bundler jar" + classpath(tasks.createBundlerJar.flatMap { it.outputZip }) + mainClass.set(null as String?) +} +tasks.registerRunTask("runPaperclip") { + description = "Spin up a test server from the Mojang mapped Paperclip jar" + classpath(tasks.createPaperclipJar.flatMap { it.outputZip }) + mainClass.set(null as String?) +} + +fill { + project("paper") + versionFamily(paperweight.minecraftVersion.map { it.split(".", "-").takeWhile { part -> part.toIntOrNull() != null }.take(2).joinToString(".") }) + version(paperweight.minecraftVersion) + + build { + channel = providers.gradleProperty("channel").map { BuildChannel.valueOf(it.uppercase()) } + + downloads { + register("server:default") { + file = tasks.createPaperclipJar.flatMap { it.outputZip } + nameResolver.set { project, _, version, build -> "$project-$version-$build.jar" } + } + } + } +} diff --git a/infstar-server/build.gradle.kts.patch b/infstar-server/build.gradle.kts.patch new file mode 100644 index 0000000..e48344a --- /dev/null +++ b/infstar-server/build.gradle.kts.patch @@ -0,0 +1,90 @@ +--- a/paper-server/build.gradle.kts ++++ b/paper-server/build.gradle.kts +@@ -22,6 +_,17 @@ + minecraftVersion = providers.gradleProperty("mcVersion") + gitFilePatches = false + ++ // Infstar:在 Paper 的服务端源码上应用自有补丁。 ++ val infstar = forks.register("infstar") { ++ upstream.patchDir("paperServer") { ++ upstreamPath = "paper-server" ++ excludes = setOf("src/minecraft", "patches", "build.gradle.kts") ++ patchesDir = rootDirectory.dir("infstar-server/paper-patches") ++ outputDir = rootDirectory.dir("paper-server") ++ } ++ } ++ activeFork = infstar ++ + updatingMinecraft { + // oldPaperCommit = "d4fe85375af18bfa88f44d7c1e6a61904ae550cc" + } +@@ -86,7 +_,24 @@ + } + } + +-val log4jPlugins = sourceSets.create("log4jPlugins") ++// Infstar:编译生成后的 Paper 源码,而不是空的模块目录。 ++sourceSets { ++ main { ++ java { ++ srcDir("../paper-server/src/main/java") ++ srcDir("../paper-server/src/generated/java") ++ } ++ resources { srcDir("../paper-server/src/main/resources") } ++ } ++ test { ++ java { srcDir("../paper-server/src/test/java") } ++ resources { srcDir("../paper-server/src/test/resources") } ++ } ++} ++ ++val log4jPlugins = sourceSets.create("log4jPlugins") { ++ java { srcDir("../paper-server/src/log4jPlugins/java") } ++} + configurations.named(log4jPlugins.compileClasspathConfigurationName) { + extendsFrom(configurations.compileClasspath.get()) + } +@@ -109,7 +_,7 @@ + } + + dependencies { +- implementation(project(":paper-api")) ++ implementation(project(":infstar-api")) + implementation("ca.spottedleaf:leafpile:1.2.0") + implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ + implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 +@@ -163,23 +_,21 @@ + manifest { + val git = Git(rootProject.layout.projectDirectory.path) + val mcVersion = rootProject.providers.gradleProperty("mcVersion").get() +- val build = System.getenv("BUILD_NUMBER") ?: null + val buildTime = providers.environmentVariable("BUILD_STARTED_AT").map(Instant::parse).orElse(Instant.EPOCH).get() +- val gitHash = git.exec(providers, "rev-parse", "--short=7", "HEAD").get().trim() +- val implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash" +- val date = git.exec(providers, "show", "-s", "--format=%ci", gitHash).get().trim() +- val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() ++ val buildVersion = project.version.toString() ++ val gitHash = buildVersion.substringAfterLast('-') ++ val gitBranch = runCatching { git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() }.getOrDefault("unknown") + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", +- "Implementation-Title" to "Paper", +- "Implementation-Version" to implementationVersion, +- "Implementation-Vendor" to date, +- "Specification-Title" to "Paper", ++ "Implementation-Title" to "Infstar", ++ "Implementation-Version" to buildVersion, ++ "Implementation-Vendor" to "Infstar", ++ "Specification-Title" to "Infstar", + "Specification-Version" to project.version, +- "Specification-Vendor" to "Paper Team", +- "Brand-Id" to "papermc:paper", +- "Brand-Name" to "Paper", +- "Build-Number" to (build ?: ""), ++ "Specification-Vendor" to "Infstar", ++ "Brand-Id" to "infstarmc:infstar", ++ "Brand-Name" to "Infstar", ++ "Build-Number" to "", + "Build-Time" to buildTime.toString(), + "Git-Branch" to gitBranch, + "Git-Commit" to gitHash, diff --git a/infstar-server/paper-patches/.gitkeep b/infstar-server/paper-patches/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/infstar-server/paper-patches/features/0001-Brand-server-runtime-as-Infstar.patch b/infstar-server/paper-patches/features/0001-Brand-server-runtime-as-Infstar.patch new file mode 100644 index 0000000..5e04e1b --- /dev/null +++ b/infstar-server/paper-patches/features/0001-Brand-server-runtime-as-Infstar.patch @@ -0,0 +1,34 @@ +From e254ce543d684930c74959c5064f1498f17accce Mon Sep 17 00:00:00 2001 +From: Coldsmiles +Date: Sun, 6 Sep 2026 12:05:28 +0800 +Subject: [PATCH] Brand server runtime as Infstar + + +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +index 74ffdc823c06d33bd7c15fcce770b60bbad29c04..70f908474dfcf371a63c1cb8c4813db0e9580f20 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +@@ -30,9 +30,9 @@ public record ServerBuildInfoImpl( + private static final String ATTRIBUTE_GIT_BRANCH = "Git-Branch"; + private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit"; + +- private static final String BRAND_PAPER_NAME = "Paper"; ++ private static final String BRAND_INFSTAR_NAME = "Infstar"; + +- private static final String BUILD_DEV = "DEV"; ++ private static final String BUILD_DEV = "dev"; + + public ServerBuildInfoImpl() { + this(JarManifests.manifest(CraftServer.class)); +@@ -42,9 +42,9 @@ public record ServerBuildInfoImpl( + this( + getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) + .map(Key::key) +- .orElse(BRAND_PAPER_ID), ++ .orElse(BRAND_INFSTAR_ID), + getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) +- .orElse(BRAND_PAPER_NAME), ++ .orElse(BRAND_INFSTAR_NAME), + SharedConstants.getCurrentVersion().id(), + SharedConstants.getCurrentVersion().name(), + getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) diff --git a/scripts/prepare-test-server.ps1 b/scripts/prepare-test-server.ps1 new file mode 100644 index 0000000..26a9013 --- /dev/null +++ b/scripts/prepare-test-server.ps1 @@ -0,0 +1,38 @@ +param( + [Parameter(Mandatory = $true)] + [ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })] + [string] $JarPath, + [int] $Port = 25565 +) + +$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$runDirectory = Join-Path $projectRoot "run" +$targetJar = Join-Path $runDirectory "infstar.jar" + +New-Item -ItemType Directory -Path $runDirectory -Force | Out-Null +Copy-Item -LiteralPath (Resolve-Path $JarPath).Path -Destination $targetJar -Force + +@" +# Infstar 本地测试服务器配置 +server-port=$Port +motd=Infstar Local Test Server +online-mode=false +enable-command-block=true +spawn-protection=0 +view-distance=8 +simulation-distance=6 +enable-rcon=false +"@ | Set-Content -LiteralPath (Join-Path $runDirectory "server.properties") -Encoding utf8 + +"eula=true" | Set-Content -LiteralPath (Join-Path $runDirectory "eula.txt") -Encoding ascii + +@" +这是 Infstar 的本地测试服务器目录,不应提交到 Git 仓库。 + +启动方式: + ..\scripts\start-test-server.ps1 + +该目录包含世界、日志、配置和构建产物,已通过 .gitignore 排除。 +"@ | Set-Content -LiteralPath (Join-Path $runDirectory "README.txt") -Encoding utf8 + +Write-Output "测试服务器已准备完成:$runDirectory" diff --git a/scripts/prepare-test-server.sh b/scripts/prepare-test-server.sh new file mode 100644 index 0000000..cb9861b --- /dev/null +++ b/scripts/prepare-test-server.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 || $# -gt 2 ]]; then + echo "用法:$0 <服务端 JAR 路径> [端口]" >&2 + exit 2 +fi + +project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +run_directory="$project_root/run" +jar_path="$(realpath "$1")" +port="${2:-25565}" + +mkdir -p "$run_directory" +cp -f "$jar_path" "$run_directory/infstar.jar" + +cat > "$run_directory/server.properties" < "$run_directory/eula.txt" +echo "测试服务器已准备完成:$run_directory" diff --git a/scripts/start-test-server.ps1 b/scripts/start-test-server.ps1 new file mode 100644 index 0000000..54ce82b --- /dev/null +++ b/scripts/start-test-server.ps1 @@ -0,0 +1,28 @@ +param( + [int] $MemoryMinGb = 2, + [int] $MemoryMaxGb = 4 +) + +$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$runDirectory = Join-Path $projectRoot "run" +$serverJar = Join-Path $runDirectory "infstar.jar" + +if (-not (Test-Path -LiteralPath $serverJar -PathType Leaf)) { + throw "未找到 $serverJar。请先运行 prepare-test-server.ps1。" +} + +$java = Get-Command java -ErrorAction SilentlyContinue +if ($null -eq $java) { + throw "未找到 Java。Infstar 26.2 需要 Java 25。" +} + +Push-Location $runDirectory +try { + & $java.Source "-Xms${MemoryMinGb}G" "-Xmx${MemoryMaxGb}G" "-jar" $serverJar "--nogui" + if ($LASTEXITCODE -ne 0) { + throw "测试服务器退出,代码:$LASTEXITCODE" + } +} +finally { + Pop-Location +} diff --git a/scripts/start-test-server.sh b/scripts/start-test-server.sh new file mode 100644 index 0000000..33f05b7 --- /dev/null +++ b/scripts/start-test-server.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +run_directory="$project_root/run" +server_jar="$run_directory/infstar.jar" +java_command="${JAVA_HOME:+$JAVA_HOME/bin/}java" +memory_min_gb="${INFSTAR_MEMORY_MIN_GB:-2}" +memory_max_gb="${INFSTAR_MEMORY_MAX_GB:-4}" + +if [[ ! -f "$server_jar" ]]; then + echo "未找到 $server_jar,请先运行 prepare-test-server.sh。" >&2 + exit 1 +fi + +if ! command -v "$java_command" >/dev/null 2>&1; then + echo "未找到 Java。Infstar 26.2 需要 Java 25。" >&2 + exit 1 +fi + +cd "$run_directory" +exec "$java_command" \ + "-Xms${memory_min_gb}G" \ + "-Xmx${memory_max_gb}G" \ + -jar "$server_jar" \ + --nogui diff --git a/scripts/update-paper.ps1 b/scripts/update-paper.ps1 new file mode 100644 index 0000000..860e442 --- /dev/null +++ b/scripts/update-paper.ps1 @@ -0,0 +1,15 @@ +param( + [Parameter(Mandatory = $true)] + [ValidatePattern('^[0-9a-fA-F]{40}$')] + [string] $PaperCommit +) + +$propertiesPath = Join-Path $PSScriptRoot "..\gradle.properties" +$properties = Get-Content -LiteralPath $propertiesPath -Raw +$updated = [regex]::Replace($properties, '(?m)^paperCommit=.*$', "paperCommit=$PaperCommit") +if ($updated -eq $properties) { + throw "paperCommit was not found in $propertiesPath" +} +Set-Content -LiteralPath $propertiesPath -Value $updated -NoNewline + +Write-Output "已将 Paper 固定到 $PaperCommit。合并前请运行 applyAllPatches 和兼容性测试。" diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..a8fa688 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,33 @@ +import java.util.Locale + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") + } +} + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +if (!file(".git").exists()) { + error("Infstar 必须从 Git 仓库检出;Paperweight 需要 Git 元数据来应用补丁。") +} + +rootProject.name = "infstar" + +for (name in listOf("infstar-api", "infstar-server")) { + include(name.lowercase(Locale.ENGLISH)) +} + +gradle.lifecycle.beforeProject { + val minecraftVersion = providers.gradleProperty("mcVersion").get().trim() + val gitHash = providers.exec { + commandLine("git", "rev-parse", "--short=7", "HEAD") + isIgnoreExitValue = true + }.standardOutput.asText.map { output -> + output.trim().takeIf { it.matches(Regex("[0-9a-fA-F]{7,40}")) } ?: "unknown" + }.get() + version = "$minecraftVersion-dev-$gitHash" +}