add block/item registration events
Some checks failed
Publish to snapshot maven / build (push) Failing after 43s

This commit is contained in:
moehreag 2024-06-12 17:19:33 +02:00
parent 5140540cd1
commit ab2d4519af
41 changed files with 327 additions and 28 deletions

View file

@ -48,6 +48,16 @@ allprojects {
} }
} }
tasks.processTestResources {
inputs.property("version", version)
inputs.property("game_version", libs.versions.minecraft.get())
filesMatching("frog.mod.toml") {
expand("version" to version,
"game_version" to libs.versions.minecraft.get())
}
}
tasks.runClient { tasks.runClient {
classpath(sourceSets.test.get().runtimeClasspath) classpath(sourceSets.test.get().runtimeClasspath)
} }
@ -84,12 +94,14 @@ allprojects {
evaluationDependsOnChildren() evaluationDependsOnChildren()
// TODO: add testmods of libraries to general client runtime classpath
dependencies { dependencies {
project.subprojects.filter { project.subprojects.filter {
it.projectDir.resolve("src/main/resources/frog.mod.toml").exists() it.projectDir.resolve("src/main/resources/frog.mod.toml").exists()
}.forEach { }.forEach {
project.tasks.jar { project.tasks.jar {
dependsOn(it.tasks.jar) dependsOn(it.tasks.build)
} }
api(it) api(it)
include(it) include(it)

View file

@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CrashReport.class) @Mixin(CrashReport.class)
public class CrashReportMixin { public abstract class CrashReportMixin {
@Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At("TAIL")) @Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At("TAIL"))
private void addModInfoToCrashReport(StringBuilder builder, CallbackInfo ci){ private void addModInfoToCrashReport(StringBuilder builder, CallbackInfo ci){

View file

@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@Mixin(ClientBrandRetriever.class) @Mixin(ClientBrandRetriever.class)
public class ClientBrandRetrieverMixin { public abstract class ClientBrandRetrieverMixin {
@ModifyReturnValue(method = "getClientModName", at = @At("RETURN")) @ModifyReturnValue(method = "getClientModName", at = @At("RETURN"))
private static String setClientBrand(String original){ private static String setClientBrand(String original){

View file

@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TitleScreen.class) @Mixin(TitleScreen.class)
public class TitleScreenMixin { public abstract class TitleScreenMixin {
@Unique @Unique
private static final int MOD_COUNT = FrogLoader.getInstance().getMods().size(); private static final int MOD_COUNT = FrogLoader.getInstance().getMods().size();

View file

@ -1,4 +1,4 @@
package dev.frogmc.froglib.entrypoints; package dev.frogmc.froglib.entrypoints.api;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;

View file

@ -1,4 +1,4 @@
package dev.frogmc.froglib.entrypoints; package dev.frogmc.froglib.entrypoints.api;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;

View file

@ -1,4 +1,4 @@
package dev.frogmc.froglib.entrypoints; package dev.frogmc.froglib.entrypoints.api;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;

View file

@ -1,5 +1,8 @@
package dev.frogmc.froglib.entrypoints; package dev.frogmc.froglib.entrypoints.impl;
import dev.frogmc.froglib.entrypoints.api.ClientExtension;
import dev.frogmc.froglib.entrypoints.api.MainExtension;
import dev.frogmc.froglib.entrypoints.api.ServerExtension;
import dev.frogmc.frogloader.api.FrogLoader; import dev.frogmc.frogloader.api.FrogLoader;
public class ExtensionLauncher { public class ExtensionLauncher {
@ -11,6 +14,7 @@ public class ExtensionLauncher {
}); });
} }
public static void invokeServer() { public static void invokeServer() {
FrogLoader.getInstance().getMods().forEach(mod -> { FrogLoader.getInstance().getMods().forEach(mod -> {
mod.extensions().runIfPresent("server", ServerExtension.class, ext -> ext.onServerInit(mod)); mod.extensions().runIfPresent("server", ServerExtension.class, ext -> ext.onServerInit(mod));

View file

@ -1,6 +1,6 @@
package dev.frogmc.froglib.entrypoints.mixin.client; package dev.frogmc.froglib.entrypoints.impl.mixin.client;
import dev.frogmc.froglib.entrypoints.ExtensionLauncher; import dev.frogmc.froglib.entrypoints.impl.ExtensionLauncher;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.main.GameConfig; import net.minecraft.client.main.GameConfig;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -11,7 +11,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Minecraft.class) @Mixin(Minecraft.class)
public abstract class MinecraftMixin { public abstract class MinecraftMixin {
@Inject(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;running:Z")) @Inject(
method = "<init>",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Options;<init>(Lnet/minecraft/client/Minecraft;Ljava/io/File;)V")
)
private void initializeClient(GameConfig gameConfig, CallbackInfo ci) { private void initializeClient(GameConfig gameConfig, CallbackInfo ci) {
ExtensionLauncher.invokeClient(); ExtensionLauncher.invokeClient();
} }

View file

@ -1,6 +1,6 @@
package dev.frogmc.froglib.entrypoints.mixin.server; package dev.frogmc.froglib.entrypoints.impl.mixin.server;
import dev.frogmc.froglib.entrypoints.ExtensionLauncher; import dev.frogmc.froglib.entrypoints.impl.ExtensionLauncher;
import net.minecraft.server.Main; import net.minecraft.server.Main;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Main.class) @Mixin(Main.class)
public abstract class MinecraftServerMainMixin { public abstract class MainMixin {
@Inject(method = "main", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;spin(Ljava/util/function/Function;)Lnet/minecraft/server/MinecraftServer;")) @Inject(method = "main", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;spin(Ljava/util/function/Function;)Lnet/minecraft/server/MinecraftServer;"))
private static void initializeServer(String[] args, CallbackInfo ci) { private static void initializeServer(String[] args, CallbackInfo ci) {

View file

@ -42,14 +42,14 @@ public class EventGenerator {
int modifier = m.getModifiers(); int modifier = m.getModifiers();
if (Modifier.isPublic(modifier) && Modifier.isAbstract(modifier) && m.getReturnType() == void.class) { if (Modifier.isPublic(modifier) && Modifier.isAbstract(modifier) && m.getReturnType() == void.class) {
if (functionalMethod != null) { if (functionalMethod != null) {
throw new IllegalArgumentException("Not a functional interface"); throw new IllegalArgumentException("Not a functional interface (multiple abstract methods)");
} }
functionalMethod = m; functionalMethod = m;
} }
} }
if (functionalMethod == null) { if (functionalMethod == null) {
throw new IllegalArgumentException("Not a functional interface"); throw new IllegalArgumentException("Not a functional interface, note: the main method needs to have 'void' return type");
} }
String pkg = "dev/frogmc/froglib/events/generated/"; String pkg = "dev/frogmc/froglib/events/generated/";

View file

@ -1,10 +1,10 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "dev.frogmc.froglib.entrypoints.mixin", "package": "dev.frogmc.froglib.entrypoints.impl.mixin",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "JAVA_21",
"server": [ "server": [
"server.MinecraftServerMainMixin" "server.MainMixin"
], ],
"client": [ "client": [
"client.MinecraftMixin" "client.MinecraftMixin"

View file

@ -0,0 +1,4 @@
dependencies {
api(project(":library:resourceloader"))
}

View file

@ -0,0 +1,8 @@
package dev.frogmc.froglib.keybinds.api;
import net.minecraft.client.KeyMapping;
@FunctionalInterface
public interface KeyListener {
void onPress(KeyMapping key);
}

View file

@ -3,15 +3,34 @@ package dev.frogmc.froglib.keybinds.api;
import dev.frogmc.froglib.keybinds.impl.KeyMappingsImpl; import dev.frogmc.froglib.keybinds.impl.KeyMappingsImpl;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
/**
* API used for simple registration of keybindings.
*/
public interface KeyMappings { public interface KeyMappings {
private static KeyMappings getInstance() { private static KeyMappings getInstance() {
return KeyMappingsImpl.getInstance(); return KeyMappingsImpl.getInstance();
} }
/**
* Register a keybinding.
* @param mapping the key to register
*/
static void register(KeyMapping mapping) { static void register(KeyMapping mapping) {
getInstance().registerKey(mapping); getInstance().registerKey(mapping);
} }
void registerKey(KeyMapping mapping); /**
* Register a keybinding with a listener to receive presses.
* <p>Note: This listener does not provide functionality for continuously pressed bindings.</p>
* @param mapping the key to register
* @param listener the listener to be called when the key is pressed.
*/
static void register(KeyMapping mapping, KeyListener listener) {
getInstance().registerKeyWithListener(mapping, listener);
}
void registerKey(KeyMapping mapping);
void registerKeyWithListener(KeyMapping mapping, KeyListener listener);
} }

View file

@ -1,5 +1,7 @@
package dev.frogmc.froglib.keybinds.impl; package dev.frogmc.froglib.keybinds.impl;
import dev.frogmc.froglib.events.api.Events;
import dev.frogmc.froglib.keybinds.api.KeyListener;
import dev.frogmc.froglib.keybinds.api.KeyMappings; import dev.frogmc.froglib.keybinds.api.KeyMappings;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -16,4 +18,14 @@ public class KeyMappingsImpl implements KeyMappings {
// TODO maybe synchronize? // TODO maybe synchronize?
Minecraft.getInstance().options.keyMappings = ArrayUtils.add(Minecraft.getInstance().options.keyMappings, mapping); Minecraft.getInstance().options.keyMappings = ArrayUtils.add(Minecraft.getInstance().options.keyMappings, mapping);
} }
@Override
public void registerKeyWithListener(KeyMapping mapping, KeyListener listener) {
registerKey(mapping);
Events.POST_CLIENT_TICK.register(c -> {
while (mapping.consumeClick()){
listener.onPress(mapping);
}
});
}
} }

View file

@ -19,4 +19,4 @@ depends = [
mixin = [ mixin = [
"froglib.keybinds.mixins.json" "froglib.keybinds.mixins.json"
] ]
accesswidener = "froglib.accesswidener" accesswidener = "froglib.keybinds.accesswidener"

View file

@ -1,7 +1,7 @@
package dev.frogmc.froglib.keybinds.test; package dev.frogmc.froglib.keybinds.test;
import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.platform.InputConstants;
import dev.frogmc.froglib.entrypoints.ClientExtension; import dev.frogmc.froglib.entrypoints.api.ClientExtension;
import dev.frogmc.froglib.keybinds.api.KeyMappings; import dev.frogmc.froglib.keybinds.api.KeyMappings;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;

View file

@ -0,0 +1,4 @@
{
"key.froglib.test": "FrogLib Test Keybind",
"category.froglib": "FrogLib Test Category"
}

View file

@ -2,9 +2,9 @@
format_version = "1.0.0" format_version = "1.0.0"
[frog.mod] [frog.mod]
id = "frog_lib_keybinds_test" id = "froglib_keybinds_test"
name = "FrogLib Keybinds Test Mod" name = "FrogLib Keybinds Test Mod"
version = "1.0.0" version = "$version"
license = "Apache-2.0" license = "Apache-2.0"
credits = [ credits = [
{ name = "FrogMC Team", roles = ["author"] } { name = "FrogMC Team", roles = ["author"] }
@ -12,7 +12,7 @@ credits = [
[frog.dependencies] [frog.dependencies]
depends = [ depends = [
{ id = "minecraft", versions = "~1.20.6", name = "Minecraft" } { id = "minecraft", versions = "~$game_version", name = "Minecraft" }
] ]
[frog.extensions] [frog.extensions]

View file

@ -0,0 +1,4 @@
dependencies {
api(project(":library:resourceloader"))
}

View file

@ -0,0 +1,13 @@
package dev.frogmc.froglib.registries.api;
import java.util.Collection;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import org.apache.commons.lang3.tuple.Pair;
@FunctionalInterface
public interface BlockRegistration {
Collection<Pair<ResourceLocation, Block>> register();
}

View file

@ -0,0 +1,11 @@
package dev.frogmc.froglib.registries.api;
import java.util.Collection;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.apache.commons.lang3.tuple.Pair;
public interface ItemRegistration {
Collection<Pair<ResourceLocation, Item>> register();
}

View file

@ -0,0 +1,10 @@
package dev.frogmc.froglib.registries.api;
import java.util.Collection;
import dev.frogmc.froglib.events.api.Event;
public class RegistryEvents {
public static final Event<BlockRegistration> BLOCK_REGISTRATION = Event.of(list -> () -> list.stream().map(BlockRegistration::register).flatMap(Collection::stream).toList());
public static final Event<ItemRegistration> ITEM_REGISTRATION = Event.of(list -> () -> list.stream().map(ItemRegistration::register).flatMap(Collection::stream).toList());
}

View file

@ -0,0 +1,40 @@
package dev.frogmc.froglib.registries.impl;
import java.util.Collection;
import java.util.function.Consumer;
import dev.frogmc.froglib.registries.api.RegistryEvents;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.commons.lang3.tuple.Pair;
public class RegistrationImpl {
private static <T> void register(Registry<T> registry, Consumer<T> preAction, Collection<Pair<ResourceLocation, T>> entries, Consumer<T> postAction) {
entries.forEach(p -> {
preAction.accept(p.getValue());
Registry.register(registry, p.getKey(), p.getValue());
postAction.accept(p.getValue());
});
}
public static void register(){
register(BuiltInRegistries.BLOCK, b -> {}, RegistryEvents.BLOCK_REGISTRATION.invoker().register(), block -> {
for (BlockState blockState : block.getStateDefinition().getPossibleStates()) {
Block.BLOCK_STATE_REGISTRY.add(blockState);
blockState.initCache();
}
block.getLootTable();
});
register(BuiltInRegistries.ITEM, item -> {
if (item instanceof BlockItem b) {
b.registerBlocks(Item.BY_BLOCK, item);
}
}, RegistryEvents.ITEM_REGISTRATION.invoker().register(), item -> {});
}
}

View file

@ -0,0 +1,17 @@
package dev.frogmc.froglib.registries.impl.mixin;
import dev.frogmc.froglib.registries.impl.RegistrationImpl;
import net.minecraft.core.registries.BuiltInRegistries;
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(BuiltInRegistries.class)
public abstract class BuiltinRegistriesMixin {
@Inject(method = "bootStrap", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/registries/BuiltInRegistries;createContents()V"))
private static void inject(CallbackInfo ci) {
RegistrationImpl.register();
}
}

View file

@ -0,0 +1,19 @@
[frog]
format_version = "1.0.0"
[frog.mod]
id = "froglib_block"
name = "FrogLib Block"
version = "$version"
license = "Apache-2.0"
credits = [
{ name = "FrogMC Team", roles = ["author"] }
]
[frog.dependencies]
depends = [
{ id = "minecraft", versions = "~$game_version", name = "Minecraft" }
]
[frog.extensions]
mixin = "froglib.block.mixins.json"

View file

@ -0,0 +1,17 @@
{
"required": true,
"minVersion": "0.8",
"package": "dev.frogmc.froglib.registries.impl.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"BuiltinRegistriesMixin"
],
"server": [
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,21 @@
package dev.frogmc.froglib.registries.test;
import java.util.List;
import dev.frogmc.froglib.registries.api.RegistryEvents;
import dev.frogmc.frogloader.api.extensions.PreLaunchExtension;
import org.apache.commons.lang3.tuple.Pair;
public class BlockTest implements PreLaunchExtension {
@Override
public void onPreLaunch() {
RegistryEvents.BLOCK_REGISTRATION.register(() -> List.of(
Pair.of(BlockTestBlocks.loc, BlockTestBlocks.b)
));
RegistryEvents.ITEM_REGISTRATION.register(() -> List.of(
Pair.of(BlockTestBlocks.loc, BlockTestBlocks.bItem)
));
}
}

View file

@ -0,0 +1,15 @@
package dev.frogmc.froglib.registries.test;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.StainedGlassBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
public class BlockTestBlocks {
public static final ResourceLocation loc = new ResourceLocation("froglib_block_test", "testblock");
public static final Block b = new StainedGlassBlock(DyeColor.MAGENTA, BlockBehaviour.Properties.of());
public static final Item bItem = new BlockItem(b, new Item.Properties());
}

View file

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "minecraft:block/anvil"
}
}
}

View file

@ -0,0 +1,3 @@
{
"block.froglib_block_test.testblock": "Test Block"
}

View file

@ -0,0 +1,3 @@
{
"parent": "minecraft:block/anvil"
}

View file

@ -0,0 +1,19 @@
[frog]
format_version = "1.0.0"
[frog.mod]
id = "froglib_block_test"
name = "FrogLib Block Test"
version = "$version"
license = "Apache-2.0"
credits = [
{ name = "FrogMC Team", roles = ["author"] }
]
[frog.dependencies]
depends = [
{ id = "minecraft", versions = "~$game_version", name = "Minecraft" }
]
[frog.extensions]
prelaunch = "dev.frogmc.froglib.registries.test.BlockTest"

View file

@ -1,6 +1,6 @@
package dev.frogmc.froglib.resources.impl; package dev.frogmc.froglib.resources.impl;
import dev.frogmc.froglib.entrypoints.ClientExtension; import dev.frogmc.froglib.entrypoints.api.ClientExtension;
import dev.frogmc.froglib.events.api.Events; import dev.frogmc.froglib.events.api.Events;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;
import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.ReloadableResourceManager;

View file

@ -1,6 +1,6 @@
package dev.frogmc.froglib.resources.impl; package dev.frogmc.froglib.resources.impl;
import dev.frogmc.froglib.entrypoints.ServerExtension; import dev.frogmc.froglib.entrypoints.api.ServerExtension;
import dev.frogmc.froglib.events.api.Events; import dev.frogmc.froglib.events.api.Events;
import dev.frogmc.frogloader.api.mod.ModProperties; import dev.frogmc.frogloader.api.mod.ModProperties;
import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.ReloadableResourceManager;

View file

@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PackRepository.class) @Mixin(PackRepository.class)
public class PackRepositoryMixin { public abstract class PackRepositoryMixin {
@Inject(method = "rebuildSelected", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;")) @Inject(method = "rebuildSelected", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;"))
private void injectModResources(Collection<String> ids, CallbackInfoReturnable<List<Pack>> cir, @Local List<Pack> packs){ private void injectModResources(Collection<String> ids, CallbackInfoReturnable<List<Pack>> cir, @Local List<Pack> packs){

View file

@ -23,3 +23,5 @@ include("library:keybinds")
findProject(":library:keybinds")?.name = "keybinds" findProject(":library:keybinds")?.name = "keybinds"
include("library:resourceloader") include("library:resourceloader")
findProject(":library:resourceloader")?.name = "resourceloader" findProject(":library:resourceloader")?.name = "resourceloader"
include("library:registries")
findProject(":library:registries")?.name = "registries"

View file

@ -0,0 +1,11 @@
package dev.frogmc.froglib.test;
import dev.frogmc.froglib.entrypoints.api.MainExtension;
import dev.frogmc.frogloader.api.mod.ModProperties;
public class FroglibTest implements MainExtension {
@Override
public void onInit(ModProperties mod) {
}
}

View file

@ -0,0 +1,21 @@
[frog]
format_version = "1.0.0"
[frog.mod]
id = "froglib_test"
name = "FrogLib Test Mod"
version = "$version"
license = "Apache-2.0"
credits = [
{ name = "FrogMC Team", roles = ["author"] }
]
[frog.dependencies]
depends = [
{ id = "minecraft", versions = "~$game_version", name = "Minecraft" }
]
[frog.extensions]
main = [
"dev.frogmc.froglib.test.FroglibTest"
]