Compare commits

...

19 Commits

Author SHA1 Message Date
Johannes Frohnmeyer de00f51b06
fix: build in gradle:latest
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2024-04-25 22:21:50 +02:00
Johannes Frohnmeyer 3fa30b834d
chore: update to 1.20.5
ci/woodpecker/push/woodpecker Pipeline failed Details
ci/woodpecker/tag/woodpecker Pipeline failed Details
2024-04-25 21:51:15 +02:00
Johannes Frohnmeyer 0e036599cf
fix: don't respond to events for other players if on client
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2024-03-31 14:13:42 +02:00
Johannes Frohnmeyer 3b5e262bea
fix: hook additional methods for a better chance to catch damage
ci/woodpecker/tag/woodpecker Pipeline is pending Details
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-03-31 13:58:58 +02:00
Johannes Frohnmeyer 26ef532b30
chore: update to 1.20.4
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-12-07 21:16:27 +01:00
Johannes Frohnmeyer 70758f3966
fix: switch default crash provider. Someone had reported issues with the previous default, this one might work better.
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-11-02 17:16:12 +01:00
Johannes Frohnmeyer d924c522a7
chore: update to 1.20.2
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-09-22 21:16:51 +02:00
Johannes Frohnmeyer d074812af3
feat: Use JNA for WinApiProvider, remove natives
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-07-28 18:54:31 +02:00
Johannes Frohnmeyer 49b97ad38d
Bump to 1.20
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2023-06-09 17:31:37 +02:00
Johannes Frohnmeyer d9bdd56c6b
Bump to 1.19.4 and add "Hang" crash
ci/woodpecker/push/woodpecker Pipeline failed Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2023-03-14 22:31:09 +01:00
Johannes Frohnmeyer 2a20216d32
Additional crash providers: OOM, Segfault and StackOverflow
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-03-11 12:13:47 +01:00
Johannes Frohnmeyer 21690627d4
Update natives for new paths
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/manual/woodpecker Pipeline failed Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2023-01-06 11:43:12 +01:00
Johannes Frohnmeyer 1cd569d200
Ensure initialized
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-29 15:59:00 +01:00
Johannes Frohnmeyer 2289839966
Use new config compiler and experiment with panama (untested and disabled)
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-29 15:51:08 +01:00
Johannes Frohnmeyer 41b9062065
Update to 1.19.3
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2022-12-07 22:30:46 +01:00
Johannes Frohnmeyer 875901d280
Tweak build
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-03 16:30:16 +01:00
Johannes Frohnmeyer dd47e1008e
Simplify build script
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-03 16:19:36 +01:00
Johannes Frohnmeyer d10d8e36cd
Attempt to fix
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-02 19:03:35 +01:00
Johannes Frohnmeyer b672e8f929
Migrate to new infrastructure
ci/woodpecker/manual/woodpecker Pipeline failed Details
2022-12-02 18:59:18 +01:00
25 changed files with 490 additions and 260 deletions

View File

