/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.server.launch;
import static com.google.common.io.Resources.getResource;
import static org.spongepowered.asm.mixin.MixinEnvironment.Side.SERVER;
import static org.spongepowered.server.launch.VanillaCommandLine.ACCESS_TRANSFORMER;
import static org.spongepowered.server.launch.VanillaCommandLine.NO_REDIRECT_STDOUT;
import static org.spongepowered.server.launch.VanillaCommandLine.SCAN_CLASSPATH;
import static org.spongepowered.server.launch.VanillaCommandLine.SCAN_FULL_CLASSPATH;
import static org.spongepowered.server.launch.VanillaLaunch.Environment.DEVELOPMENT;
import static org.spongepowered.server.launch.VanillaLaunch.Environment.PRODUCTION;
import joptsimple.OptionSet;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.extensibility.IRemapper;
import org.spongepowered.common.launch.SpongeLaunch;
import org.spongepowered.server.launch.console.LoggingPrintStream;
import org.spongepowered.server.launch.plugin.VanillaLaunchPluginManager;
import org.spongepowered.server.launch.transformer.at.AccessTransformers;
import org.spongepowered.server.launch.transformer.deobf.SrgRemapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public final class VanillaServerTweaker implements ITweaker {
private static final String FORGE_GRADLE_CSV_DIR = "net.minecraftforge.gradle.GradleStart.csvDir";
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
if (gameDir == null) {
gameDir = new File("");
}
SpongeLaunch.initPaths(gameDir);
OptionSet options = VanillaCommandLine.parse(args);
if (!options.has(NO_REDIRECT_STDOUT)) {
System.setOut(new LoggingPrintStream(System.out, LogManager.getLogger("STDOUT"), Level.INFO));
System.setErr(new LoggingPrintStream(System.err, LogManager.getLogger("STDERR"), Level.ERROR));
}
List<String> unrecognizedOptions = VanillaCommandLine.getUnrecognizedOptions();
if (!unrecognizedOptions.isEmpty()) {
VanillaLaunch.getLogger().warn("Found unrecognized command line option(s): {}", unrecognizedOptions);
}
}
@Override
public void injectIntoClassLoader(LaunchClassLoader loader) {
VanillaLaunch.getLogger().info("Initializing Sponge...");
// Command line options should be always parsed at this point
OptionSet options = VanillaCommandLine.getOptions().get();
configureLaunchClassLoader(loader);
// Configure class transformers
configureDeobfuscation(loader);
registerAccessTransformers(options);
configureMixinEnvironment();
searchPlugins(options);
// Register remaining class transformers
registerTransformers(loader);
VanillaLaunch.getLogger().info("Initialization finished. Starting Minecraft server...");
}
private static void configureDeobfuscation(LaunchClassLoader loader) {
// Check if we're running in de-obfuscated environment already
VanillaLaunch.getLogger().debug("Applying runtime de-obfuscation...");
if (VanillaLaunch.ENVIRONMENT == PRODUCTION) {
// Enable Notch->Searge deobfuscation
VanillaLaunch.getLogger().info("De-obfuscation mappings are provided by MCP (http://www.modcoderpack.com)");
Launch.blackboard.put("vanilla.srg_mappings", getResource("mappings.srg"));
loader.registerTransformer("org.spongepowered.server.launch.transformer.deobf.NotchDeobfuscationTransformer");
} else {
// Enable Searge->MCP deobfuscation (if running in ForgeGradle)
String mcpDir = System.getProperty(FORGE_GRADLE_CSV_DIR);
if (mcpDir != null) {
Launch.blackboard.put("vanilla.mcp_mappings", Paths.get(mcpDir));
loader.registerTransformer("org.spongepowered.server.launch.transformer.deobf.SeargeDeobfuscationTransformer");
} else {
VanillaLaunch.getLogger().warn("SRG -> MCP de-obfuscation is disabled because MCP mappings cannot be found");
}
}
}
private static void registerAccessTransformers(OptionSet options) {
VanillaLaunch.getLogger().debug("Registering access transformers...");
try {
// Apply our access transformers
AccessTransformers.register(getResource("META-INF/common_at.cfg"));
AccessTransformers.register(getResource("META-INF/vanilla_at.cfg"));
} catch (IOException e) {
throw new LaunchException("Failed to register SpongeCommon/SpongeVanilla access transformers", e);
}
// Apply access transformers from command line
for (String at : options.valuesOf(ACCESS_TRANSFORMER)) {
try {
// First check if the AT exists as file
Path path = Paths.get(at);
if (Files.isReadable(path)) {
AccessTransformers.register(path);
} else {
// Try as resource in classpath instead
AccessTransformers.register(getResource(at));
}
} catch (IOException e) {
VanillaLaunch.getLogger().error("Failed to load access transformer from {}", at, e);
}
}
}
private static void configureMixinEnvironment() {
VanillaLaunch.getLogger().debug("Initializing Mixin environment...");
SpongeLaunch.setupMixinEnvironment();
Mixins.addConfiguration("mixins.vanilla.core.json");
Mixins.addConfiguration("mixins.vanilla.entityactivation.json");
Mixins.addConfiguration("mixins.vanilla.chunkio.json");
MixinEnvironment.getDefaultEnvironment().setSide(SERVER);
// Add our remapper to Mixin's remapper chain
SrgRemapper remapper = VanillaLaunch.getRemapper();
if (remapper instanceof IRemapper) {
MixinEnvironment.getDefaultEnvironment().getRemappers().add((IRemapper) remapper);
}
}
private static void searchPlugins(OptionSet options) {
VanillaLaunch.getLogger().debug("Searching for plugins...");
try {
// Search for plugins (and apply access transformers if available)
VanillaLaunchPluginManager.findPlugins(
VanillaLaunch.ENVIRONMENT == DEVELOPMENT || options.has(SCAN_CLASSPATH), options.has(SCAN_FULL_CLASSPATH));
} catch (IOException e) {
throw new LaunchException("Failed to search for plugins", e);
}
}
private static void registerTransformers(LaunchClassLoader loader) {
// Register the access transformer (at this point new access transformers can be no longer registered)
loader.registerTransformer("org.spongepowered.server.launch.transformer.at.AccessTransformer");
// Superclass transformer
loader.registerTransformer(SpongeLaunch.SUPERCLASS_TRANSFORMER);
SpongeLaunch.setupSuperClassTransformer();
}
@Override
public String getLaunchTarget() {
return "org.spongepowered.server.SpongeVanilla";
}
@Override
public String[] getLaunchArguments() {
return new String[0];
}
private static void configureLaunchClassLoader(LaunchClassLoader loader) {
// Logging
loader.addClassLoaderExclusion("org.slf4j.");
loader.addClassLoaderExclusion("org.jline.");
loader.addClassLoaderExclusion("com.sun.");
loader.addClassLoaderExclusion("com.mojang.util.QueueLogAppender");
// Sponge Launch
loader.addClassLoaderExclusion("joptsimple.");
loader.addClassLoaderExclusion("com.google.common.");
loader.addClassLoaderExclusion("org.spongepowered.common.launch.");
loader.addClassLoaderExclusion("org.spongepowered.server.launch.");
loader.addClassLoaderExclusion("org.spongepowered.plugin.");
// Don't allow transforming libraries
loader.addTransformerExclusion("com.google.");
loader.addTransformerExclusion("org.apache.");
loader.addTransformerExclusion("io.netty.");
loader.addTransformerExclusion("com.flowpowered.");
loader.addTransformerExclusion("it.unimi.dsi.fastutil.");
loader.addTransformerExclusion("com.github.benmanes.caffeine.");
// Guice
loader.addTransformerExclusion("org.aopalliance.");
// Configurate
loader.addTransformerExclusion("ninja.leaping.configurate.");
loader.addTransformerExclusion("com.typesafe.config.");
loader.addTransformerExclusion("org.yaml.snakeyaml.");
// Database connectors
loader.addTransformerExclusion("com.zaxxer.hikari.");
loader.addTransformerExclusion("org.h2.");
loader.addTransformerExclusion("org.mariadb.");
loader.addTransformerExclusion("org.sqlite.");
}
}