Merge staging/1.16.5 (#371)

* add config for sand duping (#352)

* Lithium: cache chunk gen sea level (#349)

based off: a55cfd1c91

* PaperPR: Inline shift fields in EnumDirection (#350)

* Introducing: Yatoclip (#360)

* New async nbt cache (#347)

* update pom

* whoops

* Try to address path issue and improve Jenkins build speed

* Detailed lag and crash reports (#369)

Added "Suspected Plugins" to Watchdog and crash reports

* Drop sand duping

* Add branch specific versions

* Remove copyright

* Revert mysql-connector-java version bump

* Small fixes

* More detailed lag and crash reports

* Don't use branch information when generating metadata

* Fix Jenkins Builds version command

* Fixup patches

* Fix patch notes

* Pull Request compatibility for branch detection

* Fix Pull Request compatibility for branch detection

* Set context classloader before launch

* Inject server jar to SystemClassLoader before launch

* Try fix compile in java8

* Run tests on CodeMC and Github Actions

Co-authored-by: Simon Gardling <Titaniumtown@gmail.com>
Co-authored-by: Zoe <duplexsys@protonmail.com>
Co-authored-by: Hugo Planque <12386279+HookWoods@users.noreply.github.com>
This commit is contained in:
ishland 2021-02-01 18:44:34 +08:00 committed by GitHub
parent 2e12bc7147
commit c2fd399caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2206 additions and 130 deletions

View File

@ -29,11 +29,6 @@ jobs:
fail-fast: false
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.4.1
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v1
@ -80,12 +75,12 @@ jobs:
- name: Build Yatopia
run: |
./gradlew paperclip
./gradlew clean build yatoclip
- name: Upload Artifact
if: github.ref != 'refs/heads/ver/1.16.4'
uses: actions/upload-artifact@v2
with:
name: Yatopia-${{ matrix.java }}
path: yatopia-1.16.5-paperclip.jar
path: yatopia-1.16.5-yatoclip.jar

165
Jenkinsfile vendored
View File

@ -1,91 +1,74 @@
pipeline {
agent { label 'slave' }
options { timestamps() }
stages {
stage('Cleanup') {
steps {
scmSkip(deleteBuild: true, skipPattern:'.*\\[CI-SKIP\\].*')
sh 'rm -rf ./target'
sh 'rm -rf ./Paper/Paper-API ./Paper/Paper-Server ./Paper/work/Spigot/Spigot-API ./Paper/work/Spigot/Spigot-Server'
sh 'rm -rf ./Yatopia-API ./Yatopia-Server'
sh 'chmod +x ./gradlew'
}
}
stage('Init project & submodules') {
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT',
) {
sh './gradlew initGitSubmodules'
}
}
}
stage('Decompile & apply patches') {
tools {
jdk "OpenJDK 8"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT',
) {
sh '''
./gradlew setupUpstream
./gradlew applyPatches
'''
}
}
}
stage('Build') {
tools {
jdk "OpenJDK 8"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT'
) {
withCredentials([usernamePassword(credentialsId: 'jenkins-deploy', usernameVariable: 'ORG_GRADLE_PROJECT_mavenUsername', passwordVariable: 'ORG_GRADLE_PROJECT_mavenPassword')]) {
sh '''
./gradlew build
./gradlew publish
'''
}
}
}
}
stage('Build Launcher') {
tools {
jdk "OpenJDK 8"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT'
) {
sh '''
mkdir -p "./target"
./gradlew paperclip
basedir=$(pwd)
paperworkdir="$basedir/Paper/work"
mcver=$(cat "$paperworkdir/BuildData/info.json" | grep minecraftVersion | cut -d '"' -f 4)
cp "yatopia-$mcver-paperclip.jar" "./target/yatopia-$mcver-paperclip-b$BUILD_NUMBER.jar"
'''
}
}
post {
success {
archiveArtifacts "target/*.jar"
}
failure {
cleanWs()
}
}
}
}
}
pipeline {
agent { label 'slave' }
options { timestamps() }
stages {
stage('Cleanup') {
steps {
scmSkip(deleteBuild: true, skipPattern:'.*\\[CI-SKIP\\].*')
sh 'rm -rf ./target'
sh 'rm -rf ./Paper/Paper-API ./Paper/Paper-Server ./Paper/work/Spigot/Spigot-API ./Paper/work/Spigot/Spigot-Server'
sh 'rm -rf ./Yatopia-API ./Yatopia-Server'
sh 'chmod +x ./gradlew'
}
}
stage('Init project & submodules') {
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT',
) {
sh './gradlew initGitSubmodules'
}
}
}
stage('Decompile & apply patches') {
tools {
jdk "OpenJDK 8"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT',
) {
sh '''
./gradlew setupUpstream
./gradlew applyPatches
'''
}
}
}
stage('Build') {
tools {
jdk "OpenJDK 8"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT'
) {
withCredentials([usernamePassword(credentialsId: 'jenkins-deploy', usernameVariable: 'ORG_GRADLE_PROJECT_mavenUsername', passwordVariable: 'ORG_GRADLE_PROJECT_mavenPassword')]) {
sh '''
./gradlew clean build yatoclip publish
mkdir -p "./target"
basedir=$(pwd)
paperworkdir="$basedir/Paper/work"
mcver=$(cat "$paperworkdir/BuildData/info.json" | grep minecraftVersion | cut -d '"' -f 4)
cp "yatopia-$mcver-yatoclip.jar" "./target/yatopia-$mcver-yatoclip-b$BUILD_NUMBER.jar"
'''
}
}
}
post {
success {
archiveArtifacts "target/*.jar"
}
failure {
cleanWs()
}
}
}
}
}

View File

@ -8,6 +8,7 @@ This is an overview over all patches that are currently used.
| Side | Patch | Author | CoAuthors |
| ----- | ------------- |:-------------:| -----:|
| server | (PaperPR) Inline shift direction fields | Andrew Steinborn | |
| server | AFK API | William Blake Galbreath | |
| api | AFK API | William Blake Galbreath | |
| server | Ability to re-add farmland mechanics from Alpha | Yive | |
@ -240,6 +241,7 @@ This is an overview over all patches that are currently used.
| server | Multi-Threaded Server Ticking Vanilla | Spottedleaf | |
| server | Multi-Threaded ticking CraftBukkit | Spottedleaf | |
| server | Name craft scheduler threads according to the plugin using | Spottedleaf | |
| server | New nbt cache | Hugo Planque | ishland |
| server | Nuke streams off BlockPosition | Ivan Pekov | |
| server | Nuke streams off SectionPosition | Ivan Pekov | |
| server | Optimise EntityInsentient#checkDespawn | Spottedleaf | |
@ -339,6 +341,8 @@ This is an overview over all patches that are currently used.
| server | Stop wasting resources on JsonList#get | Ivan Pekov | |
| server | Striders give saddle back | Ben Kerllenevich | |
| server | Strip raytracing for EntityLiving#hasLineOfSight | Paul Sauve | |
| server | Suspected plugins report | ishland | |
| api | Suspected plugins report | ishland | |
| server | Swap priority of checks in chunk ticking | Paul Sauve | |
| server | Time scoreboard search | Spottedleaf | |
| server | Timings stuff | William Blake Galbreath | |
@ -374,3 +378,4 @@ This is an overview over all patches that are currently used.
| server | lithium VoronoiBiomeAccessTypeMixin | JellySquid | |
| server | lithium enum_values | JellySquid | |
| server | lithium reduce allocations | JellySquid | Mykyta Komarnytskyy |
| server | lithium: cache chunk gen sea level | SuperCoder7979 | |

11
Yatoclip/build.gradle.kts Normal file
View File

@ -0,0 +1,11 @@
repositories {
mavenCentral()
maven("https://jitpack.io/")
}
dependencies {
implementation("com.github.ishlandbukkit:jbsdiff:deff66b794")
implementation("com.google.code.gson:gson:2.8.6")
implementation("commons-io:commons-io:2.8.0")
}

View File

@ -0,0 +1,28 @@
package org.yatopiamc.yatoclip;
import java.lang.instrument.Instrumentation;
import java.nio.file.Path;
import java.util.jar.JarFile;
public class YatoclipLaunch {
private static Instrumentation inst = null;
public static void premain(String args, Instrumentation inst) {
YatoclipLaunch.inst = inst;
}
public static void agentmain(final String agentArgs, final Instrumentation inst) {
YatoclipLaunch.inst = inst;
}
@SuppressWarnings("unused")
static void injectClasspath(Path setup) throws Throwable {
if(inst == null) {
throw new RuntimeException("Instrumentation API handle not found");
}
inst.appendToSystemClassLoaderSearch(new JarFile(setup.toFile()));
inst = null;
}
}

View File

@ -0,0 +1,51 @@
package org.yatopiamc.yatoclip;
import java.io.Serializable;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
public class PatchesMetadata {
public final Set<PatchMetadata> patches;
public final Set<Relocation> relocations;
public final Set<String> copyExcludes;
public PatchesMetadata(Set<PatchMetadata> patches, Set<Relocation> relocations, Set<String> copyExcludes) {
Objects.requireNonNull(copyExcludes);
this.copyExcludes = Collections.unmodifiableSet(copyExcludes);
Objects.requireNonNull(relocations);
this.relocations = Collections.unmodifiableSet(relocations);
Objects.requireNonNull(patches);
this.patches = Collections.unmodifiableSet(patches);
}
public static class PatchMetadata {
public final String name;
public final String originalHash;
public final String targetHash;
public final String patchHash;
public PatchMetadata(String name, String originalHash, String targetHash, String patchHash) {
this.name = name;
this.originalHash = originalHash;
this.targetHash = targetHash;
this.patchHash = patchHash;
}
}
public static class Relocation implements Serializable {
public final String from;
public final String to;
public final boolean includeSubPackages;
public Relocation(String from, String to, boolean includeSubPackages) {
Objects.requireNonNull(from);
Objects.requireNonNull(to);
this.from = from.replaceAll("\\.", "/");
this.to = to.replaceAll("\\.", "/");
this.includeSubPackages = includeSubPackages;
}
}
}

View File

