From e6cc61a27249ab472c3e3ce3cf25a48dc8e3a883 Mon Sep 17 00:00:00 2001 From: Yannik Bretschneider Date: Sun, 2 May 2021 02:58:39 +0200 Subject: [PATCH] feat: rewrote EntityBuilder to check for entity correctness when building entities --- .../gamelibrary/events/EventBuilder.java | 625 ++++++++---------- 1 file changed, 270 insertions(+), 355 deletions(-) diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/events/EventBuilder.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/events/EventBuilder.java index 140d089..89a6ad9 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/events/EventBuilder.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/events/EventBuilder.java @@ -49,12 +49,25 @@ public class EventBuilder { /** * Creates a new {@link EventBuilder} used for building {@link Event}s */ - public EventBuilder () { + public EventBuilder() { } + + /** + * Creates a new {@link EventBuilder} used for building {@link Event}s. + * + * @param eventType is the type of Event that the final event will have. + */ + public EventBuilder(EventType eventType) { + this.type = eventType; + } + // with for builder pattern style things + /** + * Deprecated. Use constructor with EventType instead. + */ public EventBuilder withType(EventType type) { this.type = type; return this; @@ -161,6 +174,153 @@ public class EventBuilder { return this; } + /** + * A method to check whether the current event is actually built correctly. If that is not the case, it + * throws an {@link IllegalStateException}. This occurs if for example a property is null even though it shouldn't + * be. The check is based on the EventType. !!! Using this method is strongly recommended when working with + * entities, as it prevents unnoticed bugs before they might happen !!! + * + * @throws IllegalStateException if the current event is non-valid + */ + public EventBuilder check() throws IllegalStateException { + if (this.type == null) throw new IllegalStateException("The eventType is null"); + else { + switch (this.type) { + // all of those events do not need any extra values except the EventType + case Ack: + case Nack: + case Req: + case PauseStartEvent: + case PauseStopEvent: + case TurnTimeoutEvent: + case DisconnectEvent: + break; + + // GameState needs very specific properties + case GameStateEvent: + if (this.entities == null || + this.turnOrder == null || + this.activeCharacter == null || + this.winCondition == null) { + throwException(); + } + break; + + // CustomEvent needs only... well, CustomContent. Who would've thought! + case CustomEvent: + if (this.customContent == null) { + throwException(); + } + break; + + // DestroyedEntityEvent takes an ID and a field, and destroys the entity if the field is correct + case DestroyedEntityEvent: + if (this.targetField == null || this.targetEntity == null) { + throwException(); + } + break; + + // TakenDamage, ConsumedAP / MP and Healed all need a targetField, targetEntity and Amount. + case TakenDamageEvent: + case ConsumedAPEvent: + case ConsumedMPEvent: + case HealedEvent: + if (this.targetField == null || + this.targetEntity == null || + this.amount == null) { + throwException(); + } + break; + + // SpawnEntity needs an entity, of course. + case SpawnEntityEvent: + if (this.entity == null) { + throwException(); + } + break; + + + // Melee- and ranged attacks need the same properties (except for the type) + case MeleeAttackEvent: + case RangedAttackEvent: + if (this.originField == null || + this.targetField == null || + this.originEntity == null || + this.targetEntity == null) { + throwException(); + } + break; + + // Exchanging and using InfinityStones both uses the same keys. Hereby, using a stone like the + // RealityStone causes the stone to be used on oneself + case ExchangeInfinityStoneEvent: + case UseInfinityStoneEvent: + if (this.originField == null || + this.targetField == null || + this.originEntity == null || + this.targetEntity == null || + this.stoneType == null) { + throwException(); + } + break; + + // MoveEvents take an originField, a targetField, and an originEntity. + case MoveEvent: + if (this.originField == null || + this.targetField == null || + this.originEntity == null) { + throwException(); + } + break; + + // RoundSetupEvents take a RoundCount and a CharacterOrder + case RoundSetupEvent: + if (this.roundCount == null || this.characterOrder == null) { + throwException(); + } + break; + + // TurnEvents take a TurnCount and a NextCharacter + case TurnEvent: + if (this.turnCount == null || this.nextCharacter == null) { + throwException(); + } + break; + + // A WinEvent needs to know what player has won + case WinEvent: + if (this.playerWon == null) { + throwException(); + } + break; + + // TimeoutEvents give a message back. As the only events to do so, this might be removed later on. + case TimeoutEvent: + if (this.message == null) { + throwException(); + } + break; + + // TimeoutWarnings carry a message and the amount of time left in seconds. The message might disappear. + case TimeoutWarningEvent: + if (this.message == null || this.timeLeft == null) { + throwException(); + } + break; + } + } + return this; + } + + /** + * Utility function for throwing a specific exception + * + * @throws IllegalStateException meaning that the event is non-valid for the current builder state + */ + private void throwException() throws IllegalStateException { + throw new IllegalStateException("Properties malformed for " + this.type); + } + /** * Builds a generic {@link GameEvent} from the values given to the builder * + * * @return a {@link GameEvent} based on the builder. Caution: non-given fields are null! */ - public GameEvent buildGenericGameEvent() { + public GameEvent buildGameEvent() throws IllegalStateException { + this.check(); var gameEvent = new GameEvent(); gameEvent.type = this.type; gameEvent.roundCount = this.roundCount; - gameEvent.turnCount= this.turnCount; + gameEvent.turnCount = this.turnCount; gameEvent.characterOrder = this.characterOrder; gameEvent.nextCharacter = this.nextCharacter; @@ -192,15 +354,38 @@ public class EventBuilder { /** * Builds an {@link EntityEvent} from the values given to the builder. + * This method checks for the correctness of the event before building it. If this is not desired, + * use the {@link #buildCharacterEventUnchecked()} method instead + * * + * * @return a {@link EntityEvent} based on the builder. Caution: non-given fields are null! */ - public EntityEvent buildGenericEntityEvent() { + public EntityEvent buildEntityEvent() throws IllegalStateException { + this.check(); + return this.buildEntityEventUnchecked(); + } + + + /** + * Builds an {@link EntityEvent} from the values given to the builder without checking whether this + * event is valid. Please use checked method {@link #buildCharacterEvent()} instead + * + * + * + * @return a {@link EntityEvent} based on the builder. Caution: non-given fields are null! + */ + public EntityEvent buildEntityEventUnchecked() { var entityEvent = new EntityEvent(); entityEvent.type = this.type; entityEvent.targetEntity = this.targetEntity; @@ -212,6 +397,9 @@ public class EventBuilder { /** * Builds a generic {@link CharacterEvent} from the values given to the builder. + * This method checks for the correctness of the event before building it. If this is not desired, + * use the {@link #buildCharacterEventUnchecked()} method instead + * * + * * @return a {@link CharacterEvent} based on the builder. Caution: non-given fields are null! + * @throws IllegalStateException if the event being built is non-valid. */ - public CharacterEvent buildGenericCharacterEvent() { + public CharacterEvent buildCharacterEvent() throws IllegalStateException { + this.check(); + return buildCharacterEventUnchecked(); + } + + + /** + * Builds a generic {@link CharacterEvent} from the values given to the builder without checking whether this + * event is valid. Please use checked method {@link #buildCharacterEvent()} instead + * + * + * + * @return a {@link CharacterEvent} based on the builder. Caution: non-given fields are null! + * @throws IllegalStateException if the event being built is non-valid. + */ + public CharacterEvent buildCharacterEventUnchecked() { var characterEvent = new CharacterEvent(); characterEvent.type = this.type; characterEvent.originEntity = this.originEntity; @@ -233,8 +444,11 @@ public class EventBuilder { return characterEvent; } + /** * Builds a generic {@link GameStateEvent} from the values given to the builder. + * This method checks for the correctness of the event before building it. If this is not desired, + * use the {@link #buildGameStateEventUnchecked()} method instead * + * + * @return a {@link GameStateEvent} based on the builder. Caution: non-given fields are null! + * @throws IllegalStateException if the event being built is non-valid. + */ + public GameStateEvent buildGameStateEvent() throws IllegalStateException { + this.check(); + return this.buildGameStateEventUnchecked(); + } + + + /** + * Builds a generic {@link GameStateEvent} from the values given to the builder without checking whether this + * event is valid. Please use checked method {@link #buildGameStateEvent()} instead + * + * * @return a {@link GameStateEvent} based on the builder. Caution: non-given fields are null! */ - public GameStateEvent buildGenericGameStateEvent() { - var gamestateEvent = new GameStateEvent(); - gamestateEvent.type = this.type; - gamestateEvent.entities = this.entities; - gamestateEvent.turnOrder = this.turnOrder; - gamestateEvent.activeCharacter = this.activeCharacter; - gamestateEvent.winCondition = this.winCondition; - return gamestateEvent; + public GameStateEvent buildGameStateEventUnchecked() { + var gameStateEvent = new GameStateEvent(); + gameStateEvent.type = this.type; + gameStateEvent.entities = this.entities; + gameStateEvent.turnOrder = this.turnOrder; + gameStateEvent.activeCharacter = this.activeCharacter; + gameStateEvent.winCondition = this.winCondition; + return gameStateEvent; } /** - * Builds a {@link CustomEvent} from the values given to the builder + * Builds a {@link CustomEvent} from the values given to the builder. This method checks for the correctness of the + * event before building it. If this is not desired, use the {@link #buildCustomEventUnchecked()} method instead * + * + * @return a {@link CustomEvent} based on the builder. Caution: non-given fields are null! + * @throws IllegalStateException if the event being built is non-valid. + */ + public CustomEvent buildCustomEvent() throws IllegalStateException { + this.check(); + var customEvent = new CustomEvent(); + return this.buildCustomEventUnchecked(); + } + + /** + * Builds a {@link CustomEvent} from the values given to the builder without checking whether this event + * is valid. Please use checked method {@link #buildCustomEvent()} instead + * + * * @return a {@link CustomEvent} based on the builder. Caution: non-given fields are null! */ - public CustomEvent buildGenericCustomEvent() { + public CustomEvent buildCustomEventUnchecked() { var customEvent = new CustomEvent(); customEvent.type = this.type; customEvent.teamIdentifier = this.teamIdentifier; @@ -270,343 +524,4 @@ public class EventBuilder { return customEvent; } - /** Builds a new Ack {@link GameStateEvent}. Can have a no declared type. */ - public GameStateEvent buildAckEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.Ack) throw new IllegalStateException("EventType was not Ack"); - var ackEvent = new GameStateEvent(); - ackEvent.type = EventType.Ack; - return ackEvent; - } - - /** Builds a new Nack {@link GameStateEvent}. Can have no declared type. */ - public GameStateEvent buildNackEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.Nack) throw new IllegalStateException("EventType was not Ack"); - var nackEvent = new GameStateEvent(); - nackEvent.type = EventType.Nack; - return nackEvent; - } - - /** Builds a new Req {@link GameStateEvent}. Can have no declared type. */ - public GameStateEvent buildReqEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.Req) throw new IllegalStateException("EventType was not Req"); - var reqEvent = new GameStateEvent(); - reqEvent.type = EventType.Req; - return reqEvent; - } - - /** - * Builds a new {@link GameStateEvent}. - * This event needs the fields entities, turnOrder, activeCharacter and winCondition. - */ - public GameStateEvent buildGameStateEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.GameStateEvent || - this.entities == null || - this.turnOrder == null || - this.activeCharacter == null || - this.winCondition == null - ) { - throw new IllegalStateException("Properties malformed for GameStateEvent"); - } - var gameStateEvent = new GameStateEvent(); - gameStateEvent.type = EventType.GameStateEvent; - gameStateEvent.entities = this.entities; - gameStateEvent.turnOrder = this.turnOrder; - gameStateEvent.activeCharacter = this.activeCharacter; - gameStateEvent.winCondition = this.winCondition; - - return gameStateEvent; - } - - - /** - * Builds a new {@link CustomEvent}. - * This event needs the field customContent. - */ - public CustomEvent buildCustomEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.CustomEvent || - this.customContent == null - ) { - throw new IllegalStateException("Properties malformed for CustomEvent"); - } - var customEvent = new CustomEvent(); - customEvent.type = EventType.CustomEvent; - customEvent.customContent = this.customContent; - - return customEvent; - } - - /** - * Builds a new {@link EntityEvent}. - * This event needs the fields targetField and targetEntity. - */ - public EntityEvent buildDestroyedEntityEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.DestroyedEntityEvent || - this.targetField == null || - this.targetEntity == null - ) { - throw new IllegalStateException("Properties malformed for DestroyedEntityEvent"); - } - var destroyedEntityEvent = new EntityEvent(); - destroyedEntityEvent.type = EventType.DestroyedEntityEvent; - destroyedEntityEvent.targetField = this.targetField; - destroyedEntityEvent.targetEntity = this.targetEntity; - - return destroyedEntityEvent; - } - - /** - * Builds a new {@link EntityEvent}. - * This event needs the fields targetField, targetEntity and amount. - */ - public EntityEvent buildTakenDamageEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.TakenDamageEvent || - this.targetField == null || - this.targetEntity == null || - this.amount == null - ) { - throw new IllegalStateException("Properties malformed for TakenDamageEvent"); - } - var takenDamageEvent = new EntityEvent(); - takenDamageEvent.type = EventType.TakenDamageEvent; - takenDamageEvent.targetField = this.targetField; - takenDamageEvent.targetEntity = this.targetEntity; - takenDamageEvent.amount = this.amount; - - return takenDamageEvent; - } - - /** - * Builds a new {@link EntityEvent}. - * This event needs the fields targetField, targetEntity and amount. - */ - public EntityEvent buildConsumedAPEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.ConsumedAPEvent || - this.targetField == null || - this.targetEntity == null || - this.amount == null - ) { - throw new IllegalStateException("Properties malformed for ConsumedAPEvent"); - } - var consumedAPEvent = new EntityEvent(); - consumedAPEvent.type = EventType.ConsumedAPEvent; - consumedAPEvent.targetField = this.targetField; - consumedAPEvent.targetEntity = this.targetEntity; - consumedAPEvent.amount = this.amount; - - return consumedAPEvent; - } - - /** - * Builds a new {@link EntityEvent}. - * This event needs the fields targetField, targetEntity and amount. - */ - public EntityEvent buildConsumedMPEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.ConsumedMPEvent || - this.targetField == null || - this.targetEntity == null || - this.amount == null - ) { - throw new IllegalStateException("Properties malformed for ConsumedMPEvent"); - } - var consumedAPEvent = new EntityEvent(); - consumedAPEvent.type = EventType.ConsumedMPEvent; - consumedAPEvent.targetField = this.targetField; - consumedAPEvent.targetEntity = this.targetEntity; - consumedAPEvent.amount = this.amount; - - return consumedAPEvent; - } - - - /** - * Builds a new {@link EntityEvent}. - * This event needs the field entity. - */ - public EntityEvent buildSpawnEntityEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.SpawnEntityEvent || - this.targetField == null || - this.targetEntity == null || - this.amount == null - ) { - throw new IllegalStateException("Properties malformed for SpawnEntityEvent"); - } - var spawnEntityEvent = new EntityEvent(); - spawnEntityEvent.type = EventType.SpawnEntityEvent; - spawnEntityEvent.entity = this.entity; - - return spawnEntityEvent; - } - - /** - * Builds a new {@link EntityEvent}. - * This event needs the fields targetField, targetEntity and amount. - */ - public EntityEvent buildHealedEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.HealedEvent || - this.targetField == null || - this.targetEntity == null || - this.amount == null - ) { - throw new IllegalStateException("Properties malformed for HealedEvent"); - } - var healedEvent = new EntityEvent(); - healedEvent.type = EventType.HealedEvent; - healedEvent.targetField = this.targetField; - healedEvent.targetEntity = this.targetEntity; - healedEvent.amount = this.amount; - - return healedEvent; - } - - - - - /** - * Builds a new {@link CharacterEvent}. - * This event needs the fields originField, targetField, originEntity and targetEntity. - */ - public CharacterEvent buildMeleeAttackEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.MeleeAttackEvent || - this.originField == null || - this.targetField == null || - this.originEntity == null || - this.targetEntity == null - ) { - throw new IllegalStateException("Properties malformed for MeleeAttackEvent"); - } - var meleeAttackEvent = new CharacterEvent(); - meleeAttackEvent.type = EventType.MeleeAttackEvent; - meleeAttackEvent.originField = this.originField; - meleeAttackEvent.targetField = this.targetField; - meleeAttackEvent.originEntity = this.originEntity; - meleeAttackEvent.targetEntity = this.targetEntity; - - return meleeAttackEvent; - } - - - /** - * Builds a new {@link CharacterEvent}. - * This event needs the fields originField, targetField, originEntity and targetEntity. - */ - public CharacterEvent buildRangedAttackEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.RangedAttackEvent || - this.originField == null || - this.targetField == null || - this.originEntity == null || - this.targetEntity == null - ) { - throw new IllegalStateException("Properties malformed for RangedAttackEvent"); - } - var rangedAttackEvent = new CharacterEvent(); - rangedAttackEvent.type = EventType.RangedAttackEvent; - rangedAttackEvent.originField = this.originField; - rangedAttackEvent.targetField = this.targetField; - rangedAttackEvent.originEntity = this.originEntity; - rangedAttackEvent.targetEntity = this.targetEntity; - - return rangedAttackEvent; - } - - - /** - * Builds a new {@link CharacterEvent}. - * This event needs the fields stoneType, originField, targetField, originEntity and targetEntity. - */ - public CharacterEvent buildExchangeInfinityStoneEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.ExchangeInfinityStoneEvent || - this.originField == null || - this.targetField == null || - this.originEntity == null || - this.targetEntity == null || - this.stoneType == null - ) { - throw new IllegalStateException("Properties malformed for ExchangeInfinityStoneEvent"); - } - var exchangeInfinityStoneEvent = new CharacterEvent(); - exchangeInfinityStoneEvent.type = EventType.ExchangeInfinityStoneEvent; - exchangeInfinityStoneEvent.originField = this.originField; - exchangeInfinityStoneEvent.targetField = this.targetField; - exchangeInfinityStoneEvent.originEntity = this.originEntity; - exchangeInfinityStoneEvent.targetEntity = this.targetEntity; - - return exchangeInfinityStoneEvent; - } - - - - /** - * Builds a new {@link CharacterEvent}. - * This event needs the fields originField, targetField and originEntity. - */ - public CharacterEvent buildMoveEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.MoveEvent || - this.originField == null || - this.targetField == null || - this.originEntity == null - ) { - throw new IllegalStateException("Properties malformed for MoveEvent"); - } - var moveEvent = new CharacterEvent(); - moveEvent.type = EventType.ExchangeInfinityStoneEvent; - moveEvent.originField = this.originField; - moveEvent.targetField = this.targetField; - moveEvent.originEntity = this.originEntity; - - return moveEvent; - } - - - /** - * Builds a new {@link CharacterEvent}. - * This event needs the fields stoneType, originField, targetField, originEntity and targetEntity. - */ - public CharacterEvent buildUseInfinityStoneEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.UseInfinityStoneEvent || - this.originField == null || - this.targetField == null || - this.originEntity == null || - this.targetEntity == null || - this.stoneType == null - ) { - throw new IllegalStateException("Properties malformed for UseInfinityStoneEvent"); - } - var useInfinityStoneEvent = new CharacterEvent(); - useInfinityStoneEvent.type = EventType.UseInfinityStoneEvent; - useInfinityStoneEvent.originField = this.originField; - useInfinityStoneEvent.targetField = this.targetField; - useInfinityStoneEvent.originEntity = this.originEntity; - useInfinityStoneEvent.targetEntity = this.targetEntity; - - return useInfinityStoneEvent; - } - - - - /** - * Builds a new {@link GameEvent}. - * This event needs the fields roundCount and characterOrder. - */ - public GameEvent buildRoundSetupEvent() throws IllegalStateException { - if (this.type == null || this.type != EventType.RoundSetupEvent || - this.roundCount == null || - this.characterOrder == null - ) { - throw new IllegalStateException("Properties malformed for RoundSetupEvent"); - } - var roundSetupEvent = new GameEvent(); - roundSetupEvent.type = EventType.UseInfinityStoneEvent; - roundSetupEvent.roundCount = this.roundCount; - roundSetupEvent.characterOrder = this.characterOrder; - - return roundSetupEvent; - } - - - - - - - -} - +} \ No newline at end of file