Rework how methods are registered (now very cursed) and disable potentially unsafe methods for the curseforge release

This commit is contained in:
JFronny 2021-04-13 09:30:45 +02:00
parent dbf52c7d5b
commit 27ac41c990
No known key found for this signature in database
GPG Key ID: BEC5ACBBD4EE17E5
29 changed files with 352 additions and 159 deletions

View File

@ -22,4 +22,5 @@ deploy:
script:
- .\src\main\c\build.bat
- .\gradlew --build-cache publishModrinth
- .\gradlew --build-cache curseforge
- rm build/libs/*
- .\gradlew --build-cache -Pflavor=curseforge curseforge

View File

@ -23,6 +23,7 @@ The values are explained in more detail below
- SemiUnsafe_Universal_Shutdown: Attempts to run a shutdown command. Since these are specific to some systems this might not always work.
- None: Do nothing
Please note that all methods marked "Unsafe" as well as the shutdown method are not available in the curseforge release
# Building
BreakMe consists of two parts: The main mod and the DLL used for Unsafe_Windows_WinAPI, which are built separately.\

View File

@ -12,7 +12,10 @@ archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
ext.flavor = project.hasProperty('flavor') ? project.getProperty('flavor') : 'modrinth'
repositories {
maven { url = "https://maven.shedaniel.me/"; name = "Cloth Config" }
maven { url = 'https://maven.terraformersmc.com/'; name = "ModMenu" }
}
@ -25,18 +28,8 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
modCompile "me.sargunvohra.mcmods:autoconfig1u:3.3.1"
include "me.sargunvohra.mcmods:autoconfig1u:3.3.1"
modApi ("me.shedaniel.cloth:config-2:4.8.3") {
exclude(group: "net.fabricmc.fabric-api")
}
include ("me.shedaniel.cloth:config-2:4.8.3") {
exclude(group: "net.fabricmc.fabric-api")
}
modImplementation "com.terraformersmc:modmenu:1.14.15"
modApi include ("me.shedaniel.cloth:cloth-config-fabric:4.11.19")
modImplementation "com.terraformersmc:modmenu:1.16.9"
}
processResources {
@ -49,6 +42,9 @@ processResources {
from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json"
if (flavor == "curseforge") {
exclude "native/natives.dll"
}
}
}
@ -71,6 +67,11 @@ jar {
from "LICENSE"
}
if (flavor == "curseforge") {
sourceSets.main.java.filter.exclude("**/unsafe/*")
sourceSets.main.resources.exclude("**/native/*")
}
import com.modrinth.minotaur.TaskModrinthUpload
task publishModrinth (type: TaskModrinthUpload){

View File

@ -3,12 +3,12 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.3
loader_version=0.11.1
yarn_mappings=1.16.5+build.6
loader_version=0.11.3
# Mod Properties
mod_version=1.1
mod_version=2.0
maven_group=io.gitlab.jfronny
archives_base_name=breakme
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.29.4+1.16
fabric_version=0.32.5+1.16

View File

@ -0,0 +1,76 @@
package io.gitlab.jfronny.breakme;
import io.gitlab.jfronny.breakme.config.Cfg;
import io.gitlab.jfronny.breakme.config.CrashCause;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import me.shedaniel.autoconfig.AutoConfig;
import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer;
import net.fabricmc.api.ModInitializer;
import net.minecraft.entity.player.PlayerEntity;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BreakMe implements ModInitializer {
public static final String MOD_ID = "breakme";
private static final Logger log = LogManager.getFormatterLogger(MOD_ID);
public static Cfg cfg;
private static final Map<String, CrashProvider> crashProviders;
static {
//Get config
AutoConfig.register(Cfg.class, JanksonConfigSerializer::new);
cfg = AutoConfig.getConfigHolder(Cfg.class).getConfig();
//Get crash providers
crashProviders = new HashMap<>();
try {
List<Class<?>> classes = ClassFinder.find(CrashProvider.class.getPackage().getName());
for (Class<?> clazz : classes) {
if (CrashProvider.class.isAssignableFrom(clazz) && !CrashProvider.class.equals(clazz)) {
try {
CrashProvider provider = (CrashProvider) clazz.getDeclaredConstructor().newInstance();
crashProviders.put(provider.getName(), provider);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
if (!crashProviders.containsKey(cfg.method)) {
cfg.method = "None";
Log("Could not find specified crash provider, defaulting to None");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void Log(String str) {
log.log(Level.INFO, "[" + MOD_ID + "] " + str);
}
@Override
public void onInitialize() {
Log("Prepare for trouble");
}
public static void Crash(PlayerEntity player) throws Exception {
if (cfg.event == CrashCause.All || cfg.event == CrashCause.Damage || (cfg.event == CrashCause.Death && player.isDead())) {
Log("invoking the crash");
if (!crashProviders.containsKey(cfg.method)) {
cfg.method = "None";
Log("Could not find specified crash provider, defaulting to None");
}
crashProviders.get(cfg.method).crash();
}
}
public static String[] getProviders() {
return crashProviders.keySet().toArray(new String[0]);
}
}

View File

@ -0,0 +1,40 @@
package io.gitlab.jfronny.breakme;
import io.gitlab.jfronny.breakme.config.Cfg;
import io.gitlab.jfronny.breakme.config.CrashMethod;
import me.shedaniel.autoconfig.AutoConfig;
import me.shedaniel.autoconfig.gui.registry.GuiRegistry;
import me.shedaniel.autoconfig.gui.registry.api.GuiRegistryAccess;
import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
import net.fabricmc.api.ClientModInitializer;
import net.minecraft.text.TranslatableText;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import static me.shedaniel.autoconfig.util.Utils.getUnsafely;
import static me.shedaniel.autoconfig.util.Utils.setUnsafely;
public class BreakMeClient implements ClientModInitializer {
ConfigEntryBuilder builder = ConfigEntryBuilder.create();
@Override
public void onInitializeClient() {
GuiRegistry registry = AutoConfig.getGuiRegistry(Cfg.class);
registry.registerAnnotationProvider(this::get, CrashMethod.class);
registry.registerTypeProvider(this::get, String.class);
}
private List<AbstractConfigListEntry> get(String i13n, Field field, Object config, Object defaults, GuiRegistryAccess guiProvider) {
return Collections.singletonList(
builder.startSelector(
new TranslatableText(i13n),
BreakMe.getProviders(),
getUnsafely(field, config, getUnsafely(field, defaults))
).setDefaultValue(() -> "None")
.setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
.build()
);
}
}

View File

@ -0,0 +1,36 @@
package io.gitlab.jfronny.breakme;
import net.fabricmc.loader.api.FabricLoader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
public class ClassFinder {
public static List<Class<?>> find(String packageName) throws NoSuchElementException, IOException {
String path = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Path p = FabricLoader.getInstance().getModContainer(BreakMe.MOD_ID).get().getPath(path);
return findInternal(p, classLoader);
}
private static List<Class<?>> findInternal(Path path, ClassLoader classLoader) throws IOException {
List<Class<?>> result = new ArrayList<>();
Files.list(path).forEach(s -> {
try {
if (Files.isDirectory(s)) {
result.addAll(findInternal(s, classLoader));
} else if (s.getFileName().toString().endsWith(".class")) {
String p = s.toString().replace('/', '.');
result.add(classLoader.loadClass(p.substring(1, p.length() - 6)));
}
} catch (Throwable e) {
e.printStackTrace();
}
});
return result;
}
}

View File

@ -0,0 +1,13 @@
package io.gitlab.jfronny.breakme;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import io.gitlab.jfronny.breakme.config.Cfg;
import me.shedaniel.autoconfig.AutoConfig;
public class ModMenuAPI implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return screen -> AutoConfig.getConfigScreen(Cfg.class, screen).get();
}
}

View File

@ -1,73 +0,0 @@
package io.gitlab.jfronny.breakme.breakme;
import io.gitlab.jfronny.breakme.breakme.config.Cfg;
import io.gitlab.jfronny.breakme.breakme.config.CrashCause;
import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
import me.sargunvohra.mcmods.autoconfig1u.serializer.JanksonConfigSerializer;
import net.fabricmc.api.ModInitializer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.dedicated.command.StopCommand;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Locale;
public class BreakMe implements ModInitializer {
public static final String MOD_ID = "breakme";
private static final Logger log = LogManager.getFormatterLogger(MOD_ID);
public static Cfg cfg;
static {
AutoConfig.register(Cfg.class, JanksonConfigSerializer::new);
cfg = AutoConfig.getConfigHolder(Cfg.class).getConfig();
}
public static void Log(String str) {
log.log(Level.INFO, "[" + MOD_ID + "] " + str);
}
@Override
public void onInitialize() {
Log("Prepare for trouble");
}
public static void Crash(PlayerEntity player) throws Exception {
Runtime runtime = Runtime.getRuntime();
if (cfg.event == CrashCause.All || cfg.event == CrashCause.Damage || (cfg.event == CrashCause.Death && player.isDead())) {
Log("invoking the crash");
switch (cfg.method) {
case Unsafe_Universal_Forkbomb:
forkbomb.main(new String[0]);
break;
case Unsafe_Windows_WinAPI:
NativeCrash.Crash();
break;
case Broken_Universal_ExitCode:
System.exit(-1);
break;
case Safe_Universal_Exception:
//This is cool because it will crash but that is good
MinecraftClient.getInstance().stop();
StopCommand.register(null);
throw new Exception("You did bad, now die");
case SemiUnsafe_Universal_Exception:
throw new SecurityException("You did bad, now die");
case SemiUnsafe_Universal_Shutdown:
String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if (OS.contains("win")) {
runtime.exec("shutdown -s -t 0");
} else {
if (!OS.contains("nux")) {
Log("This OS is not supported for this, will try GNU/Linux method (detected: " + OS + ")");
}
runtime.exec("shutdown 0");
}
break;
case None:
break;
}
}
}
}

View File

@ -1,13 +0,0 @@
package io.gitlab.jfronny.breakme.breakme;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import io.gitlab.jfronny.breakme.breakme.config.Cfg;
import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
public class ModMenuAPI implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return screen -> AutoConfig.getConfigScreen(Cfg.class, screen).get();
}
}

View File

@ -1,16 +0,0 @@
package io.gitlab.jfronny.breakme.breakme;
import java.io.IOException;
public class NativeCrash {
private native void CrashWindows_Native();
public static void Crash() {
try {
NativeUtils.loadLibraryFromJar("/native/natives.dll");
new NativeCrash().CrashWindows_Native();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,17 +0,0 @@
package io.gitlab.jfronny.breakme.breakme.config;
import io.gitlab.jfronny.breakme.breakme.BreakMe;
import me.sargunvohra.mcmods.autoconfig1u.ConfigData;
import me.sargunvohra.mcmods.autoconfig1u.annotation.Config;
import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry.Gui.EnumHandler;
import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.Comment;
@Config(name = BreakMe.MOD_ID)
public class Cfg implements ConfigData {
@Comment("What should cause a crash")
@EnumHandler(option = EnumHandler.EnumDisplayOption.BUTTON)
public CrashCause event = CrashCause.Death;
@Comment("The method used to perform the crash")
@EnumHandler(option = EnumHandler.EnumDisplayOption.BUTTON)
public CrashMethod method = CrashMethod.Safe_Universal_Exception;
}

View File

@ -1,12 +0,0 @@
package io.gitlab.jfronny.breakme.breakme.config;
public enum CrashMethod {
Unsafe_Universal_Forkbomb,
Unsafe_Windows_WinAPI,
Broken_Universal_ExitCode,
Safe_Universal_Exception,
SemiUnsafe_Universal_Exception,
SemiUnsafe_Universal_Shutdown,
None
}

View File

@ -0,0 +1,17 @@
package io.gitlab.jfronny.breakme.config;
import io.gitlab.jfronny.breakme.BreakMe;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.Config;
import me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.EnumHandler;
import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment;
@Config(name = BreakMe.MOD_ID)
public class Cfg implements ConfigData {
@Comment("What should cause a crash")
@EnumHandler(option = EnumHandler.EnumDisplayOption.BUTTON)
public CrashCause event = CrashCause.Death;
@Comment("The method used to perform the crash")
@CrashMethod
public String method = "Safe_Universal_Exception";
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.breakme.breakme.config;
package io.gitlab.jfronny.breakme.config;
public enum CrashCause {
Damage,

View File

@ -0,0 +1,4 @@
package io.gitlab.jfronny.breakme.config;
public @interface CrashMethod {
}

View File

@ -0,0 +1,6 @@
package io.gitlab.jfronny.breakme.crash;
public interface CrashProvider {
void crash() throws Exception;
String getName();
}

View File

@ -0,0 +1,13 @@
package io.gitlab.jfronny.breakme.crash;
public class NoneProvider implements CrashProvider {
@Override
public void crash() throws Exception {
}
@Override
public String getName() {
return "None";
}
}

View File

@ -0,0 +1,19 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import net.minecraft.client.MinecraftClient;
import net.minecraft.server.dedicated.command.StopCommand;
public class ExceptionProvider implements CrashProvider {
@Override
public void crash() throws Exception {
MinecraftClient.getInstance().stop();
StopCommand.register(null);
throw new Exception("You did bad, now die");
}
@Override
public String getName() {
return "Safe_Universal_Exception";
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
public class ExitCodeProvider implements CrashProvider {
@Override
public void crash() {
System.exit(-1);
}
@Override
public String getName() {
return "Broken_Universal_ExitCode";
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
public class SecurityExceptionProvider implements CrashProvider {
@Override
public void crash() throws Exception {
throw new SecurityException("You did bad, now die");
}
@Override
public String getName() {
return "SemiUnsafe_Universal_Exception";
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.breakme.crash.unsafe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
public class ForkbombProvider implements CrashProvider {
@Override
public void crash() {
forkbomb.main(new String[0]);
}
@Override
public String getName() {
return "Unsafe_Universal_Forkbomb";
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.breakme.breakme;
package io.gitlab.jfronny.breakme.crash.unsafe;
import java.io.*;
import java.nio.file.FileSystemNotFoundException;

View File

@ -0,0 +1,28 @@
package io.gitlab.jfronny.breakme.crash.unsafe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import java.util.Locale;
import static io.gitlab.jfronny.breakme.BreakMe.Log;
public class ShutdownProvider implements CrashProvider {
@Override
public void crash() throws Exception {
Runtime runtime = Runtime.getRuntime();
String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if (OS.contains("win")) {
runtime.exec("shutdown -s -t 0");
} else {
if (!OS.contains("nux")) {
Log("This OS is not supported for this, will try GNU/Linux method (detected: " + OS + ")");
}
runtime.exec("shutdown 0");
}
}
@Override
public String getName() {
return "SemiUnsafe_Universal_Shutdown";
}
}

View File

@ -0,0 +1,23 @@
package io.gitlab.jfronny.breakme.crash.unsafe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import java.io.IOException;
public class WinApiProvider implements CrashProvider {
private native void CrashWindows_Native();
@Override
public void crash() {
try {
NativeUtils.loadLibraryFromJar("/native/natives.dll");
new WinApiProvider().CrashWindows_Native();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getName() {
return "Unsafe_Windows_WinAPI";
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.breakme.breakme;
package io.gitlab.jfronny.breakme.crash.unsafe;
import java.io.File;
import java.io.IOException;
@ -17,7 +17,7 @@ public class forkbomb {
t = new File(f, "javaw.exe");
//noinspection InfiniteLoopStatement
while (true) {
Runtime.getRuntime().exec(new String[]{t.toString(), "-cp", System.getProperty("java.class.path"), "io.gitlab.jfronny.breakme.breakme.forkbomb"});
Runtime.getRuntime().exec(new String[]{t.toString(), "-cp", System.getProperty("java.class.path"), "io.gitlab.jfronny.breakme.crash.unsafe.forkbomb"});
}
} catch (IOException e) {
e.printStackTrace();

View File

@ -1,13 +1,11 @@
package io.gitlab.jfronny.breakme.breakme.mixin;
package io.gitlab.jfronny.breakme.mixin;
import io.gitlab.jfronny.breakme.breakme.BreakMe;
import io.gitlab.jfronny.breakme.breakme.config.CrashCause;
import io.gitlab.jfronny.breakme.BreakMe;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerEntity.class)

View File

@ -1,7 +1,7 @@
{
"required": true,
"minVersion": "0.8",
"package": "io.gitlab.jfronny.breakme.breakme.mixin",
"package": "io.gitlab.jfronny.breakme.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinPlayerEntity"

View File

@ -16,10 +16,13 @@
"environment": "*",
"entrypoints": {
"main": [
"io.gitlab.jfronny.breakme.breakme.BreakMe"
"io.gitlab.jfronny.breakme.BreakMe"
],
"client": [
"io.gitlab.jfronny.breakme.BreakMeClient"
],
"modmenu": [
"io.gitlab.jfronny.breakme.breakme.ModMenuAPI"
"io.gitlab.jfronny.breakme.ModMenuAPI"
]
},
"mixins": [