parseResult(ResultSet resultSet) throws SQLExcep
String name = resultSet.getString("Name");
boolean premium = resultSet.getBoolean("Premium");
+ int floodgateNum = resultSet.getInt("Floodgate");
+ FloodgateState floodgate;
+
+ // if the player wasn't migrated to the new database format
+ if (resultSet.wasNull()) {
+ floodgate = FloodgateState.NOT_MIGRATED;
+ } else {
+ floodgate = FloodgateState.fromInt(floodgateNum);
+ }
+
String lastIp = resultSet.getString("LastIp");
Instant lastLogin = resultSet.getTimestamp("LastLogin").toInstant();
- return Optional.of(new StoredProfile(userId, uuid, name, premium, lastIp, lastLogin));
+ return Optional.of(new StoredProfile(userId, uuid, name, premium, floodgate, lastIp, lastLogin));
}
return Optional.empty();
@@ -154,9 +182,10 @@ public void save(StoredProfile playerProfile) {
saveStmt.setString(1, uuid);
saveStmt.setString(2, playerProfile.getName());
saveStmt.setBoolean(3, playerProfile.isPremium());
- saveStmt.setString(4, playerProfile.getLastIp());
+ saveStmt.setInt(4, playerProfile.getFloodgate().getValue());
+ saveStmt.setString(5, playerProfile.getLastIp());
- saveStmt.setLong(5, playerProfile.getRowId());
+ saveStmt.setLong(6, playerProfile.getRowId());
saveStmt.execute();
}
} else {
@@ -165,7 +194,9 @@ public void save(StoredProfile playerProfile) {
saveStmt.setString(2, playerProfile.getName());
saveStmt.setBoolean(3, playerProfile.isPremium());
- saveStmt.setString(4, playerProfile.getLastIp());
+ saveStmt.setBoolean(3, playerProfile.isPremium());
+ saveStmt.setInt(4, playerProfile.getFloodgate().getValue());
+ saveStmt.setString(5, playerProfile.getLastIp());
saveStmt.execute();
try (ResultSet generatedKeys = saveStmt.getGeneratedKeys()) {
@@ -183,6 +214,14 @@ public void save(StoredProfile playerProfile) {
}
}
+ /**
+ * SQLite has a slightly different syntax, so this will be overridden by SQLiteStorage
+ * @return An SQL Statement to create the `premium` table
+ */
+ protected String getCreateTableStmt() {
+ return CREATE_TABLE_STMT;
+ }
+
@Override
public void close() {
dataSource.close();
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLiteStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLiteStorage.java
index b428cbc1e..195cf2bb7 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLiteStorage.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLiteStorage.java
@@ -31,15 +31,23 @@
import org.sqlite.SQLiteConfig;
import java.nio.file.Path;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SQLiteStorage extends SQLStorage {
+ protected static final String CREATE_TABLE_STMT = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
+ + "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ + "`UUID` CHAR(36), "
+ + "`Name` VARCHAR(16) NOT NULL, "
+ + "`Premium` BOOLEAN NOT NULL, "
+ + "`LastIp` VARCHAR(255) NOT NULL, "
+ + "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
+ //the premium shouldn't steal the cracked account by changing the name
+ + "UNIQUE (`Name`) "
+ + ')';
+
private static final String SQLITE_DRIVER = "org.sqlite.SQLiteDataSource";
private final Lock lock = new ReentrantLock();
@@ -103,12 +111,9 @@ public void save(StoredProfile playerProfile) {
}
@Override
- public void createTables() throws SQLException {
- try (Connection con = dataSource.getConnection();
- Statement createStmt = con.createStatement()) {
- // SQLite has a different syntax for auto increment
- createStmt.executeUpdate(CREATE_TABLE_STMT.replace("AUTO_INCREMENT", "AUTOINCREMENT"));
- }
+ protected String getCreateTableStmt() {
+ // SQLite has a different syntax for auto increment
+ return CREATE_TABLE_STMT.replace("AUTO_INCREMENT", "AUTOINCREMENT");
}
private static String replacePathVariables(Path dataFolder, String input) {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/storage/StoredProfile.java b/core/src/main/java/com/github/games647/fastlogin/core/storage/StoredProfile.java
index 0da7775cb..625d383f7 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/storage/StoredProfile.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/storage/StoredProfile.java
@@ -26,6 +26,7 @@
package com.github.games647.fastlogin.core.storage;
import com.github.games647.craftapi.model.Profile;
+import com.github.games647.fastlogin.core.shared.FloodgateState;
import java.time.Instant;
import java.util.Objects;
@@ -39,20 +40,28 @@ public class StoredProfile extends Profile {
private final ReentrantLock saveLock = new ReentrantLock();
private boolean premium;
+ private FloodgateState floodgate;
private String lastIp;
private Instant lastLogin;
- public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) {
+ public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, FloodgateState floodgate,
+ String lastIp, Instant lastLogin) {
super(uuid, playerName);
this.rowId = rowId;
this.premium = premium;
+ this.floodgate = floodgate;
this.lastIp = lastIp;
this.lastLogin = lastLogin;
}
+ public StoredProfile(UUID uuid, String playerName, boolean premium, FloodgateState isFloodgate, String lastIp) {
+ this(-1, uuid, playerName, premium, isFloodgate, lastIp, Instant.now());
+ }
+
+ @Deprecated
public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
- this(-1, uuid, playerName, premium, lastIp, Instant.now());
+ this(-1, uuid, playerName, premium, FloodgateState.FALSE, lastIp, Instant.now());
}
public ReentrantLock getSaveLock() {
@@ -96,6 +105,18 @@ public synchronized void setPremium(boolean premium) {
this.premium = premium;
}
+ public synchronized FloodgateState getFloodgate() {
+ return floodgate;
+ }
+
+ public synchronized boolean isFloodgateMigrated() {
+ return floodgate != FloodgateState.NOT_MIGRATED;
+ }
+
+ public synchronized void setFloodgate(FloodgateState floodgate) {
+ this.floodgate = floodgate;
+ }
+
public synchronized String getLastIp() {
return lastIp;
}
@@ -128,7 +149,7 @@ public synchronized boolean equals(Object o) {
}
return rowId == that.rowId && premium == that.premium
- && Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
+ && Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
}
@Override
@@ -141,6 +162,7 @@ public synchronized String toString() {
return this.getClass().getSimpleName() + '{'
+ "rowId=" + rowId
+ ", premium=" + premium
+ + ", floodgate=" + floodgate
+ ", lastIp='" + lastIp + '\''
+ ", lastLogin=" + lastLogin
+ "} " + super.toString();
From e566740ddea1b4a67372c576ee12dbad6aa65a44 Mon Sep 17 00:00:00 2001
From: Smart123s <28480228+Smart123s@users.noreply.github.com>
Date: Tue, 16 May 2023 14:24:29 +0200
Subject: [PATCH 4/6] Differentiate Floodgate players during login
---
.../core/shared/FloodgateManagement.java | 42 +++++++++++++++++--
.../fastlogin/core/shared/FloodgateState.java | 2 +-
.../fastlogin/core/shared/JoinManagement.java | 30 ++++++++++---
.../fastlogin/core/storage/SQLStorage.java | 1 -
4 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
index f0c276514..4ca3b6941 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
@@ -80,6 +80,38 @@ public void run() {
}
profile = core.getStorage().loadProfile(username);
+
+ if (profile.isSaved()) {
+ if (!profile.isFloodgateMigrated()) {
+ if (isLinked) {
+ profile.setFloodgate(FloodgateState.LINKED);
+ core.getPlugin().getLog().info(
+ "Player {} will be migrated to the v2 database schema as a linked Floodgate user",
+ username);
+ } else {
+ profile.setFloodgate(FloodgateState.TRUE);
+ core.getPlugin().getLog().info(
+ "Player {} will be migrated to the v2 database schema as a Floodgate user", username);
+ }
+ } else if (profile.getFloodgate() == FloodgateState.TRUE && isLinked) {
+ core.getPlugin().getLog()
+ .info("Player {} is already stored by FastLogin as a non-linked Bedrock Edition player",
+ username);
+ return;
+ } else if (profile.getFloodgate() == FloodgateState.FALSE && isLinked) {
+ profile.setFloodgate(FloodgateState.LINKED);
+ core.getPlugin().getLog().info(
+ "Player {} will be changed from a Java player to a linked Floodgate player",
+ username);
+ }
+ } else {
+ if (isLinked) {
+ profile.setFloodgate(FloodgateState.LINKED);
+ } else {
+ profile.setFloodgate(FloodgateState.TRUE);
+ }
+ }
+
AuthPlugin authPlugin = core.getAuthPluginHook();
try {
@@ -119,13 +151,17 @@ public void run() {
}
}
+ // defer auto registration, if it's not enabled in the config
if (!isRegistered && !isAutoAuthAllowed(autoRegisterFloodgate)) {
return;
}
- //logging in from bedrock for a second time threw an error with UUID
- if (profile == null) {
- profile = new StoredProfile(getUUID(player), username, true, getAddress(player).toString());
+ // stop the auto login procedure, if the current connection's parameters don't match the one stored in our
+ // database
+ // ex. we stored a LINKED account, but the current connection is not linked
+ if ((profile.getFloodgate() == FloodgateState.LINKED && !isLinked)
+ || (profile.getFloodgate() == FloodgateState.TRUE && isLinked)) {
+ return;
}
//start Bukkit/Bungee specific tasks
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
index 874deb9ba..caa867faf 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2015-2022 games647 and contributors
+ * Copyright (c) 2015-2023 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
index 75b9f500b..013dd3ca3 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
@@ -49,19 +49,33 @@ public JoinManagement(FastLoginCore
core, AuthPlugin
authHook, Bedro
public void onLogin(String username, S source) {
core.getPlugin().getLog().info("Handling player {}", username);
- StoredProfile profile = core.getStorage().loadProfile(username);
- if (profile == null) {
- return;
- }
//check if the player is connecting through Bedrock Edition
if (bedrockService != null && bedrockService.isBedrockConnection(username)) {
- //perform Bedrock specific checks and skip Java checks, if they are not needed
+ //perform Bedrock specific checks
if (bedrockService.performChecks(username, source)) {
+ //skip Java checks, since they are not needed
return;
}
}
+ StoredProfile profile = core.getStorage().loadProfile(username);
+
+ //can't be a premium Java player, if it's not saved in the database
+ if (profile == null) {
+ return;
+ }
+
+ if (!profile.isFloodgateMigrated()) {
+ profile.setFloodgate(FloodgateState.FALSE);
+ core.getPlugin().getLog().info(
+ "Player {} will be migrated to the v2 database schema as a JAVA user", username);
+ } else if (profile.getFloodgate() == FloodgateState.TRUE) {
+ core.getPlugin().getLog().info("Player {} is already stored by FastLogin as a Bedrock Edition player",
+ username);
+ return;
+ }
+
callFastLoginPreLoginEvent(username, source, profile);
Configuration config = core.getConfig();
@@ -139,6 +153,12 @@ private boolean checkNameChange(S source, String username, Profile profile) {
if (core.getConfig().get("nameChangeCheck", false)) {
StoredProfile storedProfile = core.getStorage().loadProfile(profile.getId());
if (storedProfile != null) {
+ if (storedProfile.getFloodgate() == FloodgateState.TRUE) {
+ core.getPlugin().getLog()
+ .info("Player {} is already stored by FastLogin as a Bedrock Edition player.", username);
+ return false;
+ }
+
//uuid exists in the database
core.getPlugin().getLog().info("GameProfile {} changed it's username", profile);
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java
index f10a07963..ade10a409 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java
@@ -26,7 +26,6 @@
package com.github.games647.fastlogin.core.storage;
import com.github.games647.craftapi.UUIDAdapter;
-import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.FloodgateState;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
From 485af5e044cac3ad99a51529376934dec7bddb1f Mon Sep 17 00:00:00 2001
From: games647
Date: Mon, 11 Dec 2023 15:04:03 +0100
Subject: [PATCH 5/6] Document states
---
.../fastlogin/core/shared/FloodgateState.java | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
index caa867faf..9e5df985a 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateState.java
@@ -26,9 +26,25 @@
package com.github.games647.fastlogin.core.shared;
public enum FloodgateState {
+
+ /**
+ * Purely Java profile
+ */
FALSE(0),
+
+ /**
+ * Purely Bedrock profile
+ */
TRUE(1),
+
+ /**
+ * Bedrock profile is bidirectional associated with the Java Mojang profile.
+ */
LINKED(2),
+
+ /**
+ * Data before floodgate database migration. Floodgate state is unknown.
+ */
NOT_MIGRATED(3);
private int value;
From 434a276d30e05e458b4a7873464e9564df37110c Mon Sep 17 00:00:00 2001
From: games647
Date: Mon, 11 Dec 2023 15:05:50 +0100
Subject: [PATCH 6/6] Invert if and document migration flow for better
readability
---
.../core/shared/FloodgateManagement.java | 24 ++++++++++---------
.../fastlogin/core/shared/JoinManagement.java | 15 ++++++------
2 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
index 4ca3b6941..9a59f550b 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FloodgateManagement.java
@@ -82,7 +82,19 @@ public void run() {
profile = core.getStorage().loadProfile(username);
if (profile.isSaved()) {
- if (!profile.isFloodgateMigrated()) {
+ if (profile.isFloodgateMigrated()) {
+ if (profile.getFloodgate() == FloodgateState.TRUE && isLinked) {
+ core.getPlugin().getLog()
+ .info("Player {} is already stored by FastLogin as a non-linked Bedrock Edition player",
+ username);
+ return;
+ } else if (profile.getFloodgate() == FloodgateState.FALSE && isLinked) {
+ profile.setFloodgate(FloodgateState.LINKED);
+ core.getPlugin().getLog().info(
+ "Player {} will be changed from a Java player to a linked Floodgate player",
+ username);
+ }
+ } else {
if (isLinked) {
profile.setFloodgate(FloodgateState.LINKED);
core.getPlugin().getLog().info(
@@ -93,16 +105,6 @@ public void run() {
core.getPlugin().getLog().info(
"Player {} will be migrated to the v2 database schema as a Floodgate user", username);
}
- } else if (profile.getFloodgate() == FloodgateState.TRUE && isLinked) {
- core.getPlugin().getLog()
- .info("Player {} is already stored by FastLogin as a non-linked Bedrock Edition player",
- username);
- return;
- } else if (profile.getFloodgate() == FloodgateState.FALSE && isLinked) {
- profile.setFloodgate(FloodgateState.LINKED);
- core.getPlugin().getLog().info(
- "Player {} will be changed from a Java player to a linked Floodgate player",
- username);
}
} else {
if (isLinked) {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
index 013dd3ca3..c5c41aac7 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java
@@ -52,9 +52,8 @@ public void onLogin(String username, S source) {
//check if the player is connecting through Bedrock Edition
if (bedrockService != null && bedrockService.isBedrockConnection(username)) {
- //perform Bedrock specific checks
+ //perform Bedrock specific checks and skip Java checks if no longer needed
if (bedrockService.performChecks(username, source)) {
- //skip Java checks, since they are not needed
return;
}
}
@@ -66,14 +65,16 @@ public void onLogin(String username, S source) {
return;
}
- if (!profile.isFloodgateMigrated()) {
+ if (profile.isFloodgateMigrated()) {
+ if (profile.getFloodgate() == FloodgateState.TRUE) {
+ // migrated and enabled floodgate player, however the above bedrocks fails, so the current connection
+ // isn't premium
+ return;
+ }
+ } else {
profile.setFloodgate(FloodgateState.FALSE);
core.getPlugin().getLog().info(
"Player {} will be migrated to the v2 database schema as a JAVA user", username);
- } else if (profile.getFloodgate() == FloodgateState.TRUE) {
- core.getPlugin().getLog().info("Player {} is already stored by FastLogin as a Bedrock Edition player",
- username);
- return;
}
callFastLoginPreLoginEvent(username, source, profile);