@ -1,61 +0,0 @@
stages:
- compile_native
- build
- deploy
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=-Xmx2G"
build_natives:
tags:
- windows
stage: compile_native
script:
- .\src\main\c\build.bat
artifacts:
paths:
- src/main/resources/native/natives.dll
only:
- master
build_test:
image: gradle:jdk17
stage: deploy
script:
- gradle --build-cache deployDebug -Pmaven="$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/maven"
- rm src/main/java/io/gitlab/jfronny/breakme/crash/KnownProviders.java
- mv build/libs/* ./
- mv build/devlibs/*-dev.jar ./
- rm *-maven.jar *-sources.jar *-testmod.jar
- mv *-dev.jar dev-free.zip
- mv *.jar latest.zip
- gradle --build-cache -Pflavor=curseforge build
- rm src/main/java/io/gitlab/jfronny/breakme/crash/KnownProviders.java
- cp build/libs/* ./
- cp build/devlibs/*-dev.jar ./
- rm *-maven.jar *-sources.jar *-testmod.jar
- mv *-dev.jar dev.zip
- mv *.jar latest-cf.jar
- mv latest.zip latest.jar
- mv dev-free.zip latest-dev.jar
- mv dev.zip latest-cf-dev.jar
artifacts:
paths:
- latest.jar
- latest-cf.jar
- latest-dev.jar
- latest-cf-dev.jar
only:
- master
deploy:
image: gradle:jdk17
rules:
- if: $CI_COMMIT_TAG && '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^master/'
stage: deploy
script:
- gradle --build-cache build publish modrinth modrinthSyncBody -Prelease -Pmaven="$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/maven"
- rm src/main/java/io/gitlab/jfronny/breakme/crash/KnownProviders.java
- rm build/libs/*
- rm build/devlibs/*
- gradle --build-cache -Pflavor=curseforge build curseforge -Prelease

32
.woodpecker.yml Normal file
View File

@ -0,0 +1,32 @@
#include https://pages.frohnmeyer-wds.de/scripts/clone.yml
steps:
build_test:
image: gradle:latest
pull: true
commands:
- mkdir artifacts
- if [ $CI_PIPELINE_EVENT = tag ]; then
- gradle --build-cache moveArtifacts curseforge -Pflavour=curseforge -Prelease
- gradle --build-cache moveArtifacts publish modrinth modrinthSyncBody -Prelease -Pmaven="https://maven.frohnmeyer-wds.de/artifacts"
- else
- gradle --build-cache moveArtifacts -Pflavour=curseforge
- gradle --build-cache moveArtifacts deployDebug -Pmaven="https://maven.frohnmeyer-wds.de/artifacts"
- fi
secrets: [ maven_token, maven_name, modrinth_api_token, curseforge_api_token ]
artifacts:
image: woodpeckerci/plugin-s3
pull: true
settings:
bucket: pages
region: nebula
path_style: true
endpoint: https://s3.frohnmeyer-wds.de
access_key: pages
secret_key:
from_secret: pages_secret
source: build/artifacts/**/*
strip_prefix: build/
target: /${CI_REPO}
when:
- branch: ${CI_REPO_DEFAULT_BRANCH}

View File

@ -17,18 +17,21 @@ The values are explained in more detail below
- Method:
- Unsafe_Universal_Forkbomb: Launch a self-multiplying process
- Unsafe_Windows_WinAPI: Do some JNI-magic to instantly produce a blue-screen on windows
- Unsafe_Universal_OOM: Causes an OOM exception
- Broken_Universal_ExitCode: Exit the integrated Server. The game still displays, but you can no longer interact with the world or quit.
- Safe_Universal_Hang: Hang both the client and integrated server thread.
- Safe_Universal_Exception: Performs an invalid operation. Behaves like every other crash and just closes the game, leaving a crash log.
- SemiUnsafe_Universal_Exception: Throws a security exceptions. This does not work properly for 1.16.2
- SemiUnsafe_Universal_Shutdown: Attempts to run a shutdown command. Since these are specific to some systems this might not always work.
- SemiUnsafe_Universal_Segfault: Causes a segmentation fault using lwjgl.
- SemiUnsafe_Universal_StackOverflow: Causes a stack overflow via infinite recursion.
- None: Do nothing
Please note that all methods marked "Unsafe" as well as the shutdown method are not available in the curseforge release
Please note that all methods marked "Unsafe" as well as the shutdown method are not available in the CurseForge release
<!-- modrinth_exclude.start -->
# Building
BreakMe consists of two parts: The main mod and the DLL used for Unsafe_Windows_WinAPI, which are built separately.\
If you don't build on windows you can use the dll contained in the latest [CI build](https://gitlab.com/jfmods/BreakMe/-/jobs/artifacts/master/browse/build/libs?job=build_test) and place it in src/main/resources/native.\
If you want to build the dll, use [this](https://gitlab.com/jfmods/BreakMe/-/blob/master/src/main/c/build.bat) as a reference (requires VS build tools).\
The mod itself is built like any other fabric mod: using gradle. Look at the [CI config](https://gitlab.com/jfmods/BreakMe/-/blob/master/.gitlab-ci.yml#L10) for an example\
A prebuilt DLL is included in the source tree and will be removed once it is replaced by a panama implementation.\
If you want to build it yourself, use src/main/c/build.bat to build it on a windows PC.\
The mod itself is built like any other fabric mod: using gradle
<!-- modrinth_exclude.end -->

View File

