Allow interception at any level in the message class hierarchy.

This commit is contained in:
Major-
2015-08-24 13:38:54 +01:00
parent ac27bacbc5
commit d9c34cd9ed
15 changed files with 205 additions and 158 deletions
+109 -109
View File
@@ -1,111 +1,111 @@
<messages>
<message>
<type>org.apollo.game.message.impl.ButtonMessage</type>
<chain>
<handler>org.apollo.game.message.handler.DialogueButtonHandler</handler>
<handler>org.apollo.game.message.handler.BankButtonMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ChatMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ChatVerificationHandler</handler>
<handler>org.apollo.game.message.handler.ChatMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ClosedInterfaceMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ClosedInterfaceMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.CommandMessage</type>
<chain>
<handler>org.apollo.game.message.handler.CommandMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.DialogueContinueMessage</type>
<chain>
<handler>org.apollo.game.message.handler.DialogueContinueMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.EnteredAmountMessage</type>
<chain>
<handler>org.apollo.game.message.handler.EnteredAmountMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.RemoveEquippedItemHandler</handler>
<handler>org.apollo.game.message.handler.BankMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemOnItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.ItemOnItemVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemOnObjectMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemOnObjectVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.InventoryItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.EquipItemHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.MagicOnItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.NpcActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.NpcActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ObjectActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ObjectActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.PlayerActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.PlayerActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.PlayerDesignMessage</type>
<chain>
<handler>org.apollo.game.message.handler.PlayerDesignVerificationHandler</handler>
<handler>org.apollo.game.message.handler.PlayerDesignMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.SwitchItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.SwitchItemMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.WalkMessage</type>
<chain>
<handler>org.apollo.game.message.handler.WalkMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ButtonMessage</type>
<chain>
<handler>org.apollo.game.message.handler.DialogueButtonHandler</handler>
<handler>org.apollo.game.message.handler.BankButtonMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ChatMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ChatVerificationHandler</handler>
<handler>org.apollo.game.message.handler.ChatMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ClosedInterfaceMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ClosedInterfaceMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.CommandMessage</type>
<chain>
<handler>org.apollo.game.message.handler.CommandMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.DialogueContinueMessage</type>
<chain>
<handler>org.apollo.game.message.handler.DialogueContinueMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.EnteredAmountMessage</type>
<chain>
<handler>org.apollo.game.message.handler.EnteredAmountMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.RemoveEquippedItemHandler</handler>
<handler>org.apollo.game.message.handler.BankMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemOnItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.ItemOnItemVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemOnObjectMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemOnObjectVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ItemOptionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
<handler>org.apollo.game.message.handler.EquipItemHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.MagicOnItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ItemVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.NpcActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.NpcActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.ObjectActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.ObjectActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.PlayerActionMessage</type>
<chain>
<handler>org.apollo.game.message.handler.PlayerActionVerificationHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.PlayerDesignMessage</type>
<chain>
<handler>org.apollo.game.message.handler.PlayerDesignVerificationHandler</handler>
<handler>org.apollo.game.message.handler.PlayerDesignMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.SwitchItemMessage</type>
<chain>
<handler>org.apollo.game.message.handler.SwitchItemMessageHandler</handler>
</chain>
</message>
<message>
<type>org.apollo.game.message.impl.WalkMessage</type>
<chain>
<handler>org.apollo.game.message.handler.WalkMessageHandler</handler>
</chain>
</message>
</messages>
@@ -19,15 +19,6 @@ import org.apollo.game.model.inv.SynchronizationInventoryListener;
*/
public final class ItemVerificationHandler extends MessageHandler<InventoryItemMessage> {
/**
* Creates the ItemVerificationHandler.
*
* @param world The {@link World} the {@link InventoryItemMessage} occurred in.
*/
public ItemVerificationHandler(World world) {
super(world);
}
/**
* A supplier for an {@link Inventory}.
*
@@ -69,6 +60,15 @@ public final class ItemVerificationHandler extends MessageHandler<InventoryItemM
inventories.putIfAbsent(id, supplier);
}
/**
* Creates the ItemVerificationHandler.
*
* @param world The {@link World} the {@link InventoryItemMessage} occurred in.
*/
public ItemVerificationHandler(World world) {
super(world);
}
@Override
public void handle(Player player, InventoryItemMessage message) {
InventorySupplier supplier = inventories.get(message.getInterfaceId());
@@ -11,14 +11,14 @@ import com.google.common.base.MoreObjects;
/**
* A chain of {@link MessageHandler}s
*
* @param <M> The Message type this chain represents.
* @author Graham
* @author Ryley
* @param <M> The Message type this chain represents.
*/
public final class MessageHandlerChain<M extends Message> {
/**
* The handlers.
* The List of MessageHandlers.
*/
private final List<MessageHandler<M>> handlers = new ArrayList<>();
@@ -50,8 +50,7 @@ public final class MessageHandlerChain<M extends Message> {
*
* @param player The Player to handle this message for.
* @param message The Message.
* @return {@code true} if and only if the Message propagated down the chain without being terminated, otherwise
* {@code false}.
* @return {@code true} iff the Message propagated down the chain without being terminated.
*/
public boolean notify(Player player, M message) {
for (MessageHandler<M> handler : handlers) {
@@ -1,5 +1,7 @@
package org.apollo.game.message.handler;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
@@ -16,27 +18,36 @@ import org.apollo.net.message.Message;
public final class MessageHandlerChainSet {
/**
* The {@link Map} of Message types to {@link MessageHandlerChain}s
* The {@link Map} of {@link Message} {@link Class} types to {@link MessageHandlerChain}s
*/
private final Map<Class<? extends Message>, MessageHandlerChain<? extends Message>> chains = new HashMap<>();
/**
* The {@link Map} of {@link Message} {@link Class} types to {@link Deque}s of all Classes in the chain.
*/
private final Map<Class<? extends Message>, Deque<Class<? extends Message>>> classes = new HashMap<>();
/**
* Notifies the appropriate {@link MessageHandlerChain} that a {@link Message} has been received.
*
* @param player The {@link Player} receiving the Message.
* @param message The Message.
* @return {@code true} if the Message propagated down the chain without being terminated or if the chain for the
* Message was not found, otherwise {@code false}.
* @return {@code true} iff the Message propagated down the chain without being terminated.
*/
@SuppressWarnings("unchecked")
public <M extends Message> boolean notify(Player player, M message) {
Class<M> clazz = (Class<M>) message.getClass();
while (clazz.getSuperclass() != Message.class) {
clazz = (Class<M>) clazz.getSuperclass();
Deque<Class<? extends Message>> classes = this.classes.computeIfAbsent(message.getClass(),
this::getMessageClasses);
for (Class<? extends Message> type : classes) {
MessageHandlerChain<? super M> chain = (MessageHandlerChain<? super M>) chains.get(type);
if (chain != null && !chain.notify(player, message)) {
return false;
}
}
MessageHandlerChain<M> chain = (MessageHandlerChain<M>) chains.computeIfAbsent(clazz, MessageHandlerChain::new);
return chain.notify(player, message);
return true;
}
/**
@@ -51,4 +62,23 @@ public final class MessageHandlerChainSet {
chain.addHandler((MessageHandler<M>) handler);
}
/**
* Gets the {@link Deque} of {@link Class}es that can be handled.
*
* @param type The Class type of the message. Must not be the Class for {@link Message} itself.
* @return The Deque of Classes. Will never be {@code null}.
*/
@SuppressWarnings("unchecked")
private <M extends Message> Deque<Class<? extends Message>> getMessageClasses(Class<M> type) {
Deque<Class<? extends Message>> classes = new ArrayDeque<>();
Class<? super M> clazz = type;
do {
classes.addFirst((Class<? extends Message>) clazz);
clazz = clazz.getSuperclass();
} while (clazz != Message.class);
return classes;
}
}
@@ -1,10 +1,13 @@
package org.apollo.game.message.impl;
import com.google.common.base.Preconditions;
import org.apollo.net.message.Message;
import java.util.NoSuchElementException;
import java.util.OptionalInt;
/**
* A {@link Message} that represents some sort of action on an item in an inventory. Note that this is the parent of
* both item option and item action message, and so cannot be used to determine when one of those messages is fired.
* A {@link Message} that represents some sort of action on an item in an inventory.
*
* @author Chris Fletcher
*/
@@ -21,9 +24,9 @@ public abstract class InventoryItemMessage extends Message {
private final int interfaceId;
/**
* The option number (1-5).
* The option number (1-5 if present).
*/
private final int option;
private final OptionalInt option;
/**
* The item's slot.
@@ -31,14 +34,14 @@ public abstract class InventoryItemMessage extends Message {
private final int slot;
/**
* Creates the item action message.
* Creates the InventoryItemMessage.
*
* @param option The option number.
* @param option The option number, if applicable.
* @param interfaceId The interface id.
* @param id The id.
* @param slot The slot.
*/
protected InventoryItemMessage(int option, int interfaceId, int id, int slot) {
protected InventoryItemMessage(OptionalInt option, int interfaceId, int id, int slot) {
this.option = option;
this.interfaceId = interfaceId;
this.id = id;
@@ -67,9 +70,10 @@ public abstract class InventoryItemMessage extends Message {
* Gets the option number.
*
* @return The option number.
* @throws NoSuchElementException If there is no option.
*/
public final int getOption() {
return option;
return option.getAsInt();
}
/**
@@ -81,4 +85,13 @@ public abstract class InventoryItemMessage extends Message {
return slot;
}
/**
* Returns whether or not this InventoryItemMessage has an option number.
*
* @return {@code true} iff this InventoryItemMessage has an option number.
*/
public final boolean hasOption() {
return option.isPresent();
}
}
@@ -2,6 +2,8 @@ package org.apollo.game.message.impl;
import org.apollo.net.message.Message;
import java.util.OptionalInt;
/**
* A {@link Message} sent by the client that represents some sort of action on an item. Note that the actual message
* sent by the client is one of the five item action messages, but this is the message that should be intercepted (and
@@ -12,7 +14,7 @@ import org.apollo.net.message.Message;
public abstract class ItemActionMessage extends InventoryItemMessage {
/**
* Creates the item action message.
* Creates the ItemActionMessage.
*
* @param option The option number.
* @param interfaceId The interface id.
@@ -20,7 +22,7 @@ public abstract class ItemActionMessage extends InventoryItemMessage {
* @param slot The slot.
*/
public ItemActionMessage(int option, int interfaceId, int id, int slot) {
super(option, interfaceId, id, slot);
super(OptionalInt.of(option), interfaceId, id, slot);
}
}
@@ -1,5 +1,7 @@
package org.apollo.game.message.impl;
import java.util.OptionalInt;
/**
* A {@link InventoryItemMessage} sent by the client when a player uses one inventory item on another.
*
@@ -33,7 +35,7 @@ public final class ItemOnItemMessage extends InventoryItemMessage {
* @param targetSlot The slot of the target item.
*/
public ItemOnItemMessage(int usedInterface, int usedId, int usedSlot, int targetInterface, int targetId, int targetSlot) {
super(0, usedInterface, usedId, usedSlot);
super(OptionalInt.empty(), usedInterface, usedId, usedSlot);
this.targetInterface = targetInterface;
this.targetSlot = targetSlot;
this.targetId = targetId;
@@ -3,6 +3,8 @@ package org.apollo.game.message.impl;
import org.apollo.game.model.Position;
import org.apollo.net.message.Message;
import java.util.OptionalInt;
/**
* A {@link Message} sent by the client when an item is used on an object.
*
@@ -31,7 +33,7 @@ public final class ItemOnObjectMessage extends InventoryItemMessage {
* @param y The y coordinate.
*/
public ItemOnObjectMessage(int interfaceId, int itemId, int itemSlot, int objectId, int x, int y) {
super(0, interfaceId, itemId, itemSlot);
super(OptionalInt.empty(), interfaceId, itemId, itemSlot);
this.objectId = objectId;
position = new Position(x, y);
}
@@ -1,5 +1,7 @@
package org.apollo.game.message.impl;
import java.util.OptionalInt;
/**
* An {@link InventoryItemMessage} sent by the client when an item's option is clicked (e.g. equip, eat, drink, etc).
* Note that the actual message sent by the client is one of the five item option messages, but this is the message that
@@ -10,7 +12,7 @@ package org.apollo.game.message.impl;
public abstract class ItemOptionMessage extends InventoryItemMessage {
/**
* Creates the item option message.
* Creates the ItemOptionMessage.
*
* @param option The option number.
* @param interfaceId The interface id.
@@ -18,7 +20,7 @@ public abstract class ItemOptionMessage extends InventoryItemMessage {
* @param slot The slot.
*/
public ItemOptionMessage(int option, int interfaceId, int id, int slot) {
super(option, interfaceId, id, slot);
super(OptionalInt.of(option), interfaceId, id, slot);
}
}
@@ -1,5 +1,7 @@
package org.apollo.game.message.impl;
import java.util.OptionalInt;
/**
* A {@link InventoryItemMessage} sent by the client when a player casts a spell on an inventory item.
*
@@ -21,7 +23,7 @@ public final class MagicOnItemMessage extends InventoryItemMessage {
* @param spell The spell id.
*/
public MagicOnItemMessage(int interfaceId, int id, int slot, int spell) {
super(0, interfaceId, id, slot);
super(OptionalInt.empty(), interfaceId, id, slot);
this.spell = spell;
}
@@ -20,7 +20,6 @@ public final class RemoveObjectMessageEncoder extends MessageEncoder<RemoveObjec
builder.put(DataType.BYTE, DataTransformation.NEGATE, message.getType() << 2 | message.getOrientation());
builder.put(DataType.BYTE, message.getPositionOffset());
System.out.println("Sending rm obj: type=" + message.getType() + ", orient=" + message.getOrientation() + ",posoff=" + Integer.toBinaryString(message.getPositionOffset()));
return builder.toGamePacket();
}
@@ -188,7 +188,7 @@ public final class Release377 extends Release {
register(UpdateSkillMessage.class, new UpdateSkillMessageEncoder());
register(OpenInterfaceSidebarMessage.class, new OpenInterfaceSidebarMessageEncoder());
register(EnterAmountMessage.class, new EnterAmountMessageEncoder());
register(SetWidgetTextMessage.class, new SetWidgetTexMessageEncoder());
register(SetWidgetTextMessage.class, new SetWidgetTextMessageEncoder());
register(NpcSynchronizationMessage.class, new NpcSynchronizationMessageEncoder());
register(SetWidgetVisibilityMessage.class, new SetWidgetVisibilityMessageEncoder());
register(SetWidgetItemModelMessage.class, new SetWidgetItemModelMessageEncoder());
@@ -14,7 +14,7 @@ import org.apollo.net.release.MessageEncoder;
*
* @author Graham
*/
public final class SetWidgetTexMessageEncoder extends MessageEncoder<SetWidgetTextMessage> {
public final class SetWidgetTextMessageEncoder extends MessageEncoder<SetWidgetTextMessage> {
@Override
public GamePacket encode(SetWidgetTextMessage message) {
@@ -36,7 +36,7 @@ public final class GameSession extends Session {
/**
* The queue of pending {@link Message}s.
*/
private final BlockingQueue<Message> messageQueue = new ArrayBlockingQueue<>(GameConstants.MESSAGES_PER_PULSE);
private final BlockingQueue<Message> messages = new ArrayBlockingQueue<>(GameConstants.MESSAGES_PER_PULSE);
/**
* The player.
@@ -82,8 +82,9 @@ public final class GameSession extends Session {
* @param chainSet The {@link MessageHandlerChainSet}
*/
public void handlePendingMessages(MessageHandlerChainSet chainSet) {
Message message;
while ((message = messageQueue.poll()) != null) {
while (!messages.isEmpty()) {
Message message = messages.poll();
try {
chainSet.notify(player, message);
} catch (Exception reason) {
@@ -103,10 +104,10 @@ public final class GameSession extends Session {
@Override
public void messageReceived(Object message) {
if (messageQueue.size() >= GameConstants.MESSAGES_PER_PULSE) {
if (messages.size() >= GameConstants.MESSAGES_PER_PULSE) {
logger.warning("Too many messages in queue for game session, dropping...");
} else {
messageQueue.add((Message) message);
messages.add((Message) message);
}
}
@@ -188,11 +188,6 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
}
}
if (messages.size() > 0) {
System.out.println("Sending in mode " + mode + ", new regions=" + newRegions);
}
messages.forEach(player::send);
}