Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
if: runner.os != 'Windows'
run: chmod +x gradlew

- name: Set up JDK 21
- name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
java-version: "25"
cache: gradle

- name: Validate Gradle wrapper
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
OneShot Sword — Fabric mod for Minecraft
OneShot Sword — Fabric mod for Minecraft 26.1

Overpowered sword that deletes most mobs/players on hit and buffs the attacker. Single-player or controlled servers only.

Versioning note
- Minecraft now uses the `year.drop.hotfix` scheme (for example, `26.1`).

Build (PowerShell)
```powershell
.\gradlew.bat clean build
```
Jar: build/libs/

Requirements
- Java 25
- Fabric Loader 0.18.5+
- Fabric API 0.144.3+26.1

Notes
- Pack format 42; assets under src/main/resources/assets/oneshot/
- Resource pack format 84; assets under src/main/resources/assets/oneshot/
- Texture is a placeholder; replace textures/item/oneshot_sword.png
- Sword applies heavy status effects (Speed/Jump boosted, Dolphin’s Grace, Night Vision) and attempts an instant kill.

Expand Down
18 changes: 8 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// Minimal Fabric Loom buildscript wiring (buildscript/classpath + apply)
plugins {
id("fabric-loom") version "1.14.6"
id("net.fabricmc.fabric-loom") version "1.15-SNAPSHOT"
java
`maven-publish`
}

val minecraftVersion = "1.21.11"
val yarnMappings = "1.21.11+build.1"
val loaderVersion = "0.18.2"
val fabricApiVersion = "0.139.5+1.21.11"
val minecraftVersion = "26.1"
val loaderVersion = "0.18.5"
val fabricApiVersion = "0.144.3+26.1"