@ -1,48 +0,0 @@
import java.util.stream.Collectors
import java.nio.file.Files
import java.nio.file.Path
apply from: "https://jfmods.gitlab.io/scripts/gradle/v2.gradle"
ext.flavor = project.hasProperty('flavor') ? project.getProperty('flavor') : 'modrinth'
dependencies {
modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v1:$project.libjf_version")
// Dev env
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:$project.libjf_version")
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny-v1:$project.libjf_version")
modLocalRuntime("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}")
modLocalRuntime("com.terraformersmc:modmenu:4.0.6")
}
def list(String dir) {
return Files.list(Path.of("$projectDir/$dir"))
.map(p -> p.fileName.toString())
.map(p -> p.substring(0, p.lastIndexOf('.')))
.filter(p -> p.endsWith("Provider"))
.toArray(String[]::new)
}
String[] paths = list("src/main/java/io/gitlab/jfronny/breakme/crash/safe")
if (flavor == "curseforge") {
sourceSets.main.java.filter.exclude("**/unsafe/*")
sourceSets.main.resources.exclude("**/native/*")
} else {
paths += list("src/main/java/io/gitlab/jfronny/breakme/crash/unsafe")
}
Files.writeString(Path.of("$projectDir/src/main/java/io/gitlab/jfronny/breakme/crash/KnownProviders.java"), """
package io.gitlab.jfronny.breakme.crash;
import io.gitlab.jfronny.breakme.crash.safe.*;
${if (flavor == "curseforge") "" else "import io.gitlab.jfronny.breakme.crash.unsafe.*;"}
import java.util.Map;
public class KnownProviders {
public static final Map<String, CrashProvider> PROVIDERS = Map.of(
${Arrays.stream(paths).map(path -> "\"${path.substring(0, path.length() - "Provider".length())}\", new $path()").collect(Collectors.joining(",\n "))}
);
}
""")

108
build.gradle.kts Normal file
View File

@ -0,0 +1,108 @@
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeSpec
import io.gitlab.jfronny.scripts.*
import java.nio.file.Files
import java.util.*
import java.util.function.Supplier
import javax.lang.model.element.Modifier.*
plugins {
id("jfmod") version "1.6-SNAPSHOT"
}
allprojects { group = "io.gitlab.jfronny" }
base.archivesName = "breakme"
// https://fabricmc.net/develop/
jfMod {
minecraftVersion = "1.20.5"
yarn("build.1")
loaderVersion = "0.15.10"
libJfVersion = "3.15.5"
fabricApiVersion = "0.97.6+1.20.5"
modrinth {
projectId = "breakme"
requiredDependencies.add("libjf")
optionalDependencies.add("modmenu")
}
curseforge {
projectId = "400842"
requiredDependencies.add("libjf")
optionalDependencies.add("modmenu")
}
}
if (flavour == "") flavour = "modrinth"
dependencies {
modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v2")
// Dev env
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil")
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny")
modLocalRuntime("net.fabricmc.fabric-api:fabric-api")
modLocalRuntime("com.terraformersmc:modmenu:10.0.0-beta.1")
}
loom {
runs {
this.named("client").get().vmArg("-Xmx2G")
}
}
fun list(`package`: String) = Files.list(projectDir.resolve("src/main/java").resolve(`package`.replace('.', '/')).toPath()).use { stream ->
stream
.map { it.fileName.toString() }
.map { it.substring(0, it.lastIndexOf('.')) }
.filter { it.endsWith("Provider") }
.map { ClassName.get(`package`, it) }
.toList()
}
val classes = LinkedList(list("io.gitlab.jfronny.breakme.crash.safe"))
if (flavour == "curseforge") {
sourceSets.main.get().java.filter.exclude("**/unsafe/*")
} else {
classes.addAll(list("io.gitlab.jfronny.breakme.crash.unsafe"))
}
sourceSets {
main {
generate(project) {
val providerType = ClassName.get("io.gitlab.jfronny.breakme.crash", "CrashProvider")
val supplierType = ParameterizedTypeName.get(ClassName.get(Supplier::class.java), providerType)
`enum`("io.gitlab.jfronny.breakme.crash", "Method") {
modifiers(PUBLIC)
superInterface(providerType)
for (klazz in classes) {
val name = klazz.simpleName()
enumConstant(name.substring(0, name.length - "Provider".length), TypeSpec.anonymousClassBuilder("\$T::new", klazz).build())
}
field(supplierType, "provider", PRIVATE, FINAL)
constructor {
parameter(supplierType, "provider")
code {
addStatement("this.provider = new \$T<>(provider)", ClassName.get("io.gitlab.jfronny.commons", "LazySupplier"))
}
}
method("crash") {
modifiers(PUBLIC)
exception(Exception::class.java)
annotation(Override::class.java)
code {
addStatement("provider.get().crash()")
}
}
}
}
}
}

