/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion 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. */ package illarion.client.net; import illarion.client.net.annotations.ReplyMessage; import illarion.client.net.server.*; import org.jetbrains.annotations.Contract; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; /** * The Factory for commands the server sends to the client. This factory creates the required message objects on * demand. * * @author Martin Karing <nitram@illarion.org> */ @SuppressWarnings("OverlyCoupledClass") public final class ReplyFactory { /** * The singleton instance of this factory. */ @Nonnull private static final ReplyFactory INSTANCE = new ReplyFactory(); /** * The logger that takes care for the logging output of this class. */ @Nonnull private static final Logger log = LoggerFactory.getLogger(ReplyFactory.class); /** * This map stores the message classes along with the IDs of the command encoded in them. */ @Nonnull private final Map<Integer, Class<? extends ServerReply>> replyMap; /** * The default constructor of the factory. This registers all commands. */ @SuppressWarnings({"OverlyLongMethod", "OverlyCoupledMethod"}) private ReplyFactory() { replyMap = new HashMap<>(); register(AppearanceMsg.class); register(AttackMsg.class); register(AttributeMsg.class); register(BookMsg.class); register(CarryLoadMsg.class); register(ChangeItemMsg.class); register(CharacterAnimationMsg.class); register(CloseShowcaseMsg.class); register(CloseDialogMsg.class); register(DateTimeMsg.class); register(DialogCraftingMsg.class); register(DialogCraftingUpdateMsg.class); register(DialogInputMsg.class); register(DialogMerchantMsg.class); register(DialogMessageMsg.class); register(DialogSelectionMsg.class); register(DisconnectMsg.class); register(GraphicEffectMsg.class); register(InformMsg.class); register(IntroduceMsg.class); register(InventoryMsg.class); register(ItemUpdateMsg.class); register(KeepAliveMsg.class); register(LocationMsg.class); register(LookAtCharMsg.class); register(LookAtDialogItemMsg.class); register(LookAtInvMsg.class); register(LookAtMapItemMsg.class); register(LookAtShowcaseMsg.class); register(LookAtTileMsg.class); register(MagicFlagMsg.class); register(MapCompleteMsg.class); register(MapStripeMsg.class); register(MoveMsg.class); register(MusicMsg.class); register(PlayerIdMsg.class); register(PutItemMsg.class); register(QuestMsg.class); register(QuestDeleteMsg.class); register(QuestAvailabilityMsg.class); register(RemoveCharMsg.class); register(RemoveItemMsg.class); register(SayMsg.class); register(ShoutMsg.class); register(WhisperMsg.class); register(ShowcaseMsg.class); register(ShowcaseSingleMsg.class); register(SkillMsg.class); register(SoundEffectMsg.class); register(TargetLostMsg.class); register(TurnCharMsg.class); register(WeatherMsg.class); } /** * Register a class as replay message class. Those classes need to implement the {@link ServerReply} interface * and they require the contain the {@link ReplyMessage} annotation. * * @param clazz the class to register as reply. */ private void register(@Nonnull Class<? extends ServerReply> clazz) { ReplyMessage messageData = clazz.getAnnotation(ReplyMessage.class); if (messageData == null) { log.error("Illegal class supplied to register! No annotation: {}", clazz.getName()); return; } if (replyMap.containsKey(messageData.replyId())) { log.error("Class with duplicated key: {}", clazz.getName()); return; } replyMap.put(messageData.replyId(), clazz); } /** * Get a replay instance. This class will check if there is any reply fitting the ID registered and create a new * instance of it. * * @param id the ID of the reply * @return the newly created reply instance */ @Nullable @Contract(pure = true) public ServerReply getReply(int id) { Class<? extends ServerReply> replyClass = replyMap.get(id); if (replyClass == null) { log.error("Illegal reply requested. ID: 0x{}", Integer.toHexString(id)); return null; } try { return replyClass.getConstructor().newInstance(); } catch (InstantiationException e) { log.error("Failed to create instance of reply class!", e); } catch (IllegalAccessException e) { log.error("Access to reply class constructor was denied.", e); } catch (NoSuchMethodException e) { log.error("Failed to locate required constructor.", e); } catch (InvocationTargetException e) { log.error("Problem while executing the constructor.", e); } return null; } /** * Get the singleton instance of this class. * * @return the singleton instance of this class */ @Nonnull @Contract(pure = true) public static ReplyFactory getInstance() { return INSTANCE; } }