Initial commit
This commit is contained in:
@ -0,0 +1,13 @@
|
||||
package cz.jzitnik.chronos;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ChronosApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ChronosApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cz.jzitnik.chronos.controllers;
|
||||
|
||||
import cz.jzitnik.chronos.entities.Game;
|
||||
import cz.jzitnik.chronos.payload.responses.UnifiedResponse;
|
||||
import cz.jzitnik.chronos.repository.GameRepository;
|
||||
import cz.jzitnik.chronos.services.GameService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||
@RestController
|
||||
@RequestMapping("/game")
|
||||
public class GameController {
|
||||
@Autowired
|
||||
private GameService gameService;
|
||||
|
||||
@Autowired
|
||||
private GameRepository gameRepository;
|
||||
|
||||
@PostMapping("/new")
|
||||
public UnifiedResponse<Game, Error> createGame() {
|
||||
var game = gameService.createGame();
|
||||
gameRepository.save(game);
|
||||
|
||||
return UnifiedResponse.success(game);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package cz.jzitnik.chronos.controllers;
|
||||
|
||||
import cz.jzitnik.chronos.payload.errors.NotFoundError;
|
||||
import cz.jzitnik.chronos.payload.requests.PlayerNameRequest;
|
||||
import cz.jzitnik.chronos.payload.responses.UnifiedResponse;
|
||||
import cz.jzitnik.chronos.repository.GameRepository;
|
||||
import cz.jzitnik.chronos.repository.PlayerRepository;
|
||||
import cz.jzitnik.chronos.services.GameService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||
@RestController
|
||||
@RequestMapping("/game/players")
|
||||
public class PlayerController {
|
||||
@Autowired
|
||||
private GameRepository gameRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayerRepository playerRepository;
|
||||
|
||||
@Autowired
|
||||
private GameService gameService;
|
||||
|
||||
@PostMapping("/new")
|
||||
public ResponseEntity<UnifiedResponse<String, Error>> newPlayer(@RequestParam String gameKey, @RequestBody PlayerNameRequest playerNameRequest) {
|
||||
var gameOptional = gameRepository.findByGameKey(gameKey);
|
||||
|
||||
if (gameOptional.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
|
||||
UnifiedResponse.failure(new NotFoundError("Game with key " + gameKey + " was not found!"))
|
||||
);
|
||||
}
|
||||
|
||||
var game = gameOptional.get();
|
||||
|
||||
var player = gameService.addPlayer(game, playerNameRequest.getName());
|
||||
|
||||
gameRepository.save(game);
|
||||
|
||||
return ResponseEntity.ok(
|
||||
UnifiedResponse.success(player.getPlayerKey())
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public class Character {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "charowner", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "character-inventory")
|
||||
private List<Item> inventory = new ArrayList<>();
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Interaction interaction;
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "room-characters")
|
||||
private Room room;
|
||||
|
||||
private String dialog;
|
||||
|
||||
public Character(String name, Room room, String dialog) {
|
||||
this.name = name;
|
||||
this.room = room;
|
||||
this.dialog = dialog;
|
||||
}
|
||||
}
|
35
backend/src/main/java/cz/jzitnik/chronos/entities/Game.java
Normal file
35
backend/src/main/java/cz/jzitnik/chronos/entities/Game.java
Normal file
@ -0,0 +1,35 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public class Game {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
private String gameKey;
|
||||
|
||||
@OneToMany(mappedBy = "game", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "game-players")
|
||||
private List<Player> players = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "game", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "game-rooms")
|
||||
private List<Room> rooms = new ArrayList<>();
|
||||
|
||||
public void addPlayer(Player player) {
|
||||
this.players.add(player);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
public enum Interaction {
|
||||
Farmer
|
||||
}
|
37
backend/src/main/java/cz/jzitnik/chronos/entities/Item.java
Normal file
37
backend/src/main/java/cz/jzitnik/chronos/entities/Item.java
Normal file
@ -0,0 +1,37 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public class Item {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ItemType itemType;
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "player-inventory")
|
||||
private Player owner;
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "character-inventory")
|
||||
private Character charowner;
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "room-items")
|
||||
private Room room;
|
||||
|
||||
public Item(ItemType itemType, Character charowner) {
|
||||
this.itemType = itemType;
|
||||
this.charowner = charowner;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
public enum ItemType {
|
||||
KEY_FRAGMENT,
|
||||
LUCK_POTION
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public class Player {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "player-inventory")
|
||||
@Size(max = 3)
|
||||
private List<Item> inventory = new ArrayList<>();
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "game-players")
|
||||
private Game game;
|
||||
|
||||
@ManyToOne
|
||||
@Column(name = "current_room")
|
||||
private Room currentRoom;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
@JsonIgnore
|
||||
private String playerKey;
|
||||
|
||||
public Player(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
45
backend/src/main/java/cz/jzitnik/chronos/entities/Room.java
Normal file
45
backend/src/main/java/cz/jzitnik/chronos/entities/Room.java
Normal file
@ -0,0 +1,45 @@
|
||||
package cz.jzitnik.chronos.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Room {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "room", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "room-items")
|
||||
private List<Item> items = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "room", cascade = CascadeType.ALL)
|
||||
@JsonManagedReference(value = "room-characters")
|
||||
private List<Character> characters = new ArrayList<>();
|
||||
|
||||
@ManyToOne
|
||||
@JsonBackReference(value = "game-rooms")
|
||||
private Game game;
|
||||
|
||||
@OneToMany(mappedBy = "current_room", cascade = CascadeType.ALL)
|
||||
@JsonIgnore
|
||||
private List<Player> players = new ArrayList<>();
|
||||
|
||||
public Room(String name, Game game) {
|
||||
this.name = name;
|
||||
this.game = game;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cz.jzitnik.chronos.payload.errors;
|
||||
|
||||
public class NotFoundError extends Error {
|
||||
public NotFoundError() {
|
||||
super("Not Found Error");
|
||||
}
|
||||
|
||||
public NotFoundError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cz.jzitnik.chronos.payload.requests;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class PlayerNameRequest {
|
||||
private String name;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cz.jzitnik.chronos.payload.responses;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class UnifiedResponse<T, U> {
|
||||
@Getter
|
||||
private final Optional<T> data;
|
||||
|
||||
@Getter
|
||||
private final Optional<U> error;
|
||||
|
||||
@Getter
|
||||
private final Boolean success;
|
||||
|
||||
private UnifiedResponse(Optional<T> data, Optional<U> error, Boolean success) {
|
||||
this.data = data;
|
||||
this.error = error;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public static <T, U> UnifiedResponse<T, U> success(T data) {
|
||||
return new UnifiedResponse<>(Optional.ofNullable(data), Optional.empty(), true);
|
||||
}
|
||||
|
||||
// Static factory method for error response
|
||||
public static <T, U> UnifiedResponse<T, U> failure(U error) {
|
||||
return new UnifiedResponse<>(Optional.empty(), Optional.ofNullable(error), false);
|
||||
}
|
||||
|
||||
// Static factory method for empty response
|
||||
public static <T, U> UnifiedResponse<T, U> empty() {
|
||||
return new UnifiedResponse<>(Optional.empty(), Optional.empty(), false);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cz.jzitnik.chronos.repository;
|
||||
|
||||
import cz.jzitnik.chronos.entities.Game;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface GameRepository extends JpaRepository<Game, Long> {
|
||||
boolean existsByGameKey(String gameKey);
|
||||
Optional<Game> findByGameKey(String gameKey);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cz.jzitnik.chronos.repository;
|
||||
|
||||
import cz.jzitnik.chronos.entities.Player;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PlayerRepository extends JpaRepository<Player, Long> {
|
||||
boolean existsByPlayerKey(String playerKey);
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package cz.jzitnik.chronos.services;
|
||||
|
||||
import cz.jzitnik.chronos.entities.Game;
|
||||
import cz.jzitnik.chronos.entities.Player;
|
||||
import cz.jzitnik.chronos.repository.GameRepository;
|
||||
import cz.jzitnik.chronos.repository.PlayerRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@Service
|
||||
public class GameService {
|
||||
@Autowired
|
||||
private GameRepository gameRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayerRepository playerRepository;
|
||||
|
||||
@Autowired
|
||||
private InitGameService initGameService;
|
||||
|
||||
private static final int KEY_LENGTH = 10;
|
||||
private static final String CHAR_POOL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
public String generateKey() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
StringBuilder gameKey = new StringBuilder(KEY_LENGTH);
|
||||
|
||||
for (int i = 0; i < KEY_LENGTH; i++) {
|
||||
int index = random.nextInt(CHAR_POOL.length());
|
||||
gameKey.append(CHAR_POOL.charAt(index));
|
||||
}
|
||||
|
||||
return gameKey.toString();
|
||||
}
|
||||
|
||||
public String generateUniqueGameKey() {
|
||||
var key = generateKey();
|
||||
|
||||
if (gameRepository.existsByGameKey(key)) {
|
||||
return generateUniqueGameKey();
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public String generateUniquePlayerKey() {
|
||||
var key = generateKey();
|
||||
|
||||
if (playerRepository.existsByPlayerKey(key)) {
|
||||
return generateUniquePlayerKey();
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public Game createGame() {
|
||||
Game newGame = new Game();
|
||||
newGame.setGameKey(generateUniqueGameKey());
|
||||
|
||||
initGameService.initGame(newGame);
|
||||
|
||||
return gameRepository.save(newGame);
|
||||
}
|
||||
|
||||
public Player addPlayer(Game game, String playerName) {
|
||||
var player = new Player(playerName);
|
||||
player.setPlayerKey(generateUniquePlayerKey());
|
||||
player.setGame(game);
|
||||
|
||||
game.addPlayer(player);
|
||||
|
||||
return player;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package cz.jzitnik.chronos.services;
|
||||
|
||||
import cz.jzitnik.chronos.entities.*;
|
||||
import cz.jzitnik.chronos.entities.Character;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
public class InitGameService {
|
||||
public void initGame(Game game) {
|
||||
var rooms = new ArrayList<Room>();
|
||||
|
||||
|
||||
// Outside
|
||||
var outside = new Room(
|
||||
"Outside", // Name
|
||||
game
|
||||
);
|
||||
var outside_characters = new ArrayList<Character>();
|
||||
var elf_items = new ArrayList<Item>();
|
||||
var elf = new Character(
|
||||
"Skřítek",
|
||||
outside,
|
||||
"Ahoj já jsem skřítek... Můj dialog doplnit"
|
||||
);
|
||||
elf_items.add(new Item(ItemType.LUCK_POTION, elf));
|
||||
elf.setInventory(elf_items);
|
||||
outside_characters.add(elf);
|
||||
outside.setCharacters(outside_characters);
|
||||
|
||||
// Stodola
|
||||
var stodola = new Room(
|
||||
"Stodola",
|
||||
game
|
||||
);
|
||||
var stodola_characters = new ArrayList<Character>();
|
||||
var farmar = new Character(
|
||||
"Farmář",
|
||||
stodola,
|
||||
"Ahoj já jsem farmář a budeš se mnou hrát kámen nůžky papír."
|
||||
);
|
||||
farmar.setInteraction(Interaction.Farmer);
|
||||
stodola_characters.add(farmar);
|
||||
stodola.setCharacters(stodola_characters);
|
||||
|
||||
rooms.add(outside);
|
||||
rooms.add(stodola);
|
||||
|
||||
game.setRooms(rooms);
|
||||
}
|
||||
}
|
9
backend/src/main/resources/application.properties
Normal file
9
backend/src/main/resources/application.properties
Normal file
@ -0,0 +1,9 @@
|
||||
spring.application.name=chronos
|
||||
|
||||
# DB
|
||||
spring.datasource.url=${DATASOURCE_URL}
|
||||
spring.datasource.username=${DB_USERNAME}
|
||||
spring.datasource.password=${DB_PASSWORD}
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.properties.hibernate.default_schema=app
|
@ -0,0 +1,13 @@
|
||||
package cz.jzitnik.chronos;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ChronosApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user