Compare commits

...

3 commits

Author SHA1 Message Date
c58c8725e8 Added README 2026-04-24 21:03:48 +02:00
Gu://em_
b3bdec1049 tar dur, jws demotivant 2026-04-04 22:25:16 +02:00
Gu://em_
e1301f007b trop tard 2026-04-04 00:01:13 +02:00
19 changed files with 133 additions and 24 deletions

31
README.md Normal file
View file

@ -0,0 +1,31 @@
# JWS
> **Note** This is a school project, therefore it probably won't interest you if you are looking for something useful.
## Overview
JWS stands for Java Web Services and as its name doesn't suggest at all, it's basically a web server for a a pokemon-like game. It's written in Java and built onto Quarkus and Jakarta, providing a REST API with strict server-side rules like cooldowns and cheating prevention.
## Architecture
> **Note** Source code is located inside the `yakamon` folder, both others are school requirements and aren't important
Each layer is strictly separated and can only communicate via to the one directly below or above it via converters.
### Presentation Layer (REST & DTOs)
* located at `yakamon/jws/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation`
* Handled by JAX-RS (Jakarta RESTful Web Services).
* Exposes standard HTTP endpoints (`/player`, `/move`, `/yakadex`, etc.). Full API specification can be found inside `yakamon/src/main/resources/openapi.yaml`
* Implements strict **Data Transfer Objects (DTOs)** for both Requests and Responses to ensure the internal database models are never exposed directly to the client.
### Business Logic Layer (Services)
* located at `yakamon/jws/yakamon/src/main/java/fr/epita/assistants/yakamon/domain`
* Acts as the brain of the application.
* Implements complex game rules: validating if a target tile is walkable based on the terrain type, calculating cooldowns between moves, and managing creature capture probabilities.
* **Converters** are used to translate Entities from the Data Layer into DTOs for the Presentation Layer.
### Data Access Layer (Hibernate ORM)
* located at `yakamon/jws/yakamon/src/main/java/fr/epita/assistants/yakamon/data`
* Manages persistence using **Hibernate ORM** with the Active Record / Repository pattern.
* Defines relational entities (Player, Game, Yakamon, Item) mapped to a PostgreSQL database.

View file

@ -9,10 +9,34 @@ public class ItemModel {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id; private Integer id;
@Enumerated @Enumerated
public ItemType type; private ItemType type;
public Integer quantity; private Integer quantity;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public ItemType getType() {
return type;
}
public void setType(ItemType type) {
this.type = type;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
} }

View file

