-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(events): add dynamic event system for mod compatibility
Implement a dynamic event system to support toggleable events and type-tracked events, improving the mod's adaptability and interaction with other mods. This system includes the introduction of `Event`, `ToggleableEvent`, and `TypeTrackedEvent` classes, and facilitates event-driven functionalities across different event sources. The event system is designed to be flexible and easily extensible, allowing for bettermod integration and compatibility. It also introduces a mechanism to invalidate and re-initialize events when necessary, ensuring the system remains up-to-date with the current state of the game and its mods.
- Loading branch information
Showing
9 changed files
with
402 additions
and
1 deletion.
There are no files selected for viewing
78 changes: 78 additions & 0 deletions
78
common/src/main/java/cn/evole/mods/mcbot/api/event/Event.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package cn.evole.mods.mcbot.api.event; | ||
|
||
import java.lang.reflect.Array; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* @Project: McBot | ||
* @Author: cnlimiter | ||
* @CreateTime: 2024/8/11 20:30 | ||
* @Description: | ||
*/ | ||
public class Event<T> | ||
{ | ||
/** | ||
* The invoker field. This should be updated by the implementation to | ||
* always refer to an instance containing all code that should be | ||
* executed upon event emission. | ||
*/ | ||
protected volatile T invoker; | ||
|
||
/** | ||
* Returns the invoker instance. | ||
* | ||
* <p>An "invoker" is an object which hides multiple registered | ||
* listeners of type T under one instance of type T, executing | ||
* them and leaving early as necessary. | ||
* | ||
* @return The invoker instance. | ||
*/ | ||
public final T invoker() { return invoker; } | ||
|
||
private final Function<T[], T> invokerFactory; | ||
protected final Object lock = new Object(); | ||
private T[] listeners; | ||
|
||
private void addListener(T listener) | ||
{ | ||
int oldLength = listeners.length; | ||
listeners = Arrays.copyOf(listeners, oldLength + 1); | ||
listeners[oldLength] = listener; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public Event(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
this.invokerFactory = invokerFactory; | ||
this.listeners = (T[]) Array.newInstance(type, 0); | ||
update(); | ||
} | ||
|
||
void update() | ||
{ | ||
this.invoker = invokerFactory.apply(listeners); | ||
} | ||
|
||
/** | ||
* Register a listener to the event. | ||
* | ||
* @param listener The desired listener. | ||
*/ | ||
public void register(T listener) | ||
{ | ||
Objects.requireNonNull(listener, "Tried to register a null listener!"); | ||
|
||
synchronized (lock) | ||
{ | ||
addListener(listener); | ||
update(); | ||
} | ||
} | ||
|
||
public int listenerCount() | ||
{ | ||
return listeners.length; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
common/src/main/java/cn/evole/mods/mcbot/api/event/EventFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cn.evole.mods.mcbot.api.event; | ||
|
||
import com.google.common.collect.MapMaker; | ||
|
||
import java.util.Collections; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* @Project: McBot | ||
* @Author: cnlimiter | ||
* @CreateTime: 2024/8/11 20:31 | ||
* @Description: from <a href="https://github.com/AHilyard/Iceberg/blob/1.21-multi/common/src/main/java/com/anthonyhilyard/iceberg/events/EventFactory.java">...</a> | ||
*/ | ||
public final class EventFactory | ||
{ | ||
private static final Set<Event<?>> EVENTS = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); | ||
|
||
private EventFactory() { } | ||
|
||
public static void invalidate() | ||
{ | ||
EVENTS.forEach(Event::update); | ||
} | ||
|
||
public static <T> Event<T> create(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
Event<T> event = new Event<>(type, invokerFactory); | ||
EVENTS.add(event); | ||
return event; | ||
} | ||
|
||
public static <S, T> TypeTrackedEvent<S, T> createTypeTracked(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
TypeTrackedEvent<S, T> event = new TypeTrackedEvent<>(type, invokerFactory); | ||
EVENTS.add(event); | ||
return event; | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
common/src/main/java/cn/evole/mods/mcbot/api/event/ToggleableEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package cn.evole.mods.mcbot.api.event; | ||
|
||
import java.lang.reflect.Array; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* @Project: McBot | ||
* @Author: cnlimiter | ||
* @CreateTime: 2024/8/11 20:33 | ||
* @Description: from <a href="https://github.com/AHilyard/Iceberg/blob/1.21-multi/common/src/main/java/com/anthonyhilyard/iceberg/events/ToggleableEvent.java">...</a> | ||
*/ | ||
public class ToggleableEvent<T> | ||
{ | ||
private T dummyInvoker; | ||
private boolean disabled = false; | ||
private Event<T> event; | ||
|
||
@SuppressWarnings("unchecked") | ||
private ToggleableEvent(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
event = EventFactory.create(type, invokerFactory); | ||
this.dummyInvoker = invokerFactory.apply((T[]) Array.newInstance(type, 0)); | ||
} | ||
|
||
public static <T> ToggleableEvent<T> create(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
return new ToggleableEvent<>(type, invokerFactory); | ||
} | ||
|
||
public void register(T listener) | ||
{ | ||
event.register(listener); | ||
} | ||
|
||
public T invoker() | ||
{ | ||
if (!disabled) | ||
{ | ||
return event.invoker(); | ||
} | ||
else | ||
{ | ||
return dummyInvoker; | ||
} | ||
} | ||
|
||
public boolean disable() | ||
{ | ||
if (disabled) | ||
{ | ||
return false; | ||
} | ||
else | ||
{ | ||
disabled = true; | ||
return true; | ||
} | ||
} | ||
|
||
public boolean enable() | ||
{ | ||
if (!disabled) | ||
{ | ||
return false; | ||
} | ||
else | ||
{ | ||
disabled = false; | ||
return true; | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
common/src/main/java/cn/evole/mods/mcbot/api/event/TypeTrackedEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cn.evole.mods.mcbot.api.event; | ||
|
||
import com.google.common.collect.Maps; | ||
|
||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* @Project: McBot | ||
* @Author: cnlimiter | ||
* @CreateTime: 2024/8/11 20:31 | ||
* @Description: from <a href="https://github.com/AHilyard/Iceberg/blob/1.21-multi/common/src/main/java/com/anthonyhilyard/iceberg/events/TypeTrackedEvent.java">...</a> | ||
*/ | ||
public class TypeTrackedEvent<S, T> extends Event<T> | ||
{ | ||
private final Map<Class<? extends S>, T> listenerTypes = Maps.newHashMap(); | ||
|
||
public TypeTrackedEvent(Class<? super T> type, Function<T[], T> invokerFactory) | ||
{ | ||
super(type, invokerFactory); | ||
} | ||
|
||
@Override | ||
public void register(T listener) | ||
{ | ||
throw new UnsupportedOperationException("Register(listener) unsupported. Use Register(type, listener) instead!"); | ||
} | ||
|
||
public void register(Class<? extends S> type, T listener) | ||
{ | ||
super.register(listener); | ||
listenerTypes.put(type, listener); | ||
} | ||
|
||
public Map<Class<? extends S>, T> getListenerTypes() | ||
{ | ||
return listenerTypes; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
common/src/main/java/cn/evole/mods/mcbot/api/event/mod/McBotEvents.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cn.evole.mods.mcbot.api.event.mod; | ||
|
||
|
||
import cn.evole.mods.mcbot.api.event.ToggleableEvent; | ||
import net.minecraft.server.level.ServerPlayer; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
|
||
public class McBotEvents { | ||
/** | ||
* 当玩家发送一条消息(并转发到QQ)后触发。 | ||
* 包含发送消息的玩家、message_id和消息内容 | ||
*/ | ||
public static final ToggleableEvent<PlayerChat> ON_CHAT = ToggleableEvent.create(PlayerChat.class, callbacks -> (player, message_id, message) -> { | ||
for (PlayerChat callback : callbacks) { | ||
callback.onChat(player, message_id, message); | ||
} | ||
}); | ||
|
||
/** | ||
* 当玩家发送一条消息后(并在任何处理之前)触发。 | ||
* 如果取消它,消息将不会发送到游戏和QQ。 | ||
* 包含发送消息的玩家、消息内容和CallBackInfo。 | ||
*/ | ||
public static final ToggleableEvent<EarlyPlayerChat> BEFORE_CHAT = ToggleableEvent.create(EarlyPlayerChat.class, callbacks -> (player, message, ci) -> { | ||
for (EarlyPlayerChat callback : callbacks) { | ||
callback.onChat(player, message, ci); | ||
} | ||
}); | ||
|
||
@FunctionalInterface | ||
public interface PlayerChat { | ||
void onChat(ServerPlayer player, int message_id, String message); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface EarlyPlayerChat { | ||
void onChat(ServerPlayer player, String message, CallbackInfo ci); | ||
} | ||
} |
113 changes: 113 additions & 0 deletions
113
common/src/main/java/cn/evole/mods/mcbot/api/event/server/ServerGameEvents.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package cn.evole.mods.mcbot.api.event.server; | ||
|
||
import cn.evole.mods.mcbot.api.event.ToggleableEvent; | ||
import net.minecraft.advancements.Advancement; | ||
import net.minecraft.server.MinecraftServer; | ||
import net.minecraft.server.level.ServerLevel; | ||
import net.minecraft.server.level.ServerPlayer; | ||
import net.minecraft.world.damagesource.DamageSource; | ||
import net.minecraft.world.entity.player.Player; | ||
import net.minecraft.world.level.Level; | ||
import net.minecraft.world.level.block.state.BlockState; | ||
|
||
/** | ||
* Description: | ||
* Author: cnlimiter | ||
* Date: 2022/1/18 9:36 | ||
* Version: 1.0 | ||
*/ | ||
public final class ServerGameEvents { | ||
public static final ToggleableEvent<PlayerTick> PLAYER_TICK = ToggleableEvent.create(PlayerTick.class, callbacks -> (world, player) -> { | ||
for (PlayerTick callback : callbacks) { | ||
callback.onTick(world, player); | ||
} | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerDeath> PLAYER_DEATH = ToggleableEvent.create(PlayerDeath.class, callbacks -> (source, player) -> { | ||
for (PlayerDeath callback : callbacks) { | ||
callback.onDeath(source, player); | ||
} | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerChangeDimension> PLAYER_CHANGE_DIMENSION = ToggleableEvent.create(PlayerChangeDimension.class, callbacks -> (world, player) -> { | ||
for (PlayerChangeDimension callback : callbacks) { | ||
callback.onChangeDimension(world, player); | ||
} | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerDigSpeedCalc> ON_PLAYER_DIG_SPEED_CALC = ToggleableEvent.create(PlayerDigSpeedCalc.class, callbacks -> (world, player, digSpeed, state) -> { | ||
for (PlayerDigSpeedCalc callback : callbacks) { | ||
float newSpeed = callback.onDigSpeedCalc(world, player, digSpeed, state); | ||
if (newSpeed != digSpeed) { | ||
return newSpeed; | ||
} | ||
} | ||
|
||
return -1; | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerLoggedIn> PLAYER_LOGGED_IN = ToggleableEvent.create(PlayerLoggedIn.class, callbacks -> (server, player) -> { | ||
for (PlayerLoggedIn callback : callbacks) { | ||
callback.onPlayerLoggedIn(server, player); | ||
} | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerLoggedOut> PLAYER_LOGGED_OUT = ToggleableEvent.create(PlayerLoggedOut.class, callbacks -> (server, player) -> { | ||
for (PlayerLoggedOut callback : callbacks) { | ||
callback.onPlayerLoggedOut(server, player); | ||
} | ||
}); | ||
|
||
public static final ToggleableEvent<PlayerAdvancement> PLAYER_ADVANCEMENT = ToggleableEvent.create(PlayerAdvancement.class, callbacks -> (player, advancement) -> { | ||
for (PlayerAdvancement callback : callbacks) { | ||
callback.onAdvancement(player, advancement); | ||
} | ||
}); | ||
|
||
|
||
public static final ToggleableEvent<ServerChat> SERVER_CHAT = ToggleableEvent.create(ServerChat.class, callbacks -> (player, message) -> { | ||
for (ServerChat callback : callbacks) { | ||
callback.onChat(player, message); | ||
} | ||
}); | ||
|
||
@FunctionalInterface | ||
public interface PlayerAdvancement { | ||
void onAdvancement(Player player, Advancement advancement); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerTick { | ||
void onTick(ServerLevel world, ServerPlayer player); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerDeath { | ||
void onDeath(DamageSource source, ServerPlayer player); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerChangeDimension { | ||
void onChangeDimension(ServerLevel world, ServerPlayer player); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerDigSpeedCalc { | ||
float onDigSpeedCalc(Level world, Player player, float digSpeed, BlockState state); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerLoggedIn { | ||
void onPlayerLoggedIn(MinecraftServer server, ServerPlayer player); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface PlayerLoggedOut { | ||
void onPlayerLoggedOut(MinecraftServer server, ServerPlayer player); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface ServerChat { | ||
void onChat(ServerPlayer player, String message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package cn.evole.mods.mcbot; | ||
|
||
import net.fabricmc.api.ModInitializer; | ||
|
||
public class McBotFabric implements ModInitializer { | ||
|
||
@Override | ||
public void onInitialize() { | ||
CommonClass.init(); | ||
} | ||
} |
Oops, something went wrong.