From bd72950db59215dfcc468045daecab59d65b6bbf Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 6 Sep 2018 12:42:16 +0300 Subject: [PATCH 01/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20H2db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- h2_playermanager/build.gradle | 16 ++++++++ .../java/mc/core/h2db/TestH2Database.java | 38 +++++++++++++++++++ .../src/test/resources/springTest.xml | 21 ++++++++++ .../src/test/resources/sqls/create_tables.sql | 11 ++++++ settings.gradle | 1 + 5 files changed, 87 insertions(+) create mode 100644 h2_playermanager/build.gradle create mode 100644 h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java create mode 100644 h2_playermanager/src/test/resources/springTest.xml create mode 100644 h2_playermanager/src/test/resources/sqls/create_tables.sql diff --git a/h2_playermanager/build.gradle b/h2_playermanager/build.gradle new file mode 100644 index 0000000..727fc10 --- /dev/null +++ b/h2_playermanager/build.gradle @@ -0,0 +1,16 @@ +version '1.0-SNAPSHOT' + +dependencies { + /* Core */ + compile_excludeCopy project(':core') + + /* Spring */ + compile (group: 'org.springframework', name: 'spring-jdbc', version: spring_version) { + exclude group: 'commons-logging' + } + + /* Database */ + compile (group: 'com.h2database', name: 'h2', version: '1.4.196') + + testCompile (group: 'org.springframework', name: 'spring-test', version: spring_version) +} \ No newline at end of file diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java new file mode 100644 index 0000000..69b6b12 --- /dev/null +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java @@ -0,0 +1,38 @@ +package mc.core.h2db; + +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; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"classpath:springTest.xml"}) +public class TestH2Database { + @Autowired + private JdbcTemplate jdbcTemplate; + + @PostConstruct + public void init() throws IOException { + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); + } + + @Test + public void testConnect() { + final String sql = "SELECT 1"; + jdbcTemplate.execute(sql); + } + + @Test + public void testExistsTable() { + jdbcTemplate.execute("SELECT COUNT(*) FROM players"); + } + + +} diff --git a/h2_playermanager/src/test/resources/springTest.xml b/h2_playermanager/src/test/resources/springTest.xml new file mode 100644 index 0000000..8cebaaf --- /dev/null +++ b/h2_playermanager/src/test/resources/springTest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/h2_playermanager/src/test/resources/sqls/create_tables.sql b/h2_playermanager/src/test/resources/sqls/create_tables.sql new file mode 100644 index 0000000..a7d2209 --- /dev/null +++ b/h2_playermanager/src/test/resources/sqls/create_tables.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS players ( + id INT AUTO_INCREMENT, + uuid VARCHAR(36) NOT NULL, + name VARCHAR(16) NOT NULL, + location_x DOUBLE NOT NULL, + location_y DOUBLE NOT NULL, + location_z DOUBLE NOT NULL, + location_yaw FLOAT NOT NULL, + location_pitch FLOAT NOT NULL, + location_world varchar(64) NOT NULL +); \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 52ad5e4..f9dbb2c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,6 +2,7 @@ rootProject.name = 'mc-server' include('core') // Core include('flat_world') +include('h2_playermanager') include('vanilla_commands') include('proto_1.12.2') // Protocol 1.12.2 include('proto_1.12.2_netty') // Protocol 1.12.2 (Netty impl.) From 56bcd7a7304f1764a56a32804c40550d46b1f6f0 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 6 Sep 2018 13:20:59 +0300 Subject: [PATCH 02/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20H2Player?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/java/mc/core/player/Player.java | 2 +- .../java/mc/core/player/SimplePlayer.java | 2 +- .../src/main/java/mc/core/h2db/H2Player.java | 23 +++++++++++++++++++ .../netty/handlers/LoginHandler.java | 4 ++-- 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 h2_playermanager/src/main/java/mc/core/h2db/H2Player.java diff --git a/core/src/main/java/mc/core/player/Player.java b/core/src/main/java/mc/core/player/Player.java index 11c41a3..ad8e8ad 100644 --- a/core/src/main/java/mc/core/player/Player.java +++ b/core/src/main/java/mc/core/player/Player.java @@ -12,7 +12,7 @@ import java.util.UUID; public interface Player { int getId(); - UUID getUUID(); + UUID getUuid(); String getName(); boolean isOnline(); diff --git a/core/src/main/java/mc/core/player/SimplePlayer.java b/core/src/main/java/mc/core/player/SimplePlayer.java index 2472758..e88ae77 100644 --- a/core/src/main/java/mc/core/player/SimplePlayer.java +++ b/core/src/main/java/mc/core/player/SimplePlayer.java @@ -30,7 +30,7 @@ public class SimplePlayer implements Player { } @Override - public UUID getUUID() { + public UUID getUuid() { return uuid; } diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java new file mode 100644 index 0000000..c02aa15 --- /dev/null +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java @@ -0,0 +1,23 @@ +package mc.core.h2db; + +import lombok.Data; +import mc.core.EntityLocation; +import mc.core.network.NetChannel; +import mc.core.player.Player; +import mc.core.player.PlayerSettings; + +import java.util.List; +import java.util.UUID; + +@Data +public class H2Player implements Player { + private int id; + private UUID uuid; + private String name; + private boolean online = false; + private List loadedChunks; + private NetChannel channel; + private EntityLocation location; + private boolean flying = false; + private PlayerSettings settings; +} diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java index 771b1c3..0444e64 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java @@ -53,7 +53,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand world.getSpawn())); channel.writeAndFlush(new LoginSuccessPacket( - player.getUUID(), + player.getUuid(), packet.getPlayerName())); channel.attr(ATTR_PLAYER).set(player); channel.attr(ATTR_STATE).set(State.PLAY); @@ -102,7 +102,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand PlayerListItemPacket pkt5 = new PlayerListItemPacket(); pkt5.setAction(PlayerListItemPacket.Action.ADD_PLAYER); PlayerListItemPacket.PlayerData playerData = new PlayerListItemPacket.PlayerData(); - playerData.setUuid(player.getUUID()); + playerData.setUuid(player.getUuid()); playerData.setName(player.getName()); playerData.setGameMode(PlayerMode.CREATIVE); playerData.setPing(0); From e23e530d1e5d0af4980e7d005fa7eeb6239793af Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 6 Sep 2018 13:21:29 +0300 Subject: [PATCH 03/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B8=D0=BC=D1=8F=20=D0=BC=D0=B8=D1=80?= =?UTF-8?q?=D0=B0=20(World.getName())?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/java/mc/core/world/World.java | 1 + flat_world/src/main/java/mc/world/flat/FlatWorld.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/src/main/java/mc/core/world/World.java b/core/src/main/java/mc/core/world/World.java index 20bf437..9bb676b 100644 --- a/core/src/main/java/mc/core/world/World.java +++ b/core/src/main/java/mc/core/world/World.java @@ -8,6 +8,7 @@ import mc.core.EntityLocation; import mc.core.world.chunk.Chunk; public interface World { + String getName(); WorldType getWorldType(); EntityLocation getSpawn(); diff --git a/flat_world/src/main/java/mc/world/flat/FlatWorld.java b/flat_world/src/main/java/mc/world/flat/FlatWorld.java index 6ec745f..e080400 100644 --- a/flat_world/src/main/java/mc/world/flat/FlatWorld.java +++ b/flat_world/src/main/java/mc/world/flat/FlatWorld.java @@ -14,6 +14,8 @@ import mc.core.world.chunk.ChunkSection; @Slf4j public class FlatWorld implements World { + @Getter + private final String name = "flat"; @Getter private final WorldType worldType = WorldType.FLAT; private EntityLocation spawn; From a6f4c42b4ef998cb865cb467cc966c0498c997c3 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 6 Sep 2018 13:27:26 +0300 Subject: [PATCH 04/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D1=81=D0=B5=D1=80?= =?UTF-8?q?=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/mc/core/h2db/H2PlayerSerializer.java | 39 ++++++++++++++++++ .../java/mc/core/h2db/TestH2Database.java | 41 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java new file mode 100644 index 0000000..80102cd --- /dev/null +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java @@ -0,0 +1,39 @@ +package mc.core.h2db; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +public class H2PlayerSerializer { + 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 static void serialize(final H2Player player, final JdbcTemplate jdbcTemplate) 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.getLocation().getWorld().getName()); + + return stmt; + }, keyHolder); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed, no rows affected."); + } + + player.setId(keyHolder.getKey().intValue()); + } +} diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java index 69b6b12..1636884 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java @@ -1,5 +1,7 @@ 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; @@ -11,16 +13,37 @@ 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 static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:springTest.xml"}) public class TestH2Database { @Autowired private JdbcTemplate jdbcTemplate; + private World mockWorld; + + private void createMockWorld() { + mockWorld = mock(World.class); + when(mockWorld.getName()).thenReturn("mock_world"); + } @PostConstruct public void init() throws IOException { jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); + createMockWorld(); + } + + private H2Player buildPlayer(final String name, final EntityLocation location) { + H2Player player = new H2Player(); + player.setUuid(UUID.randomUUID()); + player.setName(name); + player.setLocation(location.clone()); + return player; } @Test @@ -34,5 +57,23 @@ public class TestH2Database { jdbcTemplate.execute("SELECT COUNT(*) FROM players"); } + @Test + public void testSerialize() throws SQLException { + final H2Player player = buildPlayer("player1", new EntityLocation(1.5d, 6.8d, 0.01d, 0f, 36.9f, mockWorld)); + H2PlayerSerializer.serialize(player, jdbcTemplate); + 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.getLocation().getWorld().getName(), resultSet.getString("location_world")); + }); + } } From ef7bde7138571e730e3eed46a1b0f2bba17455ed Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 7 Sep 2018 20:50:20 +0300 Subject: [PATCH 05/20] =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20xml=20=D0=BD=D0=B0=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/mc/core/h2db/SpringConfig.java | 38 +++++++++++++++++++ .../java/mc/core/h2db/TestH2Database.java | 11 +----- .../src/test/resources/springTest.xml | 21 ---------- 3 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java delete mode 100644 h2_playermanager/src/test/resources/springTest.xml diff --git a/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java new file mode 100644 index 0000000..4e18747 --- /dev/null +++ b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java @@ -0,0 +1,38 @@ +package mc.core.h2db; + +import mc.core.world.World; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Configuration +public class SpringConfig { + @Bean + public World mockWorld() { + World mockWorld = mock(World.class); + when(mockWorld.getName()).thenReturn("mock_world"); + return mockWorld; + } + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dmds = new DriverManagerDataSource(); + dmds.setDriverClassName("org.h2.Driver"); + dmds.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"); + dmds.setUsername("sa"); + return dmds; + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(); + jdbcTemplate.setDataSource(dataSource); + return jdbcTemplate; + } +} diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java index 1636884..77a2292 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java @@ -17,25 +17,18 @@ import java.sql.SQLException; import java.util.UUID; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = {"classpath:springTest.xml"}) +@ContextConfiguration(classes = {SpringConfig.class}) public class TestH2Database { @Autowired private JdbcTemplate jdbcTemplate; + @Autowired private World mockWorld; - private void createMockWorld() { - mockWorld = mock(World.class); - when(mockWorld.getName()).thenReturn("mock_world"); - } - @PostConstruct public void init() throws IOException { jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); - createMockWorld(); } private H2Player buildPlayer(final String name, final EntityLocation location) { diff --git a/h2_playermanager/src/test/resources/springTest.xml b/h2_playermanager/src/test/resources/springTest.xml deleted file mode 100644 index 8cebaaf..0000000 --- a/h2_playermanager/src/test/resources/springTest.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file From ea1e159c871524a3557ccd384f246bdadebd86d1 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 7 Sep 2018 22:18:29 +0300 Subject: [PATCH 06/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20Locati?= =?UTF-8?q?on's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/mc/core/EntityLocation.java | 17 ++++++++ core/src/main/java/mc/core/Location.java | 17 ++++++++ .../test/java/mc/core/TestEntityLocation.java | 40 +++++++++++++++++-- core/src/test/java/mc/core/TestLocation.java | 19 +++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/mc/core/EntityLocation.java b/core/src/main/java/mc/core/EntityLocation.java index 0eec9d2..56b4605 100644 --- a/core/src/main/java/mc/core/EntityLocation.java +++ b/core/src/main/java/mc/core/EntityLocation.java @@ -8,6 +8,8 @@ import lombok.Getter; import lombok.Setter; import mc.core.world.World; +import java.util.Objects; + public class EntityLocation extends Location implements Cloneable { @Getter @Setter @@ -31,4 +33,19 @@ public class EntityLocation extends Location implements Cloneable { public EntityLocation clone() { return (EntityLocation) super.clone(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + EntityLocation that = (EntityLocation) o; + return Float.compare(that.yaw, yaw) == 0 && + Float.compare(that.pitch, pitch) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), yaw, pitch); + } } diff --git a/core/src/main/java/mc/core/Location.java b/core/src/main/java/mc/core/Location.java index 4a4c1b4..60afc2b 100644 --- a/core/src/main/java/mc/core/Location.java +++ b/core/src/main/java/mc/core/Location.java @@ -13,6 +13,7 @@ import mc.core.world.chunk.ChunkSection; import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import java.util.Objects; public class Location implements Cloneable { @Getter @@ -88,4 +89,20 @@ public class Location implements Cloneable { return null; } } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Location location = (Location) obj; + return Double.compare(location.x, x) == 0 && + Double.compare(location.y, y) == 0 && + Double.compare(location.z, z) == 0 && + Objects.equals(refWorld.get(), location.refWorld.get()); + } + + @Override + public int hashCode() { + return Objects.hash(x, y, z, refWorld.get()); + } } diff --git a/core/src/test/java/mc/core/TestEntityLocation.java b/core/src/test/java/mc/core/TestEntityLocation.java index 06fbfa7..25ac1d7 100644 --- a/core/src/test/java/mc/core/TestEntityLocation.java +++ b/core/src/test/java/mc/core/TestEntityLocation.java @@ -1,19 +1,30 @@ package mc.core; import mc.core.world.World; +import org.junit.Before; import org.junit.Test; +import java.util.concurrent.ThreadLocalRandom; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class TestEntityLocation { + private World mockWorld; + + @Before + public void before() { + mockWorld = mock(World.class); + when(mockWorld.getName()).thenReturn("mock_world"); + } + @Test public void cloneTest() { - World dummyWorld = mock(World.class); - - EntityLocation firstLocation = new EntityLocation(10, 20, 30, 40, 50, dummyWorld); - assertSame("Lost world reference before cloning", dummyWorld, firstLocation.getWorld()); + EntityLocation firstLocation = new EntityLocation(10, 20, 30, 40, 50, mockWorld); + assertSame("Lost world reference before cloning", mockWorld, firstLocation.getWorld()); EntityLocation locationClone = firstLocation.clone(); assertEquals("X mismatch", firstLocation.getX(), locationClone.getX(), 0); @@ -23,4 +34,25 @@ public class TestEntityLocation { assertEquals("Yaw mismatch", firstLocation.getYaw(), locationClone.getYaw(), 0); assertSame("World mismatch (accidental clone of the World object?)", firstLocation.getWorld(), locationClone.getWorld()); } + + @Test + public void testEquals() { + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); + final double minD = 0.0d, maxD = 10.0d; + final float minF = 0.0f, maxF = 359.9f; + final double x = rnd.nextDouble(minD, maxD); + final double y = rnd.nextDouble(minD, maxD); + final double z = rnd.nextDouble(minD, maxD); + final float yaw = rnd.nextFloat() * (maxF - minF) + minF; + final float pitch = rnd.nextFloat() * (maxF - minF) + minF; + + EntityLocation loc1 = new EntityLocation(x, y, z, yaw, pitch, mockWorld); + EntityLocation loc2 = new EntityLocation(x, y, z, yaw, pitch, mockWorld); + assertEquals(loc1, loc2); + + loc2 = new EntityLocation(x+3, y+1, z+2, yaw, pitch, mockWorld); + assertNotEquals(loc1, loc2); + loc2 = new EntityLocation(x, y, z, yaw+5, pitch-1, mockWorld); + assertNotEquals(loc1, loc2); + } } diff --git a/core/src/test/java/mc/core/TestLocation.java b/core/src/test/java/mc/core/TestLocation.java index 3d6715e..18d537e 100644 --- a/core/src/test/java/mc/core/TestLocation.java +++ b/core/src/test/java/mc/core/TestLocation.java @@ -5,7 +5,10 @@ import mc.core.world.chunk.Chunk; import org.junit.Before; import org.junit.Test; +import java.util.concurrent.ThreadLocalRandom; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.*; public class TestLocation { @@ -121,4 +124,20 @@ public class TestLocation { assertEquals(-2, chunk.getX()); assertEquals(-2, chunk.getZ()); } + + @Test + public void testEquals() { + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); + final double minD = 0.0d, maxD = 10.0d; + final double x = rnd.nextDouble(minD, maxD); + final double y = rnd.nextDouble(minD, maxD); + final double z = rnd.nextDouble(minD, maxD); + + Location loc1 = new Location(x, y, z, world); + Location loc2 = new Location(x, y, z, world); + assertEquals(loc1, loc2); + + loc2 = new Location(x+3, y+1, z+2, world); + assertNotEquals(loc1, loc2); + } } From ab5dcc64c2581ebe12d8a3ee1faa89f2b74d0b9e Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 7 Sep 2018 22:34:31 +0300 Subject: [PATCH 07/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B4=D0=B5=D1=81=D0=B5=D1=80=D0=B8=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20(+=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/mc/core/h2db/H2PlayerSerializer.java | 77 ++++++++++++++++++- .../test/java/mc/core/h2db/SpringConfig.java | 2 + .../java/mc/core/h2db/TestH2Database.java | 64 +++++++++++++-- 3 files changed, 135 insertions(+), 8 deletions(-) diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java index 80102cd..af5d4c8 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java @@ -1,19 +1,33 @@ 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 static void serialize(final H2Player player, final JdbcTemplate jdbcTemplate) throws SQLException { + 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); @@ -36,4 +50,65 @@ public class H2PlayerSerializer { 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"), + world + )); + } + } } diff --git a/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java index 4e18747..e4417e3 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java @@ -2,6 +2,7 @@ package mc.core.h2db; import mc.core.world.World; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -12,6 +13,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @Configuration +@ComponentScan("mc.core.h2db") public class SpringConfig { @Bean public World mockWorld() { diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java index 77a2292..f194431 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java @@ -15,6 +15,7 @@ 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; @@ -25,18 +26,32 @@ public class TestH2Database { 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 H2Player buildPlayer(final String name, final EntityLocation location) { - H2Player player = new H2Player(); + 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(name); - player.setLocation(location.clone()); - return player; + 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, + mockWorld + )); } @Test @@ -52,8 +67,8 @@ public class TestH2Database { @Test public void testSerialize() throws SQLException { - final H2Player player = buildPlayer("player1", new EntityLocation(1.5d, 6.8d, 0.01d, 0f, 36.9f, mockWorld)); - H2PlayerSerializer.serialize(player, jdbcTemplate); + createPlayer(); + h2PlayerSerializer.serialize(player); assertEquals(1, player.getId()); @@ -69,4 +84,39 @@ public class TestH2Database { assertEquals(player.getLocation().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.getLocation().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); + } } From fc542c05efe1d540067fbf737ae01959ef35482a Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sat, 8 Sep 2018 17:26:33 +0300 Subject: [PATCH 08/20] optimize imports --- .../mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java | 2 -- .../proto_1_12_2/packets/PlayerBlockPlacementPacket.java | 1 - .../core/network/proto_1_12_2/packets/PlayerDiggingPacket.java | 1 - .../network/proto_1_12_2/packets/ByteArrayInputNetStream.java | 1 - .../proto_1_12_2/packets/TestByteArrayInputNetStream.java | 1 - 5 files changed, 6 deletions(-) diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java index b3d6cd7..ae0ab8d 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java @@ -4,7 +4,6 @@ */ package mc.core.network.proto_1_12_2.packets; -import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -17,7 +16,6 @@ import mc.core.world.chunk.Chunk; import mc.core.world.chunk.ChunkSection; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java index c40fffd..ebcf00d 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerBlockPlacementPacket.java @@ -7,7 +7,6 @@ import mc.core.world.block.BlockLocation; import mc.core.network.CSPacket; import mc.core.network.NetInputStream; import mc.core.network.proto_1_12_2.Direction; -import mc.core.utils.CompactedCoords; @Getter @ToString diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java index 9ce0dc9..355cc57 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/PlayerDiggingPacket.java @@ -8,7 +8,6 @@ import mc.core.world.block.BlockLocation; import mc.core.network.CSPacket; import mc.core.network.NetInputStream; import mc.core.network.proto_1_12_2.Direction; -import mc.core.utils.CompactedCoords; import java.util.Arrays; diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java index 2c17c19..c51beaf 100644 --- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java @@ -3,7 +3,6 @@ package mc.core.network.proto_1_12_2.packets; import mc.core.network.proto_1_12_2.NetInputStream_p340; import java.io.ByteArrayInputStream; -import java.io.EOFException; public class ByteArrayInputNetStream extends NetInputStream_p340 { private ByteArrayInputStream bais; diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestByteArrayInputNetStream.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestByteArrayInputNetStream.java index 85bfc7c..72d1574 100644 --- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestByteArrayInputNetStream.java +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/TestByteArrayInputNetStream.java @@ -3,7 +3,6 @@ package mc.core.network.proto_1_12_2.packets; import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream; import org.junit.Test; -import java.io.ByteArrayInputStream; import java.util.Random; import static org.junit.Assert.*; From 24499bb0b183e711a4892dcba68f532411a18364 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 9 Sep 2018 12:32:25 +0300 Subject: [PATCH 09/20] =?UTF-8?q?=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20H2Player=20=D0=BF=D0=BE=20id=20=D0=B8=20uu?= =?UTF-8?q?id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/mc/core/h2db/H2Player.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java index b3e7bdc..a33b94a 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java @@ -11,6 +11,7 @@ import mc.core.world.World; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.List; +import java.util.Objects; import java.util.UUID; @Data @@ -41,4 +42,18 @@ public class H2Player implements Player { public void setWorld(World world) { this.$refWorld = new WeakReference<>(world); } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + H2Player player = (H2Player) obj; + return id == player.id && + Objects.equals(uuid, player.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(id, uuid); + } } From 295a7685e8e9bbe3bee9b068fe85443afcb55f44 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 13 Sep 2018 14:00:43 +0300 Subject: [PATCH 10/20] H2PlayerSerializer -> H2PlayerDAO --- .../main/java/mc/core/h2db/H2PlayerDAO.java | 153 ++++++++++++++++++ .../java/mc/core/h2db/H2PlayerSerializer.java | 114 ------------- .../resources/sqls/create_tables.sql | 0 .../src/main/resources/sqls/delete_player.sql | 1 + .../src/main/resources/sqls/insert_player.sql | 2 + .../resources/sqls/select_player_byName.sql | 3 + .../src/main/resources/sqls/update_player.sql | 10 ++ .../resources/sqls/update_player_location.sql | 8 + .../src/test/java/mc/core/h2db/TestDAO.java | 152 +++++++++++++++++ .../java/mc/core/h2db/TestH2Database.java | 122 -------------- 10 files changed, 329 insertions(+), 236 deletions(-) create mode 100644 h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java delete mode 100644 h2_playermanager/src/main/java/mc/core/h2db/H2PlayerSerializer.java rename h2_playermanager/src/{test => main}/resources/sqls/create_tables.sql (100%) create mode 100644 h2_playermanager/src/main/resources/sqls/delete_player.sql create mode 100644 h2_playermanager/src/main/resources/sqls/insert_player.sql create mode 100644 h2_playermanager/src/main/resources/sqls/select_player_byName.sql create mode 100644 h2_playermanager/src/main/resources/sqls/update_player.sql create mode 100644 h2_playermanager/src/main/resources/sqls/update_player_location.sql create mode 100644 h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java delete mode 100644 h2_playermanager/src/test/java/mc/core/h2db/TestH2Database.java 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); - } -} From 6a4855f5a91b998164c37d4f82b96518b5db88bf Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Thu, 13 Sep 2018 22:53:47 +0300 Subject: [PATCH 11/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20H2Player=20equals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/mc/core/h2db/TestH2Player.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 h2_playermanager/src/test/java/mc/core/h2db/TestH2Player.java diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2Player.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Player.java new file mode 100644 index 0000000..4149c22 --- /dev/null +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2Player.java @@ -0,0 +1,36 @@ +package mc.core.h2db; + +import org.junit.Test; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class TestH2Player { + @Test + public void testEquals() { + UUID uuid = UUID.randomUUID(); + + H2Player player1 = new H2Player(); + player1.setId(1); + player1.setUuid(uuid); + player1.setName("Player1"); + + H2Player player2 = new H2Player(); + player2.setId(1); + player2.setUuid(uuid); + player2.setName("Player2"); + + assertEquals(player1, player2); + + player2.setId(2); + + assertNotEquals(player1, player2); + + player2.setId(1); + player2.setUuid(UUID.randomUUID()); + + assertNotEquals(player1, player2); + } +} From df3ceba78966c6e9ebf6285660cb5855a2646973 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 14 Sep 2018 01:15:25 +0300 Subject: [PATCH 12/20] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20H2PlayerDAO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/mc/core/h2db/H2Player.java | 6 +- .../main/java/mc/core/h2db/H2PlayerDAO.java | 81 +++++++++++- .../resources/sqls/insert_player_withId.sql | 2 + .../src/test/java/mc/core/h2db/TestDAO.java | 117 ++++++++++++++++++ 4 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 h2_playermanager/src/main/resources/sqls/insert_player_withId.sql diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java index a33b94a..8880503 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2Player.java @@ -40,7 +40,11 @@ public class H2Player implements Player { @Override public void setWorld(World world) { - this.$refWorld = new WeakReference<>(world); + if (world == null) { + this.$refWorld = null; + } else { + this.$refWorld = new WeakReference<>(world); + } } @Override diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java index 8bd94b0..aa9135b 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java @@ -20,14 +20,32 @@ import java.util.UUID; @Slf4j @Component public class H2PlayerDAO { - private static String INSERT_SQL, SELECT_SQL, UPDATE_SQL, UPDATE_LOCATION_SQL, DELETE_SQL; + private static String INSERT_SQL, INSERT2_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 { + private void checkPlayer(H2Player player) throws SQLException { + if (player.getName() == null || player.getName().isEmpty()) { + throw new SQLException("Field 'name' is " + (player.getName() == null ? "null" : "empty")); + } + + if (player.getUuid() == null) { + throw new SQLException("Field 'uuid' is null"); + } + + if (player.getLocation() == null) { + throw new SQLException("Fields 'location_*' is null"); + } + + if (player.getWorld() == null) { + throw new SQLException("Field 'location_world' is null"); + } + } + + private void insertWithoutId(H2Player player) throws SQLException { KeyHolder keyHolder = new GeneratedKeyHolder(); int affectedRows = jdbcTemplate.update(psc -> { @@ -52,9 +70,41 @@ public class H2PlayerDAO { player.setId(keyHolder.getKey().intValue()); } + private void insertWithId(H2Player player) throws SQLException { + int affectedRows = jdbcTemplate.update(psc -> { + PreparedStatement stmt = psc.prepareStatement(INSERT2_SQL); + + 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()); + stmt.setInt(9, player.getId()); + + return stmt; + }); + + if (affectedRows == 0) { + throw new SQLException("Serialize player failed: no rows affected."); + } + } + + public void insert(H2Player player) throws SQLException { + checkPlayer(player); + + if (player.getId() > 0) { + insertWithId(player); + } else { + insertWithoutId(player); + } + } + 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")); + throw new SQLException("Field 'name' is " + (playerBuffer.getName() == null ? "null" : "empty")); } jdbcTemplate.query(SELECT_SQL, @@ -81,9 +131,11 @@ public class H2PlayerDAO { public void update(H2Player player) throws SQLException { if (player.getId() == 0) { - throw new SQLException("Argument 'id' is zero"); + throw new SQLException("Field 'id' is zero"); } + checkPlayer(player); + int affectedRows = jdbcTemplate.update(UPDATE_SQL, pss -> { pss.setInt(9, player.getId()); pss.setString(1, player.getUuid().toString()); @@ -103,7 +155,23 @@ public class H2PlayerDAO { public void updateLocation(H2Player player) throws SQLException { if (player.getId() == 0) { - throw new SQLException("Argument 'id' is zero"); + throw new SQLException("Field 'id' is zero"); + } + + if (player.getLocation() == null) { + throw new SQLException("Fields 'location_*' is null"); + } + + if (player.getWorld() == null) { + throw new SQLException("Field 'location_world' is null"); + } + + if (player.getLocation() == null) { + throw new SQLException("Fields 'location_*' is null"); + } + + if (player.getWorld() == null) { + throw new SQLException("Field 'location_world' is null"); } int affectedRows = jdbcTemplate.update(connection -> { @@ -127,7 +195,7 @@ public class H2PlayerDAO { public void remove(H2Player player) throws SQLException { if (player.getId() == 0) { - throw new SQLException("Argument 'id' is zero"); + throw new SQLException("Field 'id' is zero"); } int affectedRows = jdbcTemplate.update(DELETE_SQL, pss -> pss.setInt(1, player.getId())); @@ -142,6 +210,7 @@ public class H2PlayerDAO { static { try { INSERT_SQL = IOUtils.resourceToString("/sqls/insert_player.sql", StandardCharsets.UTF_8); + INSERT2_SQL = IOUtils.resourceToString("/sqls/insert_player_withId.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); diff --git a/h2_playermanager/src/main/resources/sqls/insert_player_withId.sql b/h2_playermanager/src/main/resources/sqls/insert_player_withId.sql new file mode 100644 index 0000000..c0119bc --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/insert_player_withId.sql @@ -0,0 +1,2 @@ +INSERT INTO players (uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world, id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); \ 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 index 709ed52..a51e105 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java @@ -7,6 +7,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -81,6 +82,44 @@ public class TestDAO { assertPlayer(player); } + @Test(expected = DuplicateKeyException.class) + public void testInsertDuplicateKey() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + playerDAO.insert(player); + } + + @Test(expected = SQLException.class) + public void testInsertEmptyName() throws SQLException { + player.setName(""); + playerDAO.insert(player); + } + + @Test(expected = SQLException.class) + public void testInsertNullName() throws SQLException { + player.setName(null); + playerDAO.insert(player); + } + + @Test(expected = SQLException.class) + public void testInsertNullUuid() throws SQLException { + player.setUuid(null); + playerDAO.insert(player); + } + + @Test(expected = SQLException.class) + public void testInsertNullLocation() throws SQLException { + player.setLocation(null); + playerDAO.insert(player); + } + + @Test(expected = SQLException.class) + public void testInsertNullWorld() throws SQLException { + player.setWorld(null); + playerDAO.insert(player); + } + @Test public void testGetByName() throws SQLException { playerDAO.insert(this.player); @@ -98,6 +137,21 @@ public class TestDAO { assertEquals(player, this.player); } + @Test + public void testGetByNonExistsName() throws SQLException { + playerDAO.insert(this.player); + assertNotEquals(0, player.getId()); + + H2Player player = new H2Player(); + player.setName("NON_EXISTS_NAME"); + + assertNotNull(player.getName()); + assertFalse(player.getName().isEmpty()); + + playerDAO.getByName(player); + assertEquals(0, player.getId()); + } + @Test public void testUpdate() throws SQLException { playerDAO.insert(player); @@ -109,6 +163,51 @@ public class TestDAO { assertPlayer(player); } + @Test(expected = SQLException.class) + public void testUpdateEmptyName() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setName(""); + playerDAO.update(player); + } + + @Test(expected = SQLException.class) + public void testUpdateNullName() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setName(null); + playerDAO.update(player); + } + + @Test(expected = SQLException.class) + public void testUpdateNullUuid() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setUuid(null); + playerDAO.update(player); + } + + @Test(expected = SQLException.class) + public void testUpdateNullLocation() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setLocation(null); + playerDAO.update(player); + } + + @Test(expected = SQLException.class) + public void testUpdateNullWorld() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setWorld(null); + playerDAO.update(player); + } + @Test public void testUpdateLocation() throws SQLException { playerDAO.insert(player); @@ -135,6 +234,15 @@ public class TestDAO { }); } + @Test(expected = SQLException.class) + public void testUpdateLocationNull() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setLocation(null); + playerDAO.updateLocation(player); + } + @Test public void testRemove() throws SQLException { playerDAO.insert(player); @@ -149,4 +257,13 @@ public class TestDAO { ps -> ps.setInt(1, origId), rs -> {assertEquals(0, rs.getInt(1));}); } + + @Test(expected = SQLException.class) + public void testRemoveNonExistsId() throws SQLException { + playerDAO.insert(player); + assertNotEquals(0, player.getId()); + + player.setId(999); + playerDAO.remove(player); + } } From 3e6d0687ab115d7f77b6fb16ad85f87ac6447af6 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 14 Sep 2018 01:16:55 +0300 Subject: [PATCH 13/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=D1=8B?= =?UTF-8?q?=20=D0=B2=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/sqls/create_tables.sql | 9 ++++++--- h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/h2_playermanager/src/main/resources/sqls/create_tables.sql b/h2_playermanager/src/main/resources/sqls/create_tables.sql index a7d2209..f302295 100644 --- a/h2_playermanager/src/main/resources/sqls/create_tables.sql +++ b/h2_playermanager/src/main/resources/sqls/create_tables.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS players ( - id INT AUTO_INCREMENT, - uuid VARCHAR(36) NOT NULL, + id INT AUTO_INCREMENT PRIMARY KEY NOT NULL, + uuid VARCHAR(36) NOT NULL UNIQUE, name VARCHAR(16) NOT NULL, location_x DOUBLE NOT NULL, location_y DOUBLE NOT NULL, @@ -8,4 +8,7 @@ CREATE TABLE IF NOT EXISTS players ( location_yaw FLOAT NOT NULL, location_pitch FLOAT NOT NULL, location_world varchar(64) NOT NULL -); \ No newline at end of file +); + +CREATE INDEX idx_players_uuid ON players(uuid); +CREATE INDEX idx_players_name ON players(name); \ 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 index a51e105..355cfe8 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java @@ -68,6 +68,8 @@ public class TestDAO { @Before public void init() throws IOException { + jdbcTemplate.execute("DROP INDEX IF EXISTS idx_players_uuid;"); + jdbcTemplate.execute("DROP INDEX IF EXISTS idx_players_name;"); jdbcTemplate.execute("DROP TABLE IF EXISTS players;"); jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); createPlayer(); From 5a0b29c2ba8c088c6ae131133da263508a559d37 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 00:06:28 +0300 Subject: [PATCH 14/20] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20PlayerManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mc/core/embedded/FakePlayerManager.java | 20 +++--- .../mc/core/player/InMemoryPlayerManager.java | 21 ++++--- .../java/mc/core/player/PlayerManager.java | 13 ++-- .../netty/handlers/LoginHandler.java | 62 ++++++++----------- .../netty/handlers/StatusHandler.java | 2 +- .../main/java/mc/commands/ListCommand.java | 2 +- 6 files changed, 53 insertions(+), 67 deletions(-) diff --git a/core/src/main/java/mc/core/embedded/FakePlayerManager.java b/core/src/main/java/mc/core/embedded/FakePlayerManager.java index a1932e3..5743632 100644 --- a/core/src/main/java/mc/core/embedded/FakePlayerManager.java +++ b/core/src/main/java/mc/core/embedded/FakePlayerManager.java @@ -1,7 +1,3 @@ -/* - * DmitriyMX - * 2018-06-29 - */ package mc.core.embedded; import mc.core.EntityLocation; @@ -65,13 +61,8 @@ public class FakePlayerManager implements PlayerManager { } @Override - public Optional getPlayer(String name) { - return Optional.empty(); - } - - @Override - public Optional getPlayerById(int id) { - return Optional.empty(); + public Player getPlayer(String name) { + return null; } @Override @@ -80,7 +71,7 @@ public class FakePlayerManager implements PlayerManager { } @Override - public int getCountOnlinePlayers() { + public int getCountPlayers() { return 0; } @@ -88,4 +79,9 @@ public class FakePlayerManager implements PlayerManager { public NetChannel getBroadcastChannel() { return FAKE_NET_CHANNEL; } + + @Override + public Player getOfflinePlayer(String name) { + return null; + } } diff --git a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java b/core/src/main/java/mc/core/player/InMemoryPlayerManager.java index 765201e..5bf7785 100644 --- a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java +++ b/core/src/main/java/mc/core/player/InMemoryPlayerManager.java @@ -59,17 +59,10 @@ public class InMemoryPlayerManager implements PlayerManager, Runnable { } @Override - public Optional getPlayer(final String name) { + public Player getPlayer(final String name) { return players.stream() .filter(player -> player.getName().equalsIgnoreCase(name)) - .findFirst(); - } - - @Override - public Optional getPlayerById(final int id) { - return players.stream() - .filter(player -> player.getId() == id) - .findFirst(); + .findFirst().get(); } @Override @@ -78,7 +71,7 @@ public class InMemoryPlayerManager implements PlayerManager, Runnable { } @Override - public int getCountOnlinePlayers() { + public int getCountPlayers() { return (int) players.stream().filter(Player::isOnline).count(); } @@ -87,6 +80,14 @@ public class InMemoryPlayerManager implements PlayerManager, Runnable { return new BroadcastNetChannel(players.stream().filter(Player::isOnline)); } + @Override + public Player getOfflinePlayer(String name) { + return players.stream() + .filter(player -> player.getName().equals(name)) + .filter(player -> !player.isOnline()) + .findFirst().orElse(null); + } + @Override public void run() { while (!Thread.currentThread().isInterrupted()) { diff --git a/core/src/main/java/mc/core/player/PlayerManager.java b/core/src/main/java/mc/core/player/PlayerManager.java index 1ac8543..4fe5485 100644 --- a/core/src/main/java/mc/core/player/PlayerManager.java +++ b/core/src/main/java/mc/core/player/PlayerManager.java @@ -1,7 +1,3 @@ -/* - * DmitriyMX - * 2018-04-15 - */ package mc.core.player; import mc.core.EntityLocation; @@ -9,15 +5,16 @@ import mc.core.network.NetChannel; import mc.core.world.World; import java.util.List; -import java.util.Optional; public interface PlayerManager { Player createPlayer(String name, EntityLocation location, World world); void joinServer(Player player); void leftServer(Player player); - Optional getPlayer(String name); - Optional getPlayerById(int id); + + Player getPlayer(String name); List getPlayers(); - int getCountOnlinePlayers(); + int getCountPlayers(); NetChannel getBroadcastChannel(); + + Player getOfflinePlayer(String name); } diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java index 5be9458..084d7f7 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java @@ -24,9 +24,6 @@ import mc.core.world.chunk.Chunk; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.Optional; -import java.util.UUID; - import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_PLAYER; import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE; @@ -39,8 +36,8 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand @Handler public void onLoginStart(Channel channel, LoginStartPacket packet) { - Optional optPlayer = playerManager.getPlayer(packet.getPlayerName()); - if (optPlayer.isPresent() && optPlayer.get().isOnline()) { + Player player = playerManager.getPlayer(packet.getPlayerName()); + if (player != null) { channel.writeAndFlush(new DisconnectPacket( Text.builder("Player \"") .append(Text.of(packet.getPlayerName(), TextColor.YELLOW)) @@ -48,11 +45,22 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand .build())) .addListener(ChannelFutureListener.CLOSE); } else { - Player player = playerManager.getPlayer(packet.getPlayerName()) - .orElseGet(() -> playerManager.createPlayer( - packet.getPlayerName(), - world.getSpawn(), - world)); + player = playerManager.getOfflinePlayer(packet.getPlayerName()); + + if (player == null) { + player = playerManager.createPlayer( + packet.getPlayerName(), + world.getSpawn(), + world + ); + + if (player == null) { + channel.writeAndFlush(new DisconnectPacket( + Text.of("Internal server error: can't create new player")) + ).addListener(ChannelFutureListener.CLOSE); + return; + } + } channel.writeAndFlush(new LoginSuccessPacket( player.getUuid(), @@ -62,10 +70,10 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand // Join Game JoinGamePacket pkt1 = new JoinGamePacket(); - pkt1.setEntityId(player.getId()); - pkt1.setMode(PlayerMode.CREATIVE); - pkt1.setDimension(0/*Overworld*/); - pkt1.setDifficulty(0/*Peaceful*/); + pkt1.setEntityId(player.getId()); //TODO отделить системный ID от EntityID + pkt1.setMode(PlayerMode.CREATIVE); //TODO перенести в Config + pkt1.setDimension(0/*Overworld*/); //TODO перенести в World + pkt1.setDifficulty(0/*Peaceful*/); //TODO перенести в Config pkt1.setLevelType(world.getWorldType().getName()); channel.write(pkt1); @@ -75,15 +83,17 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand channel.write(pkt2); // Player Abilities - PlayerAbilitiesPacket pkt3 = new PlayerAbilitiesPacket(); + PlayerAbilitiesPacket pkt3 = new PlayerAbilitiesPacket(); //TODO перенести в Player pkt3.setCanFly(true); pkt3.setFlying(true); pkt3.setGodMode(true); pkt3.setInstantDestroyBlocks(true); channel.write(pkt3); + channel.flush(); // First Chunk + //TODO необходимо отправлять больше начальных чанков ChunkDataPacket pkt8 = new ChunkDataPacket(); Chunk chunk = player.getWorld().getChunk(player.getLocation()); pkt8.setX(chunk.getX()); @@ -91,6 +101,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand pkt8.setChunk(chunk); pkt8.setInitChunk(true); channel.writeAndFlush(pkt8); + player.getLoadedChunks().add(CompactedCoords.compressXZ(0, 0)); // Player Position And Look @@ -102,6 +113,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand player.setChannel(new WrapperNetChannel(channel)); // Send items + //TODO обновление должно приходить всем игрокам на сервере PlayerListItemPacket pkt5 = new PlayerListItemPacket(); pkt5.setAction(PlayerListItemPacket.Action.ADD_PLAYER); PlayerListItemPacket.PlayerData playerData = new PlayerListItemPacket.PlayerData(); @@ -118,26 +130,6 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand pkt5.getListPlayers().add(playerData); channel.writeAndFlush(pkt5); - // Send header/footer list - PlayerListHeaderAndFooterPacket pkt6 = new PlayerListHeaderAndFooterPacket(); - Text text = Text.of(TextColor.GOLD, "============================="); - pkt6.setHeader(text); - pkt6.setFooter(text); - channel.writeAndFlush(pkt6); - - // Send Boss bar - BossBarPacket pkt7 = new BossBarPacket(); - BossBarPacket.BarData barData = new BossBarPacket.BarData(); - barData.setTitle(Text.of(TextColor.GREEN, TextStyle.BOLD, "FORWOLK")); - barData.setColor(BossBarPacket.Color.WHITE); - barData.setDivision(BossBarPacket.Division._12); - barData.setHealth(1.0f); - barData.setFlags(BossBarPacket.Flag.NO); - pkt7.setUuid(UUID.randomUUID()); - pkt7.setAction(BossBarPacket.Action.ADD); - pkt7.setBarData(barData); - channel.writeAndFlush(pkt7); - playerManager.joinServer(player); CS_PlayerMoveEvent event = new CS_PlayerMoveEvent(player, player.getLocation()); diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/StatusHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/StatusHandler.java index 1216cce..10c48fc 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/StatusHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/StatusHandler.java @@ -26,7 +26,7 @@ public class StatusHandler extends AbstractStateHandler implements StatusStateHa responsePacket.setMaxOnline(config.getMaxPlayers()); responsePacket.setDescription(config.getDescriptionServer()); responsePacket.setFaviconBase64(config.getFaviconBase64()); - responsePacket.setOnline(playerManager.getCountOnlinePlayers()); + responsePacket.setOnline(playerManager.getCountPlayers()); channel.writeAndFlush(responsePacket); } diff --git a/vanilla_commands/src/main/java/mc/commands/ListCommand.java b/vanilla_commands/src/main/java/mc/commands/ListCommand.java index a899c0e..82411a6 100644 --- a/vanilla_commands/src/main/java/mc/commands/ListCommand.java +++ b/vanilla_commands/src/main/java/mc/commands/ListCommand.java @@ -53,7 +53,7 @@ public class ListCommand implements CommandExecutor { playerManager.getPlayers().forEach(pl -> sj.add(pl.getName())); Text message = messageFormat.apply( - "count", playerManager.getCountOnlinePlayers(), + "count", playerManager.getCountPlayers(), "players", sj.toString()); sender.getChannel().sendChatMessage(message, MessageType.SYSTEM_MESSAGE); } From 6316e14544b2c8e5f3e2da06675151832df8a917 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 00:24:51 +0300 Subject: [PATCH 15/20] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20H2PlayerDAO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/mc/core/h2db/H2PlayerDAO.java | 73 +++++++++++++------ .../src/main/resources/sqls/create_tables.sql | 2 +- .../resources/sqls/select_player_byId.sql | 3 + .../resources/sqls/select_player_byName.sql | 2 +- .../src/test/java/mc/core/h2db/TestDAO.java | 49 ++++++++----- .../resources/sqls/drop_table_players.sql | 3 + 6 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 h2_playermanager/src/main/resources/sqls/select_player_byId.sql create mode 100644 h2_playermanager/src/test/resources/sqls/drop_table_players.sql diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java index aa9135b..279c3d6 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.UUID; @@ -20,7 +21,10 @@ import java.util.UUID; @Slf4j @Component public class H2PlayerDAO { - private static String INSERT_SQL, INSERT2_SQL, SELECT_SQL, UPDATE_SQL, UPDATE_LOCATION_SQL, DELETE_SQL; + private static String INSERT_SQL, INSERT2_SQL, + SELECT_BYNAME_SQL, SELECT_BYID_SQL, + UPDATE_SQL, UPDATE_LOCATION_SQL, + DELETE_SQL; @Autowired private JdbcTemplate jdbcTemplate; @@ -102,31 +106,57 @@ public class H2PlayerDAO { } } - public void getByName(H2Player playerBuffer) throws SQLException { + private void locationDeserialize(H2Player playerBuffer, 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 \"{}\"", playerBuffer.getName()); + playerBuffer.setLocation(world.getSpawn().clone()); + } else { + playerBuffer.setLocation(new EntityLocation( + resultSet.getDouble("location_x"), + resultSet.getDouble("location_y"), + resultSet.getDouble("location_z"), + resultSet.getFloat("location_yaw"), + resultSet.getFloat("location_pitch") + )); + playerBuffer.setWorld(world); + } + } + + public boolean getByName(H2Player playerBuffer) throws SQLException { if (playerBuffer.getName() == null || playerBuffer.getName().isEmpty()) { throw new SQLException("Field 'name' is " + (playerBuffer.getName() == null ? "null" : "empty")); } - jdbcTemplate.query(SELECT_SQL, + final boolean[] result = new boolean[]{false}; + jdbcTemplate.query(SELECT_BYNAME_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); - } + locationDeserialize(playerBuffer, rs); + result[0] = true; }); + + return result[0]; + } + + public boolean getById(H2Player playerBuffer) throws SQLException { + if (playerBuffer.getId() == 0) { + throw new SQLException("Field 'id' is zero"); + } + + final boolean[] result = new boolean[]{false}; + jdbcTemplate.query(SELECT_BYID_SQL, + ps -> ps.setInt(1, playerBuffer.getId()), + rs -> { + playerBuffer.setUuid(UUID.fromString(rs.getString("uuid"))); + playerBuffer.setName(rs.getString("name")); + locationDeserialize(playerBuffer, rs); + result[0] = true; + }); + + return result[0]; } public void update(H2Player player) throws SQLException { @@ -149,7 +179,7 @@ public class H2PlayerDAO { }); if (affectedRows == 0) { - throw new SQLException("Serialize player failed, no rows affected."); + throw new SQLException("Update player failed, no rows affected."); } } @@ -189,7 +219,7 @@ public class H2PlayerDAO { }); if (affectedRows == 0) { - throw new SQLException("Serialize player failed, no rows affected."); + throw new SQLException("Update player failed, no rows affected."); } } @@ -201,7 +231,7 @@ public class H2PlayerDAO { int affectedRows = jdbcTemplate.update(DELETE_SQL, pss -> pss.setInt(1, player.getId())); if (affectedRows == 0) { - throw new SQLException("Serialize player failed, no rows affected."); + throw new SQLException("Remove player failed, no rows affected."); } player.setId(0); @@ -211,7 +241,8 @@ public class H2PlayerDAO { try { INSERT_SQL = IOUtils.resourceToString("/sqls/insert_player.sql", StandardCharsets.UTF_8); INSERT2_SQL = IOUtils.resourceToString("/sqls/insert_player_withId.sql", StandardCharsets.UTF_8); - SELECT_SQL = IOUtils.resourceToString("/sqls/select_player_byName.sql", StandardCharsets.UTF_8); + SELECT_BYNAME_SQL = IOUtils.resourceToString("/sqls/select_player_byName.sql", StandardCharsets.UTF_8); + SELECT_BYID_SQL = IOUtils.resourceToString("/sqls/select_player_byId.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); diff --git a/h2_playermanager/src/main/resources/sqls/create_tables.sql b/h2_playermanager/src/main/resources/sqls/create_tables.sql index f302295..a53b23d 100644 --- a/h2_playermanager/src/main/resources/sqls/create_tables.sql +++ b/h2_playermanager/src/main/resources/sqls/create_tables.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS players ( location_z DOUBLE NOT NULL, location_yaw FLOAT NOT NULL, location_pitch FLOAT NOT NULL, - location_world varchar(64) NOT NULL + location_world VARCHAR(64) NOT NULL ); CREATE INDEX idx_players_uuid ON players(uuid); diff --git a/h2_playermanager/src/main/resources/sqls/select_player_byId.sql b/h2_playermanager/src/main/resources/sqls/select_player_byId.sql new file mode 100644 index 0000000..0267ce6 --- /dev/null +++ b/h2_playermanager/src/main/resources/sqls/select_player_byId.sql @@ -0,0 +1,3 @@ +SELECT uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world +FROM players WHERE id = ? +LIMIT 1; \ 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 index 1a8f47a..36a9b3d 100644 --- a/h2_playermanager/src/main/resources/sqls/select_player_byName.sql +++ b/h2_playermanager/src/main/resources/sqls/select_player_byName.sql @@ -1,3 +1,3 @@ -SELECT id, uuid, name, location_x, location_y, location_z, location_yaw, location_pitch, location_world +SELECT id, uuid, 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/test/java/mc/core/h2db/TestDAO.java b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java index 355cfe8..4c9c094 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestDAO.java @@ -67,10 +67,8 @@ public class TestDAO { } @Before - public void init() throws IOException { - jdbcTemplate.execute("DROP INDEX IF EXISTS idx_players_uuid;"); - jdbcTemplate.execute("DROP INDEX IF EXISTS idx_players_name;"); - jdbcTemplate.execute("DROP TABLE IF EXISTS players;"); + public void before() throws IOException { + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/drop_table_players.sql", StandardCharsets.UTF_8)); jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); createPlayer(); assertEquals(0, player.getId()); @@ -125,33 +123,50 @@ public class TestDAO { @Test public void testGetByName() throws SQLException { playerDAO.insert(this.player); - assertNotEquals(0, player.getId()); - H2Player player = new H2Player(); - player.setName(this.player.getName()); + H2Player queryPlayer = new H2Player(); + queryPlayer.setName(player.getName()); - assertNotNull(player.getName()); - assertFalse(player.getName().isEmpty()); - assertEquals(this.player.getName(), player.getName()); + boolean result = playerDAO.getByName(queryPlayer); - playerDAO.getByName(player); + assertTrue(result); + assertEquals(player, queryPlayer); + } - assertEquals(player, this.player); + @Test + public void testGetById() throws SQLException { + playerDAO.insert(this.player); + + H2Player queryPlayer = new H2Player(); + queryPlayer.setId(player.getId()); + + boolean result = playerDAO.getById(queryPlayer); + + assertTrue(result); + assertEquals(player, queryPlayer); } @Test public void testGetByNonExistsName() throws SQLException { playerDAO.insert(this.player); - assertNotEquals(0, player.getId()); H2Player player = new H2Player(); player.setName("NON_EXISTS_NAME"); - assertNotNull(player.getName()); - assertFalse(player.getName().isEmpty()); + boolean result = playerDAO.getByName(player); + assertFalse(result); + } - playerDAO.getByName(player); - assertEquals(0, player.getId()); + @Test + public void testGetByNonExistsId() throws SQLException { + playerDAO.insert(player); + assertEquals(1, player.getId()); + + H2Player queryPlayer = new H2Player(); + queryPlayer.setId(999); + + boolean result = playerDAO.getById(queryPlayer); + assertFalse(result); } @Test diff --git a/h2_playermanager/src/test/resources/sqls/drop_table_players.sql b/h2_playermanager/src/test/resources/sqls/drop_table_players.sql new file mode 100644 index 0000000..dd4ab91 --- /dev/null +++ b/h2_playermanager/src/test/resources/sqls/drop_table_players.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS idx_players_uuid; +DROP INDEX IF EXISTS idx_players_name; +DROP TABLE IF EXISTS players; \ No newline at end of file From 41c2e4933db04680ae530bb4f71295b79a773e8d Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 00:26:13 +0300 Subject: [PATCH 16/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20H2PlayerManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/mc/core/h2db/H2PlayerManager.java | 135 +++++++++++++ .../test/java/mc/core/h2db/SpringConfig.java | 9 + .../mc/core/h2db/TestH2PlayerManager.java | 181 ++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java create mode 100644 h2_playermanager/src/test/java/mc/core/h2db/TestH2PlayerManager.java diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java new file mode 100644 index 0000000..fdd4b11 --- /dev/null +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java @@ -0,0 +1,135 @@ +package mc.core.h2db; + +import com.google.common.collect.ImmutableList; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import mc.core.EntityLocation; +import mc.core.network.BroadcastNetChannel; +import mc.core.network.NetChannel; +import mc.core.player.Player; +import mc.core.player.PlayerManager; +import mc.core.world.World; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.slf4j.helpers.MessageFormatter.format; + +@Slf4j +@Component +public class H2PlayerManager implements PlayerManager { + @Setter + @Autowired + private H2PlayerDAO h2playerDao; + private List playerList = new ArrayList<>(); + + @Override + public Player createPlayer(String name, EntityLocation location, World world) { + //TODO в дальнейшем следует в этом методе только имплементацию Player + H2Player h2Player = new H2Player(); + h2Player.setName(name); + h2Player.setUuid(UUID.randomUUID()); + h2Player.setLocation(location.clone()); + h2Player.setLoadedChunks(new ArrayList<>()); + h2Player.setWorld(world); + + try { + h2playerDao.insert(h2Player); + } catch (SQLException e) { + log.error(format("Insert player '{}'", h2Player.getName()).getMessage(), e); + return null; + } + + return h2Player; + } + + @Override + public void joinServer(Player player) { + //TODO в дальнейшем следует именно этому методу передать функции инсерта в БД + H2Player h2Player = (H2Player) player; + playerList.add(h2Player); + h2Player.setOnline(true); + } + + @Override + public void leftServer(Player player) { + H2Player h2Player = (H2Player) player; + try { + h2playerDao.update(h2Player); + } catch (SQLException e) { + log.error(format("Update player '{}'", h2Player.getName()).getMessage(), e); + playerList.remove(h2Player); + } finally { + h2Player.setId(0); + h2Player.setOnline(false); + h2Player.setWorld(null); + h2Player.getLoadedChunks().clear(); + h2Player.setSettings(null); + } + } + + @Override + public Player getPlayer(String name) { + return playerList.stream() + .filter(player -> player.getName().equals(name)) + .filter(H2Player::isOnline) + .findFirst().orElse(null); + } + + @Override + public List getPlayers() { + return playerList.stream() + .filter(H2Player::isOnline) + .collect(ImmutableList.toImmutableList()); + } + + @Override + public int getCountPlayers() { + return (int) playerList.stream() + .filter(H2Player::isOnline) + .count(); + } + + @Override + public NetChannel getBroadcastChannel() { + return new BroadcastNetChannel( + playerList.stream() + .filter(H2Player::isOnline) + .map(player -> (Player)player) + ); + } + + @Override + public Player getOfflinePlayer(String name) { + H2Player h2Player = playerList.stream() + .filter(player -> player.getName().equals(name)) + .filter(player -> !player.isOnline()) + .findFirst().orElse(null); + + if (h2Player == null) { + h2Player = playerList.stream() + .filter(player -> !player.isOnline()) + .findAny().orElse(new H2Player()); + h2Player.setName(name); + + try { + h2playerDao.getByName(h2Player); + } catch (SQLException e) { + log.error(format("getByName player '{}'", h2Player.getName()).getMessage(), e); + return null; + } + + if (h2Player.getId() == 0) { + return null; + } else { + return h2Player; + } + } else { + return h2Player; + } + } +} diff --git a/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java index e4417e3..58eb283 100644 --- a/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java +++ b/h2_playermanager/src/test/java/mc/core/h2db/SpringConfig.java @@ -4,6 +4,7 @@ import mc.core.world.World; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -37,4 +38,12 @@ public class SpringConfig { jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } + + @Bean + @Scope(value = "prototype") + public H2PlayerManager h2PlayerManager(H2PlayerDAO h2PlayerDAO) { + H2PlayerManager playerManager = new H2PlayerManager(); + playerManager.setH2playerDao(h2PlayerDAO); + return playerManager; + } } diff --git a/h2_playermanager/src/test/java/mc/core/h2db/TestH2PlayerManager.java b/h2_playermanager/src/test/java/mc/core/h2db/TestH2PlayerManager.java new file mode 100644 index 0000000..163f72c --- /dev/null +++ b/h2_playermanager/src/test/java/mc/core/h2db/TestH2PlayerManager.java @@ -0,0 +1,181 @@ +package mc.core.h2db; + +import mc.core.EntityLocation; +import mc.core.player.Player; +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.context.ApplicationContext; +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.List; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {SpringConfig.class}) +public class TestH2PlayerManager { + @Autowired + private ApplicationContext context; + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private H2PlayerDAO h2PlayerDAO; + @Autowired + private World mockWorld; + private H2PlayerManager playerManager; + + @Before + public void before() throws IOException { + playerManager = context.getBean(H2PlayerManager.class); + + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/drop_table_players.sql", StandardCharsets.UTF_8)); + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); + } + + @Test + public void testCreatePlayer() throws SQLException { + final String playerName = "NEW_PLAYER"; + final Player newPlayer = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + + assertNotNull(newPlayer); + assertEquals(H2Player.class, newPlayer.getClass()); + assertTrue(newPlayer.getId() > 0); + + final H2Player queryPlayer = new H2Player(); + queryPlayer.setName(playerName); + h2PlayerDAO.getByName(queryPlayer); + assertTrue(queryPlayer.getId() > 0); + + assertEquals(newPlayer, queryPlayer); + assertEquals(newPlayer.getName(), queryPlayer.getName()); + assertEquals(newPlayer.getLocation(), queryPlayer.getLocation()); + assertEquals(newPlayer.getWorld(), queryPlayer.getWorld()); + } + + @Test + public void testJoinServer() { + assertEquals(0, playerManager.getCountPlayers()); + + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + playerManager.joinServer(player); + + assertEquals(1, playerManager.getCountPlayers()); + assertTrue(player.isOnline()); + } + + @Test + public void testLeftServer() throws SQLException { + assertEquals(0, playerManager.getCountPlayers()); + + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + playerManager.joinServer(player); + + assertEquals(1, playerManager.getCountPlayers()); + assertTrue(player.isOnline()); + + final int playerId = player.getId(); + + final String anotherName = "ANOTHER_NAME"; + ((H2Player)player).setName(anotherName); + + playerManager.leftServer(player); + + assertEquals(0, playerManager.getCountPlayers()); + + assertFalse(player.isOnline()); + assertEquals(0, player.getId()); + assertNull(player.getWorld()); + assertTrue(player.getLoadedChunks().isEmpty()); + assertNull(player.getSettings()); + + H2Player queryPlayer = new H2Player(); + queryPlayer.setId(playerId); + boolean result = h2PlayerDAO.getById(queryPlayer); + + assertTrue(result); + ((H2Player)player).setId(playerId); + assertEquals(player, queryPlayer); + } + + @Test + public void testGetPlayer() { + assertEquals(0, playerManager.getCountPlayers()); + + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + assertNotNull(player); + + playerManager.joinServer(player); + + assertEquals(1, playerManager.getCountPlayers()); + + Player queryPlayer = playerManager.getPlayer(playerName); + + assertEquals(player, queryPlayer); + } + + @Test + public void testGetPlayers() { + assertEquals(0, playerManager.getCountPlayers()); + + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + assertNotNull(player); + + playerManager.joinServer(player); + + assertEquals(1, playerManager.getCountPlayers()); + + List players = playerManager.getPlayers(); + assertEquals(1, players.size()); + try { + players.add(new H2Player()); + fail(); + } catch (Exception e) { + assertTrue(true); + } + + assertEquals(player, players.get(0)); + } + + @Test + public void testGetCountPlayers() { + assertEquals(0, playerManager.getCountPlayers()); + + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + assertNotNull(player); + + playerManager.joinServer(player); + + assertEquals(1, playerManager.getCountPlayers()); + + playerManager.leftServer(player); + + assertEquals(0, playerManager.getCountPlayers()); + } + + @Test + public void testGetOfflinePlayer() { + final String playerName = "NEW_PLAYER"; + final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld); + playerManager.joinServer(player); + playerManager.leftServer(player); + + assertEquals(0, playerManager.getCountPlayers()); + + Player offlinePlayer = playerManager.getOfflinePlayer(playerName); + assertEquals(player, offlinePlayer); + } +} From 384ae13ecf640c2b8686a2af5996aa8e005618f2 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 18:14:22 +0300 Subject: [PATCH 17/20] =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=20In?= =?UTF-8?q?MemoryPlayerManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mc/core/player/InMemoryPlayerManager.java | 113 ------------------ .../java/mc/core/player/SimplePlayer.java | 56 --------- 2 files changed, 169 deletions(-) delete mode 100644 core/src/main/java/mc/core/player/InMemoryPlayerManager.java delete mode 100644 core/src/main/java/mc/core/player/SimplePlayer.java diff --git a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java b/core/src/main/java/mc/core/player/InMemoryPlayerManager.java deleted file mode 100644 index 5bf7785..0000000 --- a/core/src/main/java/mc/core/player/InMemoryPlayerManager.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * DmitriyMX - * 2018-04-15 - */ -package mc.core.player; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import mc.core.Config; -import mc.core.EntityLocation; -import mc.core.network.BroadcastNetChannel; -import mc.core.network.NetChannel; -import mc.core.world.World; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.*; -import java.util.stream.Collectors; - -@Slf4j -public class InMemoryPlayerManager implements PlayerManager, Runnable { - private static final Random rand = new Random(); - private List players; - private final Object lock = new Object(); - @Setter - private int keepAliveInterval = 1; - - @Autowired - public InMemoryPlayerManager(Config config) { - final int c = config.getMaxPlayers() > 50 ? 50 : config.getMaxPlayers(); - players = Collections.synchronizedList(new ArrayList<>(c)); - (new Thread(this, "KeepAlive")).start(); - } - - @Override - public Player createPlayer(String name, EntityLocation location, World world) { - SimplePlayer player = new SimplePlayer(); - player.setId(rand.nextInt(10000)); - player.setUUID(UUID.nameUUIDFromBytes(name.getBytes())); - player.setName(name); - player.getLocation().set(location); - player.setWorld(world); - player.setSettings(new PlayerSettings()); - - synchronized (lock) { - players.add(player); - lock.notify(); - } - return player; - } - - @Override - public void joinServer(Player player) { - ((SimplePlayer) player).setOnline(true); - } - - @Override - public void leftServer(Player player) { - ((SimplePlayer) player).setOnline(false); - } - - @Override - public Player getPlayer(final String name) { - return players.stream() - .filter(player -> player.getName().equalsIgnoreCase(name)) - .findFirst().get(); - } - - @Override - public List getPlayers() { - return players.stream().filter(Player::isOnline).collect(Collectors.toList()); - } - - @Override - public int getCountPlayers() { - return (int) players.stream().filter(Player::isOnline).count(); - } - - @Override - public NetChannel getBroadcastChannel() { - return new BroadcastNetChannel(players.stream().filter(Player::isOnline)); - } - - @Override - public Player getOfflinePlayer(String name) { - return players.stream() - .filter(player -> player.getName().equals(name)) - .filter(player -> !player.isOnline()) - .findFirst().orElse(null); - } - - @Override - public void run() { - while (!Thread.currentThread().isInterrupted()) { - synchronized (lock) { - while (players.size() == 0) { - try { - lock.wait(); - } catch (InterruptedException e) { - return; - } - } - } - - getBroadcastChannel().sendKeepAlive(); - - try { - Thread.sleep(keepAliveInterval); - } catch (InterruptedException e) { - return; - } - } - } -} diff --git a/core/src/main/java/mc/core/player/SimplePlayer.java b/core/src/main/java/mc/core/player/SimplePlayer.java deleted file mode 100644 index cfb002b..0000000 --- a/core/src/main/java/mc/core/player/SimplePlayer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * DmitriyMX - * 2018-04-23 - */ -package mc.core.player; - -import lombok.Data; -import mc.core.EntityLocation; -import mc.core.exception.ResourceUnloadedException; -import mc.core.network.NetChannel; -import mc.core.world.World; - -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -@Data -public class SimplePlayer implements Player { - private int id; - private UUID uuid; - private String name; - private boolean online = false; - private NetChannel channel; - private EntityLocation location = EntityLocation.ZERO(); - private Reference $refWorld; - private boolean flying = false; - private PlayerSettings settings; - private List loadedChunks = new ArrayList<>(); - - @Override - public UUID getUuid() { - return uuid; - } - - @Override - public World getWorld() { - if ($refWorld == null) { - return null; - } else if ($refWorld.get() == null) { - throw new ResourceUnloadedException("You're trying to get unloaded world"); - } else { - return $refWorld.get(); - } - } - - @Override - public void setWorld(World world) { - this.$refWorld = new WeakReference<>(world); - } - - public void setUUID(UUID uuid) { - this.uuid = uuid; - } -} From 86f1e1c3d2a4c3759598009d076f06a64b98864e Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 18:15:11 +0300 Subject: [PATCH 18/20] =?UTF-8?q?fix:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/mc/core/h2db/H2PlayerDAO.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java index 279c3d6..7004ba8 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerDAO.java @@ -10,6 +10,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.PreparedStatement; @@ -31,6 +32,11 @@ public class H2PlayerDAO { @Autowired private World world; + @PostConstruct + public void init() throws IOException { + jdbcTemplate.execute(IOUtils.resourceToString("/sqls/create_tables.sql", StandardCharsets.UTF_8)); + } + private void checkPlayer(H2Player player) throws SQLException { if (player.getName() == null || player.getName().isEmpty()) { throw new SQLException("Field 'name' is " + (player.getName() == null ? "null" : "empty")); From e57cfb6d45827351924c876d9da35b8f547b64e7 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 18:15:44 +0300 Subject: [PATCH 19/20] =?UTF-8?q?fix:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=D0=BE?= =?UTF-8?q?=D0=B2,=20=D0=B5=D1=81=D0=BB=D0=B8=20=D0=BE=D0=BD=D0=B8=20?= =?UTF-8?q?=D0=BE=D1=82=D1=81=D1=83=D1=82=D1=81=D1=82=D0=B2=D1=83=D1=8E?= =?UTF-8?q?=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- h2_playermanager/src/main/resources/sqls/create_tables.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/h2_playermanager/src/main/resources/sqls/create_tables.sql b/h2_playermanager/src/main/resources/sqls/create_tables.sql index a53b23d..c436f80 100644 --- a/h2_playermanager/src/main/resources/sqls/create_tables.sql +++ b/h2_playermanager/src/main/resources/sqls/create_tables.sql @@ -10,5 +10,5 @@ CREATE TABLE IF NOT EXISTS players ( location_world VARCHAR(64) NOT NULL ); -CREATE INDEX idx_players_uuid ON players(uuid); -CREATE INDEX idx_players_name ON players(name); \ No newline at end of file +CREATE INDEX IF NOT EXISTS idx_players_uuid ON players(uuid); +CREATE INDEX IF NOT EXISTS idx_players_name ON players(name); \ No newline at end of file From f6eeb8b545eacec09b887a9c34b000b308312116 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 16 Sep 2018 18:16:34 +0300 Subject: [PATCH 20/20] =?UTF-8?q?fix:=20=D0=BC=D0=BD=D0=BE=D0=B6=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=BE=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BF=D0=BE=20H2PlayerManager?= =?UTF-8?q?=20(=D0=B1=D0=B5=D0=B7=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/mc/core/h2db/H2PlayerManager.java | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java index fdd4b11..636460b 100644 --- a/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java +++ b/h2_playermanager/src/main/java/mc/core/h2db/H2PlayerManager.java @@ -8,12 +8,15 @@ import mc.core.network.BroadcastNetChannel; import mc.core.network.NetChannel; import mc.core.player.Player; import mc.core.player.PlayerManager; +import mc.core.player.PlayerSettings; import mc.core.world.World; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -21,11 +24,21 @@ import static org.slf4j.helpers.MessageFormatter.format; @Slf4j @Component -public class H2PlayerManager implements PlayerManager { +public class H2PlayerManager implements PlayerManager, Runnable { @Setter @Autowired private H2PlayerDAO h2playerDao; - private List playerList = new ArrayList<>(); + private List playerList = Collections.synchronizedList(new ArrayList<>()); + private final Object lock = new Object(); + @Setter + private int keepAliveInterval = 1; + @Autowired + private World world; + + @PostConstruct + public void init() { + (new Thread(this, "KeepAlive")).start(); + } @Override public Player createPlayer(String name, EntityLocation location, World world) { @@ -36,6 +49,7 @@ public class H2PlayerManager implements PlayerManager { h2Player.setLocation(location.clone()); h2Player.setLoadedChunks(new ArrayList<>()); h2Player.setWorld(world); + h2Player.setSettings(new PlayerSettings()); try { h2playerDao.insert(h2Player); @@ -51,8 +65,11 @@ public class H2PlayerManager implements PlayerManager { public void joinServer(Player player) { //TODO в дальнейшем следует именно этому методу передать функции инсерта в БД H2Player h2Player = (H2Player) player; - playerList.add(h2Player); - h2Player.setOnline(true); + synchronized (lock) { + playerList.add(h2Player); + h2Player.setOnline(true); + lock.notify(); + } } @Override @@ -62,13 +79,14 @@ public class H2PlayerManager implements PlayerManager { h2playerDao.update(h2Player); } catch (SQLException e) { log.error(format("Update player '{}'", h2Player.getName()).getMessage(), e); - playerList.remove(h2Player); + synchronized (lock) { + playerList.remove(h2Player); + } } finally { h2Player.setId(0); h2Player.setOnline(false); h2Player.setWorld(null); h2Player.getLoadedChunks().clear(); - h2Player.setSettings(null); } } @@ -116,20 +134,46 @@ public class H2PlayerManager implements PlayerManager { .findAny().orElse(new H2Player()); h2Player.setName(name); + boolean result; try { - h2playerDao.getByName(h2Player); + result = h2playerDao.getByName(h2Player); } catch (SQLException e) { log.error(format("getByName player '{}'", h2Player.getName()).getMessage(), e); return null; } - if (h2Player.getId() == 0) { - return null; - } else { + if (result) { + h2Player.setWorld(world); return h2Player; + } else { + return null; } } else { + h2Player.setWorld(world); return h2Player; } } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + while(getCountPlayers() == 0) { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + return; + } + } + } + + getBroadcastChannel().sendKeepAlive(); + + try { + Thread.sleep(keepAliveInterval); + } catch (InterruptedException e) { + return; + } + } + } }