More events and hooks

This commit is contained in:
shedaniel
2020-11-07 03:26:05 +08:00
parent b5f82c0801
commit daa3d4d8a4
34 changed files with 1201 additions and 24 deletions

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.event;
import net.minecraft.world.InteractionResult;
@FunctionalInterface
public interface Actor<T> {
InteractionResult act(T t);
}

View File

@@ -24,6 +24,7 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.jodah.typetools.TypeResolver;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
@@ -31,6 +32,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
public final class EventFactory {
@@ -73,6 +75,51 @@ public final class EventFactory {
}));
}
@SuppressWarnings("UnstableApiUsage")
public static <T> Event<T> createInteractionResultHolder(Class<T> clazz) {
return create(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() {
@Override
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
for (T listener : listeners) {
InteractionResultHolder result = (InteractionResultHolder) Objects.requireNonNull(method.invoke(listener, args));
if (result.getResult() != InteractionResult.PASS) {
return result;
}
}
return InteractionResultHolder.pass(null);
}
}));
}
@SuppressWarnings("UnstableApiUsage")
public static <T> Event<Consumer<T>> createConsumerLoop(Class<T> clazz) {
return create(listeners -> (Consumer<T>) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Consumer.class}, new AbstractInvocationHandler() {
@Override
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
for (Consumer<T> listener : listeners) {
method.invoke(listener, args);
}
return null;
}
}));
}
@SuppressWarnings("UnstableApiUsage")
public static <T> Event<Actor<T>> createActorLoop(Class<T> clazz) {
return create(listeners -> (Actor<T>) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Actor.class}, new AbstractInvocationHandler() {
@Override
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
for (Actor<T> listener : listeners) {
InteractionResult result = (InteractionResult) method.invoke(listener, args);
if (result != InteractionResult.PASS) {
return result;
}
}
return InteractionResult.PASS;
}
}));
}
private static class EventImpl<T> implements Event<T> {
private final Function<T[], T> function;
private T invoker = null;

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.event.events;
import me.shedaniel.architectury.event.Event;
import me.shedaniel.architectury.event.EventFactory;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionResultHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface ChatEvent {
/**
* Invoked when client tries to send a message, equivalent to forge's {@code ClientChatEvent}.
*/
@Environment(EnvType.CLIENT) Event<Client> CLIENT = EventFactory.createInteractionResultHolder(Client.class);
/**
* Invoked when client receives a message, equivalent to forge's {@code ClientChatReceivedEvent}.
*/
@Environment(EnvType.CLIENT) Event<ClientReceived> CLIENT_RECEIVED = EventFactory.createInteractionResultHolder(ClientReceived.class);
/**
* Invoked when server receives a message, equivalent to forge's {@code ServerChatEvent}.
*/
Event<Server> SERVER = EventFactory.createInteractionResultHolder(Server.class);
@Environment(EnvType.CLIENT)
interface Client {
@NotNull
InteractionResultHolder<String> process(String message);
}
@Environment(EnvType.CLIENT)
interface ClientReceived {
@NotNull
InteractionResultHolder<Component> process(ChatType type, Component message, @Nullable UUID sender);
}
interface Server {
@NotNull
InteractionResultHolder<Component> process(String message, Component component);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.event.events;
import com.mojang.brigadier.ParseResults;
import me.shedaniel.architectury.event.Actor;
import me.shedaniel.architectury.event.Event;
import me.shedaniel.architectury.event.EventFactory;
import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CommandPerformEvent {
/**
* Invoked after server parses a command but before server executes it, equivalent to forge's {@code CommandEvent}.
*/
public static final Event<Actor<CommandPerformEvent>> EVENT = EventFactory.createActorLoop(CommandPerformEvent.class);
@NotNull
private ParseResults<CommandSourceStack> results;
@Nullable
private Throwable throwable;
public CommandPerformEvent(@NotNull ParseResults<CommandSourceStack> results, @Nullable Throwable throwable) {
this.results = results;
this.throwable = throwable;
}
@NotNull
public ParseResults<CommandSourceStack> getResults() {
return results;
}
public void setResults(@NotNull ParseResults<CommandSourceStack> results) {
this.results = results;
}
@Nullable
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(@Nullable Throwable throwable) {
this.throwable = throwable;
}
}

View File

@@ -20,9 +20,13 @@ import com.mojang.brigadier.CommandDispatcher;
import me.shedaniel.architectury.event.Event;
import me.shedaniel.architectury.event.EventFactory;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
public interface CommandRegistrationEvent {
/**
* Invoked after server registers its commands, equivalent to forge's {@code RegisterCommandsEvent} and fabric's {@code CommandRegistrationCallback}.
*/
Event<CommandRegistrationEvent> EVENT = EventFactory.createLoop(CommandRegistrationEvent.class);
void register(CommandDispatcher<CommandSourceStack> var1);
void register(CommandDispatcher<CommandSourceStack> dispatcher, Commands.CommandSelection selection);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.event.events;
import me.shedaniel.architectury.event.Event;
import me.shedaniel.architectury.event.EventFactory;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
public interface EntityEvent {
/**
* Invoked before LivingEntity#die, equivalent to forge's {@code LivingDeathEvent}.
*/
Event<LivingDeath> LIVING_DEATH = EventFactory.createInteractionResult(LivingDeath.class);
interface LivingDeath {
InteractionResult die(LivingEntity entity, DamageSource source);
}
}

View File

@@ -21,13 +21,47 @@ import me.shedaniel.architectury.event.Event;
import me.shedaniel.architectury.event.EventFactory;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.world.InteractionResult;
import java.util.List;
@Environment(EnvType.CLIENT)
public interface GuiEvent {
/**
* Invoked after in-game hud is rendered, equivalent to forge's {@code RenderGameOverlayEvent.Post@ElementType#ALL} and fabric's {@code HudRenderCallback}.
*/
Event<RenderHud> RENDER_HUD = EventFactory.createLoop(RenderHud.class);
Event<DebugText> DEBUG_TEXT_LEFT = EventFactory.createLoop(DebugText.class);
Event<DebugText> DEBUG_TEXT_RIGHT = EventFactory.createLoop(DebugText.class);
/**
* Invoked during Screen#init after previous widgets are cleared, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Pre}.
*/
Event<ScreenInitPre> INIT_PRE = EventFactory.createInteractionResult(ScreenInitPre.class);
/**
* Invoked after Screen#init, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Post}.
*/
Event<ScreenInitPost> INIT_POST = EventFactory.createLoop(ScreenInitPost.class);
@Environment(EnvType.CLIENT)
interface RenderHud {
void renderHud(PoseStack matrices, float tickDelta);
}
@Environment(EnvType.CLIENT)
interface DebugText {
void gatherText(List<String> strings);
}
@Environment(EnvType.CLIENT)
interface ScreenInitPre {
InteractionResult init(Screen screen, List<AbstractWidget> widgets, List<GuiEventListener> children);
}
@Environment(EnvType.CLIENT)
interface ScreenInitPost {
void init(Screen screen, List<AbstractWidget> widgets, List<GuiEventListener> children);
}
}

View File

@@ -21,21 +21,49 @@ import me.shedaniel.architectury.event.EventFactory;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceKey;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import java.util.logging.Level;
public interface LifecycleEvent {
/**
* Invoked when client has been initialised, not available in forge.
*/
@Deprecated @Environment(EnvType.CLIENT) Event<ClientState> CLIENT_STARTED = EventFactory.createLoop(ClientState.class);
/**
* Invoked when client is initialising, not available in forge.
*/
@Deprecated @Environment(EnvType.CLIENT) Event<ClientState> CLIENT_STOPPING = EventFactory.createLoop(ClientState.class);
/**
* Invoked when server is starting, equivalent to forge's {@code FMLServerStartingEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTING}.
*/
Event<ServerState> SERVER_STARTING = EventFactory.createLoop(ServerState.class);
/**
* Invoked when server has started, equivalent to forge's {@code FMLServerStartedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTED}.
*/
Event<ServerState> SERVER_STARTED = EventFactory.createLoop(ServerState.class);
/**
* Invoked when server is stopping, equivalent to forge's {@code FMLServerStoppingEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPING}.
*/
Event<ServerState> SERVER_STOPPING = EventFactory.createLoop(ServerState.class);
/**
* Invoked when server has stopped, equivalent to forge's {@code FMLServerStoppedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPED}.
*/
Event<ServerState> SERVER_STOPPED = EventFactory.createLoop(ServerState.class);
Event<WorldLoad> WORLD_LOAD = EventFactory.createLoop(WorldLoad.class);
Event<WorldSave> WORLD_SAVE = EventFactory.createLoop(WorldSave.class);
/**
* Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load}.
*/
Event<ServerWorldState> SERVER_WORLD_LOAD = EventFactory.createLoop(ServerWorldState.class);
/**
* Invoked during a world is saved, equivalent to forge's {@code WorldEvent.Save}.
*/
Event<ServerWorldState> SERVER_WORLD_SAVE = EventFactory.createLoop(ServerWorldState.class);
/**
* Invoked after a world is loaded only on client, equivalent to forge's {@code WorldEvent.Load}.
*/
@Environment(EnvType.CLIENT) Event<ClientWorldState> CLIENT_WORLD_LOAD = EventFactory.createLoop(ClientWorldState.class);
interface InstanceState<T> {
void stateChanged(T instance);
@@ -47,11 +75,12 @@ public interface LifecycleEvent {
interface ServerState extends InstanceState<MinecraftServer> {}
interface WorldLoad {
void load(ResourceKey<Level> key, ServerLevel world);
interface WorldState<T extends Level> {
void act(T world);
}
interface WorldSave {
void save(ServerLevel world);
}
@Environment(EnvType.CLIENT)
interface ClientWorldState extends WorldState<ClientLevel> {}
interface ServerWorldState extends WorldState<ServerLevel> {}
}

View File

@@ -35,6 +35,8 @@ public interface TickEvent<T> {
@Environment(EnvType.CLIENT) Event<ClientWorld> CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class);
Event<ServerWorld> SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class);
Event<ServerWorld> SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class);
Event<Player> PLAYER_PRE = EventFactory.createLoop(Player.class);
Event<Player> PLAYER_POST = EventFactory.createLoop(Player.class);
void tick(T instance);
@@ -49,4 +51,6 @@ public interface TickEvent<T> {
interface ClientWorld extends WorldTick<ClientLevel> {}
interface ServerWorld extends WorldTick<ServerLevel> {}
interface Player extends TickEvent<net.minecraft.world.entity.player.Player> {}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.hooks;
import me.shedaniel.architectury.ArchitecturyPopulator;
import me.shedaniel.architectury.Populatable;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
@Environment(EnvType.CLIENT)
public final class ScreenHooks {
private ScreenHooks() {}
@Populatable
private static final Impl IMPL = null;
public static <T extends AbstractWidget> T addButton(Screen screen, T widget) {
return IMPL.addButton(screen, widget);
}
public static <T extends GuiEventListener> T addChild(Screen screen, T listener) {
return IMPL.addChild(screen, listener);
}
public interface Impl {
<T extends AbstractWidget> T addButton(Screen screen, T widget);
<T extends GuiEventListener> T addChild(Screen screen, T listener);
}
static {
ArchitecturyPopulator.populate(ScreenHooks.class);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2020 shedaniel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.shedaniel.architectury.registry;
import me.shedaniel.architectury.ArchitecturyPopulator;
import me.shedaniel.architectury.Populatable;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.color.block.BlockColor;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
@Environment(EnvType.CLIENT)
public final class ColorHandlers {
private ColorHandlers() {}
@Populatable
private static final Impl IMPL = null;
public static void registerItemColors(ItemColor color, ItemLike... items) {
IMPL.registerItemColors(color, items);
}
public static void registerBlockColors(BlockColor color, Block... blocks) {
IMPL.registerBlockColors(color, blocks);
}
public interface Impl {
void registerItemColors(ItemColor color, ItemLike... items);
void registerBlockColors(BlockColor color, Block... blocks);
}
static {
ArchitecturyPopulator.populate(ColorHandlers.class);
}
}

View File

@@ -19,6 +19,8 @@ package me.shedaniel.architectury.registry;
import me.shedaniel.architectury.ArchitecturyPopulator;
import me.shedaniel.architectury.Populatable;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -46,8 +48,30 @@ public final class Registries {
return this.provider.get(registry);
}
/**
* Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null
* Fabric: Use registry
*/
@Nullable
public <T> ResourceLocation getId(T object, ResourceKey<net.minecraft.core.Registry<T>> fallback) {
return IMPL.getId(object, fallback);
}
/**
* Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null
* Fabric: Use registry
*/
@Nullable
public <T> ResourceLocation getId(T object, net.minecraft.core.Registry<T> fallback) {
return IMPL.getId(object, fallback);
}
public interface Impl {
RegistryProvider get(String modId);
<T> ResourceLocation getId(T object, ResourceKey<net.minecraft.core.Registry<T>> fallback);
<T> ResourceLocation getId(T object, net.minecraft.core.Registry<T> fallback);
}
public interface RegistryProvider {