@ -0,0 +1,360 @@
package org.yatopiamc.yatoclip;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
public class ServerSetup {
private static final String minecraftVersion;
private static final Path cacheDirectory;
private static final Gson gson = new Gson();
private static VersionInfo versionInfo = null;
private static BuildDataInfo buildDataInfo = null;
static {
Properties prop = new Properties();
try (InputStream inputStream = ServerSetup.class.getClassLoader().getResourceAsStream("yatoclip-launch.properties")) {
prop.load(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
minecraftVersion = prop.getProperty("minecraftVersion");
cacheDirectory = Paths.get("cache", minecraftVersion);
cacheDirectory.toFile().mkdirs();
}
public static Path setup() throws IOException {
long startTime = System.nanoTime();
checkBuildData();
applyMappingsAndPatches();
System.err.println(String.format("Yatoclip server setup completed in %.2fms", (System.nanoTime() - startTime) / 1_000_000.0));
return cacheDirectory.resolve("Minecraft").resolve(minecraftVersion + "-patched.jar");
}
private static void applyMappingsAndPatches() throws IOException {
final Path minecraftDir = cacheDirectory.resolve("Minecraft");
minecraftDir.toFile().mkdirs();
final Path vanillaJar = minecraftDir.resolve(minecraftVersion + ".jar");
if (!isValidZip(vanillaJar)) {
System.err.println("Downloading vanilla jar...");
download(new URL(buildDataInfo.serverUrl), vanillaJar);
if (!isValidZip(vanillaJar)) throw new RuntimeException("Invalid vanilla jar");
}
final Path classMappedJar = minecraftDir.resolve(minecraftVersion + "-cl.jar");
final Path memberMappedJar = minecraftDir.resolve(minecraftVersion + "-m.jar");
final Path patchedJar = minecraftDir.resolve(minecraftVersion + "-patched.jar");
if (!isValidZip(classMappedJar) || !isValidZip(memberMappedJar)) {
SpecialSourceLauncher.resetSpecialSourceClassloader();
final Path buildData = cacheDirectory.resolve("BuildData");
SpecialSourceLauncher.setSpecialSourceJar(buildData.resolve("bin").resolve("SpecialSource-2.jar").toFile());
System.err.println("Applying class mapping...");
SpecialSourceLauncher.runProcess(
"map", "--only", ".", "--only", "net/minecraft", "--auto-lvt", "BASIC", "--auto-member", "SYNTHETIC",
"-i", vanillaJar.toAbsolutePath().toString(),
"-m", buildData.resolve("mappings").resolve(buildDataInfo.classMappings).toAbsolutePath().toString(),
"-o", classMappedJar.toAbsolutePath().toString()
);
System.err.println("Applying member mapping...");
SpecialSourceLauncher.runProcess(
"map", "--only", ".", "--only", "net/minecraft", "--auto-member", "LOGGER", "--auto-member", "TOKENS",
"-i", classMappedJar.toAbsolutePath().toString(),
"-m", buildData.resolve("mappings").resolve(buildDataInfo.memberMappings).toAbsolutePath().toString(),
"-o", memberMappedJar.toAbsolutePath().toString()
);
SpecialSourceLauncher.resetSpecialSourceClassloader();
if (!isValidZip(classMappedJar) || !isValidZip(memberMappedJar))
throw new RuntimeException("Unable to apply mappings");
}
if (!YatoclipPatcher.isJarUpToDate(patchedJar)){
System.err.println("Applying patches...");
YatoclipPatcher.patchJar(memberMappedJar, patchedJar);
if(!YatoclipPatcher.isJarUpToDate(patchedJar))
throw new RuntimeException("Unable to apply patches");
}
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean isValidZip(Path zipPath) {
try {
ZipFile zipFile = new ZipFile(zipPath.toFile());
zipFile.close();
} catch (Throwable t) {
return false;
}
return true;
}
private static void checkBuildData() throws IOException {
final Path buildDataDir = cacheDirectory.resolve("BuildData");
buildDataDir.toFile().mkdirs();
final Path versionInfoFile = buildDataDir.resolve("version.json");
if (!tryParseVersionInfo(versionInfoFile)) {
System.err.println("Downloading version.json...");
final URL versionInfoURI = new URL("https://hub.spigotmc.org/versions/" + minecraftVersion + ".json");
download(versionInfoURI, versionInfoFile);
if (!tryParseVersionInfo(versionInfoFile)) throw new RuntimeException("Unable to parse versionInfo");
}
final Path buildDataArchive = buildDataDir.resolve("BuildData.zip");
if (!tryParseBuildData(buildDataArchive)) {
System.err.println("Downloading BuildData...");
final URL buildDataURL = new URL("https://hub.spigotmc.org/stash/rest/api/latest/projects/SPIGOT/repos/builddata/archive?at=" + ServerSetup.versionInfo.refs.buildData + "&format=zip");
download(buildDataURL, buildDataArchive);
if (!tryParseBuildData(buildDataArchive)) throw new RuntimeException("Unable to parse BuildData");
}
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean tryParseBuildData(Path buildData) {
try {
ZipFile zipFile = new ZipFile(buildData.toFile());
((Iterator<ZipEntry>) zipFile.entries()).forEachRemaining(zipEntry -> {
if (zipEntry.isDirectory()) return;
buildData.getParent().resolve(zipEntry.getName()).getParent().toFile().mkdirs();
try (
final ReadableByteChannel source = Channels.newChannel(zipFile.getInputStream(zipEntry));
final FileChannel fileChannel = FileChannel.open(buildData.getParent().resolve(zipEntry.getName()), CREATE, WRITE, TRUNCATE_EXISTING)
) {
fileChannel.transferFrom(source, 0, Long.MAX_VALUE);
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
zipFile.close();
try (Reader reader = Files.newBufferedReader(buildData.getParent().resolve("info.json"))){
ServerSetup.buildDataInfo = gson.fromJson(reader, BuildDataInfo.class);
}
} catch (Throwable t) {
return false;
}
return true;
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean tryParseVersionInfo(Path versionInfo) {
try (Reader reader = Files.newBufferedReader(versionInfo)) {
ServerSetup.versionInfo = gson.fromJson(reader, VersionInfo.class);
} catch (Throwable t) {
return false;
}
return true;
}
private static void download(URL url, Path downloadTo) throws IOException {
try (
final ReadableByteChannel source = Channels.newChannel(url.openStream());
final FileChannel fileChannel = FileChannel.open(downloadTo, CREATE, WRITE, TRUNCATE_EXISTING)
) {
downloadTo.getParent().toFile().mkdirs();
fileChannel.transferFrom(source, 0, Long.MAX_VALUE);
}
}
static String toHex(final byte[] hash) {
final StringBuilder sb = new StringBuilder(hash.length * 2);
for (byte aHash : hash) {
sb.append(String.format("%02X", aHash & 0xFF));
}
return sb.toString();
}
public static class VersionInfo {
@SerializedName("refs")
private Refs refs;
@SerializedName("name")
private String name;
@SerializedName("description")
private String description;
@SerializedName("toolsVersion")
private int toolsVersion;
@SerializedName("javaVersions")
private List<Integer> javaVersions;
public Refs getRefs() {
return refs;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public int getToolsVersion() {
return toolsVersion;
}
public List<Integer> getJavaVersions() {
return javaVersions;
}
@Override
public String toString() {
return
"VersionInfo{" +
"refs = '" + refs + '\'' +
",name = '" + name + '\'' +
",description = '" + description + '\'' +
",toolsVersion = '" + toolsVersion + '\'' +
",javaVersions = '" + javaVersions + '\'' +
"}";
}
public static class Refs {
@SerializedName("BuildData")
private String buildData;
@SerializedName("CraftBukkit")
private String craftBukkit;
@SerializedName("Bukkit")
private String bukkit;
@SerializedName("Spigot")
private String spigot;
public String getBuildData() {
return buildData;
}
public String getCraftBukkit() {
return craftBukkit;
}
public String getBukkit() {
return bukkit;
}
public String getSpigot() {
return spigot;
}
@Override
public String toString() {
return
"Refs{" +
"buildData = '" + buildData + '\'' +
",craftBukkit = '" + craftBukkit + '\'' +
",bukkit = '" + bukkit + '\'' +
",spigot = '" + spigot + '\'' +
"}";
}
}
}
public static class BuildDataInfo {
@SerializedName("memberMapCommand")
private String memberMapCommand;
@SerializedName("packageMappings")
private String packageMappings;
@SerializedName("classMapCommand")
private String classMapCommand;
@SerializedName("finalMapCommand")
private String finalMapCommand;
@SerializedName("serverUrl")
private String serverUrl;
@SerializedName("toolsVersion")
private int toolsVersion;
@SerializedName("minecraftHash")
private String minecraftHash;
@SerializedName("minecraftVersion")
private String minecraftVersion;
@SerializedName("accessTransforms")
private String accessTransforms;
@SerializedName("memberMappings")
private String memberMappings;
@SerializedName("decompileCommand")
private String decompileCommand;
@SerializedName("classMappings")
private String classMappings;
public String getMemberMapCommand() {
return memberMapCommand;
}
public String getPackageMappings() {
return packageMappings;
}
public String getClassMapCommand() {
return classMapCommand;
}
public String getFinalMapCommand() {
return finalMapCommand;
}
public String getServerUrl() {
return serverUrl;
}
public int getToolsVersion() {
return toolsVersion;
}
public String getMinecraftHash() {
return minecraftHash;
}
public String getMinecraftVersion() {
return minecraftVersion;
}
public String getAccessTransforms() {
return accessTransforms;
}
public String getMemberMappings() {
return memberMappings;
}
public String getDecompileCommand() {
return decompileCommand;
}
public String getClassMappings() {
return classMappings;
}
}
}

View File

@ -0,0 +1,89 @@
package org.yatopiamc.yatoclip;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
public class SpecialSourceLauncher {
private static final AtomicReference<SpecialSourceClassLoader> classLoader = new AtomicReference<>(new SpecialSourceClassLoader(new URL[0], SpecialSourceLauncher.class.getClassLoader().getParent()));
private static final AtomicReference<String> mainClass = new AtomicReference<>("");
static void setSpecialSourceJar(File specialSourceJar) {
synchronized (classLoader) {
System.err.println("Setting up SpecialSource: " + specialSourceJar);
try {
classLoader.get().addURL(specialSourceJar.toURI().toURL());
mainClass.set(Yatoclip.getMainClass(specialSourceJar.toPath()));
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
static void resetSpecialSourceClassloader() {
synchronized (classLoader) {
if(!classLoader.get().isLoaded) return;
System.err.println("Releasing SpecialSource");
try {
classLoader.get().close();
classLoader.set(new SpecialSourceClassLoader(new URL[0], SpecialSourceLauncher.class.getClassLoader().getParent()));
mainClass.set("");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void runProcess(String... command) throws IOException {
if (!(command != null && command.length > 0)) throw new IllegalArgumentException();
System.err.println("Invoking SpecialSource with arguments: " + Arrays.toString(command));
AtomicReference<Throwable> thrown = new AtomicReference<>(null);
final Thread thread = new Thread(() -> {
try {
final Class<?> mainClass = Class.forName(SpecialSourceLauncher.mainClass.get(), true, classLoader.get());
final Method mainMethod = mainClass.getMethod("main", String[].class);
if (!Modifier.isStatic(mainMethod.getModifiers()) || !Modifier.isPublic(mainMethod.getModifiers()))
throw new IllegalArgumentException();
mainMethod.invoke(null, new Object[]{command});
} catch (Throwable t) {
thrown.set(t);
}
});
thread.setName("SpecialSource Thread");
thread.setContextClassLoader(classLoader.get());
thread.start();
while (thread.isAlive())
try {
thread.join();
} catch (InterruptedException ignored) {
}
if (thrown.get() != null)
throw new RuntimeException(thrown.get());
}
private static class SpecialSourceClassLoader extends URLClassLoader {
private volatile boolean isLoaded = false;
public SpecialSourceClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected synchronized void addURL(URL url) {
if (isLoaded) throw new IllegalStateException();
this.isLoaded = true;
super.addURL(url);
}
}
}

View File

@ -0,0 +1,41 @@
package org.yatopiamc.yatoclip;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.jar.JarInputStream;
public class Yatoclip {
public static void main(String... args) throws Throwable {
final Path setup = ServerSetup.setup();
launch(setup, args);
}
private static void launch(Path setup, String[] args) throws Throwable {
YatoclipLaunch.injectClasspath(setup);
final Class<?> mainClassInstance = Class.forName("org.bukkit.craftbukkit.Main", true, ClassLoader.getSystemClassLoader());
final Method mainMethod = mainClassInstance.getMethod("main", String[].class);
if(!Modifier.isPublic(mainMethod.getModifiers()) || !Modifier.isStatic(mainMethod.getModifiers())) throw new IllegalArgumentException();
mainMethod.invoke(null, new Object[]{args});
}
static String getMainClass(Path jarPath) throws IOException {
final String mainClass;
try (
InputStream inputStream = Files.newInputStream(jarPath);
JarInputStream jar = new JarInputStream(inputStream)
) {
mainClass = jar.getManifest().getMainAttributes().getValue("Main-Class");
}
return mainClass;
}
}

View File

@ -0,0 +1,24 @@
package org.yatopiamc.yatoclip;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
public class YatoclipLaunch {
public static void premain(String args, Instrumentation inst) {
}
static void injectClasspath(Path setup) throws Throwable {
final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
if(!(systemClassLoader instanceof URLClassLoader))
throw new ClassCastException("SystemClassLoader is not an instance of URLClassLoader");
final URLClassLoader classLoader = (URLClassLoader) systemClassLoader;
final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURL.setAccessible(true);
addURL.invoke(classLoader, setup.toUri().toURL());
}
}

View File

@ -0,0 +1,236 @@
package org.yatopiamc.yatoclip;
import com.google.gson.Gson;
import io.sigpipe.jbsdiff.InvalidHeaderException;
import io.sigpipe.jbsdiff.Patch;
import org.apache.commons.compress.compressors.CompressorException;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static java.util.Objects.requireNonNull;
public class YatoclipPatcher {
private static final PatchesMetadata patchesMetadata;
static {
try (
final InputStream in = YatoclipPatcher.class.getClassLoader().getResourceAsStream("patches/metadata.json");
final InputStreamReader reader = new InputStreamReader(in);
) {
patchesMetadata = new Gson().fromJson(reader, PatchesMetadata.class);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
static boolean isJarUpToDate(Path patchedJar) {
requireNonNull(patchedJar);
if (!patchedJar.toFile().isFile()) return false;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (ZipFile patchedZip = new ZipFile(patchedJar.toFile())) {
for (PatchesMetadata.PatchMetadata patchMetadata : patchesMetadata.patches) {
ZipEntry zipEntry = patchedZip.getEntry(patchMetadata.name);
if (zipEntry == null || !patchMetadata.targetHash.equals(ServerSetup.toHex(digest.digest(IOUtils.toByteArray(patchedZip.getInputStream(zipEntry))))))
return false;
}
}
return true;
} catch (Throwable t) {
System.out.println(t.toString());
return false;
}
}
static void patchJar(Path memberMappedJar, Path patchedJar) {
requireNonNull(memberMappedJar);
requireNonNull(patchedJar);
if(!memberMappedJar.toFile().isFile()) throw new IllegalArgumentException(new FileNotFoundException());
try {
patchedJar.toFile().getParentFile().mkdirs();
final ThreadLocal<ZipFile> classMappedZip = ThreadLocal.withInitial(() -> {
try {
return new ZipFile(memberMappedJar.toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
final ThreadLocal<MessageDigest> digest = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
});
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
private AtomicInteger serial = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(() -> {
try {
r.run();
} finally {
try {
classMappedZip.get().close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.setName("YatoClip Worker #" + serial.incrementAndGet());
thread.setDaemon(true);
return thread;
}
});
try {
final Set<PatchData> patchDataSet = patchesMetadata.patches.stream().map((PatchesMetadata.PatchMetadata metadata) -> new PatchData(CompletableFuture.supplyAsync(() -> {
try {
return getPatchedBytes(classMappedZip.get(), digest.get(), metadata);
} catch (IOException | CompressorException | InvalidHeaderException e) {
throw new RuntimeException(e);
}
}, executorService), metadata)).collect(Collectors.toSet());
try (ZipOutputStream patchedZip = new ZipOutputStream(new FileOutputStream(patchedJar.toFile()))) {
patchedZip.setMethod(ZipOutputStream.DEFLATED);
patchedZip.setLevel(Deflater.BEST_SPEED);
Set<String> processed = new HashSet<>();
for (PatchData patchData : patchDataSet) {
putNextEntrySafe(patchedZip, patchData.metadata.name);
final byte[] patchedBytes = patchData.patchedBytesFuture.join();
patchedZip.write(patchedBytes);
patchedZip.closeEntry();
processed.add(patchData.metadata.name);
}
((Iterator<ZipEntry>) classMappedZip.get().entries()).forEachRemaining(zipEntry -> {
if (zipEntry.isDirectory() || processed.contains(applyRelocations(zipEntry.getName())) || patchesMetadata.copyExcludes.contains(zipEntry.getName()))
return;
try {
InputStream in = classMappedZip.get().getInputStream(zipEntry);
putNextEntrySafe(patchedZip, zipEntry.getName());
patchedZip.write(IOUtils.toByteArray(in));
patchedZip.closeEntry();
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
}
} catch (IOException e) {
throw new RuntimeException(e);
}
executorService.shutdown();
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private static byte[] getPatchedBytes(ZipFile classMappedZip, MessageDigest digest, PatchesMetadata.PatchMetadata patchMetadata) throws IOException, CompressorException, InvalidHeaderException {
final byte[] originalBytes;
final ZipEntry originalEntry = classMappedZip.getEntry(applyRelocationsReverse(patchMetadata.name));
if (originalEntry != null)
try (final InputStream in = classMappedZip.getInputStream(originalEntry)) {
originalBytes = IOUtils.toByteArray(in);
}
else originalBytes = new byte[0];
final byte[] patchBytes;
try (final InputStream in = YatoclipPatcher.class.getClassLoader().getResourceAsStream("patches/" + patchMetadata.name + ".patch")) {
if (in == null)
throw new FileNotFoundException();
patchBytes = IOUtils.toByteArray(in);
}
if (!patchMetadata.originalHash.equals(ServerSetup.toHex(digest.digest(originalBytes))) || !patchMetadata.patchHash.equals(ServerSetup.toHex(digest.digest(patchBytes))))
throw new FileNotFoundException("Hash do not match");
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
Patch.patch(originalBytes, patchBytes, byteOut);
final byte[] patchedBytes = byteOut.toByteArray();
if (!patchMetadata.targetHash.equals(ServerSetup.toHex(digest.digest(patchedBytes))))
throw new FileNotFoundException("Hash do not match");
return patchedBytes;
}
private static void putNextEntrySafe(ZipOutputStream patchedZip, String name) throws IOException {
String[] split = name.split("/");
split = Arrays.copyOfRange(split, 0, split.length - 1);
StringBuilder sb = new StringBuilder();
for (String s : split) {
sb.append(s).append("/");
try {
patchedZip.putNextEntry(new ZipEntry(sb.toString()));
} catch (ZipException e) {
if (e.getMessage().startsWith("duplicate entry"))
continue;
throw e;
}
}
final ZipEntry entry = new ZipEntry(name);
patchedZip.putNextEntry(entry);
}
private static String applyRelocations(String name) {
if (!name.endsWith(".class")) return name;
if (name.indexOf('/') == -1)
name = "/" + name;
for (PatchesMetadata.Relocation relocation : patchesMetadata.relocations) {
if (name.startsWith(relocation.from) && (relocation.includeSubPackages || name.split("/").length == name.split("/").length - 1)) {
return relocation.to + name.substring(relocation.from.length());
}
}
return name;
}
private static String applyRelocationsReverse(String name) {
if (!name.endsWith(".class")) return name;
if (name.indexOf('/') == -1)
name = "/" + name;
for (PatchesMetadata.Relocation relocation : patchesMetadata.relocations) {
if (name.startsWith(relocation.to) && (relocation.includeSubPackages || name.split("/").length == name.split("/").length - 1)) {
return relocation.from + name.substring(relocation.to.length());
}
}
return name;
}
private static class PatchData {
public final CompletableFuture<byte[]> patchedBytesFuture;
public final PatchesMetadata.PatchMetadata metadata;
private PatchData(CompletableFuture<byte[]> patchedBytesFuture, PatchesMetadata.PatchMetadata metadata) {
Objects.requireNonNull(patchedBytesFuture);
Objects.requireNonNull(metadata);
this.patchedBytesFuture = patchedBytesFuture.thenApply(Objects::requireNonNull);
this.metadata = metadata;
}
}
}

View File

@ -9,7 +9,17 @@ toothpick {
groupId = "org.yatopiamc"
val versionTag = System.getenv("BUILD_NUMBER")
?: "\"${gitCmd("rev-parse", "--short", "HEAD").output}\""
forkVersion = "git-$forkName-$versionTag"
if(!System.getenv("BRANCH_NAME").isNullOrEmpty()) {
currentBranch = System.getenv("BRANCH_NAME")
} else if (!System.getenv("GITHUB_HEAD_REF").isNullOrEmpty()) {
currentBranch = System.getenv("GITHUB_HEAD_REF")
} else if (!System.getenv("GITHUB_REF").isNullOrEmpty()) {
currentBranch = System.getenv("GITHUB_REF").substring("refs/heads/".length)
} else {
currentBranch = gitCmd("rev-parse", "--abbrev-ref", "HEAD").output.toString().trim()
if(currentBranch == "HEAD") logger.warn("You are currently in \'detached HEAD\' state, branch information isn\'t available")
}
forkVersion = "git-$forkName-$currentBranch-$versionTag"
forkUrl = "https://github.com/YatopiaMC/Yatopia"
minecraftVersion = "1.16.5"
@ -32,6 +42,8 @@ toothpick {
project = project(":$forkNameLowercase-api")
patchesDir = rootProject.projectDir.resolve("patches/api")
}
logger.lifecycle("Configured version string: $calcVersionString")
}
subprojects {

View File

@ -11,6 +11,7 @@ repositories {
mavenCentral()
jcenter()
maven("https://plugins.gradle.org/m2/")
maven("https://jitpack.io/")
}
dependencies {
@ -18,6 +19,15 @@ dependencies {
implementation("com.github.jengelman.gradle.plugins:shadow:$shadowVersion")
implementation("com.github.spullara.mustache.java:compiler:$mustacheVersion")
implementation("javax.mail:mail:$javaxMailVersion")
implementation("com.github.ishlandbukkit:jbsdiff:deff66b794")
implementation("com.google.code.gson:gson:2.8.6")
implementation("com.google.guava:guava:30.0-jre")
implementation("commons-io:commons-io:2.8.0")
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
sourceCompatibility = "1.8"
}
gradlePlugin {

View File

@ -0,0 +1,269 @@
package org.yatopiamc.yatoclip.gradle;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson;
import io.sigpipe.jbsdiff.Diff;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.gradle.api.DefaultTask;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.internal.logging.progress.ProgressLogger;
import org.gradle.internal.logging.progress.ProgressLoggerFactory;
import org.gradle.work.Incremental;
import org.gradle.workers.WorkerExecutor;
import javax.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class MakePatchesTask extends DefaultTask {
@OutputDirectory
private final File outputDir = ((Copy) getProject().getTasks().getByPath("processResources")).getDestinationDir().toPath().resolve("patches").toFile();
@InputFile
@Incremental
public File originalJar = null;
@InputFile
@Incremental
public File targetJar = null;
public Set<PatchesMetadata.Relocation> getRelocations() {
return relocations;
}
public void setRelocations(Set<PatchesMetadata.Relocation> relocations) {
this.relocations = relocations;
}
@Input
public Set<PatchesMetadata.Relocation> relocations;
public File getOriginalJar() {
return originalJar;
}
public void setOriginalJar(File originalJar) {
this.originalJar = originalJar;
}
public File getTargetJar() {
return targetJar;
}
public void setTargetJar(File targetJar) {
this.targetJar = targetJar;
}
public File getOutputDir() {
return outputDir;
}
private ProgressLoggerFactory getProgressLoggerFactory() {
return ((ProjectInternal) getProject()).getServices().get(ProgressLoggerFactory.class);
}
@Inject
public WorkerExecutor getWorkerExecutor() {
throw new UnsupportedOperationException();
}
@TaskAction
public void genPatches() throws IOException, InterruptedException {
Preconditions.checkNotNull(originalJar);
Preconditions.checkNotNull(targetJar);
getLogger().lifecycle("Generating patches for " + originalJar + " -> " + targetJar);
final ProgressLogger genPatches = getProgressLoggerFactory().newOperation(getClass()).setDescription("Generate patches");
genPatches.started();
genPatches.progress("Cleanup");
outputDir.mkdirs();
FileUtils.cleanDirectory(outputDir);
genPatches.progress("Reading files");
ThreadLocal<ZipFile> originalZip = ThreadLocal.withInitial(() -> {
try {
return new ZipFile(originalJar);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
ThreadLocal<ZipFile> targetZip = ThreadLocal.withInitial(() -> {
try {
return new ZipFile(targetJar);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
Set<PatchesMetadata.PatchMetadata> patchMetadata = Sets.newConcurrentHashSet();
ThreadLocal<MessageDigest> digestThreadLocal = ThreadLocal.withInitial(() -> {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
});
ThreadLocal<ProgressLogger> progressLoggerThreadLocal = ThreadLocal.withInitial(() -> {
final ProgressLogger progressLogger = getProgressLoggerFactory().newOperation(this.getClass());
progressLogger.setDescription("Patch worker");
progressLogger.started("Idle");
return progressLogger;
});
final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),
new ThreadFactoryBuilder().setNameFormat("MakePatches-%d").setThreadFactory(r -> new Thread(() -> {
boolean isExceptionOccurred = false;
try {
r.run();
} catch (Throwable t) {
isExceptionOccurred = true;
progressLoggerThreadLocal.get().completed(t.toString(), true);
throw t;
} finally {
digestThreadLocal.remove();
if (!isExceptionOccurred)
progressLoggerThreadLocal.get().completed();
progressLoggerThreadLocal.remove();
try {
originalZip.get().close();
targetZip.get().close();
} catch (IOException e) {
e.printStackTrace();
}
}
})).build());
AtomicInteger current = new AtomicInteger(0);
final int size = targetZip.get().size();
((Iterator<ZipEntry>) targetZip.get().entries()).forEachRemaining(zipEntryT -> {
genPatches.progress("Submitting tasks (" + current.incrementAndGet() + "/" + size + ")");
if (zipEntryT.isDirectory()) return;
executorService.execute(() -> {
ZipEntry zipEntry = targetZip.get().getEntry(zipEntryT.getName());
final String child = zipEntry.getName();
progressLoggerThreadLocal.get().progress("Reading " + zipEntry.getName());
File outputFile = new File(outputDir, child + ".patch");
outputFile.getParentFile().mkdirs();
final byte[] originalBytes;
final byte[] targetBytes;
final ZipEntry oEntry = originalZip.get().getEntry(applyRelocationsReverse(child));
try (
final InputStream oin = oEntry != null ? originalZip.get().getInputStream(oEntry) : null;
final InputStream tin = targetZip.get().getInputStream(zipEntry);
) {
originalBytes = oin != null ? IOUtils.toByteArray(oin) : new byte[0];
targetBytes = IOUtils.toByteArray(tin);
} catch (Throwable e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
if (Arrays.equals(originalBytes, targetBytes)) return;
progressLoggerThreadLocal.get().progress("GenPatch " + zipEntry.getName());
try (final OutputStream out = new FileOutputStream(outputFile)) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Diff.diff(originalBytes, targetBytes, byteArrayOutputStream);
patchMetadata.add(new PatchesMetadata.PatchMetadata(child, toHex(digestThreadLocal.get().digest(originalBytes)), toHex(digestThreadLocal.get().digest(targetBytes)), toHex(digestThreadLocal.get().digest(byteArrayOutputStream.toByteArray()))));
out.write(byteArrayOutputStream.toByteArray());
} catch (Throwable t) {
Throwables.throwIfUnchecked(t);
throw new RuntimeException(t);
}
progressLoggerThreadLocal.get().progress("Idle");
});
});
genPatches.progress("Calculating exclusions");
Set<String> copyExcludes = new HashSet<>();
((Iterator<ZipEntry>) originalZip.get().entries()).forEachRemaining(zipEntry -> {
if(targetZip.get().getEntry(applyRelocations(zipEntry.getName())) == null)
copyExcludes.add(zipEntry.getName());
});
originalZip.get().close();
targetZip.get().close();
genPatches.progress("Waiting for patching to finish");
executorService.shutdown();
while (!executorService.awaitTermination(1, TimeUnit.SECONDS)) ;
digestThreadLocal.remove();
genPatches.progress("Writing patches metadata");
try (final OutputStream out = new FileOutputStream(new File(outputDir, "metadata.json"));
final Writer writer = new OutputStreamWriter(out)) {
new Gson().toJson(new PatchesMetadata(patchMetadata, relocations, copyExcludes), writer);
}
/*
genPatches.progress("Reading jar files into memory");
byte[] origin = Files.readAllBytes(originalJar.toPath());
byte[] target = Files.readAllBytes(targetJar.toPath());
genPatches.progress("Generating patch");
try(final OutputStream out = new BufferedOutputStream(new FileOutputStream(output))){
Diff.diff(origin, target, out);
}
*/
genPatches.completed();
}
private String applyRelocations(String name) {
if(!name.endsWith(".class")) return name;
if (name.indexOf('/') == -1)
name = "/" + name;
for (PatchesMetadata.Relocation relocation : relocations) {
if (name.startsWith(relocation.from) && (relocation.includeSubPackages || name.split("/").length == name.split("/").length - 1)) {
return relocation.to + name.substring(relocation.from.length());
}
}
return name;
}
private String applyRelocationsReverse(String name) {
if(!name.endsWith(".class")) return name;
if (name.indexOf('/') == -1)
name = "/" + name;
for (PatchesMetadata.Relocation relocation : relocations) {
if (name.startsWith(relocation.to) && (relocation.includeSubPackages || name.split("/").length == name.split("/").length - 1)) {
return relocation.from + name.substring(relocation.to.length());
}
}
return name;
}
public static String toHex(final byte[] hash) {
final StringBuilder sb = new StringBuilder(hash.length * 2);
for (byte aHash : hash) {
sb.append(String.format("%02X", aHash & 0xFF));
}
return sb.toString();
}
}

View File

@ -0,0 +1,51 @@
package org.yatopiamc.yatoclip.gradle;
import java.io.Serializable;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
public class PatchesMetadata {
public final Set<PatchMetadata> patches;
public final Set<Relocation> relocations;
public final Set<String> copyExcludes;
public PatchesMetadata(Set<PatchMetadata> patches, Set<Relocation> relocations, Set<String> copyExcludes) {
Objects.requireNonNull(copyExcludes);
this.copyExcludes = Collections.unmodifiableSet(copyExcludes);
Objects.requireNonNull(relocations);
this.relocations = Collections.unmodifiableSet(relocations);
Objects.requireNonNull(patches);
this.patches = Collections.unmodifiableSet(patches);
}
public static class PatchMetadata {
public final String name;
public final String originalHash;
public final String targetHash;
public final String patchHash;
public PatchMetadata(String name, String originalHash, String targetHash, String patchHash) {
this.name = name;
this.originalHash = originalHash;
this.targetHash = targetHash;
this.patchHash = patchHash;
}
}
public static class Relocation implements Serializable {
public final String from;
public final String to;
public final boolean includeSubPackages;
public Relocation(String from, String to, boolean includeSubPackages) {
Objects.requireNonNull(from);
Objects.requireNonNull(to);
this.from = from.replaceAll("\\.", "/");
this.to = to.replaceAll("\\.", "/");
this.includeSubPackages = includeSubPackages;
}
}
}

View File

@ -0,0 +1,22 @@
package org.yatopiamc.yatoclip.gradle;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Properties;
public class PropertiesUtils {
public static void saveProperties(Properties prop, Path file, String comments){
System.out.println("Saving properties file to " + file);
file.toFile().getParentFile().mkdirs();
file.toFile().delete();
try(final OutputStream out = new FileOutputStream(file.toFile())) {
prop.store(out, comments);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -5,19 +5,27 @@ import kotlinx.dom.elements
import kotlinx.dom.parseXml
import kotlinx.dom.search
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.plugins.JavaLibraryPlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.api.publish.maven.tasks.GenerateMavenPom
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.*
import org.yatopiamc.yatoclip.gradle.MakePatchesTask
import org.yatopiamc.yatoclip.gradle.PatchesMetadata
import org.yatopiamc.yatoclip.gradle.PropertiesUtils
import java.nio.charset.StandardCharsets.UTF_8
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashSet
internal fun Project.configureSubprojects() {
subprojects {
@ -49,6 +57,87 @@ internal fun Project.configureSubprojects() {
project.name.endsWith("api") -> configureApiProject()
}
}
rootProject.project("Yatoclip") {
configureYatoclipProject()
}
}
private fun Project.configureYatoclipProject() {
try {
rootProject.toothpick.serverProject.project.extensions.getByName("relocations")
} catch (e: UnknownDomainObjectException) {
return
}
apply<JavaLibraryPlugin>()
apply<ShadowPlugin>()
tasks.register<MakePatchesTask>("genPatches") {
originalJar = rootProject.toothpick.paperDir.resolve("work").resolve("Minecraft")
.resolve(rootProject.toothpick.minecraftVersion).resolve("${rootProject.toothpick.minecraftVersion}-m.jar")
targetJar = rootProject.toothpick.serverProject.project.tasks.getByName("shadowJar").outputs.files.singleFile
setRelocations(rootProject.toothpick.serverProject.project.extensions.getByName("relocations") as HashSet<PatchesMetadata.Relocation>)
dependsOn(rootProject.toothpick.serverProject.project.tasks.getByName("shadowJar"))
doLast {
val prop = Properties()
prop.setProperty("minecraftVersion", rootProject.toothpick.minecraftVersion)
PropertiesUtils.saveProperties(
prop,
outputDir.toPath().parent.resolve("yatoclip-launch.properties"),
"Yatoclip launch values"
)
}
}
val sourceSets = extensions.getByName("sourceSets") as org.gradle.api.tasks.SourceSetContainer
sourceSets.create("java9") {
java {
srcDir("src/java9")
}
}
val shadowJar by tasks.getting(ShadowJar::class) {
manifest {
attributes(
"Main-Class" to "org.yatopiamc.yatoclip.Yatoclip",
"Launcher-Agent-Class" to "org.yatopiamc.yatoclip.YatoclipLaunch",
"Premain-Class" to "org.yatopiamc.yatoclip.YatoclipLaunch",
"Multi-Release" to "true"
)
}
into("META-INF/versions/9") {
from(sourceSets.getByName("java9").output)
}
}
tasks.register<Copy>("copyJar") {
val targetName = "yatopia-${rootProject.toothpick.minecraftVersion}-yatoclip.jar"
from(shadowJar.outputs.files.singleFile) {
rename { targetName }
}
into(rootProject.projectDir)
doLast {
logger.lifecycle(">>> $targetName saved to root project directory")
}
dependsOn(shadowJar)
}
tasks.getByName("processResources").dependsOn(tasks.getByName("genPatches"))
tasks.getByName("assemble").dependsOn(tasks.getByName("copyJar"))
tasks.getByName("jar").enabled = false
val buildTask = tasks.getByName("build")
val buildTaskDependencies = HashSet(buildTask.dependsOn)
buildTask.setDependsOn(HashSet<Task>())
buildTask.onlyIf { false }
tasks.register("yatoclip") {
buildTaskDependencies.forEach {
dependsOn(it)
}
}
}
private fun Project.configureServerProject() {
@ -76,7 +165,7 @@ private fun Project.configureServerProject() {
"Implementation-Version" to toothpick.forkVersion,
"Implementation-Vendor" to SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date()),
"Specification-Title" to "Bukkit",
"Specification-Version" to "${project.version}",
"Specification-Version" to "${project.rootProject.toothpick.minecraftVersion}-${project.rootProject.toothpick.nmsRevision}",
"Specification-Vendor" to "Bukkit Team"
)
}
@ -85,11 +174,14 @@ private fun Project.configureServerProject() {
into("META-INF/maven/io.papermc.paper/paper")
}
val relocationSet = HashSet<PatchesMetadata.Relocation>()
// Don't like to do this but sadly have to do this for compatibility reasons
relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.v${toothpick.nmsPackage}") {
exclude("org.bukkit.craftbukkit.Main*")
}
relocate("net.minecraft.server", "net.minecraft.server.v${toothpick.nmsPackage}")
relocationSet.add(PatchesMetadata.Relocation("", "net.minecraft.server.v${toothpick.nmsPackage}", false))
// Make sure we relocate deps the same as Paper et al.
val pomFile = project.projectDir.resolve("pom.xml")
@ -111,9 +203,11 @@ private fun Project.configureServerProject() {
if (pattern != "org.bukkit.craftbukkit" && pattern != "net.minecraft.server") { // We handle these ourselves above
logger.debug("Imported relocation to server project shadowJar from ${pomFile.absolutePath}: $pattern to $shadedPattern")
relocate(pattern, shadedPattern)
relocationSet.add(PatchesMetadata.Relocation(pattern, shadedPattern, true))
}
}
}
project.extensions.add("relocations", relocationSet)
}
tasks.getByName("build") {
dependsOn(shadowJar)
@ -121,9 +215,8 @@ private fun Project.configureServerProject() {
extensions.configure<PublishingExtension> {
publications {
getByName<MavenPublication>("mavenJava") {
artifactId = rootProject.name
artifact(tasks["shadowJar"])
create<MavenPublication>("shadow") {
artifact(project.tasks.named("shadowJar"))
}
}
}
@ -134,7 +227,7 @@ private fun Project.configureApiProject() {
val jar by this.tasks.getting(Jar::class) {
doFirst {
buildDir.resolve("tmp/pom.properties")
.writeText("version=${project.version}")
.writeText("version=${project.rootProject.toothpick.minecraftVersion}-${project.rootProject.toothpick.nmsRevision}")
}
from(buildDir.resolve("tmp/pom.properties")) {
into("META-INF/maven/${project.group}/${project.name}")

View File

@ -32,7 +32,7 @@ fun DependencyHandlerScope.loadDependencies(project: Project) {
val groupId = dependencyElem.search("groupId").first().textContent
val artifactId = dependencyElem.search("artifactId").first().textContent
val version = dependencyElem.search("version").first().textContent.applyReplacements(
"project.version" to project.version.toString(),
"project.version" to "${project.rootProject.toothpick.minecraftVersion}-${project.rootProject.toothpick.nmsRevision}",
"minecraft.version" to project.toothpick.minecraftVersion
)
val scope = dependencyElem.search("scope").firstOrNull()?.textContent

View File

@ -33,6 +33,12 @@ open class ToothpickExtension(objects: ObjectFactory) {
lateinit var patchCreditsOutput: String
lateinit var patchCreditsTemplate: String
lateinit var currentBranch : String
val currentBranchDisplayName
get() = currentBranch.replace("/${minecraftVersion}", "")
val calcVersionString
get() = if(!currentBranch.startsWith("ver/")) { "${minecraftVersion}-${nmsRevision}-${currentBranchDisplayName.replace('/', '_')}" } else "${minecraftVersion}-${nmsRevision}"
fun server(receiver: ToothpickSubproject.() -> Unit) {
serverProject = ToothpickSubproject()
receiver(serverProject)

View File

@ -10,7 +10,7 @@ fun Project.toothpick(receiver: ToothpickExtension.() -> Unit) {
receiver(toothpick)
allprojects {
group = toothpick.groupId
version = "${toothpick.minecraftVersion}-${toothpick.nmsRevision}"
version = toothpick.calcVersionString
}
configureSubprojects()
initToothpickTasks()

View File

@ -70,7 +70,12 @@ private fun Project.updatePatches(
"--no-stat", "--zero-commit", "--full-index", "--no-signature", "-N",
"-o", patchPath.absolutePath, previousUpstreamName,
dir = projectDir,
printOut = true
printOut = false
)
)
gitCmd(
"add", patchPath.canonicalPath,
dir = patchPath,
printOut = true
)
}

View File

@ -0,0 +1,258 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: ishland <ishlandmc@yeah.net>
Date: Fri, 29 Jan 2021 09:57:47 +0800
Subject: [PATCH] Suspected plugins report
Added "Suspected Plugins" to Watchdog, crash reports and exception messages
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 26685f59b235ea5b4c4fb7ae21acb5149edaa2b3..02c20a33161094b08dc2ae9353c0504561b0b452 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -560,7 +560,11 @@ public final class SimplePluginManager implements PluginManager {
// Paper start
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
- server.getLogger().log(Level.SEVERE, msg, ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
}
// Paper end
@@ -621,7 +625,11 @@ public final class SimplePluginManager implements PluginManager {
} catch (Throwable ex) {
// Paper start - error reporting
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
- server.getLogger().log(Level.SEVERE, msg, ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
}
@@ -905,4 +913,10 @@ public final class SimplePluginManager implements PluginManager {
}
// Paper end
+ // Yatopia start - Accessor
+ @NotNull
+ public Collection<PluginLoader> getPluginLoaders() {
+ return new HashSet<>(fileAssociations.values());
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index 04fa3991f6ce4e9dad804f28fc6c947695857089..cb11eab6e13ed1c395b8f7db033c9a2817f4089c 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -111,7 +111,7 @@ public abstract class JavaPlugin extends PluginBase {
* @return File containing this plugin
*/
@NotNull
- protected File getFile() {
+ public File getFile() { // Yatopia
return file;
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 384edf9890dfbd1cddfdcac4db1ebe9a4d761f78..7c0c63c3f5734d59aa8b57fe3eb3c1fe0e137f12 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -379,7 +379,11 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(true);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
// Paper start - Disable plugins that fail to load
server.getPluginManager().disablePlugin(jPlugin, true); // Paper - close Classloader on disable - She's dead jim
return;
@@ -414,7 +418,11 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(false);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
}
if (cloader instanceof PluginClassLoader) {
@@ -432,11 +440,20 @@ public final class JavaPluginLoader implements PluginLoader {
loader.close();
}
} catch (IOException e) {
+ // Yatopia start - detailed report
server.getLogger().log(Level.WARNING, "Error closing the Plugin Class Loader for " + plugin.getDescription().getFullName());
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, _msg -> server.getLogger().log(Level.WARNING, _msg));
+ server.getLogger().log(Level.WARNING, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, e);
+ // Yatopia end
e.printStackTrace();
}
// Paper end
}
}
}
+ // Yatopia start - Accessor
+ public List<PluginClassLoader> getClassLoaders() {
+ return java.util.Collections.unmodifiableList(loaders);
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
index 7760be3e34fa20825faf145d9fb5b2855c1a4602..2e8f3efdb683c44b3e42bb0187bc907e64cde288 100644
--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
@@ -232,4 +232,13 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
'}';
}
// Paper end
+
+ // Yatopia start - Accessor
+ public java.util.Collection<Class<?>> getLoadedClasses() {
+ return java.util.Collections.unmodifiableCollection(
+ new java.util.HashSet<>(classes.values()).stream()
+ .filter(clazz -> clazz.getClassLoader() == this).collect(java.util.stream.Collectors.toSet())
+ );
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java b/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0aa9bc6ad0a85d469b29201b9da29165bafb874c
--- /dev/null
+++ b/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java
@@ -0,0 +1,105 @@
+package org.yatopiamc.yatopia.api.internal;
+
+import com.google.common.base.Suppliers;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginLoader;
+import org.bukkit.plugin.SimplePluginManager;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.plugin.java.JavaPluginLoader;
+import org.bukkit.plugin.java.PluginClassLoader;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class StackTraceUtils {
+
+ public static final String EXCEPTION_DETAILS_BELOW = "Exception details below: ";
+
+ private static final Supplier<Map<Plugin, Set<Class<?>>>> loadedClassesSupplier = Suppliers.memoizeWithExpiration(StackTraceUtils::scanForPluginClasses, 5, TimeUnit.SECONDS);
+
+ public static void print(StackTraceElement[] stackTrace, Consumer<String> out) {
+ Set<Plugin> suspectedPlugins = getSuspectedPluginsFromStackTrace(stackTrace);
+
+ printSuspectedPlugins(out, suspectedPlugins);
+ }
+
+ public static void print(Throwable t, Consumer<String> out) {
+ Set<Plugin> suspectedPlugins = getSuspectedPluginsFromStackTrace(getStackTracesFromThrowable(t).toArray(new StackTraceElement[0]));
+
+ printSuspectedPlugins(out, suspectedPlugins);
+ }
+
+ private static Set<StackTraceElement> getStackTracesFromThrowable(Throwable t) {
+ if(t == null) return Collections.emptySet();
+ Set<StackTraceElement> elements = new ObjectOpenHashSet<>();
+ elements.addAll(getStackTracesFromThrowable(t.getCause()));
+ elements.addAll(Arrays.stream(t.getSuppressed()).flatMap(throwable -> getStackTracesFromThrowable(throwable).stream()).collect(Collectors.toSet()));
+ elements.addAll(Arrays.asList(t.getStackTrace()));
+ return elements;
+ }
+
+ private static void printSuspectedPlugins(Consumer<String> out, Set<Plugin> suspectedPlugins) {
+ if (!suspectedPlugins.isEmpty()) {
+ out.accept("Suspected Plugins: ");
+ for (Plugin plugin : suspectedPlugins) {
+ StringBuilder builder = new StringBuilder("\t");
+ builder.append(plugin.getName())
+ .append("{")
+ .append(plugin.isEnabled() ? "enabled" : "disabled")
+ .append(",").append("ver=").append(plugin.getDescription().getVersion());
+ if (!plugin.isNaggable())
+ builder.append(",").append("nag");
+ if (plugin instanceof JavaPlugin)
+ builder.append(",").append("path=").append(((JavaPlugin) plugin).getFile());
+
+ builder.append("}");
+ out.accept(builder.toString());
+ }
+ } else {
+ out.accept("Suspected Plugins: None");
+ }
+ }
+
+ private static Set<Plugin> getSuspectedPluginsFromStackTrace(StackTraceElement[] stackTrace) {
+ Map<Plugin, Set<Class<?>>> loadedClasses = loadedClassesSupplier.get();
+ Set<Plugin> suspectedPlugins = new HashSet<>();
+ for (StackTraceElement stackTraceElement : stackTrace) {
+ for (Map.Entry<Plugin, Set<Class<?>>> pluginSetEntry : loadedClasses.entrySet()) {
+ if (pluginSetEntry.getValue().stream().anyMatch(clazz -> clazz.getName().equals(stackTraceElement.getClassName())))
+ suspectedPlugins.add(pluginSetEntry.getKey());
+ }
+ }
+ return suspectedPlugins;
+ }
+
+ private static Map<Plugin, Set<Class<?>>> scanForPluginClasses() {
+ Map<Plugin, Set<Class<?>>> loadedClasses = new Object2ObjectOpenHashMap<>();
+ if (Bukkit.getPluginManager() instanceof SimplePluginManager) {
+ final SimplePluginManager pluginManager = (SimplePluginManager) Bukkit.getPluginManager();
+ final Collection<PluginLoader> pluginLoaders = pluginManager.getPluginLoaders();
+ for (PluginLoader pluginLoader : pluginLoaders) {
+ if (pluginLoader instanceof JavaPluginLoader) {
+ JavaPluginLoader javaPluginLoader = (JavaPluginLoader) pluginLoader;
+ final List<PluginClassLoader> classLoaders = javaPluginLoader.getClassLoaders();
+ for (PluginClassLoader classLoader : classLoaders) {
+ loadedClasses.put(classLoader.getPlugin(), new ObjectOpenHashSet<>(classLoader.getLoadedClasses()));
+ }
+ }
+ }
+ }
+ return loadedClasses;
+ }
+
+}
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
index 4c2780c903ec354edac741b673a7174284a9849a..ed5629295324123576af992cb1b4c7ce90ffa1de 100644
--- a/src/test/java/org/bukkit/AnnotationTest.java
+++ b/src/test/java/org/bukkit/AnnotationTest.java
@@ -49,8 +49,11 @@ public class AnnotationTest {
"co/aikar/timings/TimingHistory$2$1$2",
"co/aikar/timings/TimingHistory$3",
"co/aikar/timings/TimingHistory$4",
- "co/aikar/timings/TimingHistoryEntry$1"
+ "co/aikar/timings/TimingHistoryEntry$1",
// Paper end
+ // Yatopia start
+ "org/yatopiamc/yatopia/api/internal/StackTraceUtils"
+ // Yatopia end
};
@Test

View File

@ -6,15 +6,11 @@ Subject: [PATCH] New Network System
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
diff --git a/pom.xml b/pom.xml
index 8af1a91102c5cc4c230f622e6629e46e95f17d44..9b7ed0de1054285dadff6aefc95c7207079504a6 100644
index 6109699411c349c4965de6dbdbd9f8454bc18b10..0bbc7badb1f597357d0fce7cd0f5faf5eb1d0eb5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,9 +53,17 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
- <version>4.1.50.Final</version>
+ <version>4.1.58.Final</version> <!-- Yatopia -->
@@ -56,6 +56,14 @@
<version>4.1.58.Final</version>
</dependency>
<!-- Tuinity end - fix compile issue (cannot see new api) by moving netty include BEFORE server jar -->
+ <!-- Yatopia start - Add IOUring beta support -->

View File

@ -5,7 +5,7 @@ Subject: [PATCH] Modify POM
diff --git a/pom.xml b/pom.xml
index 752d62eb3b87ab24260ec2c029bae0d2b0e3b908..4f56aa4ae78b9d3756983cde52bc1d1adda0c9d4 100644
index 752d62eb3b87ab24260ec2c029bae0d2b0e3b908..e790d779d24c2c8d4a74d458839c11bc494eeef1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,11 +1,11 @@
@ -54,6 +54,64 @@ index 752d62eb3b87ab24260ec2c029bae0d2b0e3b908..4f56aa4ae78b9d3756983cde52bc1d1a
<!-- Purpur end -->
<version>${project.version}</version>
<scope>compile</scope>
@@ -44,7 +53,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
- <version>4.1.50.Final</version>
+ <version>4.1.58.Final</version>
</dependency>
<!-- Tuinity end - fix compile issue (cannot see new api) by moving netty include BEFORE server jar -->
<dependency>
@@ -61,7 +70,7 @@
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jansi</artifactId>
- <version>3.12.1</version>
+ <version>3.19.0</version>
<scope>runtime</scope>
</dependency>
<!--
@@ -73,19 +82,19 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
- <version>2.8.1</version>
+ <version>2.14.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
- <version>2.8.1</version>
+ <version>2.14.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-iostreams</artifactId>
- <version>2.8.1</version>
+ <version>2.14.0</version>
<scope>compile</scope>
<exclusions>
<!-- included in minecraft-server -->
@@ -144,14 +153,14 @@
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
- <version>1.3</version>
+ <version>2.2</version>
<scope>test</scope>
</dependency>
<!-- for vanilla goal scanning -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
- <version>4.8.47</version>
+ <version>4.8.98</version>
<scope>test</scope>
</dependency>
<!-- for optimized protocol handling -->
@@ -161,6 +170,12 @@
<version>1.1.0-SNAPSHOT</version>
<scope>compile</scope>

View File

@ -161,22 +161,28 @@ index 58d01c6f8abcd9e1792495abd08b186f9d03f834..476939bde38246eb0fd96e6a4ba8076c
diff --git a/src/main/java/org/yatopiamc/yatopia/server/YatopiaVersionFetcher.java b/src/main/java/org/yatopiamc/yatopia/server/YatopiaVersionFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..db0f7b1acf28ebe486026b8a7507ed4c7e26fb0f
index 0000000000000000000000000000000000000000..2898bfb109c63a93971bd38cc4778da1dc37c445
--- /dev/null
+++ b/src/main/java/org/yatopiamc/yatopia/server/YatopiaVersionFetcher.java
@@ -0,0 +1,96 @@
@@ -0,0 +1,123 @@
+package org.yatopiamc.yatopia.server;
+
+import com.destroystokyo.paper.VersionHistoryManager;
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.jar.Manifest;
+import javax.annotation.Nonnull;
+
+import com.destroystokyo.paper.util.VersionFetcher;
@ -184,8 +190,6 @@ index 0000000000000000000000000000000000000000..db0f7b1acf28ebe486026b8a7507ed4c
+
+public class YatopiaVersionFetcher implements VersionFetcher {
+
+ private static final String GITHUB_BRANCH_NAME = "ver/1.16.5";
+
+ @Override
+ public long getCacheTime() {
+ return 3600000;
@ -194,15 +198,23 @@ index 0000000000000000000000000000000000000000..db0f7b1acf28ebe486026b8a7507ed4c
+ @Nonnull
+ @Override
+ public String getVersionMessage(@Nonnull String serverVersion) {
+ if(serverVersion.equals("null")) return "Custom build";
+ String[] parts = serverVersion.substring("git-Yatopia-".length()).split("[-\\s]");
+ String updateMessage = getUpdateStatusMessage("YatopiaMC/Yatopia", GITHUB_BRANCH_NAME, parts[0]);
+ String branch = String.join("-", Arrays.copyOfRange(parts, 0, parts.length - 3));
+ String version = parts[parts.length - 3];
+ String updateMessage = getUpdateStatusMessage("YatopiaMC/Yatopia", branch, version);
+ String history = getHistory();
+ return history != null ? history + "\n" + updateMessage : updateMessage;
+ }
+
+ private String getUpdateStatusMessage(String repo, String branch, String versionInfo) {
+ versionInfo = versionInfo.replace("\"", "");
+ int distance = fetchDistanceFromGitHub(repo, branch, versionInfo);
+ int distance;
+ try {
+ int jenkinsBuild = Integer.parseInt(versionInfo);
+ distance = fetchDistanceFromJenkins(branch, jenkinsBuild);
+ } catch (NumberFormatException ignored) {
+ distance = fetchDistanceFromGitHub(repo, branch, versionInfo.replace("\"", ""));
+ }
+
+ switch (distance) {
+ case -1:
@ -216,6 +228,21 @@ index 0000000000000000000000000000000000000000..db0f7b1acf28ebe486026b8a7507ed4c
+ }
+ }
+
+ // modified from PurpurVersionFetcher
+ private static int fetchDistanceFromJenkins(String branch, int jenkinsBuild) {
+ try {
+ try (BufferedReader reader = Resources.asCharSource(new URL("https://ci.codemc.io/job/YatopiaMC/job/Yatopia/job/" + URLEncoder.encode(branch, Charsets.UTF_8.name()) + "/lastStableBuild/buildNumber"), Charsets.UTF_8).openBufferedStream()) {
+ return Integer.decode(reader.readLine()) - jenkinsBuild;
+ } catch (NumberFormatException ex) {
+ ex.printStackTrace();
+ return -2;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
+ // Contributed by Techcable <Techcable@outlook.com> in GH-65
+ // from PaperVersionFetcher
+ private static int fetchDistanceFromGitHub(@Nonnull String repo, @Nonnull String branch, @Nonnull String hash) {

View File

@ -9,7 +9,7 @@ Co-authored-by: Mykyta Komarnytskyy <nkomarn@hotmail.com>
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
diff --git a/pom.xml b/pom.xml
index 4f56aa4ae78b9d3756983cde52bc1d1adda0c9d4..d8ec87143370144c04502cd7bddf57f2f5e25168 100644
index e790d779d24c2c8d4a74d458839c11bc494eeef1..04ec0b474a234c04450f04ecf7336d3bb0947420 100644
--- a/pom.xml
+++ b/pom.xml
@@ -176,6 +176,12 @@

View File

@ -5,7 +5,7 @@ Subject: [PATCH] Add NBT API as a first-class lib
diff --git a/pom.xml b/pom.xml
index d8ec87143370144c04502cd7bddf57f2f5e25168..8af1a91102c5cc4c230f622e6629e46e95f17d44 100644
index 04ec0b474a234c04450f04ecf7336d3bb0947420..9402ec96f626e5e4f36f7dd678454f9efeb7e254 100644
--- a/pom.xml
+++ b/pom.xml
@@ -358,6 +358,10 @@

View File

@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: SuperCoder7979 <25208576+SuperCoder7979@users.noreply.github.com>
Date: Fri, 22 Jan 2021 16:38:19 -0500
Subject: [PATCH] lithium: cache chunk gen sea level
Chunk generator settings are passed to the noise chunk generator through a supplier, which retrieves a given chunk generator settings registry key from the registry. The problem arises as this setting is retrieved from the supplier to get the sea level, which is called every single time a block is placed by the generator, for a total of 65536 registry lookups per chunk. As you can imagine this isn't all that fast and removing it speeds up the chunk generation time by a moderate amount (13% of CPU time spent in populateNoise without the patch vs 10% with the patch). My solution to this was to stick the supplier into a Lazy<> so it's computed once and then cached, but I'm open to suggestions. (The sea level can't be computed ahead of time due to a quirk in the new registry system, which would have been the best option here.)
This code was originally made by SuperCoder7979 in a Pull Request to lithium (https://github.com/jellysquid3/lithium-fabric/pull/179)
This code was orignally licensed under the LGPL-3.0 license.
diff --git a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java
index fa60285c0c48147ad09b9197bfe577f504dd34bd..de469f9b4f0fecc05dca7a5aacd1308db6f80a18 100644
--- a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java
+++ b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java
@@ -17,6 +17,7 @@ import javax.annotation.Nullable;
public final class ChunkGeneratorAbstract extends ChunkGenerator {
+ private int cachedSeaLevel; // Yatopia - lithium cache chunk gen settings
public static final Codec<ChunkGeneratorAbstract> d = RecordCodecBuilder.create((instance) -> {
return instance.group(WorldChunkManager.a.fieldOf("biome_source").forGetter((chunkgeneratorabstract) -> {
return chunkgeneratorabstract.b;
@@ -101,6 +102,7 @@ public final class ChunkGeneratorAbstract extends ChunkGenerator {
} else {
this.v = null;
}
+ this.cachedSeaLevel = ((GeneratorSettingBase) this.h.get()).g(); // Yatopia - lithium cache chunk gen settings
}
@@ -688,10 +690,18 @@ public final class ChunkGeneratorAbstract extends ChunkGenerator {
return this.x;
}
+ // Yatopia start - lithium cache chunk gen settings
+ /**
+ * Use cached sea level instead of retrieving from the registry every time.
+ * This method is called for every block in the chunk so this will save a lot of registry lookups.
+ *
+ * @author SuperCoder79
+ */
@Override
- public int getSeaLevel() {
- return ((GeneratorSettingBase) this.h.get()).g();
+ public int getSeaLevel() { // Cached method of getting sealevel
+ return this.cachedSeaLevel;
}
+ // Yatopia end
@Override
public List<BiomeSettingsMobs.c> getMobsFor(BiomeBase biomebase, StructureManager structuremanager, EnumCreatureType enumcreaturetype, BlockPosition blockposition) {

View File

@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn <git@steinborn.me>
Date: Mon, 18 Jan 2021 20:45:25 -0500
Subject: [PATCH] (PaperPR) Inline shift direction fields
Removes a layer of indirection for EnumDirection.getAdjacent(X|Y|Z)(), which is in the
critical section for much of the server, including the lighting engine.
diff --git a/src/main/java/net/minecraft/server/EnumDirection.java b/src/main/java/net/minecraft/server/EnumDirection.java
index 2f1e7f0d753aace655210e32ddec59a3c46aade9..74e95bfcae5212c77383f7b8780dce293fda02dc 100644
--- a/src/main/java/net/minecraft/server/EnumDirection.java
+++ b/src/main/java/net/minecraft/server/EnumDirection.java
@@ -49,6 +49,11 @@ public enum EnumDirection implements INamable {
}, (enumdirection, enumdirection1) -> {
throw new IllegalArgumentException("Duplicate keys");
}, Long2ObjectOpenHashMap::new));
+ // Paper start
+ private final int adjX;
+ private final int adjY;
+ private final int adjZ;
+ // Paper end
private EnumDirection(int i, int j, int k, String s, EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis, BaseBlockPosition baseblockposition) {
this.g = i;
@@ -58,6 +63,11 @@ public enum EnumDirection implements INamable {
this.k = enumdirection_enumaxis;
this.l = enumdirection_enumaxisdirection;
this.m = baseblockposition;
+ // Paper start
+ this.adjX = baseblockposition.getX();
+ this.adjY = baseblockposition.getY();
+ this.adjZ = baseblockposition.getZ();
+ // Paper end
}
public static EnumDirection[] a(Entity entity) {
@@ -151,15 +161,15 @@ public enum EnumDirection implements INamable {
}
public int getAdjacentX() {
- return this.m.getX();
+ return this.adjX; // Paper
}
public int getAdjacentY() {
- return this.m.getY();
+ return this.adjY; // Paper
}
public int getAdjacentZ() {
- return this.m.getZ();
+ return this.adjZ; // Paper
}
public String m() {

View File

@ -0,0 +1,137 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Hugo Planque <hookwood01@gmail.com>
Date: Thu, 21 Jan 2021 17:56:03 +0100
Subject: [PATCH] New nbt cache
The goal of this patch is to reduce I/O operations from the main thread while saving player data and also to avoid too many I/O operations while reading NBT Player file by using a cache (Which start to delete the oldest data when there is too much player compared to the map size)
Co-authored-by: ishland <ishlandmc@yeah.net>
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 2a06f663ce64529842cfb0f4cf16c28ee143f110..084f7435ce50a10cd22e967f695064fb00c9b165 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -824,7 +824,9 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
}
// Spigot start
MCUtil.asyncExecutor.shutdown(); // Paper
+ this.worldNBTStorage.executorService.shutdown(); // Yatopia
try { MCUtil.asyncExecutor.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Paper
+ this.worldNBTStorage.executorService.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Yatopia - New async nbt cache
} catch (java.lang.InterruptedException ignored) {} // Paper
if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
LOGGER.info("Saving usercache.json");
diff --git a/src/main/java/net/minecraft/server/WorldNBTStorage.java b/src/main/java/net/minecraft/server/WorldNBTStorage.java
index 1f77b251d7e7b0f023793cbf0876fc067caa75c1..caefa07da1501cb4707dcdae15f3fefdfa260aa0 100644
--- a/src/main/java/net/minecraft/server/WorldNBTStorage.java
+++ b/src/main/java/net/minecraft/server/WorldNBTStorage.java
@@ -17,6 +17,11 @@ public class WorldNBTStorage {
private static final Logger LOGGER = LogManager.getLogger();
private final File playerDir;
protected final DataFixer a;
+ // Yatopia start - NBT Cache system
+ private final org.yatopiamc.yatopia.server.cache.NBTCache dataCache = new org.yatopiamc.yatopia.server.cache.NBTCache();
+ public final java.util.concurrent.ExecutorService executorService = java.util.concurrent.Executors.newSingleThreadExecutor(
+ new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setPriority(Thread.NORM_PRIORITY - 1).build());
+ // Yatopia end
public WorldNBTStorage(Convertable.ConversionSession convertable_conversionsession, DataFixer datafixer) {
this.a = datafixer;
@@ -30,11 +35,22 @@ public class WorldNBTStorage {
NBTTagCompound nbttagcompound = entityhuman.save(new NBTTagCompound());
File file = File.createTempFile(entityhuman.getUniqueIDString() + "-", ".dat", this.playerDir);
- NBTCompressedStreamTools.a(nbttagcompound, file);
+ // NBTCompressedStreamTools.a(nbttagcompound, file); // Yatopia
+ // Yatopia start - NBT Cache system
+ Runnable task = () -> { try { NBTCompressedStreamTools.a(nbttagcompound, file);
File file1 = new File(this.playerDir, entityhuman.getUniqueIDString() + ".dat");
File file2 = new File(this.playerDir, entityhuman.getUniqueIDString() + ".dat_old");
SystemUtils.a(file1, file, file2);
+ } catch (Exception exception) {
+ WorldNBTStorage.LOGGER.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper
+ }
+ };
+ synchronized (this.dataCache){
+ this.dataCache.put(file, nbttagcompound);
+ }
+ this.executorService.execute(task);
+ // Yatopia end
} catch (Exception exception) {
WorldNBTStorage.LOGGER.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper
}
@@ -50,9 +66,18 @@ public class WorldNBTStorage {
// Spigot Start
boolean usingWrongFile = false;
boolean normalFile = file.exists() && file.isFile(); // Akarin - ensures normal file
- if ( org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file
+ // if ( org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file // Yatopia
+ // Yatopia start - NBT Cache system
+ NBTTagCompound playerData;
+ synchronized (this.dataCache){
+ playerData = this.dataCache.get(file);
+ }
+ if (playerData == null && org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file
{
file = new File( this.playerDir, java.util.UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + entityhuman.getName() ).getBytes( "UTF-8" ) ).toString() + ".dat");
+ synchronized (this.dataCache){
+ playerData = this.dataCache.get(file);
+ }
if ( file.exists() )
{
usingWrongFile = true;
@@ -60,10 +85,13 @@ public class WorldNBTStorage {
}
}
// Spigot End
-
- if (normalFile) { // Akarin - avoid double I/O operation
+ // if (normalFile) { // Akarin - avoid double I/O operation // Yatopia
+ if (playerData != null) {
+ nbttagcompound = playerData;
+ } else if (normalFile) { // Akarin - avoid double I/O operation
nbttagcompound = NBTCompressedStreamTools.a(file);
}
+ // Yatopia end
// Spigot Start
if ( usingWrongFile )
{
diff --git a/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java b/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..942163396e41795b1b5624348e42a0c0325703f8
--- /dev/null
+++ b/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java
@@ -0,0 +1,32 @@
+package org.yatopiamc.yatopia.server.cache;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.NBTTagCompound;
+
+import java.io.File;
+
+public class NBTCache extends Object2ObjectLinkedOpenCustomHashMap<File, NBTTagCompound> {
+
+ public NBTCache() {
+ super(100, 0.75F, new Strategy<File>() {
+ @Override
+ public int hashCode(File k) {
+ return k.hashCode();
+ }
+
+ @Override
+ public boolean equals(File k, File k1) {
+ return k.equals(k1);
+ }
+ });
+ }
+
+ @Override
+ public NBTTagCompound put(File k, NBTTagCompound v) {
+ if (this.size() > MinecraftServer.getServer().getPlayerCount()) {
+ this.removeLast();
+ }
+ return super.putAndMoveToFirst(k, v);
+ }
+}

View File

@ -0,0 +1,207 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: ishland <ishlandmc@yeah.net>
Date: Wed, 27 Jan 2021 23:35:30 +0800
Subject: [PATCH] Suspected plugins report
Added "Suspected Plugins" to Watchdog, crash reports and exception messages
diff --git a/src/main/java/net/minecraft/server/CrashReport.java b/src/main/java/net/minecraft/server/CrashReport.java
index cc6e6f245ee5e73bd570cf42381bf55ee0b364d3..2f8ec64c26f5ed239b4dd9f9532c402d7081f9ed 100644
--- a/src/main/java/net/minecraft/server/CrashReport.java
+++ b/src/main/java/net/minecraft/server/CrashReport.java
@@ -87,6 +87,8 @@ public class CrashReport {
if (this.h != null && this.h.length > 0) {
stringbuilder.append("-- Head --\n");
stringbuilder.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(this.h, stringbuilder::append); // Yatopia - detailed report
+ stringbuilder.append("\n"); // Yatopia
stringbuilder.append("Stacktrace:\n");
StackTraceElement[] astacktraceelement = this.h;
int i = astacktraceelement.length;
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 084f7435ce50a10cd22e967f695064fb00c9b165..e6a23b2cf343f1e58d9de030525aba9f9f5f959b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1039,7 +1039,11 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
return;
}
// Paper end
- MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
+ // Yatopia start - detailed report
+ MinecraftServer.LOGGER.error("Encountered an unexpected exception");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(throwable.getStackTrace(), MinecraftServer.LOGGER::error);
+ MinecraftServer.LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, throwable);
+ // Yatopia end
// Spigot Start
if ( throwable.getCause() != null )
{
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
index 548c62a838848a9183e14f91b21a9dc309d8a3b2..acded2b5ef02c24f52050fcc8f06ccb5a82acfc9 100644
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
@@ -382,6 +382,11 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
// Paper start
} catch (Exception e) {
+ // Yatopia - detailed report
+ LOGGER.error("Encountered unexpected exception sending packets");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, LOGGER::error);
+ LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
LOGGER.error("NetworkException: " + player, e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));;
packet.onPacketDispatchFinish(player, null);
@@ -421,6 +426,11 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
// Paper start
} catch (Exception e) {
+ // Yatopia start - detailed report
+ LOGGER.error("Encountered unexpected exception sending packets");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, LOGGER::error);
+ LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
LOGGER.error("NetworkException: " + player, e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));;
packet.onPacketDispatchFinish(player, null);
@@ -456,6 +466,11 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
// Paper start
} catch (Exception e) {
+ // Yatopia start - detailed report
+ LOGGER.error("Encountered unexpected exception sending packets");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, LOGGER::error);
+ LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
LOGGER.error("NetworkException: " + player, e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));;
packet.onPacketDispatchFinish(player, null);
diff --git a/src/main/java/net/minecraft/server/PlayerConnectionUtils.java b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
index e698dd22607b2b2c4068c5bfb03ac53eb5bac080..4e545e467042772d49ac82492694824576dcd139 100644
--- a/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
+++ b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
@@ -45,6 +45,11 @@ public class PlayerConnectionUtils {
// Paper start
catch (Exception e) {
NetworkManager networkmanager = t0.a();
+ // Yatopia start - detailed report
+ LOGGER.error("Encountered unexpected exception sending packets");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, LOGGER::error);
+ LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
if (networkmanager.getPlayer() != null) {
LOGGER.error("Error whilst processing packet {} for {}[{}]", packet, networkmanager.getPlayer().getName(), networkmanager.getSocketAddress(), e);
} else {
diff --git a/src/main/java/net/minecraft/server/ServerConnection.java b/src/main/java/net/minecraft/server/ServerConnection.java
index 0668d383db1f3a81d1053954d72678c7ac5aecec..7b9f83e63d0f9cd83a246be33af4ab91da6b2151 100644
--- a/src/main/java/net/minecraft/server/ServerConnection.java
+++ b/src/main/java/net/minecraft/server/ServerConnection.java
@@ -153,6 +153,11 @@ public class ServerConnection {
throw new ReportedException(CrashReport.a(exception, "Ticking memory connection"));
}
+ // Yatopia start - detailed report
+ ServerConnection.LOGGER.error("Encountered unexpected exception sending packets");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(exception, ServerConnection.LOGGER::error);
+ ServerConnection.LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
ServerConnection.LOGGER.warn("Failed to handle packet for {}", networkmanager.getSocketAddress(), exception);
ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error");
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 626fab23a727073f502d934fc8f8616326ee8b52..ccf13e4daf0524c7a0c8de3b574cea8c188011ce 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -988,6 +988,10 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
// Paper start - Prevent tile entity and entity crashes
String msg = "TileEntity threw exception at " + tileentity.world.getWorld().getName() + ":" + tileentity.position.getX() + "," + tileentity.position.getY() + "," + tileentity.position.getZ();
System.err.println(msg);
+ // Yatopia start - detailed report
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(throwable, System.err::println);
+ System.err.println(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW);
+ // Yatopia end
throwable.printStackTrace();
getServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable)));
// Paper end
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index 13e461ffb2ee2e7d0440c0f60809ea99629b843c..6f921e3fd85fa3eadb32743e5f9a991a00a99ad2 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -455,6 +455,7 @@ public class CraftScheduler implements BukkitScheduler {
parsePending();
while (isReady(currentTick)) {
final CraftTask task = pending.remove();
+ lastSyncTask = task; // Yatopia - detailed report
if (task.getPeriod() < CraftTask.NO_REPEATING) {
if (task.isSync()) {
runners.remove(task.getTaskId(), task);
@@ -473,12 +474,15 @@ public class CraftScheduler implements BukkitScheduler {
task.getTaskId(),
task.getOwner().getDescription().getFullName());
if (task.getOwner() == MINECRAFT) {
- net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable);
+ // Yatopia start - detailed report
+ net.minecraft.server.MinecraftServer.LOGGER.error(msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(throwable, net.minecraft.server.MinecraftServer.LOGGER::error);
+ net.minecraft.server.MinecraftServer.LOGGER.error(org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, throwable);
} else {
- task.getOwner().getLogger().log(
- Level.WARNING,
- msg,
- throwable);
+ task.getOwner().getLogger().log(Level.SEVERE, msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(throwable, _msg -> task.getOwner().getLogger().log(Level.SEVERE, _msg));
+ task.getOwner().getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, throwable);
+ // Yatopia end
}
org.bukkit.Bukkit.getServer().getPluginManager().callEvent(
new ServerExceptionEvent(new ServerSchedulerException(msg, throwable, task)));
@@ -493,6 +497,7 @@ public class CraftScheduler implements BukkitScheduler {
// We don't need to parse pending
// (async tasks must live with race-conditions if they attempt to cancel between these few lines of code)
}
+ lastSyncTask = null; // Yatopia - detailed report
final long period = task.getPeriod(); // State consistency
if (period > 0) {
task.setNextRun(currentTick + period);
@@ -635,4 +640,10 @@ public class CraftScheduler implements BukkitScheduler {
public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException {
throw new UnsupportedOperationException("Use BukkitRunnable#runTaskTimerAsynchronously(Plugin, long, long)");
}
+ // Yatopia start - detailed report
+ private volatile CraftTask lastSyncTask = null;
+ public CraftTask getLastSyncTask() {
+ return lastSyncTask;
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 476939bde38246eb0fd96e6a4ba8076c9d1b0ff4..d13bd96716db90a5d9ab65f3c7d025b75947b71a 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -140,6 +140,15 @@ public class WatchdogThread extends Thread
log.log(Level.SEVERE, "Handling packet for connection: " + packetListener);
}
}
+ // Yatopia start - detailed report - Scheduler
+ if (Bukkit.getScheduler() instanceof org.bukkit.craftbukkit.scheduler.CraftScheduler) {
+ final org.bukkit.craftbukkit.scheduler.CraftScheduler scheduler = (org.bukkit.craftbukkit.scheduler.CraftScheduler) Bukkit.getScheduler();
+ final org.bukkit.craftbukkit.scheduler.CraftTask lastSyncTask = scheduler.getLastSyncTask();
+ if (lastSyncTask != null) {
+ log.log(Level.SEVERE, "Running task " + lastSyncTask.getTaskId() + " for '" + lastSyncTask.getOwner().getDescription().getFullName() + "', TaskClass: " + lastSyncTask.getTaskClass());
+ }
+ } else log.log(Level.SEVERE, "Unofficial scheduler, unable to get task information");
+ // Yatopia end
}
// Tuinity end - log detailed tick information
@@ -271,6 +280,10 @@ public class WatchdogThread extends Thread
log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() );
}
}
+ // Yatopia start - dump plugins info
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(thread.getStackTrace(), msg -> log.log(Level.SEVERE, msg));
+ log.log(Level.SEVERE, "");
+ // Yatopia end
log.log( Level.SEVERE, "\tStack:" );
//
for ( StackTraceElement stack : thread.getStackTrace() )

View File

@ -13,6 +13,7 @@ setupSubproject("$forkNameLowercase-server") {
projectDir = File("$forkName-Server")
buildFileName = "../subprojects/server.gradle.kts"
}
setupSubproject("Yatoclip") { }
inline fun setupSubproject(name: String, block: ProjectDescriptor.() -> Unit) {
include(name)

View File

@ -7,12 +7,6 @@ dependencies {
}
publishing {
publications {
create<MavenPublication>("server") {
from(components.getByName("java"))
}
}
repositories {
maven {
url = uri("https://repo.codemc.org/repository/nms-local/")