repositories {
mavenCentral()
Expand All @@ -19,16 +18,15 @@ repositories {

dependencies {
minecraft("com.mojang:minecraft:$minecraftVersion")
mappings("net.fabricmc:yarn:$yarnMappings:v2")

modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion")
implementation("net.fabricmc:fabric-loader:$loaderVersion")
implementation("net.fabricmc.fabric-api:fabric-api:$fabricApiVersion")
}

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
}

tasks.withType<JavaCompile>().configureEach {
options.release.set(21)
options.release.set(25)
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
org.gradle.jvmargs=-Xmx3G
java.toolchain.languageVersion=21
java.toolchain.languageVersion=25
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
86 changes: 37 additions & 49 deletions src/main/java/com/example/oneshot/OneShotHandler.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.example.oneshot;

import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.ItemStack;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
* OneShotHandler: standalone handler for the sword's effect so the Item class doesn't run
Expand All @@ -15,71 +18,56 @@ public class OneShotHandler {
private static final int EFFECT_DURATION = 20 * 60 * 60; // 1 hour in ticks

public static void onAttack(LivingEntity target, LivingEntity attacker, ItemStack stack) {
// Defensive runtime guards: only run server-side and ensure attacker is a LivingEntity
net.minecraft.world.World world = target.getEntityWorld();
if (world == null || world.isClient()) {
Level world = target.level();
if (!(world instanceof ServerLevel serverLevel) || world.isClientSide()) {
return;
}

net.minecraft.entity.LivingEntity livingAttacker = null;
if (attacker instanceof net.minecraft.entity.LivingEntity) {
livingAttacker = (net.minecraft.entity.LivingEntity) attacker;
}

// For players, wipe inventory before kill so nothing drops
if (target instanceof PlayerEntity playerTarget) {
if (target instanceof Player playerTarget) {
try {
playerTarget.getInventory().clear();
playerTarget.getInventory().clearContent();
} catch (Throwable ignored) {}
}

// Attempt a proper instant-kill using DamageSources when attacker exists
if (livingAttacker != null) {
try {
net.minecraft.entity.damage.DamageSource ds = target.getDamageSources().mobAttack(livingAttacker);
try {
target.sidedDamage(ds, Float.MAX_VALUE);
} catch (Throwable inner) {
target.setHealth(0.0F);
}
} catch (Throwable t) {
try { target.setHealth(0.0F); } catch (Throwable ignored) {}
}
} else {
try {
DamageSource ds = target.damageSources().mobAttack(attacker);
target.hurtServer(serverLevel, ds, Float.MAX_VALUE);
} catch (Throwable t) {
try { target.setHealth(0.0F); } catch (Throwable ignored) {}
}

try { attacker.heal(99999.0F); } catch (Throwable ignored) {}

attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.ABSORPTION, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.RESISTANCE, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.STRENGTH, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.REGENERATION, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.ABSORPTION, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.RESISTANCE, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.STRENGTH, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.REGENERATION, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
// Speed level 9 -> amplifier 8 (doubled)
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.SPEED, EFFECT_DURATION, 8, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.HASTE, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.SPEED, EFFECT_DURATION, 8, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.HASTE, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
// Jump Boost level 5 -> amplifier 4 (doubled)
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.JUMP_BOOST, EFFECT_DURATION, 4, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.SATURATION, EFFECT_DURATION, 5, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.GLOWING, EFFECT_DURATION, 0, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.INVISIBILITY, EFFECT_DURATION, 0, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.DOLPHINS_GRACE, EFFECT_DURATION, 2, false, true, true));
attacker.addStatusEffect(new StatusEffectInstance(StatusEffects.NIGHT_VISION, EFFECT_DURATION, 0, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.JUMP_BOOST, EFFECT_DURATION, 4, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.SATURATION, EFFECT_DURATION, 5, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.GLOWING, EFFECT_DURATION, 0, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.INVISIBILITY, EFFECT_DURATION, 0, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.DOLPHINS_GRACE, EFFECT_DURATION, 2, false, true, true));
attacker.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, EFFECT_DURATION, 0, false, true, true));

if (target.isAlive()) {
try {
if (target instanceof PlayerEntity) {
((PlayerEntity) target).getInventory().clear();
if (target instanceof Player playerTarget) {
playerTarget.getInventory().clearContent();
}
} catch (Throwable ignored) {}

target.addStatusEffect(new StatusEffectInstance(StatusEffects.WITHER, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.POISON, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.WEAKNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.SLOWNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.INSTANT_DAMAGE, 1, EFFECT_AMPLIFIER, false, true, true));
target.addStatusEffect(new StatusEffectInstance(StatusEffects.LEVITATION, 60, 10, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.WITHER, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.POISON, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.SLOWNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.BLINDNESS, EFFECT_DURATION, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.INSTANT_DAMAGE, 1, EFFECT_AMPLIFIER, false, true, true));
target.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 60, 10, false, true, true));
}
}
}
63 changes: 12 additions & 51 deletions src/main/java/com/example/oneshot/OneShotMod.java
Original file line number Diff line number Diff line change
@@ -1,67 +1,28 @@
package com.example.oneshot;

import com.example.oneshot.OneShotHandler;
import com.example.oneshot.item.OneShotSword;
import net.fabricmc.api.ModInitializer;
import net.minecraft.item.Item;
import net.minecraft.item.Item.Settings;
import net.minecraft.item.ItemGroups;
import net.minecraft.util.Identifier;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.Item;

