diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java new file mode 100644 index 0000000..8bd94b0 --- /dev/null +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java @@ -0,0 +1,153 @@ +package mc.core.h2db; + +import lombok.extern.slf4j.Slf4j; +import mc.core.EntityLocation; +import mc.core.world.World; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.UUID; + +@Slf4j +@Component +public class H2PlayerDAO { + private static String INSERT_SQL, SELECT_SQL, UPDATE_SQL, UPDATE_LOCATION_SQL, DELETE_SQL; + + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private World world; + + public void insert(H2Player player) throws SQLException { + KeyHolder keyHolder = new GeneratedKeyHolder(); + + int affectedRows = jdbcTemplate.update(psc -> { + PreparedStatement stmt = psc.prepareStatement(INSERT_SQL, Statement.RETURN_GENERATED_KEYS); + + stmt.setString(1, player.getUuid().toString()); + stmt.setString(2, player.getName()); + stmt.setDouble(3, player.getLocation().getX()); + stmt.setDouble(4, player.getLocation().getY()); + stmt.setDouble(5, player.getLocation().getZ()); + stmt.setFloat(6, player.getLocation().getYaw()); + stmt.setFloat(7, player.getLocation().getPitch()); + stmt.setString(8, player.getWorld().getName()); + + return stmt; + }, keyHolder); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed: no rows affected."); + } + + player.setId(keyHolder.getKey().intValue()); + } + + public void getByName(H2Player playerBuffer) throws SQLException { + if (playerBuffer.getName() == null || playerBuffer.getName().isEmpty()) { + throw new SQLException("Argument 'name' is " + (playerBuffer.getName() == null ? "null" : "empty")); + } + + jdbcTemplate.query(SELECT_SQL, + ps -> ps.setString(1, playerBuffer.getName()), + rs -> { + playerBuffer.setId(rs.getInt("id")); + playerBuffer.setUuid(UUID.fromString(rs.getString("uuid"))); + if (!world.getName().equals(rs.getString("location_world"))) { + log.warn("Unknown world \"{}\"", rs.getString("location_world")); + log.warn("Using default (spawn) location for user \"{}\"", playerBuffer.getName()); + playerBuffer.setLocation(world.getSpawn().clone()); + } else { + playerBuffer.setLocation(new EntityLocation( + rs.getDouble("location_x"), + rs.getDouble("location_y"), + rs.getDouble("location_z"), + rs.getFloat("location_yaw"), + rs.getFloat("location_pitch") + )); + playerBuffer.setWorld(world); + } + }); + } + + public void update(H2Player player) throws SQLException { + if (player.getId() == 0) { + throw new SQLException("Argument 'id' is zero"); + } + + int affectedRows = jdbcTemplate.update(UPDATE_SQL, pss -> { + pss.setInt(9, player.getId()); + pss.setString(1, player.getUuid().toString()); + pss.setString(2, player.getName()); + pss.setDouble(3, player.getLocation().getX()); + pss.setDouble(4, player.getLocation().getY()); + pss.setDouble(5, player.getLocation().getZ()); + pss.setFloat(6, player.getLocation().getYaw()); + pss.setFloat(7, player.getLocation().getPitch()); + pss.setString(8, player.getWorld().getName()); + }); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed, no rows affected."); + } + } + + public void updateLocation(H2Player player) throws SQLException { + if (player.getId() == 0) { + throw new SQLException("Argument 'id' is zero"); + } + + int affectedRows = jdbcTemplate.update(connection -> { + PreparedStatement stmt = connection.prepareStatement(UPDATE_LOCATION_SQL); + + stmt.setInt(7, player.getId()); + stmt.setDouble(1, player.getLocation().getX()); + stmt.setDouble(2, player.getLocation().getY()); + stmt.setDouble(3, player.getLocation().getZ()); + stmt.setFloat(4, player.getLocation().getYaw()); + stmt.setFloat(5, player.getLocation().getPitch()); + stmt.setString(6, player.getWorld().getName()); + + return stmt; + }); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed, no rows affected."); + } + } + + public void remove(H2Player player) throws SQLException { + if (player.getId() == 0) { + throw new SQLException("Argument 'id' is zero"); + } + + int affectedRows = jdbcTemplate.update(DELETE_SQL, pss -> pss.setInt(1, player.getId())); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed, no rows affected."); + } + + player.setId(0); + } + + static { + try { + INSERT_SQL = IOUtils.resourceToString("/sqls/insert_player.sql", StandardCharsets.UTF_8); + SELECT_SQL = IOUtils.resourceToString("/sqls/select_player_byName.sql", StandardCharsets.UTF_8); + UPDATE_SQL = IOUtils.resourceToString("/sqls/update_player.sql", StandardCharsets.UTF_8); + UPDATE_LOCATION_SQL = IOUtils.resourceToString("/sqls/update_player_location.sql", StandardCharsets.UTF_8); + DELETE_SQL = IOUtils.resourceToString("/sqls/delete_player.sql", StandardCharsets.UTF_8); + } catch (IOException e) { + log.error("Load sql templates", e); + } + } +} diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java deleted file mode 100644 index dcd9a07..0000000 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java +++ /dev/null @@ -1,114 +0,0 @@ -package mc.core.h2db; - -import lombok.extern.slf4j.Slf4j; -import mc.core.EntityLocation; -import mc.core.world.World; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Component; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.UUID; - -@Slf4j -@Component -public class H2PlayerSerializer { - @Autowired - private World world; - @Autowired - private JdbcTemplate jdbcTemplate; - - private static final String SQL_INSERT = "INSERT INTO players " + - "(uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; - - public void serialize(final H2Player player) throws SQLException { - KeyHolder keyHolder = new GeneratedKeyHolder(); - int affectedRows = jdbcTemplate.update(connection -> { - PreparedStatement stmt = connection.prepareStatement(SQL_INSERT, Statement.RETURN_GENERATED_KEYS); - - stmt.setString(1, player.getUuid().toString()); - stmt.setString(2, player.getName()); - stmt.setDouble(3, player.getLocation().getX()); - stmt.setDouble(4, player.getLocation().getY()); - stmt.setDouble(5, player.getLocation().getZ()); - stmt.setFloat(6, player.getLocation().getYaw()); - stmt.setFloat(7, player.getLocation().getPitch()); - stmt.setString(8, player.getWorld().getName()); - - return stmt; - }, keyHolder); - - if (affectedRows == 0) { - throw new SQLException("Serialize player failed, no rows affected."); - } - - player.setId(keyHolder.getKey().intValue()); - } - - public void deserialize(H2Player player) { - if (player.getId() > 0) { - selectById(player); - } else if (player.getUuid() != null) { - selectByUuid(player); - } else if (player.getName() != null && !player.getName().isEmpty()) { - selectByName(player); - } - } - - - private void selectById(final H2Player player) { - final String sql = "SELECT * FROM players WHERE id = ? LIMIT 1;"; - jdbcTemplate.query(sql, - ps -> ps.setInt(1, player.getId()), - rs -> { - player.setUuid(UUID.fromString(rs.getString("uuid"))); - player.setName(rs.getString("name")); - deserializeLocation(player, rs); - }); - } - - private void selectByUuid(H2Player player) { - final String sql = "SELECT * FROM players WHERE uuid LIKE ? LIMIT 1;"; - jdbcTemplate.query(sql, - ps -> ps.setString(1, player.getUuid().toString()), - rs -> { - player.setId(rs.getInt("id")); - player.setName(rs.getString("name")); - deserializeLocation(player, rs); - }); - } - - private void selectByName(H2Player player) { - final String sql = "SELECT * FROM players WHERE name LIKE ? LIMIT 1;"; - jdbcTemplate.query(sql, - ps -> ps.setString(1, player.getName()), - rs -> { - player.setId(rs.getInt("id")); - player.setUuid(UUID.fromString(rs.getString("uuid"))); - deserializeLocation(player, rs); - }); - } - - private void deserializeLocation(H2Player player, ResultSet resultSet) throws SQLException { - if (!world.getName().equals(resultSet.getString("location_world"))) { - log.warn("Unknown world \"{}\"", resultSet.getString("location_world")); - log.warn("Using default (spawn) location for user \"{}\"", player.getName()); - player.setLocation(world.getSpawn().clone()); - } else { - player.setLocation(new EntityLocation( - resultSet.getDouble("location_x"), - resultSet.getDouble("location_y"), - resultSet.getDouble("location_z"), - resultSet.getFloat("location_yaw"), - resultSet.getFloat("location_pitch") - )); - player.setWorld(world); - } - } -} diff --git a/h2_playermanager/src/test/resources/sqls/create_tables.sql b/h2_playermanager/src/main/resources/sqls/create_tables.sql similarity index 100% rename from h2_playermanager/src/test/resources/sqls/create_tables.sql rename to h2_playermanager/src/main/resources/sqls/create_tables.sql diff --git a/h2_playermanager/src/main/resources/sqls/delete_player.sql b/h2_playermanager/src/main/resources/sqls/delete_player.sql new file mode 100644 index 0000000..d16f13d --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/delete_player.sql @@ -0,0 +1 @@ +DELETE FROM players WHERE id = ?; \ No newline at end of file diff --git a/h2_playermanager/src/main/resources/sqls/insert_player.sql b/h2_playermanager/src/main/resources/sqls/insert_player.sql new file mode 100644 index 0000000..ab9bdff --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/insert_player.sql @@ -0,0 +1,2 @@ +INSERT INTO players (uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world) + VALUES (?, ?, ?, ?, ?, ?, ?, ?); \ No newline at end of file diff --git a/h2_playermanager/src/main/resources/sqls/select_player_byName.sql b/h2_playermanager/src/main/resources/sqls/select_player_byName.sql new file mode 100644 index 0000000..1a8f47a --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/select_player_byName.sql @@ -0,0 +1,3 @@ +SELECT id, uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world +FROM players WHERE name LIKE ? +LIMIT 1; \ No newline at end of file diff --git a/h2_playermanager/src/main/resources/sqls/update_player.sql b/h2_playermanager/src/main/resources/sqls/update_player.sql new file mode 100644 index 0000000..0e7bb2d --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/update_player.sql @@ -0,0 +1,10 @@ +UPDATE players +SET uuid = ?, + name = ?, + location_x = ?, + location_y = ?, + location_z = ?, + location_yaw = ?, + location_pitch = ?, + location_world = ? +WHERE id = ?; \ No newline at end of file diff --git a/h2_playermanager/src/main/resources/sqls/update_player_location.sql b/h2_playermanager/src/main/resources/sqls/update_player_location.sql new file mode 100644 index 0000000..30b2d29 --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/update_player_location.sql @@ -0,0 +1,8 @@ +UPDATE players +SET location_x = ?, + location_y = ?, + location_z = ?, + location_yaw = ?, + location_pitch = ?, + location_world = ? +WHERE id = ?; \ No newline at end of file diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java new file mode 100644 index 0000000..709ed52 --- /dev/null +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java @@ -0,0 +1,152 @@ +package mc.core.h2db; + +import mc.core.EntityLocation; +import mc.core.world.World; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {SpringConfig.class}) +public class TestDAO { + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private World mockWorld; + @Autowired + private H2PlayerDAO playerDAO; + private H2Player player; + + private void createPlayer() { + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); + final double minD = 0.0d, maxD = 10.0d; + final float minF = 0.0f, maxF = 359.9f; + final int minI = 1000, maxI = 9999; + + player = new H2Player(); + player.setUuid(UUID.randomUUID()); + player.setName("player" + rnd.nextInt(minI, maxI)); + player.setLocation(new EntityLocation( + rnd.nextDouble(minD, maxD), + rnd.nextDouble(minD, maxD), + rnd.nextDouble(minD, maxD), + rnd.nextFloat() * (maxF - minF) + minF, + rnd.nextFloat() * (maxF - minF) + minF + )); + player.setWorld(mockWorld); + } + + private void assertPlayer(H2Player actualPlayer) { + final String sql = "SELECT * FROM players WHERE id = ?"; + jdbcTemplate.query(sql, + ps -> ps.setInt(1, player.getId()), + rs -> { + assertEquals(actualPlayer.getId(), rs.getInt("id")); + assertEquals(actualPlayer.getName(), rs.getString("name")); + assertEquals(actualPlayer.getLocation().getX(), rs.getDouble("location_x"), 0.01d); + assertEquals(actualPlayer.getLocation().getY(), rs.getDouble("location_y"), 0.01d); + assertEquals(actualPlayer.getLocation().getZ(), rs.getDouble("location_z"), 0.01d); + assertEquals(actualPlayer.getLocation().getYaw(), rs.getFloat("location_yaw"), 0.01f); + assertEquals(actualPlayer.getLocation().getPitch(), rs.getFloat("location_pitch"), 0.01f); + assertEquals(actualPlayer.getWorld().getName(), rs.getString("location_world")); + }); + } + + @Before + public void init() throws IOException { + jdbcTemplate.execute("DROP TABLE IF EXISTS players;"); + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); + createPlayer(); + assertEquals(0, player.getId()); + } + + @Test + public void testInsert() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + assertPlayer(player); + } + + @Test + public void testGetByName() throws SQLException { + playerDAO.insert(this.player); + assertNotEquals(0, player.getId()); + + H2Player player = new H2Player(); + player.setName(this.player.getName()); + + assertNotNull(player.getName()); + assertFalse(player.getName().isEmpty()); + assertEquals(this.player.getName(), player.getName()); + + playerDAO.getByName(player); + + assertEquals(player, this.player); + } + + @Test + public void testUpdate() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setName("UNKNOWN_PLAYER"); + playerDAO.update(player); + + assertPlayer(player); + } + + @Test + public void testUpdateLocation() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + final String origName = player.getName(); + player.setName("UNKNOWN_PLAYER"); + player.getLocation().setX(33.1d); + player.getLocation().setZ(28.99d); + playerDAO.updateLocation(player); + + final String sql = "SELECT * FROM players WHERE id = ?"; + jdbcTemplate.query(sql, + ps -> ps.setInt(1, player.getId()), + rs -> { + assertEquals(player.getId(), rs.getInt("id")); + assertEquals(origName, rs.getString("name")); + assertEquals(player.getLocation().getX(), rs.getDouble("location_x"), 0.01d); + assertEquals(player.getLocation().getY(), rs.getDouble("location_y"), 0.01d); + assertEquals(player.getLocation().getZ(), rs.getDouble("location_z"), 0.01d); + assertEquals(player.getLocation().getYaw(), rs.getFloat("location_yaw"), 0.01f); + assertEquals(player.getLocation().getPitch(), rs.getFloat("location_pitch"), 0.01f); + assertEquals(player.getWorld().getName(), rs.getString("location_world")); + }); + } + + @Test + public void testRemove() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + final int origId = player.getId(); + playerDAO.remove(player); + assertEquals(0, player.getId()); + + final String sql = "SELECT COUNT(*) FROM players WHERE id = ?"; + jdbcTemplate.query(sql, + ps -> ps.setInt(1, origId), + rs -> {assertEquals(0, rs.getInt(1));}); + } +} diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java deleted file mode 100644 index 377d8b8..0000000 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java +++ /dev/null @@ -1,122 +0,0 @@ -package mc.core.h2db; - -import mc.core.EntityLocation; -import mc.core.world.World; -import org.apache.commons.io.IOUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import javax.annotation.PostConstruct; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.sql.SQLException; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; - -import static org.junit.Assert.assertEquals; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = {SpringConfig.class}) -public class TestH2Database { - @Autowired - private JdbcTemplate jdbcTemplate; - @Autowired - private World mockWorld; - @Autowired - private H2PlayerSerializer h2PlayerSerializer; - private H2Player player; - - @PostConstruct - public void init() throws IOException { - jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); - } - - private void createPlayer() { - final ThreadLocalRandom rnd = ThreadLocalRandom.current(); - final double minD = 0.0d, maxD = 10.0d; - final float minF = 0.0f, maxF = 359.9f; - final int minI = 1000, maxI = 9999; - - player = new H2Player(); - player.setUuid(UUID.randomUUID()); - player.setName("player" + rnd.nextInt(minI, maxI)); - player.setLocation(new EntityLocation( - rnd.nextDouble(minD, maxD), - rnd.nextDouble(minD, maxD), - rnd.nextDouble(minD, maxD), - rnd.nextFloat() * (maxF - minF) + minF, - rnd.nextFloat() * (maxF - minF) + minF - )); - player.setWorld(mockWorld); - } - - @Test - public void testConnect() { - final String sql = "SELECT 1"; - jdbcTemplate.execute(sql); - } - - @Test - public void testExistsTable() { - jdbcTemplate.execute("SELECT COUNT(*) FROM players"); - } - - @Test - public void testSerialize() throws SQLException { - createPlayer(); - h2PlayerSerializer.serialize(player); - - assertEquals(1, player.getId()); - - final String sql = "SELECT * FROM players WHERE id = ?"; - jdbcTemplate.query(sql, new Object[]{player.getId()}, (resultSet) -> { - assertEquals(player.getId(), resultSet.getInt("id")); - assertEquals(player.getName(), resultSet.getString("name")); - assertEquals(player.getLocation().getX(), resultSet.getDouble("location_x"), 0.01d); - assertEquals(player.getLocation().getY(), resultSet.getDouble("location_y"), 0.01d); - assertEquals(player.getLocation().getZ(), resultSet.getDouble("location_z"), 0.01d); - assertEquals(player.getLocation().getYaw(), resultSet.getFloat("location_yaw"), 0.01f); - assertEquals(player.getLocation().getPitch(), resultSet.getFloat("location_pitch"), 0.01f); - assertEquals(player.getWorld().getName(), resultSet.getString("location_world")); - }); - } - - @Test - public void testDeserialize() { - createPlayer(); - player.setId(2); - - final String sql = "INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"; - int affectedRows = jdbcTemplate.update(sql, ps -> { - ps.setInt(1, player.getId()); - ps.setString(2, player.getUuid().toString()); - ps.setString(3, player.getName()); - ps.setDouble(4, player.getLocation().getX()); - ps.setDouble(5, player.getLocation().getY()); - ps.setDouble(6, player.getLocation().getZ()); - ps.setFloat(7, player.getLocation().getYaw()); - ps.setFloat(8, player.getLocation().getPitch()); - ps.setString(9, player.getWorld().getName()); - }); - assertEquals(1, affectedRows); - - H2Player queryPlayer = new H2Player(); - queryPlayer.setId(2); - h2PlayerSerializer.deserialize(queryPlayer); - assertEquals("Search by id", this.player, queryPlayer); - - queryPlayer = new H2Player(); - queryPlayer.setUuid(player.getUuid()); - h2PlayerSerializer.deserialize(queryPlayer); - assertEquals("Search by UUID", this.player, queryPlayer); - - queryPlayer = new H2Player(); - queryPlayer.setName(player.getName()); - h2PlayerSerializer.deserialize(queryPlayer); - assertEquals("Search by name", this.player, queryPlayer); - } -}