This commit is contained in:
C H 2021-05-30 18:50:33 +02:00
commit 1e47a63cb7
15 changed files with 476 additions and 94 deletions

View File

@ -1,13 +1,16 @@
package uulm.teamname.marvelous.gamelibrary; package uulm.teamname.marvelous.gamelibrary;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import uulm.teamname.marvelous.gamelibrary.json.ingame.IntVector2Deserializer; import uulm.teamname.marvelous.gamelibrary.json.ingame.IntVector2Deserializer;
import uulm.teamname.marvelous.gamelibrary.json.ingame.IntVector2Serializer;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
/** Represents a 2d vector of integers. */ /** Represents a 2d vector of integers. */
@JsonDeserialize(using = IntVector2Deserializer.class) @JsonDeserialize(using = IntVector2Deserializer.class)
@JsonSerialize(using = IntVector2Serializer.class)
public class IntVector2 implements Serializable { public class IntVector2 implements Serializable {
private int x; private int x;
private int y; private int y;

View File

@ -1,15 +1,18 @@
package uulm.teamname.marvelous.gamelibrary.entities; package uulm.teamname.marvelous.gamelibrary.entities;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import uulm.teamname.marvelous.gamelibrary.json.ingame.EntityIDDeserializer; import uulm.teamname.marvelous.gamelibrary.json.ingame.EntityIDDeserializer;
import java.util.Objects; import java.util.Objects;
/** Represents a distinct identification for every {@link Entity} in a game. */ /** Represents a distinct identification for every {@link Entity} in a game. */
@JsonPropertyOrder({"entityID", "ID"})
@JsonDeserialize(using = EntityIDDeserializer.class) @JsonDeserialize(using = EntityIDDeserializer.class)
public class EntityID { public class EntityID {
/** The index of the entity */ /** The index of the entity */
@JsonProperty("ID")
public final int id; public final int id;
/** The type of the entity */ /** The type of the entity */

View File

@ -3,17 +3,21 @@ package uulm.teamname.marvelous.gamelibrary.json;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jdk.jshell.spi.ExecutionControl; import jdk.jshell.spi.ExecutionControl;
import uulm.teamname.marvelous.gamelibrary.json.basic.BasicMessage;
import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage; import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage;
/** Contains JSON encoding and decoding. /** Contains JSON encoding and decoding.
*/ */
public class JSON { public class JSON {
private static final ObjectMapper mapper = new ObjectMapper();
/** Deserializes an incoming network message into a {@link EventMessage}. /** Deserializes an incoming network message into a {@link EventMessage}.
* @param input The JSON to deserialize. * @param input The JSON to deserialize.
* @return The parsed message. */ * @return The parsed message. */
public static EventMessage parse(String input) { public static EventMessage parse(String input) {
EventMessage result = null; EventMessage result = null;
ObjectMapper mapper = new ObjectMapper();
try { try {
result = mapper.readValue(input, EventMessage.class); result = mapper.readValue(input, EventMessage.class);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
@ -25,8 +29,13 @@ public class JSON {
/** Serializes a {@link EventMessage} into a JSON string. /** Serializes a {@link EventMessage} into a JSON string.
* @param input The message to serialize. * @param input The message to serialize.
* @return The message as JSON. */ * @return The message as JSON. */
public static String stringify(EventMessage input) throws ExecutionControl.NotImplementedException { public static String stringify(BasicMessage input) throws ExecutionControl.NotImplementedException {
//TODO: implement JSON.stringify String result = null;
throw new ExecutionControl.NotImplementedException("JSON.stringify is not implemented"); try {
result = mapper.writeValueAsString(input);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return result;
} }
} }

View File

@ -1,17 +1,17 @@
package uulm.teamname.marvelous.gamelibrary.json; package uulm.teamname.marvelous.gamelibrary.json;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonNodeUnwrapper { public class JsonNodeUnwrapper {
private static final ObjectMapper mapper = new ObjectMapper(); public static <T> T unwrap(JsonNode subNode, Class<T> type, ObjectCodec codec)
throws JsonProcessingException {
public static <T> T unwrap (JsonNode rootNode, String key, Class<T> type) throws JsonProcessingException { if (subNode != null) {
JsonNode subNode; return codec.treeToValue(subNode, type);
if ((subNode = rootNode.get(key)) != null) {
return mapper.readValue(subNode.toString(), type);
} }
return null; return null;
} }

View File

@ -21,9 +21,6 @@ public class EntityDeserializer extends JsonDeserializer<Entity> {
Rock Rock
} }
// static so that no reinitializations are needed
private static final ObjectMapper mapper = new ObjectMapper();
@Override @Override
public Entity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { public Entity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = p.getCodec(); ObjectCodec codec = p.getCodec();
@ -40,7 +37,7 @@ public class EntityDeserializer extends JsonDeserializer<Entity> {
node.get("ID").intValue()); node.get("ID").intValue());
result = new Character( result = new Character(
id, id,
mapper.readValue(node.get("position").toString(), IntVector2.class), codec.treeToValue(node.get("position"), IntVector2.class),
node.get("name").asText(), node.get("name").asText(),
node.get("HP").asInt(), node.get("HP").asInt(),
node.get("MP").asInt(), node.get("MP").asInt(),
@ -50,21 +47,21 @@ public class EntityDeserializer extends JsonDeserializer<Entity> {
-1 -1
); );
for (var i: mapper.readValue(node.get("stones").toString(), Integer[].class)) { for (var i: codec.treeToValue(node.get("stones"), Integer[].class)) {
((Character) result).inventory.addStone(StoneType.valueOf(i)); ((Character) result).inventory.addStone(StoneType.valueOf(i));
} }
} }
case InfinityStone -> { case InfinityStone -> {
result = new InfinityStone( result = new InfinityStone(
new EntityID(EntityType.InfinityStones, node.get("ID").asInt()), new EntityID(EntityType.InfinityStones, node.get("ID").asInt()),
mapper.readValue(node.get("position").toString(), IntVector2.class), codec.treeToValue(node.get("position"), IntVector2.class),
StoneType.valueOf(node.get("ID").asInt()) StoneType.valueOf(node.get("ID").asInt())
); );
} }
case Rock -> { case Rock -> {
result = new Rock( result = new Rock(
new EntityID(EntityType.Rocks, node.get("ID").asInt()), new EntityID(EntityType.Rocks, node.get("ID").asInt()),
mapper.readValue(node.get("position").toString(), IntVector2.class), codec.treeToValue(node.get("position"), IntVector2.class),
node.get("HP").asInt() node.get("HP").asInt()
); );
} }

View File

@ -1,12 +1,10 @@
package uulm.teamname.marvelous.gamelibrary.json.ingame; package uulm.teamname.marvelous.gamelibrary.json.ingame;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.Entity; import uulm.teamname.marvelous.gamelibrary.entities.Entity;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
@ -14,19 +12,14 @@ import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
import uulm.teamname.marvelous.gamelibrary.events.Event; import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.events.EventBuilder; import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
import uulm.teamname.marvelous.gamelibrary.events.EventType; import uulm.teamname.marvelous.gamelibrary.events.EventType;
import uulm.teamname.marvelous.gamelibrary.events.GamestateEvent;
import static uulm.teamname.marvelous.gamelibrary.json.JsonNodeUnwrapper.unwrap; import static uulm.teamname.marvelous.gamelibrary.json.JsonNodeUnwrapper.unwrap;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
public class EventDeserializer extends JsonDeserializer<Event> { public class EventDeserializer extends JsonDeserializer<Event> {
// static so that no reinitializations are needed
private static final ObjectMapper mapper = new ObjectMapper();
@Override @Override
public Event deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { public Event deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = p.getCodec(); ObjectCodec codec = p.getCodec();
@ -76,40 +69,40 @@ public class EventDeserializer extends JsonDeserializer<Event> {
// } // }
// necessary because of exact type mismatch // necessary because of exact type mismatch
var stoneTypeID = unwrap(node, "stoneType", EntityID.class); var stoneTypeID = unwrap(node.get("stoneType"), EntityID.class, codec);
builder.withTargetEntity(unwrap(node, "targetEntity", EntityID.class)) builder.withTargetEntity(unwrap(node.get("targetEntity"), EntityID.class, codec))
.withTargetField(unwrap(node, "targetField", IntVector2.class)) .withTargetField(unwrap(node.get("targetField"), IntVector2.class, codec))
.withAmount(unwrap(node, "targetField", Integer.class)) .withAmount(unwrap(node.get("targetField"), Integer.class, codec))
.withEntity(unwrap(node, "entity", Entity.class)) .withEntity(unwrap(node.get("entity"), Entity.class, codec))
.withOriginEntity(unwrap(node, "originEntity", EntityID.class)) .withOriginEntity(unwrap(node.get("originEntity"), EntityID.class, codec))
.withOriginField(unwrap(node, "originField", IntVector2.class)) .withOriginField(unwrap(node.get("originField"), IntVector2.class, codec))
.withStoneType(stoneTypeID != null ? StoneType.valueOf(stoneTypeID.id) : null) .withStoneType(stoneTypeID != null ? StoneType.valueOf(stoneTypeID.id) : null)
.withRoundCount(unwrap(node, "roundCount", Integer.class)) .withRoundCount(unwrap(node.get("roundCount"), Integer.class, codec))
.withTurnCount(unwrap(node, "turnCount", Integer.class)) .withTurnCount(unwrap(node.get("turnCount"), Integer.class, codec))
.withCharacterOrder(unwrap(node, "characterOrder", EntityID[].class)) .withCharacterOrder(unwrap(node.get("characterOrder"), EntityID[].class, codec))
.withNextCharacter(unwrap(node, "nextCharacter", EntityID.class)) .withNextCharacter(unwrap(node.get("nextCharacter"), EntityID.class, codec))
.withPlayerWon(unwrap(node, "playerWon", Integer.class)) .withPlayerWon(unwrap(node.get("playerWon"), Integer.class, codec))
.withMessage(unwrap(node, "message", String.class)) .withMessage(unwrap(node.get("message"), String.class, codec))
.withTimeLeft(unwrap(node, "timeLeft", Integer.class)) .withTimeLeft(unwrap(node.get("timeLeft"), Integer.class, codec))
.withEntities(unwrap(node, "entities", Entity[].class)) .withEntities(unwrap(node.get("entities"), Entity[].class, codec))
.withTurnOrder(unwrap(node, "turnOrder", EntityID[].class)) .withTurnOrder(unwrap(node.get("turnOrder"), EntityID[].class, codec))
.withMapSize(unwrap(node, "mapSize", IntVector2.class)) .withMapSize(unwrap(node.get("mapSize"), IntVector2.class, codec))
.withActiveCharacter(unwrap(node, "activeCharacter", EntityID.class)) .withActiveCharacter(unwrap(node.get("activeCharacter"), EntityID.class, codec))
.withStoneCooldowns(unwrap(node, "stoneCooldowns",Integer[].class)) .withStoneCooldowns(unwrap(node.get("stoneCooldowns"), Integer[].class, codec))
.withWinCondition(unwrap(node, "winCondition",Boolean.class)) .withWinCondition(unwrap(node.get("winCondition"), Boolean.class, codec))
.withTeamIdentifier(unwrap(node, "teamIdentifier", String.class)) .withTeamIdentifier(unwrap(node.get("teamIdentifier"), String.class, codec))
.withCustomContent(unwrap(node,"customContent", HashMap.class)); .withCustomContent(unwrap(node.get("customContent"), HashMap.class, codec));
builder.withTargetEntity(unwrap(node, "targetEntity", EntityID.class)); builder.withTargetEntity(unwrap(node.get("targetEntity"), EntityID.class, codec));
builder.withTargetField(unwrap(node, "targetField", IntVector2.class)); builder.withTargetField(unwrap(node.get("targetField"), IntVector2.class, codec));
switch (eventType) { switch (eventType) {
case Ack, Nack, GamestateEvent -> { return builder.buildGameStateEvent(); } case Ack, Nack, GamestateEvent -> { return builder.buildGameStateEvent(); }

View File

@ -12,13 +12,9 @@ import uulm.teamname.marvelous.gamelibrary.IntVector2;
import java.io.IOException; import java.io.IOException;
public class IntVector2Deserializer extends JsonDeserializer<IntVector2> { public class IntVector2Deserializer extends JsonDeserializer<IntVector2> {
// static so that no reinitializations are needed
private static final ObjectMapper mapper = new ObjectMapper();
@Override @Override
public IntVector2 deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { public IntVector2 deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var values = mapper.readValue(p, Integer[].class); var values = p.readValueAs(Integer[].class);
IntVector2 result = new IntVector2(values[0], values[1]); IntVector2 result = new IntVector2(values[0], values[1]);
return result; return result;
} }

View File

@ -0,0 +1,27 @@
package uulm.teamname.marvelous.gamelibrary.json.ingame;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import java.io.IOException;
public class IntVector2Serializer extends StdSerializer<IntVector2> {
public IntVector2Serializer() {
this(null);
}
protected IntVector2Serializer(Class<IntVector2> t) {
super(t);
}
@Override
public void serialize(IntVector2 value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartArray();
gen.writeNumber(value.getX());
gen.writeNumber(value.getY());
gen.writeEndArray();
}
}

View File

@ -6,12 +6,9 @@ import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.Entity;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.StoneType; import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.*; import uulm.teamname.marvelous.gamelibrary.requests.*;
import java.io.IOException; import java.io.IOException;
@ -21,9 +18,6 @@ import static uulm.teamname.marvelous.gamelibrary.json.JsonNodeUnwrapper.unwrap;
public class RequestDeserializer extends JsonDeserializer<Request> { public class RequestDeserializer extends JsonDeserializer<Request> {
// static so that no reinitializations are needed
private static final ObjectMapper mapper = new ObjectMapper();
@Override @Override
public Request deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { public Request deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObjectCodec codec = p.getCodec(); ObjectCodec codec = p.getCodec();
@ -38,12 +32,12 @@ public class RequestDeserializer extends JsonDeserializer<Request> {
} }
var requestBuilder = new RequestBuilder(requestType) var requestBuilder = new RequestBuilder(requestType)
.withValue(unwrap(node, "value", int.class)) .withValue(unwrap(node.get("value"), int.class, codec))
.withOriginEntity(unwrap(node, "originEntity", EntityID.class)) .withOriginEntity(unwrap(node.get("originEntity"), EntityID.class, codec))
.withTargetEntity(unwrap(node, "targetEntity", EntityID.class)) .withTargetEntity(unwrap(node.get("targetEntity"), EntityID.class, codec))
.withOriginField(unwrap(node, "originField", IntVector2.class)) .withOriginField(unwrap(node.get("originField"), IntVector2.class, codec))
.withTargetField(unwrap(node, "targetField", IntVector2.class)) .withTargetField(unwrap(node.get("targetField"), IntVector2.class, codec))
.withStoneType(Optional.ofNullable(unwrap(node, "stoneType", EntityID.class)) .withStoneType(Optional.ofNullable(unwrap(node.get("stoneType"), EntityID.class, codec))
.map(x -> StoneType.valueOf(x.id)) .map(x -> StoneType.valueOf(x.id))
.orElseGet(() -> null)); .orElseGet(() -> null));

View File

@ -0,0 +1,83 @@
package uulm.teamname.marvelous.gamelibrary.json.ingame;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
import uulm.teamname.marvelous.gamelibrary.requests.GameRequest;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
import java.io.IOException;
public class RequestSerializer extends StdSerializer<Request> {
public RequestSerializer() {
this(null);
}
protected RequestSerializer(Class<Request> t) {
super(t);
}
@Override
public void serialize(Request value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeObjectField("requestType", value.type);
if (value instanceof GameRequest) {
serializeGameRequest((GameRequest) value, gen, provider);
} else if (value instanceof CharacterRequest) {
serializeCharacterRequest((CharacterRequest) value, gen, provider);
}
gen.writeEndObject();
}
/** Method invoked after writing the RequestType, and used to write any additional values required */
private void serializeGameRequest(GameRequest value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
return; // does nothing, but still there for consistency
}
/** Method invoked after writing the RequestType, and used to write any additional values required */
private void serializeCharacterRequest(CharacterRequest value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
// The idea here is: if ([GUARD]) {gen.write...} for all fields.
boolean hasTargetEntity =
value.type == RequestType.MeleeAttackRequest ||
value.type == RequestType.RangedAttackRequest ||
value.type == RequestType.ExchangeInfinityStoneRequest;
boolean hasStoneType =
value.type == RequestType.ExchangeInfinityStoneRequest ||
value.type == RequestType.UseInfinityStoneRequest;
boolean hasValue =
value.type == RequestType.MeleeAttackRequest ||
value.type == RequestType.RangedAttackRequest;
gen.writeObjectField("originEntity", value.originEntity);
if (hasTargetEntity) {
gen.writeObjectField("targetEntity", value.targetEntity);
}
gen.writeObjectField("originField", value.originField);
gen.writeObjectField("targetField", value.targetField);
if (hasStoneType) {
gen.writeObjectField("stoneType", new EntityID(EntityType.InfinityStones, value.stoneType.getID()));
}
if (hasValue) {
gen.writeObjectField("value", value.value);
}
}
}

View File

@ -1,5 +1,6 @@
package uulm.teamname.marvelous.gamelibrary.requests; package uulm.teamname.marvelous.gamelibrary.requests;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import uulm.teamname.marvelous.gamelibrary.IntVector2; import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.StoneType; import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
@ -7,6 +8,7 @@ import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
import java.util.Objects; import java.util.Objects;
/** Represents a character event for: {@link RequestType#MeleeAttackRequest}, {@link RequestType#RangedAttackRequest}, {@link RequestType#MoveRequest}, {@link RequestType#ExchangeInfinityStoneRequest}, {@link RequestType#UseInfinityStoneRequest}. */ /** Represents a character event for: {@link RequestType#MeleeAttackRequest}, {@link RequestType#RangedAttackRequest}, {@link RequestType#MoveRequest}, {@link RequestType#ExchangeInfinityStoneRequest}, {@link RequestType#UseInfinityStoneRequest}. */
@JsonPropertyOrder({"requestType", "originEntity", "targetEntity", "originField", "targetField", "stoneType", "value"})
public class CharacterRequest extends Request { public class CharacterRequest extends Request {
public EntityID originEntity = null; public EntityID originEntity = null;
public EntityID targetEntity = null; public EntityID targetEntity = null;

View File

@ -1,15 +1,22 @@
package uulm.teamname.marvelous.gamelibrary.requests; package uulm.teamname.marvelous.gamelibrary.requests;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonKey;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage; import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage;
import uulm.teamname.marvelous.gamelibrary.json.ingame.RequestDeserializer; import uulm.teamname.marvelous.gamelibrary.json.ingame.RequestDeserializer;
import uulm.teamname.marvelous.gamelibrary.json.ingame.RequestSerializer;
import java.util.Objects; import java.util.Objects;
/** Represents an abstract request sent inside a {@link EventMessage} between client and server. */ /** Represents an abstract request sent inside a {@link EventMessage} between client and server. */
@JsonDeserialize(using = RequestDeserializer.class) @JsonDeserialize(using = RequestDeserializer.class)
@JsonSerialize(using = RequestSerializer.class)
public abstract class Request { public abstract class Request {
public RequestType type;
@JsonProperty("requestType") public RequestType type;
@Override @Override

View File

@ -2,19 +2,27 @@ package uulm.teamname.marvelous.gamelibrary.json;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import net.jqwik.api.*;
import net.jqwik.api.lifecycle.BeforeContainer;
import net.jqwik.api.lifecycle.BeforeProperty;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.entities.Rock;
import uulm.teamname.marvelous.gamelibrary.events.Event; import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.events.EventBuilder; import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
import uulm.teamname.marvelous.gamelibrary.events.EventType; import uulm.teamname.marvelous.gamelibrary.events.EventType;
import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage; import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage;
import uulm.teamname.marvelous.gamelibrary.requests.*;
import java.util.HashMap; import java.util.*;
class JSONTest { class JSONTest {

View File

@ -0,0 +1,37 @@
package uulm.teamname.marvelous.gamelibrary.json.ingame;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.jqwik.api.*;
import net.jqwik.api.lifecycle.BeforeProperty;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.*;
class IntVector2SerializerTest {
ObjectMapper mapper = new ObjectMapper();
@BeforeProperty
void setup() {
mapper = new ObjectMapper();
}
@Property
void vectorsGetProperlyDeserialized(@ForAll @From("positions") IntVector2 vector) throws JsonProcessingException {
var jsonRepresentingVector = String.format("[%d,%d]", vector.getX(), vector.getY());
assertThat(mapper.writeValueAsString(vector))
.isEqualTo(jsonRepresentingVector);
}
@Provide("positions")
Arbitrary<IntVector2> positions() {
return Arbitraries.integers()
.tuple2()
.map(pos -> new IntVector2(pos.get1(), pos.get2()));
}
}

View File

@ -0,0 +1,223 @@
package uulm.teamname.marvelous.gamelibrary.json.ingame;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.jqwik.api.*;
import net.jqwik.api.lifecycle.BeforeProperty;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
import uulm.teamname.marvelous.gamelibrary.requests.CharacterRequest;
import uulm.teamname.marvelous.gamelibrary.requests.GameRequest;
import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
import java.util.HashSet;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
public class RequestSerializerTest {
private ObjectMapper mapper;
@BeforeProperty
void setup() {
mapper = new ObjectMapper();
}
@Property
void gameRequestsGetSerializedProperly(
@ForAll @From("gameRequests") GameRequest request)
throws JsonProcessingException {
var jsonRepresentingRequest = String.format(
"{\"requestType\":\"%s\"}",
request.type);
assertThat(mapper.writeValueAsString(request))
.isEqualTo(jsonRepresentingRequest);
}
@Property
void characterRequestsGetSerializedProperly(
@ForAll @From("characterRequests") CharacterRequest request)
throws JsonProcessingException {
var jsonRepresentingValue = request.type == RequestType.MeleeAttackRequest ||
request.type == RequestType.RangedAttackRequest ?
String.format("\"value\":%d", request.value) : "";
var jsonRepresentingTargetEntity = request.type == RequestType.MeleeAttackRequest ||
request.type == RequestType.RangedAttackRequest ||
request.type == RequestType.ExchangeInfinityStoneRequest ?
String.format(
"\"targetEntity\":{\"entityID\":\"%s\",\"ID\":%d},",
request.targetEntity.type,
request.targetEntity.id) : "";
var jsonRepresentingStoneType = request.type == RequestType.UseInfinityStoneRequest ||
request.type == RequestType.ExchangeInfinityStoneRequest ?
String.format(
"\"stoneType\":{\"entityID\":\"%s\",\"ID\":%d}",
EntityType.InfinityStones,
request.stoneType.getID()) : "";
var jsonRepresentingRequest = String.format(
"""
{
"requestType":"%s",
"originEntity":{"entityID":"%s","ID":%d},
%s
"originField":[%d,%d],
"targetField":[%d,%d]%s
%s%s
%s
}
""",
request.type,
request.originEntity.type,
request.originEntity.id,
jsonRepresentingTargetEntity,
request.originField.getX(),
request.originField.getY(),
request.targetField.getX(),
request.targetField.getY(),
jsonRepresentingStoneType.length() != 0 ? "," : "",
jsonRepresentingStoneType,
jsonRepresentingValue.length() != 0 ? "," : "",
jsonRepresentingValue
).replace("\n", "");
assertThat(mapper.writeValueAsString(request))
.isEqualTo(jsonRepresentingRequest);
}
// Note that everything that follows could be extracted into another class,
// but that's complicated, so I won't do it
static Set<RequestType> characterRequestTypes;
static Set<RequestType> gameRequestTypes;
static {
characterRequestTypes = new HashSet<>();
characterRequestTypes.add(RequestType.MeleeAttackRequest);
characterRequestTypes.add(RequestType.RangedAttackRequest);
characterRequestTypes.add(RequestType.MoveRequest);
characterRequestTypes.add(RequestType.ExchangeInfinityStoneRequest);
characterRequestTypes.add(RequestType.UseInfinityStoneRequest);
gameRequestTypes = new HashSet<>();
gameRequestTypes.add(RequestType.PauseStopRequest);
gameRequestTypes.add(RequestType.PauseStartRequest);
gameRequestTypes.add(RequestType.EndRoundRequest);
gameRequestTypes.add(RequestType.DisconnectRequest);
gameRequestTypes.add(RequestType.Req);
}
@Provide("gameRequests")
public static Arbitrary<GameRequest> gameRequests() {
return requestBuilders()
.map(RequestBuilder::buildGameRequest)
.filter(request -> gameRequestTypes.contains(request.type));
}
@Provide("characterRequests")
public static Arbitrary<CharacterRequest> characterRequests() {
return requestBuilders()
.map(RequestBuilder::buildCharacterRequest)
.filter(request -> characterRequestTypes.contains(request.type));
}
@Provide("filledRequestBuilders")
public static Arbitrary<RequestBuilder> requestBuilders() {
var emptyBuilders = Arbitraries.of(RequestType.class)
.map(RequestBuilder::new);
var buildersWithEntitiesAndValue = Combinators.combine(
emptyBuilders,
entityIDs().filter(x -> x.type == EntityType.P1 || x.type == EntityType.P2).tuple2(),
entityIDs().filter(x -> x.type == EntityType.Rocks),
Arbitraries.integers())
.as((builder, characterIDs, rockID, value) -> builder
.withOriginEntity(characterIDs.get1())
.withTargetEntity(value % 7 == 0 ? characterIDs.get2() : rockID)
.withValue(value)
);
var buildersWithStoneTypes = Combinators.combine(
buildersWithEntitiesAndValue,
Arbitraries.of(StoneType.class))
.as(RequestBuilder::withStoneType);
return Combinators.combine(
buildersWithStoneTypes,
randomPositions(),
directions(),
attackPositions())
.as((builder, position, direction, attackPos) -> {
var type = builder.buildGameRequest().type; // hacky but whatever
Tuple.Tuple2<IntVector2, IntVector2> originAndTargetPosition;
if (type == RequestType.RangedAttackRequest) {
originAndTargetPosition = attackPos;
} else {
originAndTargetPosition = Tuple.of(position, position.copy().add(direction));
}
return builder.withOriginField(originAndTargetPosition.get1())
.withTargetField(originAndTargetPosition.get2());
});
}
@Provide("entityIDs")
public static Arbitrary<EntityID> entityIDs() {
var entityTypes = Arbitraries.of(EntityType.class);
return Combinators.combine(entityTypes, Arbitraries.integers().greaterOrEqual(0))
.as((type, id) -> {
int actualID;
if (type == EntityType.P1 || type == EntityType.P2 || type == EntityType.InfinityStones) {
actualID = id % 6;
} else if (type == EntityType.NPC) {
actualID = id % 3;
} else {
actualID = id;
}
return new EntityID(type, id);
});
}
@Provide("randomPositions")
public static Arbitrary<IntVector2> randomPositions() {
return Arbitraries.integers()
.greaterOrEqual(0)
.tuple2()
.map((pos) -> new IntVector2(pos.get1(), pos.get2()));
}
@Provide("directions")
public static Arbitrary<IntVector2> directions() {
return Arbitraries.integers()
.between(-1, 1)
.tuple2()
.filter(pos -> pos.get1() != 0 && pos.get2() != 0) // eliminate zero vectors
.map(pos -> new IntVector2(pos.get1(), pos.get2()));
}
@Provide("attackPositions")
/** Returns tuples of origin vectors (of an attack), and the vector pointing to the attack dir */
public static Arbitrary<Tuple.Tuple2<IntVector2, IntVector2>> attackPositions() {
return Combinators.combine(randomPositions(), directions().tuple5())
.as((origin, dir) -> {
var attackField = origin.copy()
.add(dir.get1().scale(3))
.add(dir.get2().scale(3))
.add(dir.get3().scale(3))
.add(dir.get4().scale(3));
if (attackField.length() == 0) {
attackField.add(dir.get5().scale(3));
}
return Tuple.<IntVector2, IntVector2>of(origin, attackField);
});
}
}