public class OneShotMod implements ModInitializer {
public static final String MOD_ID = "oneshot";

// Avoid calling Settings.group(...) because the signature can vary between mappings.
// Defer creating the Item instance until onInitialize to prevent Minecraft code from
// running during static class initialization (fixes "Item id not set" NPE on startup).
public static Item ONE_SHOT_SWORD;

@Override
public void onInitialize() {
// Register the item inline. Avoid wrapping in a broad try/catch because creating an
// Item before a failing register() call can leave an intrusive holder unregistered.
Identifier swordId = Identifier.of(MOD_ID, "oneshot_sword");
Identifier swordId = Identifier.fromNamespaceAndPath(MOD_ID, "oneshot_sword");

// Safety: if the ID is already present, skip creating a second Item instance.
if (Registries.ITEM.containsId(swordId)) {
System.err.println("[OneShotMod] Item ID already registered: " + swordId + " — skipping duplicate registration.");
ONE_SHOT_SWORD = Registries.ITEM.get(swordId);
if (BuiltInRegistries.ITEM.containsKey(swordId)) {
ONE_SHOT_SWORD = BuiltInRegistries.ITEM.getValue(swordId);
} else {
System.out.println("[OneShotMod][Diag] About to construct Item settings (assigning registry key early); classloader=" + OneShotMod.class.getClassLoader());
// In modern 1.21.x, Items expect their registry key to be pre-associated in Settings
// before construction, otherwise the constructor will throw the 'Item id not set' NPE.
RegistryKey<Item> swordKey = RegistryKey.of(RegistryKeys.ITEM, swordId);
Settings settings = new Settings().registryKey(swordKey).maxCount(1);
System.out.println("[OneShotMod][Diag] Settings built: " + settings);
System.out.println("[OneShotMod][Diag] Calling Registry.register for id=" + swordId);
ONE_SHOT_SWORD = Registry.register(Registries.ITEM, swordId, new Item(settings));
System.out.println("[OneShotMod][Diag] Registry.register returned instance=" + ONE_SHOT_SWORD + " class=" + ONE_SHOT_SWORD.getClass().getName());
Identifier fetched = Registries.ITEM.getId(ONE_SHOT_SWORD);
System.out.println("[OneShotMod][Diag] Post-registration fetched id=" + fetched);
if (fetched == null) {
throw new IllegalStateException("[OneShotMod][Diag] Fetched null id for sword AFTER registration; registry corruption?");
}
ResourceKey<Item> swordKey = ResourceKey.create(Registries.ITEM, swordId);
ONE_SHOT_SWORD = Registry.register(BuiltInRegistries.ITEM, swordKey, new OneShotSword(new Item.Properties()));
}

// Only register callbacks if the sword successfully registered
if (ONE_SHOT_SWORD != null) {
// Add to Combat creative tab for visibility
ItemGroupEvents.modifyEntriesEvent(ItemGroups.COMBAT).register(entries -> entries.add(ONE_SHOT_SWORD.getDefaultStack()));
net.fabricmc.fabric.api.event.player.AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (world == null || world.isClient()) return net.minecraft.util.ActionResult.PASS;
if (player.getMainHandStack() == null) return net.minecraft.util.ActionResult.PASS;
if (player.getMainHandStack().getItem() != ONE_SHOT_SWORD) return net.minecraft.util.ActionResult.PASS;
if (entity instanceof net.minecraft.entity.LivingEntity) {
OneShotHandler.onAttack((net.minecraft.entity.LivingEntity) entity, player, player.getMainHandStack());
}
return net.minecraft.util.ActionResult.PASS;
});
}

System.out.println("[OneShotMod][Diag] onInitialize complete.");
}
}
20 changes: 15 additions & 5 deletions src/main/java/com/example/oneshot/item/OneShotSword.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package com.example.oneshot.item;

// Clean stub-only placeholder: doesn't extend Item and contains no game logic.
// This ensures there is no accidental early instantiation of an Item subclass
// that depends on Minecraft internals.
public final class OneShotSword {
private OneShotSword() {}
import com.example.oneshot.OneShotHandler;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;

public class OneShotSword extends Item {
public OneShotSword(Properties properties) {
super(properties);
}

@Override
public void hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) {
OneShotHandler.onAttack(target, attacker, stack);
super.hurtEnemy(stack, target, attacker);
}
}
4 changes: 2 additions & 2 deletions src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
]
},
"depends": {
"fabricloader": ">=0.18.2",
"fabricloader": ">=0.18.5",
"fabric": "*",
"minecraft": "1.21.11"
"minecraft": "26.1"
}
}
2 changes: 1 addition & 1 deletion src/main/resources/pack.mcmeta
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"pack": {
"pack_format": 42,
"pack_format": 84,
"description": "OneShot Sword assets"
}
}
Loading