feat: rewrote EntityBuilder to check for entity correctness when building entities

This commit is contained in:
Yannik Bretschneider 2021-05-02 02:58:39 +02:00
parent 091e6eb688
commit e6cc61a272

View File

@ -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<something> 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. <b>!!! Using this method is strongly recommended when working with
* entities, as it prevents unnoticed bugs before they might happen !!!</b>
*
* @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
* <ul>
@ -173,13 +333,15 @@ public class EventBuilder {
* <li>message describes a generic message delivered as a String in several GameEvents. It is optional.</li>
* <li>timeLeft describes the time left for a client to act before getting kicked in the TimeoutWarning event</li>
* </ul>
*
* @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
*
* <ul>
* <li>type is the {@link EventType} of the EntityEvent</li>
* <li>targetEntity is the target {@link Entity}</li>
* <li>targetField is the target field in the form of an {@link IntVector2}</li>
* <li>amount is the amount of something, like damage. It's an int.</li>
* </ul>
*
* @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 <b>without checking whether this
* event is valid. Please use checked method {@link #buildCharacterEvent()} instead</b>
*
* <ul>
* <li>type is the {@link EventType} of the EntityEvent</li>
* <li>targetEntity is the target {@link Entity}</li>
* <li>targetField is the target field in the form of an {@link IntVector2}</li>
* <li>amount is the amount of something, like damage. It's an int.</li>
* </ul>
*
* @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
*
* <ul>
* <li>type is the {@link EventType} of the EntityEvent</li>
* <li>originEntity is the origin {@link Entity}</li>
@ -219,9 +407,32 @@ public class EventBuilder {
* <li>originField is the field that an action originates in, delivered as an {@link IntVector2}</li>
* <li>targetField is the target field in the form of an {@link IntVector2}</li>
* </ul>
*
* @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 <b>without checking whether this
* event is valid. Please use checked method {@link #buildCharacterEvent()} instead</b>
*
* <ul>
* <li>type is the {@link EventType} of the EntityEvent</li>
* <li>originEntity is the origin {@link Entity}</li>
* <li>targetEntity is the target {@link Entity}</li>
* <li>originField is the field that an action originates in, delivered as an {@link IntVector2}</li>
* <li>targetField is the target field in the form of an {@link IntVector2}</li>
* </ul>
*
* @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
* <ul>
* <li>type is the {@link EventType} of the {@link GameStateEvent}</li>
* <li>entities is an array of {@link Entity Entities}</li>
@ -242,27 +456,67 @@ public class EventBuilder {
* <li>activeCharacter describes the currently active character</li>
* <li>describes whether the win condition is in effect</li>
* </ul>
*
* @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 <b>without checking whether this
* event is valid. Please use checked method {@link #buildGameStateEvent()} instead</b>
* <ul>
* <li>type is the {@link EventType} of the {@link GameStateEvent}</li>
* <li>entities is an array of {@link Entity Entities}</li>
* <li>turnOrder describes the order in which characters take turns</li>
* <li>activeCharacter describes the currently active character</li>
* <li>describes whether the win condition is in effect</li>
* </ul>
*
* @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
* <ul>
* <li>teamIdentifier is a String for identifying a specific custom content</li>
* <li>customContent is a {@link HashMap}<{@link String}, {@link Object}> resembling the JSON keys in the event</li>
* </ul>
*
* @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 <b>without checking whether this event
* is valid. Please use checked method {@link #buildCustomEvent()} instead</b>
* <ul>
* <li>teamIdentifier is a String for identifying a specific custom content</li>
* <li>customContent is a {@link HashMap}<{@link String}, {@link Object}> resembling the JSON keys in the event</li>
* </ul>
*
* @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 <b>entities, turnOrder, activeCharacter</b> and <b>winCondition</b>.
*/
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 <b>customContent</b>.
*/
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 <b>targetField</b> and <b>targetEntity</b>.
*/
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 <b>targetField, targetEntity</b> and <b>amount</b>.
*/
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 <b>targetField, targetEntity</b> and <b>amount</b>.
*/
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 <b>targetField, targetEntity</b> and <b>amount</b>.
*/
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 <b>entity</b>.
*/
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 <b>targetField, targetEntity</b> and <b>amount</b>.
*/
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 <b>originField, targetField, originEntity</b> and <b>targetEntity</b>.
*/
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 <b>originField, targetField, originEntity</b> and <b>targetEntity</b>.
*/
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 <b>stoneType, originField, targetField, originEntity</b> and <b>targetEntity</b>.
*/
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 <b>originField, targetField</b> and <b>originEntity</b>.
*/
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 <b>stoneType, originField, targetField, originEntity</b> and <b>targetEntity</b>.
*/
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 <b>roundCount</b> and <b>characterOrder</b>.
*/
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;
}
}
}