2626package com .github .games647 .fastlogin .core .storage ;
2727
2828import com .github .games647 .craftapi .UUIDAdapter ;
29+ import com .github .games647 .fastlogin .core .shared .FloodgateState ;
2930import com .zaxxer .hikari .HikariConfig ;
3031import com .zaxxer .hikari .HikariDataSource ;
3132import org .slf4j .Logger ;
3233
3334import java .sql .Connection ;
35+ import java .sql .DatabaseMetaData ;
3436import java .sql .PreparedStatement ;
3537import java .sql .ResultSet ;
3638import java .sql .SQLException ;
@@ -56,13 +58,19 @@ public abstract class SQLStorage implements AuthStorage {
5658 + "UNIQUE (`Name`) "
5759 + ')' ;
5860
59- protected static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `Name`=? LIMIT 1" ;
60- protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `UUID`=? LIMIT 1" ;
61+ protected static final String ADD_FLOODGATE_COLUMN_STMT = "ALTER TABLE `" + PREMIUM_TABLE
62+ + "` ADD COLUMN `Floodgate` INTEGER(3)" ;
63+
64+ protected static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE
65+ + "` WHERE `Name`=? LIMIT 1" ;
66+ protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE
67+ + "` WHERE `UUID`=? LIMIT 1" ;
6168 protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
62- + "` (`UUID`, `Name`, `Premium`, `LastIp`) " + "VALUES (?, ?, ?, ?) " ;
69+ + "` (`UUID`, `Name`, `Premium`, `Floodgate`, ` LastIp`) " + "VALUES (?, ?, ?, ?, ?) " ;
6370 // limit not necessary here, because it's unique
6471 protected static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
65- + "` SET `UUID`=?, `Name`=?, `Premium`=?, `LastIp`=?, `LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?" ;
72+ + "` SET `UUID`=?, `Name`=?, `Premium`=?, `Floodgate`=?, `LastIp`=?, "
73+ + "`LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?" ;
6674
6775 protected final Logger log ;
6876 protected final HikariDataSource dataSource ;
@@ -81,11 +89,23 @@ public void createTables() throws SQLException {
8189 // choose surrogate PK(ID), because UUID can be null for offline players
8290 // if UUID is always Premium UUID we would have to update offline player entries on insert
8391 // name cannot be PK, because it can be changed for premium players
84-
8592 //todo: add unique uuid index usage
8693 try (Connection con = dataSource .getConnection ();
87- Statement createStmt = con .createStatement ()) {
88- createStmt .executeUpdate (CREATE_TABLE_STMT );
94+ Statement stmt = con .createStatement ()) {
95+ stmt .executeUpdate (getCreateTableStmt ());
96+
97+ // add Floodgate column
98+ DatabaseMetaData md = con .getMetaData ();
99+ if (isColumnMissing (md , "Floodgate" )) {
100+ stmt .executeUpdate (ADD_FLOODGATE_COLUMN_STMT );
101+ }
102+
103+ }
104+ }
105+
106+ private boolean isColumnMissing (DatabaseMetaData metaData , String columnName ) throws SQLException {
107+ try (ResultSet rs = metaData .getColumns (null , null , PREMIUM_TABLE , columnName )) {
108+ return !rs .next ();
89109 }
90110 }
91111
@@ -97,7 +117,8 @@ public StoredProfile loadProfile(String name) {
97117 loadStmt .setString (1 , name );
98118
99119 try (ResultSet resultSet = loadStmt .executeQuery ()) {
100- return parseResult (resultSet ).orElseGet (() -> new StoredProfile (null , name , false , "" ));
120+ return parseResult (resultSet ).orElseGet (() -> new StoredProfile (null , name , false ,
121+ FloodgateState .FALSE , "" ));
101122 }
102123 } catch (SQLException sqlEx ) {
103124 log .error ("Failed to query profile: {}" , name , sqlEx );
@@ -124,15 +145,25 @@ public StoredProfile loadProfile(UUID uuid) {
124145
125146 private Optional <StoredProfile > parseResult (ResultSet resultSet ) throws SQLException {
126147 if (resultSet .next ()) {
127- long userId = resultSet .getInt (1 );
148+ long userId = resultSet .getInt ("UserID" );
149+
150+ UUID uuid = Optional .ofNullable (resultSet .getString ("UUID" )).map (UUIDAdapter ::parseId ).orElse (null );
128151
129- UUID uuid = Optional .ofNullable (resultSet .getString (2 )).map (UUIDAdapter ::parseId ).orElse (null );
152+ String name = resultSet .getString ("Name" );
153+ boolean premium = resultSet .getBoolean ("Premium" );
154+ int floodgateNum = resultSet .getInt ("Floodgate" );
155+ FloodgateState floodgate ;
156+
157+ // if the player wasn't migrated to the new database format
158+ if (resultSet .wasNull ()) {
159+ floodgate = FloodgateState .NOT_MIGRATED ;
160+ } else {
161+ floodgate = FloodgateState .fromInt (floodgateNum );
162+ }
130163
131- String name = resultSet .getString (3 );
132- boolean premium = resultSet .getBoolean (4 );
133- String lastIp = resultSet .getString (5 );
134- Instant lastLogin = resultSet .getTimestamp (6 ).toInstant ();
135- return Optional .of (new StoredProfile (userId , uuid , name , premium , lastIp , lastLogin ));
164+ String lastIp = resultSet .getString ("LastIp" );
165+ Instant lastLogin = resultSet .getTimestamp ("LastLogin" ).toInstant ();
166+ return Optional .of (new StoredProfile (userId , uuid , name , premium , floodgate , lastIp , lastLogin ));
136167 }
137168
138169 return Optional .empty ();
@@ -150,9 +181,10 @@ public void save(StoredProfile playerProfile) {
150181 saveStmt .setString (1 , uuid );
151182 saveStmt .setString (2 , playerProfile .getName ());
152183 saveStmt .setBoolean (3 , playerProfile .isPremium ());
153- saveStmt .setString (4 , playerProfile .getLastIp ());
184+ saveStmt .setInt (4 , playerProfile .getFloodgate ().getValue ());
185+ saveStmt .setString (5 , playerProfile .getLastIp ());
154186
155- saveStmt .setLong (5 , playerProfile .getRowId ());
187+ saveStmt .setLong (6 , playerProfile .getRowId ());
156188 saveStmt .execute ();
157189 }
158190 } else {
@@ -161,7 +193,9 @@ public void save(StoredProfile playerProfile) {
161193
162194 saveStmt .setString (2 , playerProfile .getName ());
163195 saveStmt .setBoolean (3 , playerProfile .isPremium ());
164- saveStmt .setString (4 , playerProfile .getLastIp ());
196+ saveStmt .setBoolean (3 , playerProfile .isPremium ());
197+ saveStmt .setInt (4 , playerProfile .getFloodgate ().getValue ());
198+ saveStmt .setString (5 , playerProfile .getLastIp ());
165199
166200 saveStmt .execute ();
167201 try (ResultSet generatedKeys = saveStmt .getGeneratedKeys ()) {
@@ -179,6 +213,14 @@ public void save(StoredProfile playerProfile) {
179213 }
180214 }
181215
216+ /**
217+ * SQLite has a slightly different syntax, so this will be overridden by SQLiteStorage
218+ * @return An SQL Statement to create the `premium` table
219+ */
220+ protected String getCreateTableStmt () {
221+ return CREATE_TABLE_STMT ;
222+ }
223+
182224 @ Override
183225 public void close () {
184226 dataSource .close ();
0 commit comments