View File

@ -1,16 +0,0 @@
# https://fabricmc.net/develop/
minecraft_version=1.19.2
yarn_mappings=build.8
loader_version=0.14.9
maven_group=io.gitlab.jfronny
archives_base_name=breakme
fabric_version=0.60.0+1.19.2
libjf_version=3.0.3
modrinth_id=breakme
modrinth_required_dependencies=libjf
modrinth_optional_dependencies=modmenu
curseforge_id=400842
curseforge_required_dependencies=libjf
curseforge_optional_dependencies=modmenu

9
settings.gradle.kts Normal file
View File

@ -0,0 +1,9 @@
pluginManagement {
repositories {
maven("https://maven.fabricmc.net/") // FabricMC
maven("https://maven.frohnmeyer-wds.de/artifacts") // scripts
gradlePluginPortal()
}
}
rootProject.name = "breakme"

View File

@ -0,0 +1,21 @@
package io.gitlab.jfronny.breakme.client;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.thread.ThreadExecutor;
import java.util.Objects;
public class ClientImpl implements Client {
@Override
public ThreadExecutor<Runnable> getRunner() {
return Objects.requireNonNull(MinecraftClient.getInstance());
}
@Override
public boolean isValidPlayer(PlayerEntity player) {
MinecraftClient client = MinecraftClient.getInstance();
if (client == null || client.player == null) return false;
return client.player.getUuid().equals(player.getUuid());
}
}

View File

@ -1,14 +0,0 @@
@echo off
echo Setting up native env
cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build"
call vcvarsall x86_x64
cd %~dp0
echo Building natives
cl .\io_gitlab_jfronny_breakme_breakme_NativeCrash.cpp /I"C:\Program Files\OpenJDK\jdk-12.0.2\include" /I"C:\Program Files\OpenJDK\jdk-12.0.2\include\win32" -Fenatives.dll -MD -LD
echo Running post-build steps
del .\natives.exp
del .\natives.lib
del .\io_gitlab_jfronny_breakme_breakme_NativeCrash.obj
del ..\resources\native\natives.dll
copy /y natives.dll ..\resources\native\natives.dll
echo Natives complete

View File

