summaryrefslogtreecommitdiff
path: root/jws/epibazaar/item-producer/src
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /jws/epibazaar/item-producer/src
add: added projectsHEADmain
Diffstat (limited to 'jws/epibazaar/item-producer/src')
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/converter/ItemProducerConverter.java126
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/GameModel.java19
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/ItemModel.java28
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/PlayerModel.java37
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/GameRepository.java59
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/ItemRepository.java50
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/PlayerRepository.java62
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/GameEntity.java4
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemEntity.java13
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemsEntity.java13
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/MoveEntity.java11
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/PlayerEntity.java18
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/StartEntity.java11
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/UpgradeCostEntity.java12
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ErwenService.java4
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ItemProducerService.java199
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/HelloWorldResource.java19
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/ItemProducerResource.java154
-rw-r--r--jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/subscriber/AggregateSubscriber.java47
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/application.properties13
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/db/migration/V1__Init.sql25
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/maps/custom.epimap16
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/maps/huge.epimap30
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/maps/pretty.epimap16
-rw-r--r--jws/epibazaar/item-producer/src/main/resources/openapi.yaml287
25 files changed, 1273 insertions, 0 deletions
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/converter/ItemProducerConverter.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/converter/ItemProducerConverter.java
new file mode 100644
index 0000000..cb81247
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/converter/ItemProducerConverter.java
@@ -0,0 +1,126 @@
+package fr.epita.assistants.item_producer.converter;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.common.aggregate.ResetInventoryAggregate;
+import fr.epita.assistants.common.api.request.StartRequest;
+import fr.epita.assistants.common.api.response.*;
+import fr.epita.assistants.item_producer.data.model.PlayerModel;
+import fr.epita.assistants.item_producer.domain.entity.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ItemProducerConverter {
+ public static StartEntity Start(StartRequest r) {
+ if (r.getMapPath() == null || r.getMapPath().isEmpty())
+ return null;
+ return new StartEntity(r.getMapPath(), null);
+ }
+
+ public static Map<ItemAggregate.ResourceType, Float> DeleteItems(ResetInventoryAggregate items) {
+ Map<ItemAggregate.ResourceType, Float> map = new HashMap<>();
+ for (ItemAggregate i : items.getItems()) {
+ map.merge(i.getType(), i.getQuantity(), Float::sum);
+ }
+ return map;
+ }
+
+ public static MoveEntity Move(PlayerModel playerMove) {
+ return new MoveEntity(playerMove.getPosX(), playerMove.getPosY());
+ }
+
+ public static List<List<String>> RLEtoMap(String rle) {
+ List<String> lines = List.of(rle.split(";"));
+ List<List<String>> map = new ArrayList<>();
+ for (int k = 0; k < lines.size(); k++) {
+ map.add(new ArrayList<>());
+ for (int i = 0; i < lines.get(k).length(); i++) {
+ for (int j = 0; j < lines.get(k).charAt(i) - '0'; j++) {
+ String type = ItemAggregate.ResourceType.getResource(lines.get(k).charAt(i + 1)).toString();
+ map.get(k).add(type);
+ }
+ i++;
+ }
+ }
+ return map;
+ }
+
+ public static String MapToRLE(List<List<String>> map) {
+ List<String> rows = new ArrayList<>();
+ for (List<String> resourceRow : map) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < resourceRow.size(); i++) {
+ int j;
+ // count sequence of this resource type
+ String s = resourceRow.get(i);
+ for (j = i; j < resourceRow.size() && s.equals(resourceRow.get(j)); j++) {
+ continue;
+ }
+ int tmp = j;
+ j -= i;
+ i = tmp - 1;
+ while (j > 9) {
+ sb.append('9');
+ sb.append(ItemAggregate.ResourceType.valueOf(s).getItemInfo().getValue());
+ j -= 9;
+ }
+ if (j > 0) {
+ sb.append(j);
+ sb.append(ItemAggregate.ResourceType.valueOf(s).getItemInfo().getValue());
+ }
+ }
+ rows.add(sb.toString());
+ }
+ return String.join(";", rows);
+ }
+
+ public static StartEntity Collect(String resource) {
+ List<List<String>> map = new ArrayList<>();
+ map.add(new ArrayList<>());
+ map.getFirst().add(resource);
+ return new StartEntity(null, MapToRLE(map));
+ }
+
+ public static PlayerEntity ResponseToEntity(PlayerResponse r) {
+ return new PlayerEntity(r.getPosX(), r.getPosY(), r.getLastMove(), r.getLastCollect(),
+ r.getMoveSpeedMultiplier(), r.getStaminaMultiplier(), r.getCollectRateMultiplier());
+ }
+
+ public static StartEntity ResponseToEntity(StartResponse r) {
+ return new StartEntity(null, MapToRLE(r.getMap()));
+ }
+
+ public static StartResponse EntityToResponse(StartEntity e) {
+ if (e == null)
+ return null;
+ return new StartResponse(RLEtoMap(e.getRle()));
+ }
+ public static ItemResponse EntityToResponse(ItemEntity e) {
+ return new ItemResponse(e.getId(), e.getQuantity(), e.getType());
+ }
+
+ public static ItemsResponse EntityToResponse(ItemsEntity e) {
+ if (e == null)
+ return null;
+ return new ItemsResponse(e.getItems().stream().map(ItemProducerConverter::EntityToResponse).toList());
+ }
+
+ public static PlayerResponse EntityToResponse(PlayerEntity e) {
+ if (e == null)
+ return null;
+ return new PlayerResponse(e.getPosX(), e.getPosY(), e.getLastMove(), e.getLastCollect(),
+ e.getMoveSpeedMultiplier(), e.getStaminaMultiplier(), e.getCollectRateMultiplier());
+ }
+
+ public static MoveResponse EntityToResponse(MoveEntity e) {
+ if (e == null)
+ return null;
+ return new MoveResponse(e.getPosX(), e.getPosY());
+ }
+
+ public static UpgradeCostResponse EntityToResponse(UpgradeCostEntity e) {
+ return null;
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/GameModel.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/GameModel.java
new file mode 100644
index 0000000..7cca88c
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/GameModel.java
@@ -0,0 +1,19 @@
+package fr.epita.assistants.item_producer.data.model;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Setter
+@Table(name = "game")
+public class GameModel {
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+ String map;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/ItemModel.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/ItemModel.java
new file mode 100644
index 0000000..68d765a
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/ItemModel.java
@@ -0,0 +1,28 @@
+package fr.epita.assistants.item_producer.data.model;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Setter
+@Table(name = "item")
+public class ItemModel {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+ @Enumerated(value = EnumType.STRING)
+ ItemAggregate.ResourceType type;
+ Float quantity;
+
+ public ItemModel(float quantity, ItemAggregate.ResourceType type) {
+ this.quantity = quantity;
+ this.type = type;
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/PlayerModel.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/PlayerModel.java
new file mode 100644
index 0000000..943b5a3
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/model/PlayerModel.java
@@ -0,0 +1,37 @@
+package fr.epita.assistants.item_producer.data.model;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Table(name = "player")
+public class PlayerModel {
+ Float collectRateMultiplier;
+ Float moveSpeedMultiplier;
+ Integer posX;
+ Integer posY;
+ Float staminaMultiplier;
+ LocalDateTime lastCollect;
+ LocalDateTime lastMove;
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+
+ public PlayerModel(float collectRateMultiplier, float moveSpeedMultiplier, int posX, int posY, float staminaMultiplier, LocalDateTime lastCollect, LocalDateTime lastMove) {
+ this.collectRateMultiplier = collectRateMultiplier;
+ this.moveSpeedMultiplier = moveSpeedMultiplier;
+ this.posX = posX;
+ this.posY = posY;
+ this.staminaMultiplier = staminaMultiplier;
+ this.lastCollect = lastCollect;
+ this.lastMove = lastMove;
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/GameRepository.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/GameRepository.java
new file mode 100644
index 0000000..e48f844
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/GameRepository.java
@@ -0,0 +1,59 @@
+package fr.epita.assistants.item_producer.data.repository;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.item_producer.converter.ItemProducerConverter;
+import fr.epita.assistants.item_producer.data.model.GameModel;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.transaction.Transactional;
+
+import java.util.List;
+
+@ApplicationScoped
+public class GameRepository implements PanacheRepository<GameModel> {
+ @Transactional
+ public String getMap() {
+ return getGame().getMap();
+ }
+
+ private GameModel getGame() {
+ return findAll().firstResult();
+ }
+
+ @Transactional
+ public boolean isEmpty() {
+ return count() == 0;
+ }
+
+ @Transactional
+ public void clearDB() {
+ deleteAll();
+ }
+
+ @Transactional
+ public void addEntry(String rle) {
+ GameModel game = new GameModel();
+ game.setMap(rle);
+ persist(game);
+ }
+
+ @Transactional
+ public boolean isValidCollect(int x, int y) {
+ List<List<String>> map = ItemProducerConverter.RLEtoMap(getMap());
+ return ItemAggregate.ResourceType.valueOf(map.get(y).get(x)).getItemInfo().isCollectable();
+ }
+
+ @Transactional
+ public void setToGround(int x, int y) {
+ List<List<String>> map = ItemProducerConverter.RLEtoMap(getMap());
+ map.get(y).set(x, ItemAggregate.ResourceType.GROUND.toString());
+ String rle = ItemProducerConverter.MapToRLE(map);
+ update("map = ?1 where id = ?2", rle, getGame().getId());
+ }
+
+ @Transactional
+ public String collectResource(int posX, int posY) {
+ List<List<String>> map = ItemProducerConverter.RLEtoMap(getMap());
+ return map.get(posY).get(posX);
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/ItemRepository.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/ItemRepository.java
new file mode 100644
index 0000000..b46311f
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/ItemRepository.java
@@ -0,0 +1,50 @@
+package fr.epita.assistants.item_producer.data.repository;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.item_producer.data.model.ItemModel;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.transaction.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+@ApplicationScoped
+public class ItemRepository implements PanacheRepository<ItemModel> {
+ @Transactional
+ public void deleteItems(Map<ItemAggregate.ResourceType, Float> toDelete) {
+ for (ItemAggregate.ResourceType type : toDelete.keySet()) {
+ ItemModel item = find("type = ?1", type).firstResult();
+ if (item != null)
+ update("quantity = ?1 where type = ?2", item.getQuantity() - toDelete.get(type), type);
+ }
+ }
+
+ @Transactional
+ public List<ItemModel> getItems() {
+ return find("where quantity > 0").stream().toList();
+ }
+
+ @Transactional
+ public Optional<ItemModel> exists(ItemAggregate.ResourceType type) {
+ return find("type = ?1", type).firstResultOptional();
+ }
+
+ @Transactional
+ public void addItem(ItemAggregate.ResourceType type, Float amount) {
+ ItemModel res = new ItemModel(amount, type);
+ persist(res);
+ }
+
+ // amount should already be the updated amount
+ @Transactional
+ public void fillInventory(ItemAggregate.ResourceType type, Float amount) {
+ update("quantity = ?1 where type = ?2", amount, type);
+ }
+
+ @Transactional
+ public List<ItemModel> getMoney() {
+ return find("where type = ?1", ItemAggregate.ResourceType.MONEY).stream().toList();
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/PlayerRepository.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/PlayerRepository.java
new file mode 100644
index 0000000..b5bcde7
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/data/repository/PlayerRepository.java
@@ -0,0 +1,62 @@
+package fr.epita.assistants.item_producer.data.repository;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.common.utils.Direction;
+import fr.epita.assistants.item_producer.converter.ItemProducerConverter;
+import fr.epita.assistants.item_producer.data.model.PlayerModel;
+import io.quarkus.hibernate.orm.panache.PanacheRepository;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.transaction.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@ApplicationScoped
+public class PlayerRepository implements PanacheRepository<PlayerModel> {
+ @Transactional
+ public void clearDB() {
+ deleteAll();
+ }
+
+ @Transactional
+ public void addEntry() {
+ PlayerModel player = new PlayerModel(1, 1, 0, 0, 1, null, null);
+ persist(player);
+ }
+
+ @Transactional
+ public PlayerModel getFirstPlayer() {
+ return findAll().firstResult();
+ }
+
+ @Transactional
+ public boolean isValidMove(Direction direction, String rle) {
+ PlayerModel playerModel = getFirstPlayer();
+ int x = playerModel.getPosX() + direction.getPoint().getPosX();
+ int y = playerModel.getPosY() + direction.getPoint().getPosY();
+ List<List<String>> map = ItemProducerConverter.RLEtoMap(rle);
+ return y >= 0 && x >= 0 && x < map.getFirst().size() && y < map.getFirst().size() && ItemAggregate.ResourceType.valueOf(map.get(y).get(x)).getItemInfo().isWalkable();
+ }
+
+ @Transactional
+ public void movePlayer(Direction direction) {
+ PlayerModel player = getFirstPlayer();
+ update("posX = ?1, posY = ?2, lastMove = ?3 where id = ?4", player.getPosX() + direction.getPoint().getPosX()
+ , player.getPosY() + direction.getPoint().getPosY(), LocalDateTime.now(), player.getId());
+ }
+
+ public void updateMoveSpeed(Float newSpeed) {
+ PlayerModel player = getFirstPlayer();
+ update("moveSpeedMultiplier = ?1 where id = ?2", newSpeed, player.getId());
+ }
+
+ public void updateCollectRate(Float newRate) {
+ PlayerModel player = getFirstPlayer();
+ update("collectRateMultiplier = ?1 where id = ?2", newRate, player.getId());
+ }
+
+ public void updateStamina(Float newStamina) {
+ PlayerModel player = getFirstPlayer();
+ update("staminaMultiplier = ?1 where id = ?2", newStamina, player.getId());
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/GameEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/GameEntity.java
new file mode 100644
index 0000000..e248cb5
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/GameEntity.java
@@ -0,0 +1,4 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+public class GameEntity {
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemEntity.java
new file mode 100644
index 0000000..a8b666d
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemEntity.java
@@ -0,0 +1,13 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class ItemEntity {
+ long id;
+ float quantity;
+ ItemAggregate.ResourceType type;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemsEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemsEntity.java
new file mode 100644
index 0000000..b03b373
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/ItemsEntity.java
@@ -0,0 +1,13 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import fr.epita.assistants.common.api.response.ItemResponse;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+@AllArgsConstructor
+public class ItemsEntity {
+ List<ItemEntity> items;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/MoveEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/MoveEntity.java
new file mode 100644
index 0000000..72ea511
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/MoveEntity.java
@@ -0,0 +1,11 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class MoveEntity {
+ int posX;
+ int posY;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/PlayerEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/PlayerEntity.java
new file mode 100644
index 0000000..0913c3f
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/PlayerEntity.java
@@ -0,0 +1,18 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.time.LocalDateTime;
+
+@AllArgsConstructor
+@Getter
+public class PlayerEntity {
+ int posX;
+ int posY;
+ LocalDateTime lastMove;
+ LocalDateTime lastCollect;
+ float moveSpeedMultiplier;
+ float staminaMultiplier;
+ float collectRateMultiplier;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/StartEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/StartEntity.java
new file mode 100644
index 0000000..44a4961
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/StartEntity.java
@@ -0,0 +1,11 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class StartEntity {
+ String mapPath;
+ String rle;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/UpgradeCostEntity.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/UpgradeCostEntity.java
new file mode 100644
index 0000000..fac79e0
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/entity/UpgradeCostEntity.java
@@ -0,0 +1,12 @@
+package fr.epita.assistants.item_producer.domain.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class UpgradeCostEntity {
+ float upgradeCollectCost;
+ float upgradeMoveCost;
+ float upgradeStaminaCost;
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ErwenService.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ErwenService.java
new file mode 100644
index 0000000..fb3a507
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ErwenService.java
@@ -0,0 +1,4 @@
+package fr.epita.assistants.item_producer.domain.service;
+
+public class ErwenService {
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ItemProducerService.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ItemProducerService.java
new file mode 100644
index 0000000..2a224c7
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/domain/service/ItemProducerService.java
@@ -0,0 +1,199 @@
+package fr.epita.assistants.item_producer.domain.service;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.common.aggregate.ResetInventoryAggregate;
+import fr.epita.assistants.common.api.response.*;
+import fr.epita.assistants.common.command.*;
+import fr.epita.assistants.common.utils.Direction;
+import fr.epita.assistants.item_producer.converter.ItemProducerConverter;
+import fr.epita.assistants.item_producer.data.model.ItemModel;
+import fr.epita.assistants.item_producer.data.model.PlayerModel;
+import fr.epita.assistants.item_producer.data.repository.GameRepository;
+import fr.epita.assistants.item_producer.data.repository.ItemRepository;
+import fr.epita.assistants.item_producer.data.repository.PlayerRepository;
+import fr.epita.assistants.item_producer.domain.entity.*;
+import io.smallrye.reactive.messaging.annotations.Broadcast;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.eclipse.microprofile.reactive.messaging.Channel;
+import org.eclipse.microprofile.reactive.messaging.Emitter;
+
+import jakarta.inject.Inject;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@ApplicationScoped
+public class ItemProducerService {
+ @ConfigProperty(name = "JWS_TICK_DURATION")
+ String tps;
+ @ConfigProperty(name = "JWS_DELAY_MOVEMENT")
+ String movementDelay;
+ @ConfigProperty(name = "JWS_DELAY_COLLECT")
+ String collectDelay;
+ @ConfigProperty(name = "JWS_UPGRADE_COLLECT_COST")
+ String upgradeCollectCost;
+ @ConfigProperty(name = "JWS_UPGRADE_MOVE_COST")
+ String upgradeMoveCost;
+ @ConfigProperty(name = "JWS_UPGRADE_STAMINA_COST")
+ String upgradeStaminaCost;
+ @ConfigProperty(name = "JWS_UPGRADE_MULTIPLIER")
+ String upgradeMultiplier;
+ @Inject
+ PlayerRepository playerRepository;
+ @Inject
+ GameRepository gameRepository;
+ @Inject
+ ItemRepository itemRepository;
+
+ @Inject
+ @Channel("reset-inventory-command")
+ @Broadcast
+ Emitter<ResetInventoryCommand> resetInventoryCommandEmitter;
+ @Inject
+ @Channel("collect-item-command")
+ @Broadcast
+ Emitter<CollectItemCommand> collectItemCommandEmitter;
+ @Inject
+ @Channel("upgrade-movement-speed-command")
+ @Broadcast
+ Emitter<UpgradeMovementSpeedCommand> upgradeMovementSpeedCommandEmitter;
+ @Inject
+ @Channel("upgrade-stamina-command")
+ @Broadcast
+ Emitter<UpgradeStaminaCommand> upgradeStaminaCommandEmitter;
+ @Inject
+ @Channel("upgrade-collect-rate-command")
+ @Broadcast
+ Emitter<UpgradeCollectRateCommand> upgradeCollectRateCommandEmitter;
+
+ public StartEntity startGame(StartEntity startEntity) {
+ if (startEntity == null)
+ return null;
+ gameRepository.clearDB();
+ playerRepository.clearDB();
+ try (Stream<String> stream = Files.lines(Paths.get(startEntity.getMapPath()))) {
+ if (new File(startEntity.getMapPath()).isDirectory())
+ return null;
+ String rle = stream.collect(Collectors.joining(";"));
+ gameRepository.addEntry(rle);
+ playerRepository.addEntry();
+ resetInventoryCommandEmitter.send(new ResetInventoryCommand());
+ return new StartEntity(null, rle);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public void clearItems(ResetInventoryAggregate items) {
+ itemRepository.deleteItems(ItemProducerConverter.DeleteItems(items));
+ }
+
+ public ItemsEntity getAllItems() {
+ return new ItemsEntity(itemRepository.getItems().stream().map(m -> new ItemEntity(m.getId(),
+ m.getQuantity(), m.getType())).toList());
+ }
+
+ public boolean isGameRunning() {
+ return !gameRepository.isEmpty();
+ }
+
+ public boolean canMove() {
+ long tickRate = Long.parseLong(tps);
+ long moveDelay = Long.parseLong(movementDelay);
+ PlayerEntity player = getCurrentPlayer();
+ return player.getLastMove() == null || player.getLastMove().plus((long) (moveDelay * tickRate * player.getMoveSpeedMultiplier()),
+ ChronoUnit.MILLIS).isBefore(LocalDateTime.now());
+ }
+
+ public boolean canCollect() {
+ long tickRate = Long.parseLong(tps);
+ long collectDelayLong = Long.parseLong(collectDelay);
+ PlayerEntity player = getCurrentPlayer();
+ return player.getLastCollect() == null || player.getLastCollect().plus((long) (collectDelayLong * tickRate * player.getMoveSpeedMultiplier()),
+ ChronoUnit.MILLIS).isBefore(LocalDateTime.now());
+ }
+
+ public PlayerEntity getCurrentPlayer() {
+ PlayerModel p = playerRepository.getFirstPlayer();
+ return new PlayerEntity(p.getPosX(), p.getPosY(), p.getLastMove(), p.getLastCollect(),
+ p.getMoveSpeedMultiplier(), p.getStaminaMultiplier(), p.getCollectRateMultiplier());
+ }
+
+ public UpgradeCostEntity getUpgradeCosts() {
+ return new UpgradeCostEntity(Float.parseFloat(upgradeCollectCost),
+ Float.parseFloat(upgradeMoveCost), Float.parseFloat(upgradeStaminaCost));
+ }
+
+ public MoveEntity move(Direction direction) {
+ if (direction == null)
+ return null;
+ if (!playerRepository.isValidMove(direction, gameRepository.getMap()))
+ return null;
+ playerRepository.movePlayer(direction);
+ return ItemProducerConverter.Move(playerRepository.getFirstPlayer());
+ }
+
+ public StartEntity collect() {
+ PlayerEntity player = getCurrentPlayer();
+ if (!gameRepository.isValidCollect(player.getPosX(), player.getPosY()))
+ return null;
+ String item = gameRepository.collectResource(player.getPosX(), player.getPosY());
+ gameRepository.setToGround(player.getPosX(), player.getPosY());
+ collectItemCommandEmitter.send(new CollectItemCommand(ItemAggregate.ResourceType.valueOf(item),
+ player.getCollectRateMultiplier()));
+ return ItemProducerConverter.Collect(item);
+ }
+
+ public void collectItem(ItemAggregate agr) {
+ Optional<ItemModel> item = itemRepository.exists(agr.getType());
+ if (item.isPresent()) {
+ itemRepository.fillInventory(agr.getType(), agr.getQuantity());
+ } else {
+ itemRepository.addItem(agr.getType(), agr.getQuantity());
+ }
+ }
+
+ public Double getMoney() {
+ List<ItemModel> entries = itemRepository.getMoney();
+ if (entries.isEmpty())
+ return null;
+ return entries.stream().mapToDouble(m -> m.getQuantity().doubleValue()).sum();
+ }
+
+ public void upgradeMove() {
+ upgradeMovementSpeedCommandEmitter.send(new UpgradeMovementSpeedCommand(Float.parseFloat(upgradeMoveCost)));
+ }
+
+ public void updateMove() {
+ playerRepository.updateMoveSpeed(getCurrentPlayer().getMoveSpeedMultiplier() - Float.parseFloat(upgradeMultiplier));
+ }
+
+ public void upgradeCollect() {
+ upgradeCollectRateCommandEmitter.send(new UpgradeCollectRateCommand(Float.parseFloat(upgradeMoveCost)));
+ }
+
+ public void updateCollect() {
+ playerRepository.updateCollectRate(getCurrentPlayer().getCollectRateMultiplier() - Float.parseFloat(upgradeMultiplier));
+ }
+
+ public void upgradeStamina() {
+ upgradeStaminaCommandEmitter.send(new UpgradeStaminaCommand(Float.parseFloat(upgradeMoveCost)));
+ }
+
+ public void updateStamina() {
+ playerRepository.updateStamina(getCurrentPlayer().getStaminaMultiplier() - Float.parseFloat(upgradeMultiplier));
+ }
+
+ public void pay(Float newMoney) {
+ itemRepository.fillInventory(ItemAggregate.ResourceType.MONEY, newMoney);
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/HelloWorldResource.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/HelloWorldResource.java
new file mode 100644
index 0000000..9f9cb70
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/HelloWorldResource.java
@@ -0,0 +1,19 @@
+package fr.epita.assistants.item_producer.presentation.rest;
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/hello")
+@Produces(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.TEXT_PLAIN)
+public class HelloWorldResource {
+ @GET @Path("/")
+ public String helloWorld() {
+ return "Hello World!";
+ }
+
+ @GET @Path("/{name}")
+ public String helloWorld(@PathParam("name") String name) {
+ return "Hello " + name + "!";
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/ItemProducerResource.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/ItemProducerResource.java
new file mode 100644
index 0000000..fcebee9
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/rest/ItemProducerResource.java
@@ -0,0 +1,154 @@
+package fr.epita.assistants.item_producer.presentation.rest;
+
+import fr.epita.assistants.common.api.request.MoveRequest;
+import fr.epita.assistants.common.api.request.StartRequest;
+import fr.epita.assistants.common.api.response.MoveResponse;
+import fr.epita.assistants.common.api.response.StartResponse;
+import fr.epita.assistants.common.utils.ErrorInfo;
+import fr.epita.assistants.item_producer.converter.ItemProducerConverter;
+import fr.epita.assistants.item_producer.domain.service.ItemProducerService;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+@Path("/")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ItemProducerResource {
+ @Inject
+ ItemProducerService itemProducerService;
+
+ @Path("/")
+ @GET
+ public Response getInventory() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running"));
+ else
+ rb = Response.ok(ItemProducerConverter.EntityToResponse(itemProducerService.getAllItems()));
+ return rb.build();
+ }
+
+ @Path("player")
+ @GET
+ public Response getPlayer() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running"));
+ else
+ rb = Response.ok(ItemProducerConverter.EntityToResponse(itemProducerService.getCurrentPlayer()));
+ return rb.build();
+ }
+ @Path("start")
+ @POST
+ public Response start(StartRequest startRequest) {
+ StartResponse r = ItemProducerConverter.EntityToResponse(itemProducerService.startGame(ItemProducerConverter.Start(startRequest)));
+ Response.ResponseBuilder res;
+ if (r == null)
+ res = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Bad request for /start"));
+ else
+ res = Response.ok(r);
+ return res.build();
+ }
+ @Path("upgrades")
+ @GET
+ public Response getUpgrades() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running"));
+ else
+ rb = Response.ok(ItemProducerConverter.EntityToResponse(itemProducerService.getUpgradeCosts()));
+ return rb.build();
+ }
+
+ @Path("collect")
+ @POST
+ public Response collect() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running"));
+ else if (!itemProducerService.canCollect())
+ rb = Response.status(Response.Status.TOO_MANY_REQUESTS).entity(new ErrorInfo("Input too early, wait " +
+ "before trying to move again"));
+ else {
+ StartResponse rep = ItemProducerConverter.EntityToResponse(itemProducerService.collect());
+ if (rep == null)
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Can't collect on this tile"));
+ else
+ rb = Response.ok(rep);
+ }
+ return rb.build();
+ }
+
+ @Path("move")
+ @POST
+ public Response move(MoveRequest moveRequest) {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running"));
+ else if (!itemProducerService.canMove())
+ rb = Response.status(Response.Status.TOO_MANY_REQUESTS).entity(new ErrorInfo("Input too early, wait " +
+ "before trying to move again"));
+ else {
+ MoveResponse rep = ItemProducerConverter.EntityToResponse(itemProducerService.move(moveRequest.getDirection()));
+ if (rep == null)
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Tried to move out of bounds " +
+ "(or maybe you felt like you were Jesus)"));
+ else
+ rb = Response.ok(rep);
+ }
+ return rb.build();
+ }
+
+ @Path("upgrade/collect")
+ @PATCH
+ public Response upgradeCollect() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running."));
+ else if (itemProducerService.getMoney() == null)
+ rb = Response.status(Response.Status.NOT_FOUND).entity(new ErrorInfo("No money was found."));
+ else if (itemProducerService.getMoney() < itemProducerService.getUpgradeCosts().getUpgradeCollectCost())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("You are poor."));
+ else {
+ itemProducerService.upgradeCollect();
+ rb = Response.noContent();
+ }
+ return rb.build();
+ }
+
+ @Path("upgrade/move")
+ @PATCH
+ public Response upgradeMove() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running."));
+ else if (itemProducerService.getMoney() == null)
+ rb = Response.status(Response.Status.NOT_FOUND).entity(new ErrorInfo("No money was found."));
+ else if (itemProducerService.getMoney() < itemProducerService.getUpgradeCosts().getUpgradeMoveCost())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("You are poor."));
+ else {
+ itemProducerService.upgradeMove();
+ rb = Response.noContent();
+ }
+ return rb.build();
+ }
+
+ @Path("upgrade/stamina")
+ @PATCH
+ public Response upgradeStamina() {
+ Response.ResponseBuilder rb;
+ if (!itemProducerService.isGameRunning())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("Game is not running."));
+ else if (itemProducerService.getMoney() == null)
+ rb = Response.status(Response.Status.NOT_FOUND).entity(new ErrorInfo("No money was found."));
+ else if (itemProducerService.getMoney() < itemProducerService.getUpgradeCosts().getUpgradeStaminaCost())
+ rb = Response.status(Response.Status.BAD_REQUEST).entity(new ErrorInfo("You are poor."));
+ else {
+ itemProducerService.upgradeStamina();
+ rb = Response.noContent();
+ }
+ return rb.build();
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/subscriber/AggregateSubscriber.java b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/subscriber/AggregateSubscriber.java
new file mode 100644
index 0000000..c6f3231
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/java/fr/epita/assistants/item_producer/presentation/subscriber/AggregateSubscriber.java
@@ -0,0 +1,47 @@
+package fr.epita.assistants.item_producer.presentation.subscriber;
+
+import fr.epita.assistants.common.aggregate.ItemAggregate;
+import fr.epita.assistants.common.aggregate.ResetInventoryAggregate;
+import fr.epita.assistants.common.aggregate.UpgradeItemProducerAggregate;
+import fr.epita.assistants.item_producer.domain.service.ItemProducerService;
+import io.smallrye.reactive.messaging.annotations.Broadcast;
+import jakarta.inject.Inject;
+import org.eclipse.microprofile.reactive.messaging.Incoming;
+
+public class AggregateSubscriber {
+ @Inject
+ ItemProducerService itemProducerService;
+
+ @Broadcast
+ @Incoming("reset-inventory-aggregate")
+ public void commandListener(ResetInventoryAggregate aggregate) {
+ itemProducerService.clearItems(aggregate);
+ }
+
+ @Broadcast
+ @Incoming("collect-item-aggregate")
+ public void commandListener(ItemAggregate agr) {
+ itemProducerService.collectItem(agr);
+ }
+
+ @Broadcast
+ @Incoming("upgrade-collect-rate-aggregate")
+ public void collectCommandListener(UpgradeItemProducerAggregate agr) {
+ itemProducerService.pay(agr.getNewMoney());
+ itemProducerService.updateCollect();
+ }
+
+ @Broadcast
+ @Incoming("upgrade-movement-speed-aggregate")
+ public void moveCommandListener(UpgradeItemProducerAggregate agr) {
+ itemProducerService.pay(agr.getNewMoney());
+ itemProducerService.updateMove();
+ }
+
+ @Broadcast
+ @Incoming("upgrade-stamina-aggregate")
+ public void staminaCommandListener(UpgradeItemProducerAggregate agr) {
+ itemProducerService.pay(agr.getNewMoney());
+ itemProducerService.updateStamina();
+ }
+}
diff --git a/jws/epibazaar/item-producer/src/main/resources/application.properties b/jws/epibazaar/item-producer/src/main/resources/application.properties
new file mode 100644
index 0000000..498479f
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/application.properties
@@ -0,0 +1,13 @@
+%dev.quarkus.http.port=8081
+quarkus.datasource.db-kind=postgresql
+quarkus.datasource.username=postgres
+quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/item_producer?currentSchema=public
+quarkus.transaction-manager.default-transaction-timeout=3000s
+quarkus.hibernate-orm.log.queries-slower-than-ms=200
+quarkus.http.cors=true
+quarkus.http.cors.origins=*
+quarkus.hibernate-orm.database.generation=drop-and-create
+
+quarkus.kafka.devservices.image-name=reg.undercloud.cri.epita.fr/docker/redpandadata/redpanda:v24.1.2
+quarkus.devservices.enabled=true
+%test.quarkus.devservices.enabled=false
diff --git a/jws/epibazaar/item-producer/src/main/resources/db/migration/V1__Init.sql b/jws/epibazaar/item-producer/src/main/resources/db/migration/V1__Init.sql
new file mode 100644
index 0000000..0eede01
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/db/migration/V1__Init.sql
@@ -0,0 +1,25 @@
+CREATE TABLE IF NOT EXISTS "game"
+(
+ id SERIAL PRIMARY KEY NOT NULL,
+ map TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "item"
+(
+ id SERIAL PRIMARY KEY NOT NULL,
+
+ type VARCHAR(64) NOT NULL,
+ quantity FLOAT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "player"
+(
+ id SERIAL PRIMARY KEY NOT NULL,
+ pos_x INTEGER NOT NULL,
+ pos_y INTEGER NOT NULL,
+ last_move TIMESTAMP,
+ last_collect TIMESTAMP,
+ move_speed_multiplier FLOAT NOT NULL,
+ stamina_multiplier FLOAT NOT NULL,
+ collect_rate_multiplier FLOAT NOT NULL
+);
diff --git a/jws/epibazaar/item-producer/src/main/resources/maps/custom.epimap b/jws/epibazaar/item-producer/src/main/resources/maps/custom.epimap
new file mode 100644
index 0000000..5b510f3
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/maps/custom.epimap
@@ -0,0 +1,16 @@
+2G4R3W5G2R
+5G3W4R2G2W
+6R3G2W5G
+3W5G6R2W
+4G3R4W5G
+2W5R3G4W2G
+6G4R3W3G
+4R6G3W3R
+5G2W5R4G
+3W5R4G3W1G
+2G6W3R5G
+4W5G3R4W
+3G4R5W4G
+5R3G6W2R
+2W5G4R5W
+9G7G \ No newline at end of file
diff --git a/jws/epibazaar/item-producer/src/main/resources/maps/huge.epimap b/jws/epibazaar/item-producer/src/main/resources/maps/huge.epimap
new file mode 100644
index 0000000..d8384bd
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/maps/huge.epimap
@@ -0,0 +1,30 @@
+9G9G9G3G
+8G3W3R2G2R2G2W1R4G1R2G
+7G2W1G3R1W1G1R1W2G1R1G1R1G1R1G2W2R1G
+1G1W4G1R1G1W1R5G2R4G1R1W1R2G1W3G
+1G1W1R2G2R1G1W2R3G2W2G2O3G2R3G1W1G
+1G1W1G2W7G4O3G2O3R3G2W1G
+1G1R4G1R3G1R3G2O3G2O2R3G2W2G
+4G3R3G3R8O3R2G1W3G
+4G3R1G5R4O2G1W2R1G2R5G
+4G9R1G3O9G4G
+5G4R2W2O1R5O2G3W1R5G
+3G4R2G8O1G7W1G2W2G
+3G1R1G2R1G1W9O1W2G1R2W6G
+1G1R1G2R2G1R1W1G6O4W9O1G
+1G4R1G2R1W1G3O1G2O5W1G7O1G
+1G1W2R1W1G1R1G2W3O2R1O4W1G1R7O1G
+5G3W3G2O1G1W1G3W1G1W1G7O1G
+1G2R2G2W4R2O3G3W1R1G1W1G1R5O1G
+1G1W1R1G2R4W1R1G1R1G3R2W5G5O1G
+1G2R2G1R2W1R2G6R1W1G9O1O1G
+2G2W9R2R3W2G9O1G
+6G3W2G4R3W2G2O1W6O1G
+1G3R2G3W2G3R1G3W2R2O1R6O1G
+1G3R1G1W3R2W1R2G2W1G3R2O1R1O1G1O1G1W1O1G
+3G2W1G2R1G2R2G2W3G1R1W2G2R1G1W1G1R1O1G
+3G1W1R3G2R4G1R2G1W1G1R2W2R1G1W3R1G
+1G4W1G3R2W3G2R3W1R2W3R1G2R2G
+2G3W1G2R6G2R2G2R1G1W1G2W1G1R1G1W1G
+2G3W1G1W1G4W9G1G2R1W1G2R2G
+9G9G9G3G \ No newline at end of file
diff --git a/jws/epibazaar/item-producer/src/main/resources/maps/pretty.epimap b/jws/epibazaar/item-producer/src/main/resources/maps/pretty.epimap
new file mode 100644
index 0000000..3699c22
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/maps/pretty.epimap
@@ -0,0 +1,16 @@
+9G7G
+2G6O3G4R1G
+2G5O4G3R2G
+2G4O4G3W2G1G
+2G3O4G5W1G1G
+2G3O3G5W3G
+2G3G4W4G3R
+2G3R3G4W3O1G
+1G2R3G4W4O2G
+1G3G3W6O3G
+1G2W4G5O4G
+1G2W3G4R3G3G
+1G3R4G4R1G3G
+1G5R4G4R2G
+1G5R3G6G1G
+9G7G
diff --git a/jws/epibazaar/item-producer/src/main/resources/openapi.yaml b/jws/epibazaar/item-producer/src/main/resources/openapi.yaml
new file mode 100644
index 0000000..16e0465
--- /dev/null
+++ b/jws/epibazaar/item-producer/src/main/resources/openapi.yaml
@@ -0,0 +1,287 @@
+---
+openapi: 3.1.0
+tags:
+- name: Game Management
+- name: Player Actions
+- name: Upgrades
+paths:
+ /:
+ get:
+ summary: Retrieve all inventory resources
+ description: Fetch all resources currently available in the inventory.
+ x-quarkus-openapi-method-ref: m98752433_-1109396335
+ tags:
+ - Game Management
+ responses:
+ "200":
+ description: Resources successfully retrieved.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ItemsResponse"
+ "400":
+ description: The game is not running.
+ servers:
+ - url: http://localhost:8081/
+ /collect:
+ post:
+ summary: Collect resources
+ description: Collect the resource available on the player's current tile.
+ x-quarkus-openapi-method-ref: m98752433_799402137
+ tags:
+ - Player Actions
+ responses:
+ "200":
+ description: Resource successfully collected and sent to inventory.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/StartResponse"
+ "400":
+ description: Invalid tile or the game is not running.
+ "429":
+ description: Player has recently collected and must wait before collecting
+ again.
+ servers:
+ - url: http://localhost:8081/
+ /move:
+ post:
+ summary: Move player
+ description: "Move the player in the specified direction (left, right, up, or\
+ \ down)."
+ x-quarkus-openapi-method-ref: m98752433_-1586243046
+ tags:
+ - Player Actions
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/MoveRequest"
+ required: true
+ responses:
+ "200":
+ description: Player successfully moved.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/MoveResponse"
+ "400":
+ description: Invalid direction or the game is not running.
+ "429":
+ description: Player has recently moved and must wait before moving again.
+ servers:
+ - url: http://localhost:8081/
+ /player:
+ get:
+ summary: Retrieve player information
+ description: Fetch information about the current player.
+ x-quarkus-openapi-method-ref: m98752433_-1988770431
+ tags:
+ - Game Management
+ responses:
+ "200":
+ description: Player information successfully retrieved.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PlayerResponse"
+ "400":
+ description: The game is not running.
+ servers:
+ - url: http://localhost:8081/
+ /start:
+ post:
+ summary: Start the game
+ description: Initialize and start the item-producer game. Synchronizes the database
+ with the `Inventory` service.
+ x-quarkus-openapi-method-ref: m98752433_677674067
+ tags:
+ - Game Management
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/StartRequest"
+ required: true
+ responses:
+ "200":
+ description: The game started successfully.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/StartResponse"
+ "400":
+ description: Invalid `path` provided.
+ servers:
+ - url: http://localhost:8081/
+ /upgrade/collect:
+ patch:
+ summary: Upgrade collection rate
+ description: Increase the amount of resources collected per action.
+ x-quarkus-openapi-method-ref: m98752433_274012880
+ tags:
+ - Upgrades
+ responses:
+ "204":
+ description: Collection rate upgrade successfully applied.
+ "400":
+ description: Insufficient funds or the game is not running.
+ "404":
+ description: The money was not found.
+ servers:
+ - url: http://localhost:8081/
+ /upgrade/move:
+ patch:
+ summary: Upgrade move speed
+ description: Decrease the tick rate required for the player to move.
+ x-quarkus-openapi-method-ref: m98752433_1889564488
+ tags:
+ - Upgrades
+ responses:
+ "204":
+ description: Move speed upgrade successfully applied.
+ "400":
+ description: Insufficient funds or the game is not running.
+ "404":
+ description: The money was not found.
+ servers:
+ - url: http://localhost:8081/
+ /upgrade/stamina:
+ patch:
+ summary: Upgrade stamina
+ description: Decrease the tick rate required to collect resources.
+ x-quarkus-openapi-method-ref: m98752433_-1142370765
+ tags:
+ - Upgrades
+ responses:
+ "204":
+ description: Stamina upgrade successfully applied.
+ "400":
+ description: Insufficient funds or the game is not running.
+ "404":
+ description: The money was not found.
+ servers:
+ - url: http://localhost:8081/
+ /upgrades:
+ get:
+ summary: Retrieve upgrade costs
+ description: Fetch the costs of all available upgrades.
+ x-quarkus-openapi-method-ref: m98752433_-99503618
+ tags:
+ - Game Management
+ responses:
+ "200":
+ description: Upgrade costs successfully retrieved.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UpgradeCostResponse"
+ "400":
+ description: The game is not running.
+ servers:
+ - url: http://localhost:8081/
+components:
+ schemas:
+ Direction:
+ type: string
+ enum:
+ - UP
+ - DOWN
+ - RIGHT
+ - LEFT
+ ItemResponse:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int32
+ quantity:
+ type: number
+ format: float
+ type:
+ $ref: "#/components/schemas/ResourceType"
+ ItemsResponse:
+ type: object
+ properties:
+ itemsResponse:
+ type: array
+ items:
+ $ref: "#/components/schemas/ItemResponse"
+ LocalDateTime:
+ type: string
+ format: date-time
+ examples:
+ - 2022-03-10T12:15:50
+ MoveRequest:
+ type: object
+ properties:
+ direction:
+ $ref: "#/components/schemas/Direction"
+ MoveResponse:
+ type: object
+ properties:
+ posX:
+ type: integer
+ format: int32
+ posY:
+ type: integer
+ format: int32
+ PlayerResponse:
+ type: object
+ properties:
+ posX:
+ type: integer
+ format: int32
+ posY:
+ type: integer
+ format: int32
+ lastMove:
+ $ref: "#/components/schemas/LocalDateTime"
+ lastCollect:
+ $ref: "#/components/schemas/LocalDateTime"
+ moveSpeedMultiplier:
+ type: number
+ format: float
+ staminaMultiplier:
+ type: number
+ format: float
+ collectRateMultiplier:
+ type: number
+ format: float
+ ResourceType:
+ type: string
+ enum:
+ - MONEY
+ - GROUND
+ - WATER
+ - ROCK
+ - WOOD
+ StartRequest:
+ type: object
+ properties:
+ mapPath:
+ type: string
+ StartResponse:
+ type: object
+ properties:
+ map:
+ type: array
+ items:
+ type: array
+ items:
+ $ref: "#/components/schemas/ResourceType"
+ UpgradeCostResponse:
+ type: object
+ properties:
+ upgradeCollectCost:
+ type: number
+ format: float
+ upgradeMoveCost:
+ type: number
+ format: float
+ upgradeStaminaCost:
+ type: number
+ format: float
+info:
+ title: item-producer API
+ version: 1.0.0