@ -59,8 +59,8 @@ public class GameService {
// Initialize player inventory // Initialize player inventory
ItemModel yakaballs = new ItemModel(); ItemModel yakaballs = new ItemModel();
yakaballs.type = ItemType.YAKABALL; yakaballs.setType(ItemType.YAKABALL);
yakaballs.quantity = 5; yakaballs.setQuantity(5);
// Store // Store
playerRepository.persist(player); playerRepository.persist(player);

View file

@ -145,7 +145,7 @@ public class PlayerService {
ItemModel yakaballItem = itemRepository ItemModel yakaballItem = itemRepository
.find("type", ItemType.YAKABALL) .find("type", ItemType.YAKABALL)
.firstResult(); .firstResult();
if (yakaballItem == null || yakaballItem.quantity < 1) { if (yakaballItem == null || yakaballItem.getQuantity() < 1) {
throw new WebApplicationException( throw new WebApplicationException(
Response.status(400).entity("Not enough Yakaballs").build() Response.status(400).entity("Not enough Yakaballs").build()
); );
@ -170,7 +170,7 @@ public class PlayerService {
ErrorCode.BAD_REQUEST.throwException("Yakamon species not found"); ErrorCode.BAD_REQUEST.throwException("Yakamon species not found");
} }
yakaballItem.quantity -= 1; yakaballItem.setQuantity(yakaballItem.getQuantity() - 1);
itemRepository.persist(yakaballItem); itemRepository.persist(yakaballItem);
YakamonModel newYakamon = new YakamonModel(); YakamonModel newYakamon = new YakamonModel();
@ -342,11 +342,11 @@ public class PlayerService {
.firstResult(); .firstResult();
if (item == null) { if (item == null) {
item = new ItemModel(); item = new ItemModel();
item.type = itemType; item.setType(itemType);
item.quantity = 1; item.setQuantity(1);
itemRepository.persist(item); itemRepository.persist(item);
} else { } else {
item.quantity += 1; item.setQuantity(item.getQuantity() + 1);
itemRepository.persist(item); itemRepository.persist(item);
} }
} }

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api; package fr.epita.assistants.yakamon.presentation.api;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class YakamonAction { public class YakamonAction {

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.request; package fr.epita.assistants.yakamon.presentation.api.request;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class StartGameRequest { public class StartGameRequest {

View file

@ -1,8 +1,10 @@
package fr.epita.assistants.yakamon.presentation.api.request; package fr.epita.assistants.yakamon.presentation.api.request;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class TeamFeedRequest { public class TeamFeedRequest {
public Integer quantity; public Integer quantity;

View file

@ -1,8 +1,10 @@
package fr.epita.assistants.yakamon.presentation.api.request; package fr.epita.assistants.yakamon.presentation.api.request;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class TeamRenameRequest { public class TeamRenameRequest {
public String newNickname; public String newNickname;

View file

@ -2,21 +2,37 @@ package fr.epita.assistants.yakamon.presentation.api.response;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class GetInventoryResponse { public class GetInventoryResponse {
public class ItemType { public class ItemType {
public String type; public String type;
public String value; public String value;
public ItemType() {}
public ItemType(String type, String value) {
this.type = type;
this.value = value;
}
} }
public class Items { public class Items {
public ItemType itemType; public ItemType itemType;
public Integer quantity; public Integer quantity;
public Items() {}
public Items(ItemType itemType, Integer quantity) {
this.itemType = itemType;
this.quantity = quantity;
}
} }
List<Items> items; public List<Items> items;
} }

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.response; package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakamonAction; import fr.epita.assistants.yakamon.presentation.api.YakamonAction;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class PlayerCatchResponse extends YakamonAction { public class PlayerCatchResponse extends YakamonAction {
public PlayerCatchResponse( public PlayerCatchResponse(

View file

@ -3,20 +3,36 @@ package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.utils.tile.ItemType; import fr.epita.assistants.yakamon.utils.tile.ItemType;
import fr.epita.assistants.yakamon.utils.tile.TerrainType; import fr.epita.assistants.yakamon.utils.tile.TerrainType;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class PlayerCollectResponse { public class PlayerCollectResponse {
public class TileType { public class TileType {
public class Collectible { public class Collectible {
ItemType type; public ItemType type;
String value; public String value;
public Collectible() {}
public Collectible(ItemType type, String value) {
this.type = type;
this.value = value;
}
} }
TerrainType terrainType; public TerrainType terrainType;
Collectible collectible; public Collectible collectible;
public TileType() {}
public TileType(TerrainType terrainType, Collectible collectible) {
this.terrainType = terrainType;
this.collectible = collectible;
}
} }
public TileType tileType; public TileType tileType;

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.response; package fr.epita.assistants.yakamon.presentation.api.response;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class PlayerInfosResponse { public class PlayerInfosResponse {
@ -10,7 +12,7 @@ public class PlayerInfosResponse {
public Integer posX; public Integer posX;
public Integer posY; public Integer posY;
public String lastMove; public String lastMove;
public String lastCatch;
public String lastCollect; public String lastCollect;
public String lastCatch;
public String lastFeed; public String lastFeed;
} }

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.response; package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakamonAction; import fr.epita.assistants.yakamon.presentation.api.YakamonAction;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class TeamEvolveResponse extends YakamonAction { public class TeamEvolveResponse extends YakamonAction {
public TeamEvolveResponse( public TeamEvolveResponse(

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.response; package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakamonAction; import fr.epita.assistants.yakamon.presentation.api.YakamonAction;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class TeamFeedResponse extends YakamonAction { public class TeamFeedResponse extends YakamonAction {
public TeamFeedResponse( public TeamFeedResponse(

View file

@ -2,9 +2,11 @@ package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakamonAction; import fr.epita.assistants.yakamon.presentation.api.YakamonAction;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class TeamInfosResponse { public class TeamInfosResponse {
YakamonAction[] yakamons; public YakamonAction[] yakamons;
} }

View file

@ -1,7 +1,9 @@
package fr.epita.assistants.yakamon.presentation.api.response; package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakamonAction; import fr.epita.assistants.yakamon.presentation.api.YakamonAction;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class TeamRenameResponse extends YakamonAction { public class TeamRenameResponse extends YakamonAction {
public TeamRenameResponse( public TeamRenameResponse(

View file

@ -2,9 +2,11 @@ package fr.epita.assistants.yakamon.presentation.api.response;
import fr.epita.assistants.yakamon.presentation.api.YakadexEntry; import fr.epita.assistants.yakamon.presentation.api.YakadexEntry;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class YakadexAllInfosResponse { public class YakadexAllInfosResponse {
YakadexEntry[] entries; public YakadexEntry[] entries;
} }

View file

@ -41,10 +41,10 @@ public class InventoryResource {
for (ItemModel item : items) { for (ItemModel item : items) {
GetInventoryResponse.Items responseItem = temp.new Items(); GetInventoryResponse.Items responseItem = temp.new Items();
GetInventoryResponse.ItemType itemType = temp.new ItemType(); GetInventoryResponse.ItemType itemType = temp.new ItemType();
itemType.type = item.type.name(); itemType.type = item.getType().name();
itemType.value = item.type.name(); itemType.value = item.getType().name();
responseItem.itemType = itemType; responseItem.itemType = itemType;
responseItem.quantity = item.quantity; responseItem.quantity = item.getQuantity();
responseItems.add(responseItem); responseItems.add(responseItem);
} }

View file

@ -123,12 +123,12 @@ public class PlayerResource {
player.lastMove != null player.lastMove != null
? player.lastMove.format(formatter) ? player.lastMove.format(formatter)
: null, : null,
player.lastCatch != null
? player.lastCatch.format(formatter)
: null,
player.lastCollect != null player.lastCollect != null
? player.lastCollect.format(formatter) ? player.lastCollect.format(formatter)
: null, : null,
player.lastCatch != null
? player.lastCatch.format(formatter)
: null,
player.lastFeed != null player.lastFeed != null
? player.lastFeed.format(formatter) ? player.lastFeed.format(formatter)
: null : null