From e58f7b86c7e714201ca7d2f18ee591f4014645d5 Mon Sep 17 00:00:00 2001 From: applenick Date: Sat, 20 Jul 2024 16:48:25 -0700 Subject: [PATCH] =?UTF-8?q?Add=20SQLite=20support=20=F0=9F=8C=A4=EF=B8=8F?= =?UTF-8?q?=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: applenick --- .../services/SQLAssistanceService.java | 3 +- .../community/database/DatabaseConfig.java | 8 +- .../database/DatabaseConnection.java | 42 +-- .../services/SQLFriendshipService.java | 73 +++--- .../services/SQLModerationService.java | 219 ++++++++-------- .../nick/services/SQLNickService.java | 58 ++--- .../requests/services/SQLRequestService.java | 9 +- .../sessions/services/SQLSessionService.java | 63 +++-- .../users/services/AddressHistoryService.java | 243 ++++++++---------- .../users/services/SQLUserService.java | 84 +++--- .../pgm/community/utils/DatabaseUtils.java | 34 +++ src/main/resources/config.yml | 18 +- 12 files changed, 430 insertions(+), 424 deletions(-) create mode 100644 src/main/java/dev/pgm/community/utils/DatabaseUtils.java diff --git a/src/main/java/dev/pgm/community/assistance/services/SQLAssistanceService.java b/src/main/java/dev/pgm/community/assistance/services/SQLAssistanceService.java index 61d87b53..f1cc57ac 100644 --- a/src/main/java/dev/pgm/community/assistance/services/SQLAssistanceService.java +++ b/src/main/java/dev/pgm/community/assistance/services/SQLAssistanceService.java @@ -9,6 +9,7 @@ import dev.pgm.community.Community; import dev.pgm.community.assistance.Report; import dev.pgm.community.feature.SQLFeatureBase; +import dev.pgm.community.utils.DatabaseUtils; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -59,7 +60,7 @@ public CompletableFuture> queryList(String target) { String id = row.getString("id"); String sender = row.getString("sender"); String reason = row.getString("reason"); - long time = Long.parseLong(row.getString("time")); + long time = DatabaseUtils.parseLong(row, "time"); reports .getReports() .add(new Report( diff --git a/src/main/java/dev/pgm/community/database/DatabaseConfig.java b/src/main/java/dev/pgm/community/database/DatabaseConfig.java index 50ab3027..1067a773 100644 --- a/src/main/java/dev/pgm/community/database/DatabaseConfig.java +++ b/src/main/java/dev/pgm/community/database/DatabaseConfig.java @@ -11,6 +11,7 @@ public class DatabaseConfig { private String databaseName; private String timezone; private int maxConnections; + private String sqliteFileName; public DatabaseConfig(Configuration config) { reload(config); @@ -24,6 +25,7 @@ public void reload(Configuration config) { this.databaseName = config.getString("database.databaseName"); this.timezone = config.getString("database.timezone"); this.maxConnections = config.getInt("database.max-connections"); + this.sqliteFileName = config.getString("database.sqlite-file"); } public boolean isEnabled() { @@ -51,6 +53,10 @@ public String getTimezone() { } public int getMaxDatabaseConnections() { - return maxConnections; + return isEnabled() ? maxConnections : 1; + } + + public String getSQLiteFileName() { + return sqliteFileName; } } diff --git a/src/main/java/dev/pgm/community/database/DatabaseConnection.java b/src/main/java/dev/pgm/community/database/DatabaseConnection.java index 0772113e..e2c812e8 100644 --- a/src/main/java/dev/pgm/community/database/DatabaseConnection.java +++ b/src/main/java/dev/pgm/community/database/DatabaseConnection.java @@ -2,7 +2,9 @@ import co.aikar.idb.BukkitDB; import co.aikar.idb.DatabaseOptions; +import co.aikar.idb.DatabaseOptions.DatabaseOptionsBuilder; import co.aikar.idb.PooledDatabaseOptions; +import co.aikar.idb.PooledDatabaseOptions.PooledDatabaseOptionsBuilder; import com.google.common.collect.Maps; import dev.pgm.community.Community; import java.util.Map; @@ -17,25 +19,29 @@ public DatabaseConnection(Community plugin) { Map extraOptions = Maps.newHashMap(); extraOptions.put("serverTimezone", config.getTimezone()); - DatabaseOptions options = - DatabaseOptions.builder() - .poolName(plugin.getDescription().getName() + " DB") - .logger(plugin.getLogger()) - .mysql( - config.getUsername(), - config.getPassword(), - config.getDatabaseName(), - config.getHost()) - .build(); - - PooledDatabaseOptions poolOptions = - PooledDatabaseOptions.builder() - .options(options) - .maxConnections(config.getMaxDatabaseConnections()) - .dataSourceProperties(extraOptions) - .build(); + DatabaseOptionsBuilder builder = DatabaseOptions.builder() + .poolName(plugin.getDescription().getName() + " DB") + .logger(plugin.getLogger()); + + if (config.isEnabled()) { + builder.mysql( + config.getUsername(), config.getPassword(), config.getDatabaseName(), config.getHost()); + } else { + builder.sqlite(config.getSQLiteFileName()); + builder.minAsyncThreads(1); + builder.maxAsyncThreads(1); + } + + PooledDatabaseOptionsBuilder poolBuilder = PooledDatabaseOptions.builder() + .options(builder.build()) + .maxConnections(config.getMaxDatabaseConnections()); + + // Apply extra MySQL options + if (config.isEnabled()) { + poolBuilder.dataSourceProperties(extraOptions); + } // Setup the main global DB - BukkitDB.createHikariDatabase(plugin, poolOptions); + BukkitDB.createHikariDatabase(plugin, poolBuilder.build()); } } diff --git a/src/main/java/dev/pgm/community/friends/services/SQLFriendshipService.java b/src/main/java/dev/pgm/community/friends/services/SQLFriendshipService.java index 6b14882c..cacdb109 100644 --- a/src/main/java/dev/pgm/community/friends/services/SQLFriendshipService.java +++ b/src/main/java/dev/pgm/community/friends/services/SQLFriendshipService.java @@ -9,6 +9,7 @@ import dev.pgm.community.feature.SQLFeatureBase; import dev.pgm.community.friends.Friendship; import dev.pgm.community.friends.Friendship.FriendshipStatus; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -23,15 +24,13 @@ public class SQLFriendshipService extends SQLFeatureBase public SQLFriendshipService() { super(TABLE_NAME, TABLE_FIELDS); - this.friendshipCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public PlayerFriendships load(UUID key) throws Exception { - return new PlayerFriendships(key); - } - }); + this.friendshipCache = CacheBuilder.newBuilder() + .build(new CacheLoader() { + @Override + public PlayerFriendships load(UUID key) throws Exception { + return new PlayerFriendships(key); + } + }); } @Override @@ -97,35 +96,33 @@ public CompletableFuture> queryList(String target) { return CompletableFuture.completedFuture(new ArrayList<>(playerFriendships.getFriendships())); } else { return DB.getResultsAsync(SELECT_FRIENDSHIPS_QUERY, playerId.toString(), playerId.toString()) - .thenApplyAsync( - results -> { - if (results != null) { - for (DbRow row : results) { - String id = row.getString("id"); - String requester = row.getString("requester"); - String requested = row.getString("requested"); - String status = row.getString("status"); - long requestDate = Long.parseLong(row.getString("requestDate")); - long updateDate = Long.parseLong(row.getString("updateDate")); - - Instant requestInstant = Instant.ofEpochMilli(requestDate); - Instant updateInstant = Instant.ofEpochMilli(updateDate); - - playerFriendships - .getFriendships() - .add( - new Friendship( - UUID.fromString(id), - UUID.fromString(requester), - UUID.fromString(requested), - FriendshipStatus.valueOf(status.toUpperCase()), - requestInstant, - updateInstant)); - } - } - playerFriendships.setLoaded(true); - return new ArrayList<>(playerFriendships.getFriendships()); - }); + .thenApplyAsync(results -> { + if (results != null) { + for (DbRow row : results) { + String id = row.getString("id"); + String requester = row.getString("requester"); + String requested = row.getString("requested"); + String status = row.getString("status"); + long requestDate = DatabaseUtils.parseLong(row, "requestDate"); + long updateDate = DatabaseUtils.parseLong(row, "updateDate"); + + Instant requestInstant = Instant.ofEpochMilli(requestDate); + Instant updateInstant = Instant.ofEpochMilli(updateDate); + + playerFriendships + .getFriendships() + .add(new Friendship( + UUID.fromString(id), + UUID.fromString(requester), + UUID.fromString(requested), + FriendshipStatus.valueOf(status.toUpperCase()), + requestInstant, + updateInstant)); + } + } + playerFriendships.setLoaded(true); + return new ArrayList<>(playerFriendships.getFriendships()); + }); } } diff --git a/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java b/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java index 655a1d29..42d48e04 100644 --- a/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java +++ b/src/main/java/dev/pgm/community/moderation/services/SQLModerationService.java @@ -11,6 +11,7 @@ import dev.pgm.community.moderation.punishments.Punishment; import dev.pgm.community.moderation.punishments.PunishmentType; import dev.pgm.community.moderation.punishments.types.ExpirablePunishment; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -34,15 +35,13 @@ public class SQLModerationService extends SQLFeatureBase public SQLModerationService(ModerationConfig config) { super(TABLE_NAME, TABLE_FIELDS); this.config = config; - this.punishmentCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public PlayerPunishments load(UUID key) throws Exception { - return new PlayerPunishments(key); - } - }); + this.punishmentCache = CacheBuilder.newBuilder() + .build(new CacheLoader() { + @Override + public PlayerPunishments load(UUID key) throws Exception { + return new PlayerPunishments(key); + } + }); } @Override @@ -76,45 +75,43 @@ public CompletableFuture> queryList(String target) { return CompletableFuture.completedFuture(punishments.getPunishments()); } else { return DB.getResultsAsync(SELECT_PUNISHMENTS_QUERY, playerId.toString()) - .thenApplyAsync( - results -> { - if (results != null && !results.isEmpty()) { - for (DbRow row : results) { - String id = row.getString("id"); - String issuer = row.getString("issuer"); - String reason = row.getString("reason"); - String type = row.getString("type"); - long time = Long.parseLong(row.getString("time")); - long expires = Long.parseLong(row.getString("expires")); - Duration length = - Duration.between(Instant.ofEpochMilli(time), Instant.ofEpochMilli(expires)); - boolean active = row.get("active"); - long lastUpdateTime = Long.parseLong(row.getString("last_updated")); - String lastUpdateBy = row.getString("updated_by"); - String service = row.getString("service"); - - punishments - .getPunishments() - .add( - Punishment.of( - UUID.fromString(id), - playerId, - parseIssuer(issuer), - reason, - time, - length, - PunishmentType.valueOf(type.toUpperCase()), - active, - lastUpdateTime, - parseIssuer(lastUpdateBy), - service)); - } - } - - punishments.setLoaded(true); - - return punishments.getPunishments(); - }); + .thenApplyAsync(results -> { + if (results != null && !results.isEmpty()) { + for (DbRow row : results) { + String id = row.getString("id"); + String issuer = row.getString("issuer"); + String reason = row.getString("reason"); + String type = row.getString("type"); + long time = DatabaseUtils.parseLong(row, "time"); + long expires = DatabaseUtils.parseLong(row, "expires"); + Duration length = + Duration.between(Instant.ofEpochMilli(time), Instant.ofEpochMilli(expires)); + boolean active = row.get("active"); + long lastUpdateTime = DatabaseUtils.parseLong(row, "last_updated"); + String lastUpdateBy = row.getString("updated_by"); + String service = row.getString("service"); + + punishments + .getPunishments() + .add(Punishment.of( + UUID.fromString(id), + playerId, + parseIssuer(issuer), + reason, + time, + length, + PunishmentType.valueOf(type.toUpperCase()), + active, + lastUpdateTime, + parseIssuer(lastUpdateBy), + service)); + } + } + + punishments.setLoaded(true); + + return punishments.getPunishments(); + }); } } @@ -192,84 +189,76 @@ public CompletableFuture unmute(UUID id, @Nullable UUID issuer) { } public CompletableFuture isBanned(String id) { - return queryList(id) - .thenApplyAsync( - punishments -> { - boolean banned = false; - for (Punishment p : punishments) { - if (p.getType().isLoginPrevented() && p.isActive()) { - banned = true; - break; - } - } - return banned; - }); + return queryList(id).thenApplyAsync(punishments -> { + boolean banned = false; + for (Punishment p : punishments) { + if (p.getType().isLoginPrevented() && p.isActive()) { + banned = true; + break; + } + } + return banned; + }); } public CompletableFuture> isMuted(UUID target) { - return queryList(target.toString()) - .thenApplyAsync( - punishments -> { - if (punishments.isEmpty()) return Optional.empty(); - return punishments.stream() - .filter(p -> p.getType() == PunishmentType.MUTE && p.isActive()) - .findFirst(); - }); + return queryList(target.toString()).thenApplyAsync(punishments -> { + if (punishments.isEmpty()) return Optional.empty(); + return punishments.stream() + .filter(p -> p.getType() == PunishmentType.MUTE && p.isActive()) + .findFirst(); + }); } public CompletableFuture> getActiveBan(String id) { - return queryList(id) - .thenApplyAsync( - punishments -> { - for (Punishment p : punishments) { - if (p.getType().isLoginPrevented() && p.isActive()) { - return Optional.of(p); - } - } - return Optional.empty(); - }); + return queryList(id).thenApplyAsync(punishments -> { + for (Punishment p : punishments) { + if (p.getType().isLoginPrevented() && p.isActive()) { + return Optional.of(p); + } + } + return Optional.empty(); + }); } public CompletableFuture> getRecentPunishments(Duration period) { return DB.getResultsAsync( SELECT_RECENT_QUERY, Instant.now().toEpochMilli() - period.toMillis(), RECENT_LIMIT) - .thenApplyAsync( - results -> { - List punishments = Lists.newArrayList(); - - if (results != null && !results.isEmpty()) { - for (DbRow row : results) { - String id = row.getString("id"); - String target = row.getString("punished"); - String issuer = row.getString("issuer"); - String reason = row.getString("reason"); - String type = row.getString("type"); - long time = Long.parseLong(row.getString("time")); - long expires = Long.parseLong(row.getString("expires")); - Duration length = - Duration.between(Instant.ofEpochMilli(time), Instant.ofEpochMilli(expires)); - boolean active = row.get("active"); - long lastUpdateTime = Long.parseLong(row.getString("last_updated")); - String lastUpdateBy = row.getString("updated_by"); - String service = row.getString("service"); - punishments.add( - Punishment.of( - UUID.fromString(id), - UUID.fromString(target), - parseIssuer(issuer), - reason, - time, - length, - PunishmentType.valueOf(type.toUpperCase()), - active, - lastUpdateTime, - parseIssuer(lastUpdateBy), - service)); - } - } - - return punishments; - }); + .thenApplyAsync(results -> { + List punishments = Lists.newArrayList(); + + if (results != null && !results.isEmpty()) { + for (DbRow row : results) { + String id = row.getString("id"); + String target = row.getString("punished"); + String issuer = row.getString("issuer"); + String reason = row.getString("reason"); + String type = row.getString("type"); + long time = Long.parseLong(row.getString("time")); + long expires = Long.parseLong(row.getString("expires")); + Duration length = + Duration.between(Instant.ofEpochMilli(time), Instant.ofEpochMilli(expires)); + boolean active = row.get("active"); + long lastUpdateTime = Long.parseLong(row.getString("last_updated")); + String lastUpdateBy = row.getString("updated_by"); + String service = row.getString("service"); + punishments.add(Punishment.of( + UUID.fromString(id), + UUID.fromString(target), + parseIssuer(issuer), + reason, + time, + length, + PunishmentType.valueOf(type.toUpperCase()), + active, + lastUpdateTime, + parseIssuer(lastUpdateBy), + service)); + } + } + + return punishments; + }); } public void invalidate(UUID playerId) { diff --git a/src/main/java/dev/pgm/community/nick/services/SQLNickService.java b/src/main/java/dev/pgm/community/nick/services/SQLNickService.java index ae54ef12..e76a5c32 100644 --- a/src/main/java/dev/pgm/community/nick/services/SQLNickService.java +++ b/src/main/java/dev/pgm/community/nick/services/SQLNickService.java @@ -8,6 +8,7 @@ import dev.pgm.community.nick.Nick; import dev.pgm.community.nick.NickConfig; import dev.pgm.community.nick.NickImpl; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Instant; import java.util.List; import java.util.UUID; @@ -20,15 +21,12 @@ public class SQLNickService extends SQLFeatureBase implements Nick public SQLNickService(NickConfig config) { super(TABLE_NAME, TABLE_FIELDS); - this.nickCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public NickInfo load(UUID key) throws Exception { - return new NickInfo(key); - } - }); + this.nickCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public NickInfo load(UUID key) throws Exception { + return new NickInfo(key); + } + }); } @Override @@ -60,17 +58,17 @@ public CompletableFuture query(String target) { return CompletableFuture.completedFuture(nick.getNick()); } else { return DB.getFirstRowAsync(SELECT_NICKNAME_BY_ID_QUERY, playerId.toString()) - .thenApplyAsync( - row -> { - if (row != null) { - String nickName = row.getString("nickname"); - Instant date = Instant.ofEpochMilli(Long.parseLong(row.getString("date"))); - boolean enabled = row.get("enabled"); - nick.setNick(new NickImpl(playerId, nickName, date, enabled)); - } - nick.setLoaded(true); - return nick.getNick(); - }); + .thenApplyAsync(row -> { + if (row != null) { + String nickName = row.getString("nickname"); + long time = DatabaseUtils.parseLong(row, "date"); + Instant date = Instant.ofEpochMilli(time); + boolean enabled = DatabaseUtils.parseBoolean(row, "enabled"); + nick.setNick(new NickImpl(playerId, nickName, date, enabled)); + } + nick.setLoaded(true); + return nick.getNick(); + }); } } @@ -89,17 +87,15 @@ public CompletableFuture isNameAvailable(String name) { } public CompletableFuture queryByName(String name) { - return DB.getFirstRowAsync(SELECT_NICKNAME_BY_NAME_QUERY, name) - .thenApplyAsync( - row -> { - if (row == null) return null; - - UUID playerId = UUID.fromString(row.getString("playerId")); - String nickName = row.getString("nickname"); - Instant date = Instant.ofEpochMilli(Long.parseLong(row.getString("date"))); - boolean enabled = row.get("enabled"); - return new NickImpl(playerId, nickName, date, enabled); - }); + return DB.getFirstRowAsync(SELECT_NICKNAME_BY_NAME_QUERY, name).thenApplyAsync(row -> { + if (row == null) return null; + + UUID playerId = UUID.fromString(row.getString("playerId")); + String nickName = row.getString("nickname"); + Instant date = Instant.ofEpochMilli(Long.parseLong(row.getString("date"))); + boolean enabled = row.get("enabled"); + return new NickImpl(playerId, nickName, date, enabled); + }); } private class NickInfo { diff --git a/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java b/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java index 853b7556..097c1f69 100644 --- a/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java +++ b/src/main/java/dev/pgm/community/requests/services/SQLRequestService.java @@ -7,6 +7,7 @@ import com.google.common.collect.Lists; import dev.pgm.community.feature.SQLFeatureBase; import dev.pgm.community.requests.RequestProfile; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Instant; import java.util.List; import java.util.UUID; @@ -96,14 +97,14 @@ public CompletableFuture query(String target) { .thenApplyAsync(result -> { if (result != null) { final UUID id = UUID.fromString(result.getString("id")); - final long lastRequest = Long.parseLong(result.getString("last_request_time")); + final long lastRequest = DatabaseUtils.parseLong(result, "last_request_time"); final String lastRequestMap = result.getString("last_request_map"); - final long lastSponsor = Long.parseLong(result.getString("last_sponsor_time")); + final long lastSponsor = DatabaseUtils.parseLong(result, "last_sponsor_time"); final String lastSponsorMap = result.getString("last_sponsor_map"); final int tokens = result.getInt("tokens"); - final long lastToken = Long.parseLong(result.getString("last_token_refresh")); + final long lastToken = DatabaseUtils.parseLong(result, "last_token_refresh"); final int superVotes = result.getInt("super_votes"); - final long lastSuperVote = Long.parseLong(result.getString("last_super_vote")); + final long lastSuperVote = DatabaseUtils.parseLong(result, "last_super_vote"); final Instant lastRequestTime = lastRequest == -1 ? null : Instant.ofEpochMilli(lastRequest); diff --git a/src/main/java/dev/pgm/community/sessions/services/SQLSessionService.java b/src/main/java/dev/pgm/community/sessions/services/SQLSessionService.java index d321172e..860bb78c 100644 --- a/src/main/java/dev/pgm/community/sessions/services/SQLSessionService.java +++ b/src/main/java/dev/pgm/community/sessions/services/SQLSessionService.java @@ -8,6 +8,7 @@ import dev.pgm.community.feature.SQLFeatureBase; import dev.pgm.community.sessions.Session; import dev.pgm.community.sessions.SessionQuery; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Instant; import java.util.List; import java.util.UUID; @@ -21,15 +22,13 @@ public class SQLSessionService extends SQLFeatureBase public SQLSessionService() { super(TABLE_NAME, TABLE_FIELDS); - this.sessionCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public SessionData load(@Nonnull SessionQuery key) { - return new SessionData(key.getPlayerId(), key.ignoreDisguised()); - } - }); + this.sessionCache = CacheBuilder.newBuilder() + .build(new CacheLoader() { + @Override + public SessionData load(@Nonnull SessionQuery key) { + return new SessionData(key.getPlayerId(), key.ignoreDisguised()); + } + }); } @Override @@ -79,30 +78,28 @@ public CompletableFuture query(SessionQuery target) { return DB.getFirstRowAsync( target.ignoreDisguised() ? SELECT_DISGUISED_SESSION_QUERY : SELECT_SESSION_QUERY, target.getPlayerId().toString()) - .thenApplyAsync( - result -> { - if (result != null) { - String id = result.getString("id"); - - String player = result.getString("player"); - boolean disguised = result.get("disguised"); - - String server = result.getString("server"); - - Object startTime = result.get("start_time"); - Object endTime = result.get("end_time"); - - data.setSession( - new Session( - UUID.fromString(id), - UUID.fromString(player), - disguised, - server, - Instant.ofEpochMilli((Long) startTime), - endTime == null ? null : Instant.ofEpochMilli((Long) endTime))); - } - return data.getSession(); - }); + .thenApplyAsync(result -> { + if (result != null) { + String id = result.getString("id"); + + String player = result.getString("player"); + boolean disguised = DatabaseUtils.parseBoolean(result, "disguised"); + + String server = result.getString("server"); + + Object startTime = result.get("start_time"); + Object endTime = result.get("end_time"); + + data.setSession(new Session( + UUID.fromString(id), + UUID.fromString(player), + disguised, + server, + Instant.ofEpochMilli((Long) startTime), + endTime == null ? null : Instant.ofEpochMilli((Long) endTime))); + } + return data.getSession(); + }); } } diff --git a/src/main/java/dev/pgm/community/users/services/AddressHistoryService.java b/src/main/java/dev/pgm/community/users/services/AddressHistoryService.java index 08b718a9..404442f9 100644 --- a/src/main/java/dev/pgm/community/users/services/AddressHistoryService.java +++ b/src/main/java/dev/pgm/community/users/services/AddressHistoryService.java @@ -7,6 +7,7 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.Sets; import dev.pgm.community.database.Query; +import dev.pgm.community.utils.DatabaseUtils; import java.time.Instant; import java.util.List; import java.util.Set; @@ -25,42 +26,30 @@ public class AddressHistoryService implements AddressQuery { private LoadingCache altsCache; public AddressHistoryService() { - this.historyCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public AddressHistory load(UUID key) throws Exception { - return new AddressHistory(key); - } - }); - this.latestCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public LatestAddressInfo load(UUID key) throws Exception { - return new LatestAddressInfo(key); - } - }); - this.resolvedIPCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public ResolvedIP load(String key) throws Exception { - return new ResolvedIP(key); - } - }); - this.altsCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public IpAlts load(String key) throws Exception { - return new IpAlts(key); - } - }); + this.historyCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public AddressHistory load(UUID key) throws Exception { + return new AddressHistory(key); + } + }); + this.latestCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public LatestAddressInfo load(UUID key) throws Exception { + return new LatestAddressInfo(key); + } + }); + this.resolvedIPCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public ResolvedIP load(String key) throws Exception { + return new ResolvedIP(key); + } + }); + this.altsCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public IpAlts load(String key) throws Exception { + return new IpAlts(key); + } + }); DB.executeUpdateAsync(Query.createTable(IP_TABLE_NAME, IP_TABLE_FIELDS)); DB.executeUpdateAsync(Query.createTable(IP_USER_TABLE_NAME, IP_USER_TABLE_FIELDS)); @@ -73,33 +62,31 @@ public void trackIp(UUID id, String address) { DB.executeUpdateAsync( INSERT_LATEST_IP_QUERY, id.toString(), address, Instant.now().toEpochMilli()); - DB.getFirstRowAsync(SELECT_IP_QUERY, address) - .thenAcceptAsync( - result -> { - final UUID randomId = UUID.randomUUID(); - String ipId = randomId.toString(); - - if (result == null) { - // Track a new ip-id - DB.executeUpdateAsync(INSERT_IP_QUERY, address, ipId); - } else { - ipId = result.getString(IP_ID_FIELD); - } - - // Update alts for an already cached IP - IpAlts alts = altsCache.getUnchecked(ipId); - if (alts.isLoaded()) { - alts.getPlayerIds().add(id.toString()); - } - - Set known = getKnownIps(id).join(); - if (known == null - || known.isEmpty() - || !known.stream().anyMatch(ip -> ip.equalsIgnoreCase(address))) { - // Add user to known ip-id list - DB.executeUpdateAsync(INSERT_IP_USER_QUERY, id.toString(), ipId); - } - }); + DB.getFirstRowAsync(SELECT_IP_QUERY, address).thenAcceptAsync(result -> { + final UUID randomId = UUID.randomUUID(); + String ipId = randomId.toString(); + + if (result == null) { + // Track a new ip-id + DB.executeUpdateAsync(INSERT_IP_QUERY, address, ipId); + } else { + ipId = result.getString(IP_ID_FIELD); + } + + // Update alts for an already cached IP + IpAlts alts = altsCache.getUnchecked(ipId); + if (alts.isLoaded()) { + alts.getPlayerIds().add(id.toString()); + } + + Set known = getKnownIps(id).join(); + if (known == null + || known.isEmpty() + || !known.stream().anyMatch(ip -> ip.equalsIgnoreCase(address))) { + // Add user to known ip-id list + DB.executeUpdateAsync(INSERT_IP_USER_QUERY, id.toString(), ipId); + } + }); } public CompletableFuture getLatestAddressInfo(UUID playerId) { @@ -108,17 +95,17 @@ public CompletableFuture getLatestAddressInfo(UUID playerId) return CompletableFuture.completedFuture(info); } else { return DB.getFirstRowAsync(SELECT_LATEST_IP_QUERY, playerId.toString()) - .thenApplyAsync( - result -> { - if (result != null) { - String address = result.getString(IP_ADDRESS_FIELD); - Instant date = Instant.ofEpochMilli(Long.parseLong(result.getString(DATE_FIELD))); - info.setAddress(address); - info.setDate(date); - } - info.setLoaded(true); - return info; - }); + .thenApplyAsync(result -> { + if (result != null) { + String address = result.getString(IP_ADDRESS_FIELD); + long time = DatabaseUtils.parseLong(result, DATE_FIELD); + Instant date = Instant.ofEpochMilli(time); + info.setAddress(address); + info.setDate(date); + } + info.setLoaded(true); + return info; + }); } } @@ -129,70 +116,64 @@ public CompletableFuture getIpIds(UUID playerId) { return CompletableFuture.completedFuture(history); } else { return DB.getResultsAsync(SELECT_IP_HISTORY_QUERY, playerId.toString()) - .thenApplyAsync( - results -> { - if (results != null && !results.isEmpty()) { - for (DbRow row : results) { - String ipId = row.getString("ip_id"); - history.addAddress(ipId); - } - } - history.setLoaded(true); - return history; - }); + .thenApplyAsync(results -> { + if (results != null && !results.isEmpty()) { + for (DbRow row : results) { + String ipId = row.getString("ip_id"); + history.addAddress(ipId); + } + } + history.setLoaded(true); + return history; + }); } } public CompletableFuture> getKnownIps(UUID playerId) { - return getIpIds(playerId) - .thenApplyAsync( - addressHistory -> { - if (addressHistory.getAddresses().isEmpty()) { - return Sets.newHashSet(); - } - Set ips = Sets.newHashSet(); - for (String ipId : addressHistory.getAddresses()) { - ResolvedIP ip = resolvedIPCache.getUnchecked(ipId); - if (!ip.isLoaded()) { - DbRow row = DB.getFirstRowAsync(SELECT_IP_ID_QUERY, ipId).join(); - if (row != null) { - String resolved = row.getString(IP_ADDRESS_FIELD); - ip.setAddress(resolved); - } - } - ips.add(ip.getAddress()); - } - return ips; - }); + return getIpIds(playerId).thenApplyAsync(addressHistory -> { + if (addressHistory.getAddresses().isEmpty()) { + return Sets.newHashSet(); + } + Set ips = Sets.newHashSet(); + for (String ipId : addressHistory.getAddresses()) { + ResolvedIP ip = resolvedIPCache.getUnchecked(ipId); + if (!ip.isLoaded()) { + DbRow row = DB.getFirstRowAsync(SELECT_IP_ID_QUERY, ipId).join(); + if (row != null) { + String resolved = row.getString(IP_ADDRESS_FIELD); + ip.setAddress(resolved); + } + } + ips.add(ip.getAddress()); + } + return ips; + }); } public CompletableFuture> getAlternateAccounts(UUID playerId) { - return getIpIds(playerId) - .thenApplyAsync( - history -> { - Set ids = Sets.newHashSet(); - - for (String address : history.getAddresses()) { - IpAlts addressAlts = altsCache.getUnchecked(address); - - if (!addressAlts.isLoaded()) { - List rows = DB.getResultsAsync(SELECT_ALTS_QUERY, address).join(); - if (rows != null && !rows.isEmpty()) { - for (DbRow row : rows) { - String userId = row.getString(USER_ID_FIELD); - addressAlts.getPlayerIds().add(userId); - } - } - addressAlts.setLoaded(true); - } - ids.addAll( - addressAlts.getPlayerIds().stream() - .map(UUID::fromString) - .filter(id -> !playerId.equals(id)) - .collect(Collectors.toSet())); - } - return ids; - }); + return getIpIds(playerId).thenApplyAsync(history -> { + Set ids = Sets.newHashSet(); + + for (String address : history.getAddresses()) { + IpAlts addressAlts = altsCache.getUnchecked(address); + + if (!addressAlts.isLoaded()) { + List rows = DB.getResultsAsync(SELECT_ALTS_QUERY, address).join(); + if (rows != null && !rows.isEmpty()) { + for (DbRow row : rows) { + String userId = row.getString(USER_ID_FIELD); + addressAlts.getPlayerIds().add(userId); + } + } + addressAlts.setLoaded(true); + } + ids.addAll(addressAlts.getPlayerIds().stream() + .map(UUID::fromString) + .filter(id -> !playerId.equals(id)) + .collect(Collectors.toSet())); + } + return ids; + }); } private class IpAlts { diff --git a/src/main/java/dev/pgm/community/users/services/SQLUserService.java b/src/main/java/dev/pgm/community/users/services/SQLUserService.java index 612d2c5b..7a2ef464 100644 --- a/src/main/java/dev/pgm/community/users/services/SQLUserService.java +++ b/src/main/java/dev/pgm/community/users/services/SQLUserService.java @@ -7,6 +7,7 @@ import dev.pgm.community.feature.SQLFeatureBase; import dev.pgm.community.users.UserProfile; import dev.pgm.community.users.UserProfileImpl; +import dev.pgm.community.utils.DatabaseUtils; import dev.pgm.community.utils.NameUtils; import java.time.Instant; import java.util.List; @@ -21,15 +22,12 @@ public class SQLUserService extends SQLFeatureBase implemen public SQLUserService() { super(TABLE_NAME, TABLE_FIELDS); - this.profileCache = - CacheBuilder.newBuilder() - .build( - new CacheLoader() { - @Override - public UserData load(UUID key) throws Exception { - return new UserData(key); - } - }); + this.profileCache = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public UserData load(UUID key) throws Exception { + return new UserData(key); + } + }); } @Override @@ -54,11 +52,10 @@ public CompletableFuture query(String target) { // If Username, search through looking for existing usernames in profiles if (NameUtils.isMinecraftName(target)) { - Optional uQuery = - profileCache.asMap().values().stream() - .filter(u -> u.getUsername() != null) - .filter(u -> u.getUsername().equalsIgnoreCase(target)) - .findAny(); + Optional uQuery = profileCache.asMap().values().stream() + .filter(u -> u.getUsername() != null) + .filter(u -> u.getUsername().equalsIgnoreCase(target)) + .findAny(); // If profile is cached with matching username if (uQuery.isPresent()) { data = uQuery.get(); @@ -72,22 +69,21 @@ public CompletableFuture query(String target) { } return DB.getFirstRowAsync(data == null ? USERNAME_QUERY : PLAYERID_QUERY, target) - .thenApplyAsync( - result -> { - if (result != null) { - final UUID id = UUID.fromString(result.getString("id")); - final String username = result.getString("name"); - final long firstJoin = Long.parseLong(result.getString("first_join")); - final int joinCount = result.getInt("join_count"); - - UserData loadedData = new UserData(id); - loadedData.setProfile( - new UserProfileImpl(id, username, Instant.ofEpochMilli(firstJoin), joinCount)); - profileCache.put(id, loadedData); - return loadedData.getProfile(); - } - return null; - }); + .thenApplyAsync(result -> { + if (result != null) { + final UUID id = UUID.fromString(result.getString("id")); + final String username = result.getString("name"); + final long firstJoin = DatabaseUtils.parseLong(result, "first_join"); + final int joinCount = result.getInt("join_count"); + + UserData loadedData = new UserData(id); + loadedData.setProfile( + new UserProfileImpl(id, username, Instant.ofEpochMilli(firstJoin), joinCount)); + profileCache.put(id, loadedData); + return loadedData.getProfile(); + } + return null; + }); } private void update(UserProfile profile) { @@ -100,21 +96,19 @@ private void update(UserProfile profile) { // Increase join count, set last login, check for username change public CompletableFuture login(UUID id, String username, String address) { - return query(id.toString()) - .thenApplyAsync( - profile -> { - if (profile == null) { - // No profile? Save a new one - profile = new UserProfileImpl(id, username); - save(profile); - } else { - // Existing profile - Update name, login, joins - profile.setUsername(username); - profile.incJoinCount(); - update(profile); - } - return profile; - }); + return query(id.toString()).thenApplyAsync(profile -> { + if (profile == null) { + // No profile? Save a new one + profile = new UserProfileImpl(id, username); + save(profile); + } else { + // Existing profile - Update name, login, joins + profile.setUsername(username); + profile.incJoinCount(); + update(profile); + } + return profile; + }); } private class UserData { diff --git a/src/main/java/dev/pgm/community/utils/DatabaseUtils.java b/src/main/java/dev/pgm/community/utils/DatabaseUtils.java new file mode 100644 index 00000000..c32db574 --- /dev/null +++ b/src/main/java/dev/pgm/community/utils/DatabaseUtils.java @@ -0,0 +1,34 @@ +package dev.pgm.community.utils; + +import co.aikar.idb.DbRow; + +public class DatabaseUtils { + + public static boolean parseBoolean(DbRow row, String fieldName) throws ClassCastException { + Object obj = row.get(fieldName); + if (obj instanceof Integer) { + int activeInt = (Integer) obj; + return (activeInt != 0); + } else if (obj instanceof Boolean) { + return (Boolean) obj; + } else { + throw new ClassCastException( + "Unexpected type for '" + fieldName + "': " + obj.getClass().getName()); + } + } + + public static long parseLong(DbRow row, String fieldName) throws ClassCastException { + Object obj = row.get(fieldName); + if (obj instanceof String) { + String rawLong = (String) obj; + return Long.parseLong(rawLong); + } else if (obj instanceof Long) { + return (Long) obj; + } else if (obj instanceof Integer) { + return (Integer) obj; + } else { + throw new ClassCastException( + "Unexpected type for '" + fieldName + "': " + obj.getClass().getName()); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 4ca5f9a3..0bcbf442 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,6 +1,6 @@ # # Community -# A plugin for managing a Minecraft community +# A plugin for managing a Minecraft PGM community # general: # Display name for the network used in various locations. @@ -346,12 +346,16 @@ network: # message: false -# Database connection info database: - enabled: true # True for mysql, false for sqlite - username: "username" + enabled: false # True for MySQL, false for SQLite + + # SQLite settings (used if enabled is false) + sqlite-file: "community.sql" + + # MySQL settings (used if enabled is true) + username: "username" password: "password" databaseName: "minecraft" - host: "localhost:3306" - timezone: "America/Los_Angeles" - max-connections: 2 + host: "localhost:3306" # host and port + timezone: "America/Los_Angeles" # Database timezone + max-connections: 2 # Maximum simultaneous connections (does not impact SQLite)