@ -1,36 +0,0 @@
/*
#include <windows.h>
#include "io_gitlab_jfronny_breakme_breakme_NativeCrash.h"
#pragma comment(lib, "ntdll.lib")
extern "C" NTSTATUS NTAPI RtlAdjustPrivilege(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN OldValue);
extern "C" NTSTATUS NTAPI NtRaiseHardError(LONG ErrorStatus, ULONG NumberOfParameters, ULONG UnicodeStringParameterMask,
PULONG_PTR Parameters, ULONG ValidResponseOptions, PULONG Response);
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_breakme_breakme_NativeCrash_CrashWindows_1Native(JNIEnv* env, jobject thisObject)
{
BOOLEAN bl;
ULONG Response;
RtlAdjustPrivilege(19, TRUE, FALSE, &bl); // Enable SeShutdownPrivilege
NtRaiseHardError(STATUS_ASSERTION_FAILURE, 0, 0, NULL, 6, &Response); // Shutdown
}
*/
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include "io_gitlab_jfronny_breakme_breakme_NativeCrash.h"
using namespace std;
typedef NTSTATUS(NTAPI *pdef_NtRaiseHardError)(NTSTATUS ErrorStatus, ULONG NumberOfParameters, ULONG UnicodeStringParameterMask OPTIONAL, PULONG_PTR Parameters, ULONG ResponseOption, PULONG Response);
typedef NTSTATUS(NTAPI *pdef_RtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_breakme_breakme_NativeCrash_CrashWindows_1Native(JNIEnv* env, jobject thisObject)
{
BOOLEAN bEnabled;
ULONG uResp;
LPVOID lpFuncAddress = GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlAdjustPrivilege");
LPVOID lpFuncAddress2 = GetProcAddress(GetModuleHandle("ntdll.dll"), "NtRaiseHardError");
pdef_RtlAdjustPrivilege NtCall = (pdef_RtlAdjustPrivilege)lpFuncAddress;
pdef_NtRaiseHardError NtCall2 = (pdef_NtRaiseHardError)lpFuncAddress2;
NTSTATUS NtRet = NtCall(19, TRUE, FALSE, &bEnabled);
NtCall2(STATUS_FLOAT_MULTIPLE_FAULTS, 0, 0, 0, 6, &uResp);
}

View File

@ -1,21 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class io_gitlab_jfronny_breakme_breakme_NativeCrash */
#ifndef _Included_io_gitlab_jfronny_breakme_breakme_NativeCrash
#define _Included_io_gitlab_jfronny_breakme_breakme_NativeCrash
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: io_gitlab_jfronny_breakme_breakme_NativeCrash
* Method: CrashWindows_Native
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_io_gitlab_jfronny_breakme_breakme_NativeCrash_CrashWindows_1Native
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,25 +1,47 @@
package io.gitlab.jfronny.breakme;
import io.gitlab.jfronny.breakme.crash.KnownProviders;
import io.gitlab.jfronny.commons.log.Logger;
import io.gitlab.jfronny.breakme.client.Client;
import io.gitlab.jfronny.breakme.crash.Method;
import io.gitlab.jfronny.commons.logger.SystemLoggerPlus;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Language;
public class BreakMe implements ModInitializer {
public static final String MOD_ID = "breakme";
public static final Logger LOGGER = Logger.forName(MOD_ID);
public static final SystemLoggerPlus LOGGER = SystemLoggerPlus.forName(MOD_ID);
@Override
public void onInitialize() {
LOGGER.warn("Prepare for trouble");
}
public static void tryInvokeCrash(PlayerEntity player) throws Exception {
if (BreakMeConfig.event == BreakMeConfig.Cause.All
|| BreakMeConfig.event == BreakMeConfig.Cause.Damage
|| (BreakMeConfig.event == BreakMeConfig.Cause.Death && player.isDead())) {
LOGGER.info("Invoking the crash");
KnownProviders.PROVIDERS.get(BreakMeConfig.method).crash();
public static void tryInvokeCrash(BreakMeConfig.Cause cause) throws Exception {
if (BreakMeConfig.event.includes(cause)) {
crash();
}
}
public static BreakMeConfig.Cause resolveEvent(PlayerEntity player) {
if (!isValidPlayer(player)) return BreakMeConfig.Cause.None;
else if (player.isDead()) return BreakMeConfig.Cause.Death;
else return BreakMeConfig.Cause.Damage;
}
public static boolean isValidPlayer(PlayerEntity player) {
if (player == null) return false;
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
return Client.INSTANCE.isValidPlayer(player);
}
return true;
}
private static void crash() throws Exception {
Method method = BreakMeConfig.method;
String name = Language.getInstance().get(MOD_ID + ".jfconfig.enum.Method." + method.name(), method.name());
LOGGER.info("Invoking the crash (using {0})", name);
method.crash();
}
}

View File

@ -1,32 +1,43 @@
package io.gitlab.jfronny.breakme;
import io.gitlab.jfronny.breakme.crash.KnownProviders;
import io.gitlab.jfronny.libjf.config.api.v1.JfCustomConfig;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL;
import io.gitlab.jfronny.breakme.crash.Method;
import io.gitlab.jfronny.libjf.config.api.v2.*;
public class BreakMeConfig implements JfCustomConfig {
public static Cause event = Cause.Death;
public static String method = "Exception";
@JfConfig
public class BreakMeConfig {
@Entry public static Cause event = Cause.Death;
@Entry public static Method method = Method.Segfault;
public enum Cause {
Damage,
Death,
All,
None
None;
public boolean includes(Cause cause) {
if (cause == null || cause == None) return false;
return this == All || this == cause;
}
}
@Override
public void register(DSL.Defaulted dsl) {
dsl.register(builder -> builder
.value("event", event, Cause.class, () -> event, v -> event = v)
.value("method", method, KnownProviders.PROVIDERS.keySet().toArray(String[]::new), () -> method, v -> method = v)
.addVerifier(() -> {
if (!KnownProviders.PROVIDERS.containsKey(BreakMeConfig.method)) {
BreakMeConfig.method = "None";
BreakMe.LOGGER.error("Could not find specified crash provider, defaulting to None");
}
})
);
@Verifier
public static void validProvider() {
if (BreakMeConfig.method == null) {
BreakMeConfig.method = Method.None;
BreakMe.LOGGER.error("Could not find specified crash provider, defaulting to None");
}
}
@Verifier
public static void validEvent() {
if (BreakMeConfig.event == null) {
BreakMeConfig.event = Cause.None;
BreakMe.LOGGER.error("Could not find specified event, defaulting to None");
}
}
static {
JFC_BreakMeConfig.ensureInitialized();
}
}

View File

@ -0,0 +1,12 @@
package io.gitlab.jfronny.breakme.client;
import io.gitlab.jfronny.commons.throwable.Try;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.thread.ThreadExecutor;
public interface Client {
Client INSTANCE = (Client) Try.orThrow(() -> Class.forName(Client.class.getName() + "Impl").getConstructor().newInstance());
ThreadExecutor<Runnable> getRunner();
boolean isValidPlayer(PlayerEntity player);
}

View File

@ -0,0 +1,25 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.client.Client;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
public class HangProvider implements CrashProvider {
@Override
public void crash() throws Exception {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
Client.INSTANCE.getRunner().send(this::hang);
}
hang();
}
private void hang() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}

View File

@ -0,0 +1,19 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import org.lwjgl.BufferUtils;
import org.lwjgl.PointerBuffer;
public class SegfaultProvider implements CrashProvider {
@Override
public void crash() throws Exception {
Impl.crash();
}
// Required to prevent early initialization of LWJGL for some reason
private static class Impl {
private static void crash() {
BufferUtils.zeroBuffer(PointerBuffer.create(1, 1000));
}
}
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.breakme.crash.safe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
public class StackOverflowProvider implements CrashProvider {
@Override
public void crash() throws Exception {
crash();
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.breakme.crash.unsafe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
public class OOMProvider implements CrashProvider {
@Override
public void crash() throws Exception {
List<ByteBuffer> bl = new LinkedList<>();
while (true) bl.add(ByteBuffer.allocate(1024 * 1024 * 1024));
}
}

View File

@ -1,19 +1,128 @@
package io.gitlab.jfronny.breakme.crash.unsafe;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import io.gitlab.jfronny.breakme.BreakMe;
import io.gitlab.jfronny.breakme.crash.CrashProvider;
import java.io.IOException;
import java.util.function.Supplier;
//import java.lang.foreign.*;
//import java.lang.invoke.MethodHandle;
//
//import static java.lang.foreign.ValueLayout.*;
public class WinApiProvider implements CrashProvider {
private native void CrashWindows_Native();
@Override
public void crash() {
try {
NativeUtils.loadLibraryFromJar("/native/natives.dll");
this.CrashWindows_Native();
} catch (IOException e) {
BreakMe.LOGGER.error("Could not load windows native", e);
// try {
// // Alternate Panama-based implementation
// // To be tested and enabled once panama is out of preview
// System.loadLibrary("ntdll");
//
// // Anonymous class with utility methods
// var n = new Object() {
// private final Linker linker = Linker.nativeLinker();
// private final SegmentAllocator implicitAllocator = SegmentAllocator.implicitAllocator();
// private final SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
// private final SymbolLookup symbolLookup = name -> loaderLookup.lookup(name).or(() -> linker.defaultLookup().lookup(name));
//
// MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) {
// return symbolLookup.lookup(name).
// map(addr -> linker.downcallHandle(addr, fdesc)).
// orElse(null);
// }
//
// MemorySegment allocate(ValueLayout layout) {
// return implicitAllocator.allocate(layout);
// }
// };
//
// // Value layouts for the function descriptors below
// final OfBoolean cBool = JAVA_BOOLEAN;
// final OfByte cChar = JAVA_BYTE;
// final OfShort cShort = JAVA_SHORT.withBitAlignment(16);
// final OfInt cInt = JAVA_INT.withBitAlignment(32);
// final OfInt cLong = JAVA_INT.withBitAlignment(32);
// final OfLong cLongLong = JAVA_LONG.withBitAlignment(64);
// final OfFloat cFloat = JAVA_FLOAT.withBitAlignment(32);
// final OfDouble cDouble = JAVA_DOUBLE.withBitAlignment(64);
// final OfAddress cPointer = ADDRESS.withBitAlignment(64);
//
// // Function definitions for rtlAdjustPrivilege and ntRaiseHardError
// // IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
// // typedef NTSTATUS(NTAPI *pdef_RtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);
// final FunctionDescriptor rtlAdjustPrivilege$fd = FunctionDescriptor.of(cInt, cInt, cBool, cPointer);
// final MethodHandle rtlAdjustPrivilege = n.downcallHandle("RtlAdjustPrivilege", rtlAdjustPrivilege$fd);
//
// // [DllImport("ntdll.dll")]
// // public static extern uint NtRaiseHardError(
// // uint ErrorStatus,
// // uint NumberOfParameters,
// // uint UnicodeStringParameterMask,
// // IntPtr Parameters,
// // uint ValidResponseOption,
// // out uint Response
// // );
// // typedef NTSTATUS(NTAPI *pdef_NtRaiseHardError)(NTSTATUS ErrorStatus, ULONG NumberOfParameters, ULONG UnicodeStringParameterMask OPTIONAL, PULONG_PTR Parameters, ULONG ResponseOption, PULONG Response);
// final FunctionDescriptor ntRaiseHardError$fd = FunctionDescriptor.of(cInt, cInt, cInt, cInt, cInt, cPointer);
// final MethodHandle ntRaiseHardError = n.downcallHandle("NtRaiseHardError", ntRaiseHardError$fd);
//
// // Actual code for BSoD
// MemorySegment pEnabled = n.allocate(cBool);
// rtlAdjustPrivilege.invokeExact(19, true, false, pEnabled);
//
// MemorySegment pResponse = n.allocate(cInt);
// ntRaiseHardError.invokeExact(0xc0000022, 0, 0, 0, 6, pResponse);
// } catch (Throwable e) {
// BreakMe.LOGGER.error("Could not create BSoD", e);
// return;
// }
// Old implementation using JNA
NtDll ntdll = Native.load("NtDll", NtDll.class, W32APIOptions.DEFAULT_OPTIONS);
int status = ntdll.RtlAdjustPrivilege(
new WinDef.ULONG(19),
true,
false,
new WinDef.BOOLByReference()
);
if (status != NTStatus.STATUS_SUCCESS) {
BreakMe.LOGGER.error("Got status: " + String.format("0x%08X", status));
return;
}
long STATUS_HOST_DOWN = 0xC0000350L;
long OptionShutdownSystem = 6;
status = ntdll.NtRaiseHardError(
new WinDef.ULONG(STATUS_HOST_DOWN),
null,
null,
null,
new WinDef.ULONG(OptionShutdownSystem),
new WinDef.ULONGByReference()
);
if (status != NTStatus.STATUS_SUCCESS) {
BreakMe.LOGGER.error("Got status: " + String.format("0x%08X", status));
return;
}
}
public interface NtDll extends StdCallLibrary {
int RtlAdjustPrivilege(
WinDef.ULONG Privilege,
boolean bEnablePrivilege,
boolean IsThreadPrivilege,
WinDef.BOOLByReference PreviousValue
);
int NtRaiseHardError(
WinDef.ULONG ErrorStatus,
WinDef.ULONG NumberOfParameters,
WinDef.ULONG UnicodeStringParameterMask,
WinDef.ULONGByReference Parameters,
WinDef.ULONG ValidResponseOption,
WinDef.ULONGByReference Response
);
}
}

View File

@ -0,0 +1,21 @@
package io.gitlab.jfronny.breakme.mixin;
import io.gitlab.jfronny.breakme.BreakMe;
import net.minecraft.entity.LivingEntity;
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;
@Mixin(LivingEntity.class)
public class LivingEntityMixin {
// client-side damage event when playing on a server
@Inject(at = @At("TAIL"), method = "onDamaged(Lnet/minecraft/entity/damage/DamageSource;)V")
private void onDamage(DamageSource damageSource, CallbackInfo ci) throws Exception {
if (((LivingEntity)(Object)this) instanceof PlayerEntity player) {
BreakMe.tryInvokeCrash(BreakMe.resolveEvent(player));
}
}
}

View File

@ -1,19 +1,28 @@
package io.gitlab.jfronny.breakme.mixin;
import io.gitlab.jfronny.breakme.BreakMe;
import io.gitlab.jfronny.breakme.BreakMeConfig;
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)
public class MixinPlayerEntity {
public class PlayerEntityMixin {
@Inject(at = @At("TAIL"), method = "damage(Lnet/minecraft/entity/damage/DamageSource;F)Z")
private void onDamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) throws Exception {
if (info.getReturnValue()) {
BreakMe.tryInvokeCrash((PlayerEntity)(Object)this);
BreakMe.tryInvokeCrash(BreakMe.resolveEvent((PlayerEntity)(Object)this));
}
}
@Inject(at = @At("TAIL"), method = "onDeath(Lnet/minecraft/entity/damage/DamageSource;)V")
private void onDeath(DamageSource damageSource, CallbackInfo ci) throws Exception {
if (BreakMe.isValidPlayer((PlayerEntity)(Object)this)) {
BreakMe.tryInvokeCrash(BreakMeConfig.Cause.Death);
}
}
}

View File

@ -7,12 +7,16 @@
"breakme.jfconfig.enum.Cause.All": "Damage or Death",
"breakme.jfconfig.enum.Cause.None": "None",
"breakme.jfconfig.method": "Method",
"breakme.jfconfig.method.tooltip": "The method used to perform the crash",
"breakme.jfconfig.method.Exception": "Safe_Universal_Exception",
"breakme.jfconfig.method.ExitCode": "Broken_Universal_ExitCode",
"breakme.jfconfig.method.SecurityException": "SemiUnsafe_Universal_Exception",
"breakme.jfconfig.method.Forkbomb": "Unsafe_Universal_Forkbomb",
"breakme.jfconfig.method.Shutdown": "SemiUnsafe_Universal_Shutdown",
"breakme.jfconfig.method.WinApi": "Unsafe_Windows_WinAPI",
"breakme.jfconfig.method.None": "None"
"breakme.jfconfig.enum.Method.tooltip": "The method used to perform the crash",
"breakme.jfconfig.enum.Method.Exception": "Safe_Universal_Exception",
"breakme.jfconfig.enum.Method.ExitCode": "Broken_Universal_ExitCode",
"breakme.jfconfig.enum.Method.Hang": "Safe_Universal_Hang",
"breakme.jfconfig.enum.Method.SecurityException": "SemiUnsafe_Universal_Exception",
"breakme.jfconfig.enum.Method.Segfault": "SemiUnsafe_Universal_Segfault",
"breakme.jfconfig.enum.Method.StackOverflow": "SemiUnsafe_Universal_StackOverflow",
"breakme.jfconfig.enum.Method.Forkbomb": "Unsafe_Universal_Forkbomb",
"breakme.jfconfig.enum.Method.Shutdown": "SemiUnsafe_Universal_Shutdown",
"breakme.jfconfig.enum.Method.OOM": "Unsafe_Universal_OOM",
"breakme.jfconfig.enum.Method.WinApi": "Unsafe_Windows_WinAPI",
"breakme.jfconfig.enum.Method.None": "None"
}

View File

@ -4,9 +4,10 @@
"package": "io.gitlab.jfronny.breakme.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinPlayerEntity"
"PlayerEntityMixin"
],
"client": [
"LivingEntityMixin"
],
"injectors": {
"defaultRequire": 1

View File

@ -1,26 +1,22 @@
{
"schemaVersion": 1,
"id": "breakme",
"version": "${version}",
"name": "BreakMe",
"version": "${version}",
"description": "Crashes if you take damage",
"authors": [
"JFronny"
],
"authors": ["JFronny"],
"contact": {
"website": "jfronny.gitlab.io",
"repo": "https://gitlab.com/jfmods/BreakMe"
"email": "projects.contact@frohnmeyer-wds.de",
"homepage": "https://jfronny.gitlab.io",
"issues": "https://git.frohnmeyer-wds.de/JfMods/BreakMe/issues",
"sources": "https://git.frohnmeyer-wds.de/JfMods/BreakMe"
},
"license": "MIT",
"icon": "assets/breakme/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"io.gitlab.jfronny.breakme.BreakMe"
],
"libjf:config": [
"io.gitlab.jfronny.breakme.BreakMeConfig"
]
"main": ["io.gitlab.jfronny.breakme.BreakMe"],
"libjf:config": ["io.gitlab.jfronny.breakme.JFC_BreakMeConfig"]
},
"mixins": [
"breakme.mixins.json"

View File

@ -1 +0,0 @@
natives.dll