diff --git a/core/src/main/java/mc/core/CoreEventListener.java b/core/src/main/java/mc/core/CoreEventListener.java index 134e071..25318ba 100644 --- a/core/src/main/java/mc/core/CoreEventListener.java +++ b/core/src/main/java/mc/core/CoreEventListener.java @@ -1,8 +1,8 @@ package mc.core; -import com.google.common.eventbus.Subscribe; import lombok.extern.slf4j.Slf4j; -import mc.core.eventbus.EventBusGetter; +import mc.core.eventbus.EventBus; +import mc.core.eventbus.Subscriber; import mc.core.eventbus.events.CS_PlayerMoveEvent; import mc.core.eventbus.events.SC_ChunkLoadEvent; import mc.core.eventbus.events.SC_ChunkUnloadEvent; @@ -16,13 +16,11 @@ import java.util.Iterator; public class CoreEventListener { @PostConstruct public void registerEventHandlers() { - EventBusGetter.getInstance().register(this); + EventBus.getInstance().registerSubscribes(this); } - @Subscribe + @Subscriber public void handlerPlayerMoveEvent(CS_PlayerMoveEvent event) { - log.trace("(GameLoop) playerMoveEventHandler()"); - Chunk chunk; chunk = event.getPlayer().getWorld().getChunk(event.getOldLocation().toBlockLocation()); // Old chunk int ccX = chunk.getX(); @@ -50,7 +48,7 @@ public class CoreEventListener { } if (!eventChunkUnload.getNeedUnloadChunks().isEmpty()) { - EventBusGetter.getInstance().post(eventChunkUnload); + EventBus.getInstance().post(eventChunkUnload); } SC_ChunkLoadEvent eventChunkLoad = new SC_ChunkLoadEvent(event.getPlayer()); @@ -67,7 +65,7 @@ public class CoreEventListener { } if (!eventChunkLoad.getNeedLoadChunks().isEmpty()) { - EventBusGetter.getInstance().post(eventChunkLoad); + EventBus.getInstance().post(eventChunkLoad); } } diff --git a/core/src/main/java/mc/core/GameLoop.java b/core/src/main/java/mc/core/GameLoop.java index 4b5affc..7838fee 100644 --- a/core/src/main/java/mc/core/GameLoop.java +++ b/core/src/main/java/mc/core/GameLoop.java @@ -6,6 +6,7 @@ package mc.core; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import mc.core.eventbus.EventBus; import mc.core.player.PlayerManager; import mc.core.time.TimeProcessor; import org.springframework.beans.factory.annotation.Autowired; @@ -47,6 +48,8 @@ public class GameLoop extends Thread { /* --- --- --- */ + EventBus.getInstance().process(); + /* TODO нужно перенести этот функционал на Network */ playerManager.getBroadcastChannel().sendTimeUpdate( gameTimer.getGameTime(), diff --git a/core/src/main/java/mc/core/eventbus/Event.java b/core/src/main/java/mc/core/eventbus/Event.java index 08aa0ec..c31a008 100644 --- a/core/src/main/java/mc/core/eventbus/Event.java +++ b/core/src/main/java/mc/core/eventbus/Event.java @@ -1,13 +1,4 @@ -/* - * DmitriyMX - * 2018-05-02 - */ package mc.core.eventbus; public interface Event { - void setCanceled(boolean value); - boolean isCanceled(); - - void setLastProcess(boolean value); - boolean isLastProcess(); } diff --git a/core/src/main/java/mc/core/eventbus/EventBase.java b/core/src/main/java/mc/core/eventbus/EventBase.java deleted file mode 100644 index 7756cce..0000000 --- a/core/src/main/java/mc/core/eventbus/EventBase.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * DmitriyMX - * 2018-05-02 - */ -package mc.core.eventbus; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public abstract class EventBase implements Event { - private boolean canceled; - private boolean lastProcess; -} diff --git a/core/src/main/java/mc/core/eventbus/EventBus.java b/core/src/main/java/mc/core/eventbus/EventBus.java new file mode 100644 index 0000000..1a14331 --- /dev/null +++ b/core/src/main/java/mc/core/eventbus/EventBus.java @@ -0,0 +1,89 @@ +package mc.core.eventbus; + +import javafx.util.Pair; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.helpers.MessageFormatter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Stream; + +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EventBus { + @Getter + private static final EventBus instance = new EventBus(); + + private Queue eventQueue = new ConcurrentLinkedQueue<>(); + private Map, List>> subscribes = new HashMap<>(); + + private Stream getMethods(Object subscriberObject) { + return Stream.of(subscriberObject.getClass().getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(Subscriber.class)) + .filter(method -> method.getReturnType().equals(Void.TYPE)) + .filter(method -> method.getParameterCount() == 1) + .filter(method -> Event.class.isAssignableFrom(method.getParameterTypes()[0])); + } + + @SuppressWarnings("unchecked") + public void registerSubscribes(Object subscriberObject) { + getMethods(subscriberObject) + .forEach(method -> { + final Class type = (Class) method.getParameterTypes()[0]; + final List> pairs; + if (subscribes.containsKey(type)) { + pairs = subscribes.get(type); + } else { + pairs = new ArrayList<>(); + subscribes.put(type, pairs); + } + pairs.add(new Pair<>(subscriberObject, method)); + }); + } + + @SuppressWarnings("unchecked") + public void unregisterSubscribes(Object subscriberObject) { + getMethods(subscriberObject) + .forEach(method -> { + final Class type = (Class) method.getParameterTypes()[0]; + if (subscribes.containsKey(type)) { + final List> pairs = subscribes.get(type); + pairs.removeIf(pair -> pair.getKey() == subscriberObject); + + if (pairs.isEmpty()) { + subscribes.remove(type); + } + } + }); + } + + public void post(Event event) { + eventQueue.add(event); + } + + public void process() { + Event event; + while ((event = eventQueue.poll()) != null) { + final Class type = event.getClass(); + if (subscribes.containsKey(type)) { + final List> pairs = subscribes.get(type); + for (Pair pair : pairs) { + try { + pair.getValue().invoke(pair.getKey(), event); + } catch (IllegalAccessException | InvocationTargetException e) { + log.error(MessageFormatter.format("Invoke method '{}#{}'", + pair.getKey().getClass().getSimpleName(), + pair.getValue().getName()).getMessage(), + e + ); + } + } + } + } + } +} diff --git a/core/src/main/java/mc/core/eventbus/EventBusGetter.java b/core/src/main/java/mc/core/eventbus/EventBusGetter.java deleted file mode 100644 index 37e952b..0000000 --- a/core/src/main/java/mc/core/eventbus/EventBusGetter.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * DmitriyMX - * 2018-05-02 - */ -package mc.core.eventbus; - -import com.google.common.eventbus.EventBus; -import lombok.Getter; - -public final class EventBusGetter { - @Getter - private static final EventBus instance = new EventBus(); - - private EventBusGetter() { - } -} diff --git a/core/src/main/java/mc/core/eventbus/Subscriber.java b/core/src/main/java/mc/core/eventbus/Subscriber.java new file mode 100644 index 0000000..9a8aaee --- /dev/null +++ b/core/src/main/java/mc/core/eventbus/Subscriber.java @@ -0,0 +1,11 @@ +package mc.core.eventbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(value= ElementType.METHOD) +@Retention(value= RetentionPolicy.RUNTIME) +public @interface Subscriber { +} diff --git a/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java b/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java index 4d759ab..1d9b6ae 100644 --- a/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java +++ b/core/src/main/java/mc/core/eventbus/events/CS_PlayerMoveEvent.java @@ -1,18 +1,14 @@ -/* - * DmitriyMX - * 2018-05-02 - */ package mc.core.eventbus.events; import lombok.Getter; import lombok.Setter; import mc.core.EntityLocation; import mc.core.ImmutableEntityLocation; -import mc.core.eventbus.EventBase; +import mc.core.eventbus.Event; import mc.core.player.Player; @Getter -public class CS_PlayerMoveEvent extends EventBase { +public class CS_PlayerMoveEvent implements Event { private final Player player; private final ImmutableEntityLocation oldLocation; @Setter diff --git a/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java index ea55a7a..15148ad 100644 --- a/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java +++ b/core/src/main/java/mc/core/eventbus/events/SC_ChunkLoadEvent.java @@ -2,14 +2,14 @@ package mc.core.eventbus.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import mc.core.eventbus.EventBase; +import mc.core.eventbus.Event; import mc.core.player.Player; import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor -public class SC_ChunkLoadEvent extends EventBase { +public class SC_ChunkLoadEvent implements Event { @Getter private final Player player; @Getter diff --git a/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java index b2cce0d..d1a9517 100644 --- a/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java +++ b/core/src/main/java/mc/core/eventbus/events/SC_ChunkUnloadEvent.java @@ -2,14 +2,14 @@ package mc.core.eventbus.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import mc.core.eventbus.EventBase; +import mc.core.eventbus.Event; import mc.core.player.Player; import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor -public class SC_ChunkUnloadEvent extends EventBase { +public class SC_ChunkUnloadEvent implements Event { @Getter private final Player player; @Getter diff --git a/core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java deleted file mode 100644 index 95765f3..0000000 --- a/core/src/main/java/mc/core/eventbus/events/SC_LoginEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * DmitriyMX - * 2018-05-02 - */ -package mc.core.eventbus.events; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import mc.core.eventbus.EventBase; - -import java.net.SocketAddress; - -@RequiredArgsConstructor -@Getter -@Setter -public class SC_LoginEvent extends EventBase { - private String playerName; - private final SocketAddress remoteAddress; - private boolean deny; - private String denyReason; -} diff --git a/core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java deleted file mode 100644 index 90e5379..0000000 --- a/core/src/main/java/mc/core/eventbus/events/SC_PlayerLookEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * DmitriyMX - * 2018-05-02 - */ -package mc.core.eventbus.events; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import mc.core.EntityLocation; -import mc.core.eventbus.EventBase; -import mc.core.player.Player; - -@RequiredArgsConstructor -@Getter -@Setter -public class SC_PlayerLookEvent extends EventBase { - private final Player player; - private EntityLocation newLook; -} diff --git a/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java index d0634a7..7f479ee 100644 --- a/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java +++ b/core/src/main/java/mc/core/eventbus/events/SC_PlayerMoveEvent.java @@ -4,12 +4,12 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import mc.core.EntityLocation; -import mc.core.eventbus.EventBase; +import mc.core.eventbus.Event; import mc.core.player.Player; @RequiredArgsConstructor @Getter -public class SC_PlayerMoveEvent extends EventBase { +public class SC_PlayerMoveEvent implements Event { private final Player player; @Setter private EntityLocation newLocation; diff --git a/core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java b/core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java deleted file mode 100644 index 877df70..0000000 --- a/core/src/main/java/mc/core/eventbus/events/SC_ServerPingEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * DmitriyMX - * 2018-05-02 - */ -package mc.core.eventbus.events; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import mc.core.eventbus.EventBase; - -import java.net.SocketAddress; - -@RequiredArgsConstructor -@Getter -@Setter -public class SC_ServerPingEvent extends EventBase { - private final SocketAddress remoteAddress; - private String description; - private int online; - private int maxOnline; -} diff --git a/core/src/test/java/mc/core/TestEventBus.java b/core/src/test/java/mc/core/TestEventBus.java new file mode 100644 index 0000000..beb19c8 --- /dev/null +++ b/core/src/test/java/mc/core/TestEventBus.java @@ -0,0 +1,125 @@ +package mc.core; + +import javafx.util.Pair; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import mc.core.eventbus.Event; +import mc.core.eventbus.EventBus; +import mc.core.eventbus.Subscriber; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.internal.util.reflection.Whitebox; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +class TestEventBus { + private List resultList = new ArrayList<>(); + + @SuppressWarnings("unchecked") + private Map, List>> getEventBusFieldSubscribes() { + return (Map, List>>) + Whitebox.getInternalState(EventBus.getInstance(), "subscribes"); + } + + @BeforeEach + @SuppressWarnings("unchecked") + void before() { + getEventBusFieldSubscribes().clear(); + ((Queue) Whitebox.getInternalState(EventBus.getInstance(), "eventQueue")).clear(); + } + + + @Test + void testRegisterSubscribes() { + DumbEventHandler handler = new DumbEventHandler(); + EventBus.getInstance().registerSubscribes(handler); + + Map, List>> subscribes = getEventBusFieldSubscribes(); + assertEquals(1, subscribes.size()); + + List> pairs = subscribes.values().iterator().next(); + assertEquals(1, pairs.size()); + + Pair pair = pairs.get(0); + assertSame(handler, pair.getKey()); + assertEquals("corectSubscribe", pair.getValue().getName()); + } + + @Test + void testUnregisterSubscribes() { + DumbEventHandler handler = new DumbEventHandler(); + EventBus.getInstance().registerSubscribes(handler); + + EventBus.getInstance().unregisterSubscribes(handler); + + Map, List>> subscribes = getEventBusFieldSubscribes(); + assertEquals(0, subscribes.size()); + } + + @Test + @SuppressWarnings("unchecked") + void testPost() { + EventBus.getInstance().post(new DumbEvent()); + + Queue eventQueue = (Queue) Whitebox.getInternalState(EventBus.getInstance(), "eventQueue"); + assertEquals(1, eventQueue.size()); + } + + @Test + void testProcess() { + Stream.of(new DumbEventHandler("D1 "), new DumbEventHandler("D2 ")) + .forEach(handler -> EventBus.getInstance().registerSubscribes(handler)); + + Stream.of(new DumbEvent("message 1"), new DumbEvent("message 2")) + .forEach(event -> EventBus.getInstance().post(event)); + + EventBus.getInstance().process(); + + assertEquals(4, resultList.size()); + assertEquals("D1 message 1", resultList.get(0)); + assertEquals("D2 message 1", resultList.get(1)); + assertEquals("D1 message 2", resultList.get(2)); + assertEquals("D2 message 2", resultList.get(3)); + } + + @AllArgsConstructor + @NoArgsConstructor + private class DumbEvent implements Event { + String message; + } + + @AllArgsConstructor + @NoArgsConstructor + public class DumbEventHandler { + private String prefix = ""; + + @Subscriber + public void corectSubscribe(DumbEvent event) { + resultList.add(prefix + event.message); + } + + @Subscriber + public Object incorectSubscribeReturnType(DumbEvent event) { + return null; + } + + @Subscriber + public void incorrectSubscriberTypeParameter(Object object) { + } + + @Subscriber + public void incorrectSubscriberManyParameters(DumbEvent event, Object object) { + } + + public void someMethod() { + } + } +} diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java index 7df1c3f..d17cba5 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/NettyServer.java @@ -11,7 +11,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.AttributeKey; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import mc.core.eventbus.EventBusGetter; +import mc.core.eventbus.EventBus; import mc.core.network.Server; import mc.core.network.StartServerException; import mc.core.network.proto_1_12_2.State; @@ -65,7 +65,7 @@ public class NettyServer implements Server { public void start() throws StartServerException { log.info("Use protocol {}", StatusResponsePacket.NAME); - EventBusGetter.getInstance().register(new PlayerEventListener()); + EventBus.getInstance().registerSubscribes(new PlayerEventListener()); bossGroup = new NioEventLoopGroup(1); workerGroup = new NioEventLoopGroup(workerGroupCount); diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PlayerEventListener.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PlayerEventListener.java index 4c7e533..079ebf2 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PlayerEventListener.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/PlayerEventListener.java @@ -1,7 +1,7 @@ package mc.core.network.proto_1_12_2.netty; -import com.google.common.eventbus.Subscribe; import lombok.extern.slf4j.Slf4j; +import mc.core.eventbus.Subscriber; import mc.core.eventbus.events.SC_ChunkLoadEvent; import mc.core.eventbus.events.SC_ChunkUnloadEvent; import mc.core.eventbus.events.SC_PlayerMoveEvent; @@ -13,8 +13,8 @@ import mc.core.utils.CompactedCoords; import mc.core.world.chunk.Chunk; @Slf4j -class PlayerEventListener { - @Subscribe +public class PlayerEventListener { + @Subscriber public void playerMoveEventHandler(SC_PlayerMoveEvent event) { log.debug("(SC) playerMoveEventHandler()"); PlayerPositionAndLookPacket packet = new PlayerPositionAndLookPacket(); @@ -25,7 +25,7 @@ class PlayerEventListener { event.getPlayer().getChannel().writeAndFlush(packet); } - @Subscribe + @Subscriber public void playerChunkLoadHandler(SC_ChunkLoadEvent event) { for(Integer compressXZ : event.getNeedLoadChunks()) { int[] xz = CompactedCoords.uncompressXZ(compressXZ); @@ -41,7 +41,7 @@ class PlayerEventListener { } } - @Subscribe + @Subscriber public void playerChunkUnloadHandler(SC_ChunkUnloadEvent event) { for(Integer compressXZ : event.getNeedUnloadChunks()) { int[] xz = CompactedCoords.uncompressXZ(compressXZ); diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java index 19afb32..8a677cd 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java @@ -2,7 +2,7 @@ package mc.core.network.proto_1_12_2.netty.handlers; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; -import mc.core.eventbus.EventBusGetter; +import mc.core.eventbus.EventBus; import mc.core.eventbus.events.CS_PlayerMoveEvent; import mc.core.network.proto_1_12_2.State; import mc.core.network.proto_1_12_2.TeleportManager; @@ -15,9 +15,7 @@ import mc.core.player.PlayerMode; import mc.core.text.Text; import mc.core.text.TextColor; import mc.core.text.TextStyle; -import mc.core.utils.CompactedCoords; import mc.core.world.World; -import mc.core.world.chunk.Chunk; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -123,7 +121,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand CS_PlayerMoveEvent event = new CS_PlayerMoveEvent(player, player.getLocation()); event.setNewLocation(player.getLocation()); event.setRecalcChunk(true); - EventBusGetter.getInstance().post(event); + EventBus.getInstance().post(event); } } } diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/PlayHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/PlayHandler.java index 83b1a7e..c1675ba 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/PlayHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/PlayHandler.java @@ -1,14 +1,10 @@ -/* - * DmitriyMX - * 2018-06-23 - */ package mc.core.network.proto_1_12_2.netty.handlers; import io.netty.channel.Channel; import lombok.extern.slf4j.Slf4j; import mc.core.EntityLocation; import mc.core.chat.ChatProcessor; -import mc.core.eventbus.EventBusGetter; +import mc.core.eventbus.EventBus; import mc.core.eventbus.events.CS_PlayerMoveEvent; import mc.core.network.proto_1_12_2.TeleportManager; import mc.core.network.proto_1_12_2.packets.*; @@ -83,7 +79,7 @@ public class PlayHandler extends AbstractStateHandler implements PlayStateHandle player.getLocation().getYaw(), player.getLocation().getPitch() )); - EventBusGetter.getInstance().post(event); + EventBus.getInstance().post(event); } @Handler