Replace all ConnectionCallbacks with Event api, add EventInvokerCallback

This commit is contained in:
Bixilon 2020-11-19 21:57:06 +01:00
parent 28e62f0f86
commit 56d40455d5
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 210 additions and 79 deletions

View File

@ -20,6 +20,9 @@ import de.bixilon.minosoft.data.locale.Strings;
import de.bixilon.minosoft.data.mappings.versions.Version;
import de.bixilon.minosoft.data.mappings.versions.Versions;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.modding.event.EventInvokerCallback;
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent;
import de.bixilon.minosoft.modding.event.events.ServerListPingArriveEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.ping.ForgeModInfo;
import de.bixilon.minosoft.protocol.ping.ServerListPing;
@ -134,7 +137,8 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
if (server.getLastPing() == null) {
server.ping();
}
server.getLastPing().addPingCallback(ping -> Platform.runLater(() -> {
server.getLastPing().registerEvent(new EventInvokerCallback<ServerListPingArriveEvent>(ServerListPingArriveEvent.class, event -> Platform.runLater(() -> {
ServerListPing ping = event.getServerListPing();
if (server != this.server) {
// cell does not contains us anymore
return;
@ -193,7 +197,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
canConnect = false;
setErrorMotd(String.format("%s: %s", server.getLastPing().getLastConnectionException().getClass().getCanonicalName(), server.getLastPing().getLastConnectionException().getLocalizedMessage()));
}
}));
})));
}
private void resetCell() {
@ -265,7 +269,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
}
optionsConnect.setDisable(true);
connection.connect(server.getLastPing().getAddress(), version);
connection.addConnectionChangeCallback(this::handleConnectionCallback);
connection.registerEvent(new EventInvokerCallback<>(this::handleConnectionCallback));
server.addConnection(connection);
}
@ -334,7 +338,8 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
dialog.showAndWait();
}
private void handleConnectionCallback(Connection connection) {
private void handleConnectionCallback(ConnectionStateChangeEvent event) {
Connection connection = event.getConnection();
if (!server.getConnections().contains(connection)) {
// the card got recycled
return;

View File

@ -15,6 +15,8 @@ package de.bixilon.minosoft.gui.main;
import de.bixilon.minosoft.data.locale.LocaleManager;
import de.bixilon.minosoft.data.locale.Strings;
import de.bixilon.minosoft.modding.event.EventInvokerCallback;
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
@ -81,12 +83,13 @@ public class SessionListCell extends ListCell<Connection> implements Initializab
}
setStyle(null);
this.connection = connection;
connection.addConnectionChangeCallback(this::handleConnectionCallback);
connection.registerEvent(new EventInvokerCallback<>(this::handleConnectionCallback));
connectionId.setText(String.format("#%d", connection.getConnectionId()));
account.setText(connection.getPlayer().getAccount().getPlayerName());
}
private void handleConnectionCallback(Connection connection) {
private void handleConnectionCallback(ConnectionStateChangeEvent event) {
Connection connection = event.getConnection();
if (this.connection != connection) {
// the card got recycled
return;

View File

@ -0,0 +1,41 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event;
import de.bixilon.minosoft.modding.event.events.ConnectionEvent;
import de.bixilon.minosoft.modding.loading.Priorities;
public abstract class EventInvoker {
protected final EventListener listener;
protected final boolean ignoreCancelled;
protected final Priorities priority;
public EventInvoker(boolean ignoreCancelled, Priorities priority, EventListener listener) {
this.ignoreCancelled = ignoreCancelled;
this.listener = listener;
this.priority = priority;
}
public boolean isIgnoreCancelled() {
return ignoreCancelled;
}
public Priorities getPriority() {
return priority;
}
public abstract void invoke(ConnectionEvent event);
public abstract Class<? extends ConnectionEvent> getEventType();
}

View File

@ -0,0 +1,54 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event;
import de.bixilon.minosoft.modding.event.events.ConnectionEvent;
import de.bixilon.minosoft.modding.loading.Priorities;
public class EventInvokerCallback<V extends ConnectionEvent> extends EventInvoker {
private final InvokerCallback<V> callback;
Class<? extends ConnectionEvent> eventType = ConnectionEvent.class;
public EventInvokerCallback(boolean ignoreCancelled, InvokerCallback<V> callback) {
super(ignoreCancelled, Priorities.NORMAL, null);
this.callback = callback;
}
// if you need instant fireing support
public EventInvokerCallback(Class<? extends ConnectionEvent> eventType, InvokerCallback<V> callback) {
this(false, callback);
this.eventType = eventType; // ToDo: how to get the class of V? seems to be impossible
}
public EventInvokerCallback(InvokerCallback<V> callback) {
this(false, callback);
}
public void invoke(ConnectionEvent event) {
if (eventType != event.getClass()) {
return;
}
callback.handle((V) event);
}
public Class<? extends ConnectionEvent> getEventType() {
return eventType;
}
public interface InvokerCallback<V> {
void handle(V event);
}
}

View File

@ -16,23 +16,23 @@ package de.bixilon.minosoft.modding.event;
import de.bixilon.minosoft.modding.event.events.CancelableEvent;
import de.bixilon.minosoft.modding.event.events.ConnectionEvent;
import de.bixilon.minosoft.modding.event.events.annotations.EventHandler;
import de.bixilon.minosoft.modding.loading.Priorities;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class EventMethod {
private final EventHandler annotation;
private final EventListener listener;
public class EventInvokerMethod extends EventInvoker {
private final Method method;
private final Class<? extends ConnectionEvent> eventType;
public EventMethod(EventHandler annotation, EventListener listener, Method method) {
this.annotation = annotation;
this.listener = listener;
public EventInvokerMethod(boolean ignoreCancelled, Priorities priority, EventListener listener, Method method) {
super(ignoreCancelled, priority, listener);
this.method = method;
eventType = (Class<? extends ConnectionEvent>) method.getParameters()[0].getType();
}
public EventHandler getAnnotation() {
return annotation;
public EventInvokerMethod(EventHandler annotation, EventListener listener, Method method) {
this(annotation.ignoreCancelled(), annotation.priority(), listener, method);
}
public Method getMethod() {
@ -43,7 +43,7 @@ public class EventMethod {
if (!method.getParameters()[0].getType().isAssignableFrom(event.getClass())) {
return;
}
if (!annotation.ignoreCancelled() && event instanceof CancelableEvent cancelableEvent && cancelableEvent.isCancelled()) {
if (!ignoreCancelled && event instanceof CancelableEvent cancelableEvent && cancelableEvent.isCancelled()) {
return;
}
try {
@ -52,4 +52,8 @@ public class EventMethod {
e.printStackTrace();
}
}
public Class<? extends ConnectionEvent> getEventType() {
return eventType;
}
}

View File

@ -23,16 +23,16 @@ import java.util.HashMap;
import java.util.HashSet;
public class EventManager {
private final HashSet<EventMethod> globalEventListeners = new HashSet<>();
private final HashMap<HashSet<ServerAddressValidator>, HashSet<EventMethod>> specificEventListeners = new HashMap<>();
private final HashSet<EventInvoker> globalEventListeners = new HashSet<>();
private final HashMap<HashSet<ServerAddressValidator>, HashSet<EventInvoker>> specificEventListeners = new HashMap<>();
public void registerGlobalListener(EventListener listener) {
globalEventListeners.addAll(getEventMethods(listener));
}
private HashSet<EventMethod> getEventMethods(EventListener listener) {
private HashSet<EventInvoker> getEventMethods(EventListener listener) {
Class<? extends EventListener> clazz = listener.getClass();
HashSet<EventMethod> eventMethods = new HashSet<>();
HashSet<EventInvoker> eventInvokers = new HashSet<>();
for (Method method : clazz.getMethods()) {
EventHandler annotation = method.getAnnotation(EventHandler.class);
if (annotation == null) {
@ -44,12 +44,12 @@ public class EventManager {
if (!ConnectionEvent.class.isAssignableFrom(method.getParameters()[0].getType())) {
continue;
}
eventMethods.add(new EventMethod(annotation, listener, method));
eventInvokers.add(new EventInvokerMethod(annotation, listener, method));
}
return eventMethods;
return eventInvokers;
}
public HashSet<EventMethod> getGlobalEventListeners() {
public HashSet<EventInvoker> getGlobalEventListeners() {
return globalEventListeners;
}
@ -61,7 +61,7 @@ public class EventManager {
specificEventListeners.put(serverAddresses, getEventMethods(listener));
}
public HashMap<HashSet<ServerAddressValidator>, HashSet<EventMethod>> getSpecificEventListeners() {
public HashMap<HashSet<ServerAddressValidator>, HashSet<EventInvoker>> getSpecificEventListeners() {
return specificEventListeners;
}
}

View File

@ -0,0 +1,41 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event.events;
import de.bixilon.minosoft.modding.event.events.annotations.Unsafe;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.protocol.ConnectionStates;
/**
* Fired when the connection state was just changed
*/
@Unsafe
public class ConnectionStateChangeEvent extends ConnectionEvent {
private final ConnectionStates previousState;
private final ConnectionStates currentState;
public ConnectionStateChangeEvent(Connection connection, ConnectionStates previousState, ConnectionStates currentState) {
super(connection);
this.previousState = previousState;
this.currentState = currentState;
}
public ConnectionStates getPreviousState() {
return previousState;
}
public ConnectionStates getCurrentState() {
return currentState;
}
}

View File

@ -11,10 +11,26 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.main;
package de.bixilon.minosoft.modding.event.events;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.ping.ServerListPing;
public interface ConnectionChangeCallback {
void handle(Connection connection);
import javax.annotation.Nullable;
/**
* Fired when a ping arrives from the server or the ping already arrived and the event got registered to late
*/
public class ServerListPingArriveEvent extends ConnectionEvent {
private final ServerListPing serverListPing;
public ServerListPingArriveEvent(Connection connection, ServerListPing serverListPing) {
super(connection);
this.serverListPing = serverListPing;
}
@Nullable
public ServerListPing getServerListPing() {
return serverListPing;
}
}

View File

@ -13,5 +13,9 @@
package de.bixilon.minosoft.modding.event.events.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Unsafe {
}

View File

@ -21,21 +21,16 @@ import de.bixilon.minosoft.data.mappings.recipes.Recipes;
import de.bixilon.minosoft.data.mappings.versions.Version;
import de.bixilon.minosoft.data.mappings.versions.VersionMapping;
import de.bixilon.minosoft.data.mappings.versions.Versions;
import de.bixilon.minosoft.gui.main.ConnectionChangeCallback;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.logging.LogLevels;
import de.bixilon.minosoft.modding.event.EventMethod;
import de.bixilon.minosoft.modding.event.events.CancelableEvent;
import de.bixilon.minosoft.modding.event.events.ConnectionEvent;
import de.bixilon.minosoft.modding.event.events.PacketReceiveEvent;
import de.bixilon.minosoft.modding.event.events.PacketSendEvent;
import de.bixilon.minosoft.modding.event.EventInvoker;
import de.bixilon.minosoft.modding.event.events.*;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
import de.bixilon.minosoft.protocol.packets.serverbound.handshaking.PacketHandshake;
import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketLoginStart;
import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusPing;
import de.bixilon.minosoft.protocol.packets.serverbound.status.PacketStatusRequest;
import de.bixilon.minosoft.protocol.ping.PingCallback;
import de.bixilon.minosoft.protocol.ping.ServerListPing;
import de.bixilon.minosoft.protocol.protocol.*;
import de.bixilon.minosoft.util.DNSUtil;
@ -43,7 +38,6 @@ import de.bixilon.minosoft.util.ServerAddress;
import org.xbill.DNS.TextParseException;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@ -55,9 +49,7 @@ public class Connection {
final PacketSender sender = new PacketSender(this);
final LinkedBlockingQueue<ClientboundPacket> handlingQueue = new LinkedBlockingQueue<>();
final VelocityHandler velocityHandler = new VelocityHandler(this);
final HashSet<PingCallback> pingCallbacks = new HashSet<>();
final HashSet<ConnectionChangeCallback> connectionChangeCallbacks = new HashSet<>();
final LinkedList<EventMethod> eventListeners = new LinkedList<>();
final LinkedList<EventInvoker> eventListeners = new LinkedList<>();
final int connectionId;
final Player player;
final String hostname;
@ -282,15 +274,6 @@ public class Connection {
return connectionId;
}
public void addPingCallback(PingCallback callback) {
if (getConnectionState() == ConnectionStates.FAILED || getConnectionState() == ConnectionStates.FAILED_NO_RETRY || lastPing != null) {
// ping done
callback.handle(lastPing);
return;
}
pingCallbacks.add(callback);
}
public ConnectionStates getConnectionState() {
return state;
}
@ -320,7 +303,7 @@ public class Connection {
if (a == null || b == null) {
return 0;
}
return -(b.getAnnotation().priority().ordinal() - a.getAnnotation().priority().ordinal());
return -(b.getPriority().ordinal() - a.getPriority().ordinal());
});
// connection established, starting threads and logging in
startHandlingThread();
@ -370,11 +353,7 @@ public class Connection {
case FAILED_NO_RETRY -> handlePingCallbacks(null);
}
// handle callbacks
connectionChangeCallbacks.forEach((callback -> callback.handle(this)));
}
public HashSet<PingCallback> getPingCallbacks() {
return pingCallbacks;
fireEvent(new ConnectionStateChangeEvent(this, previousState, state));
}
public int getDesiredVersionNumber() {
@ -387,21 +366,23 @@ public class Connection {
public void handlePingCallbacks(@Nullable ServerListPing ping) {
this.lastPing = ping;
pingCallbacks.forEach((callback -> callback.handle(ping)));
fireEvent(new ServerListPingArriveEvent(this, ping));
}
public void registerEvent(EventInvoker method) {
eventListeners.add(method);
if (method.getEventType() == ServerListPingArriveEvent.class) {
if (getConnectionState() == ConnectionStates.FAILED || getConnectionState() == ConnectionStates.FAILED_NO_RETRY || lastPing != null) {
// ping done
method.invoke(new ServerListPingArriveEvent(this, lastPing));
}
}
}
public Exception getLastConnectionException() {
return (lastException != null) ? lastException : network.getLastException();
}
public void addConnectionChangeCallback(ConnectionChangeCallback callback) {
connectionChangeCallbacks.add(callback);
}
public HashSet<ConnectionChangeCallback> getConnectionChangeCallbacks() {
return connectionChangeCallbacks;
}
public ServerListPing getLastPing() {
return lastPing;
}

View File

@ -1,18 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.ping;
public interface PingCallback {
void handle(ServerListPing ping);
}