1+ package com .simplexity .simplepms .common .database ;
2+
3+ import com .simplexity .simplepms .common .database .objects .PlayerBlock ;
4+ import com .simplexity .simplepms .common .database .objects .PlayerSettings ;
5+ import com .simplexity .simplepms .common .logger .Logger ;
6+ import com .zaxxer .hikari .HikariConfig ;
7+ import com .zaxxer .hikari .HikariDataSource ;
8+
9+ import java .sql .*;
10+ import java .util .ArrayList ;
11+ import java .util .List ;
12+ import java .util .UUID ;
13+
14+ public class SQLHandler {
15+
16+ private SQLHandler () { }
17+
18+ private static SQLHandler instance ;
19+ private static boolean usingMySQL ;
20+
21+ public static SQLHandler getInstance () {
22+ if (instance == null ) {
23+ instance = new SQLHandler ();
24+ }
25+ return instance ;
26+ }
27+
28+ private static HikariDataSource dataSource ;
29+ private static final HikariConfig hikariConfig = new HikariConfig ();
30+
31+ public void init () {
32+ if (dataSource == null ) {
33+ // TODO: Logger error, need to call setupConfig first
34+ return ;
35+ }
36+ try (Connection connection = getConnection ()) {
37+ PreparedStatement blocklistInitStatement = connection .prepareStatement ("""
38+ CREATE TABLE IF NOT EXISTS blocklist (
39+ player_uuid VARCHAR (36) NOT NULL,
40+ blocked_player_uuid VARCHAR(36) NOT NULL,
41+ blocked_player_name VARCHAR(256),
42+ block_reason VARCHAR(256),
43+ PRIMARY KEY (player_uuid, blocked_player_uuid)
44+ );""" );
45+ blocklistInitStatement .execute ();
46+ PreparedStatement playerSettingsInitStatement = connection .prepareStatement ("""
47+ CREATE TABLE IF NOT EXISTS settings (
48+ player_uuid VARCHAR (36) NOT NULL PRIMARY KEY,
49+ socialspy_enabled BOOLEAN NOT NULL,
50+ messages_disabled BOOLEAN NOT NULL
51+ );""" );
52+ playerSettingsInitStatement .execute ();
53+ updateDatabaseColumns ();
54+ } catch (SQLException e ) {
55+ Logger .getLogger ().warn ("Failed to connect to database: {}" , e .getMessage (), e );
56+ }
57+ }
58+
59+ public void reloadDatabase (String jdbcUrl , String username , String password ) {
60+ Logger .getLogger ().info ("Reconnecting to SimplePMs database..." );
61+ shutdownConnection ();
62+ setupConfig (jdbcUrl , username , password );
63+ Logger .getLogger ().info ("Database reloaded successfully" );
64+ }
65+
66+
67+ public PlayerSettings getSettings (UUID playerUUID ) {
68+ String queryString = "SELECT socialspy_enabled, messages_disabled FROM settings WHERE player_uuid = ?;" ;
69+ PlayerSettings settings = null ;
70+ try (Connection connection = getConnection ()) {
71+ PreparedStatement statement = connection .prepareStatement (queryString );
72+ statement .setString (1 , String .valueOf (playerUUID ));
73+ try (ResultSet resultSet = statement .executeQuery ()) {
74+ if (!resultSet .next ()) {
75+ settings = new PlayerSettings (playerUUID );
76+ updateSettings (playerUUID , settings .isSocialSpyEnabled (), settings .areMessagesDisabled ());
77+ } else {
78+ boolean socialSpy = resultSet .getBoolean ("socialspy_enabled" );
79+ boolean messagesDisabled = resultSet .getBoolean ("messages_disabled" );
80+ settings = new PlayerSettings (playerUUID , socialSpy , messagesDisabled );
81+ }
82+ }
83+ } catch (SQLException e ) {
84+ Logger .getLogger ().warn ("Failed to retrieve settings from database: {}" , e .getMessage (), e );
85+ }
86+ return settings ;
87+ }
88+
89+ public List <PlayerBlock > getBlockedPlayers (UUID playerUUID ) {
90+ String queryString = "SELECT blocked_player_uuid, block_reason, blocked_player_name from blocklist WHERE player_uuid = ?;" ;
91+ List <PlayerBlock > blockedPlayers = new ArrayList <>();
92+ try (Connection connection = getConnection ()) {
93+ PreparedStatement statement = connection .prepareStatement (queryString );
94+ statement .setString (1 , String .valueOf (playerUUID ));
95+ try (ResultSet resultSet = statement .executeQuery ()) {
96+ while (resultSet .next ()) {
97+ UUID blockedPlayerUUID = UUID .fromString (resultSet .getString ("blocked_player_uuid" ));
98+ String blockedPlayerName = resultSet .getString ("blocked_player_name" );
99+ String reason = resultSet .getString ("block_reason" );
100+ PlayerBlock block = new PlayerBlock (playerUUID , blockedPlayerName , blockedPlayerUUID , reason );
101+ blockedPlayers .add (block );
102+ }
103+ }
104+ } catch (SQLException e ) {
105+ Logger .getLogger ().warn ("Failed to get blocked players: {}" , e .getMessage (), e );
106+ }
107+ return blockedPlayers ;
108+ }
109+
110+ public void addBlockedPlayer (UUID playerUUID , UUID blockedPlayerUUID , String blockedPlayerName , String reason ) {
111+ String queryString = "REPLACE INTO blocklist (player_uuid, blocked_player_uuid, blocked_player_name, block_reason) VALUES (?, ?, ?, ?);" ;
112+ try (Connection connection = getConnection ()) {
113+ PreparedStatement statement = connection .prepareStatement (queryString );
114+ statement .setString (1 , String .valueOf (playerUUID ));
115+ statement .setString (2 , String .valueOf (blockedPlayerUUID ));
116+ statement .setString (3 , blockedPlayerName );
117+ statement .setString (4 , reason );
118+ statement .executeUpdate ();
119+ } catch (SQLException e ) {
120+ Logger .getLogger ().warn ("Failed to add blocked player: {}" , e .getMessage (), e );
121+ e .printStackTrace ();
122+ }
123+ }
124+
125+ public void removeBlockedPlayer (UUID playerUUID , UUID blockedPlayerUUID ) {
126+ String queryString = "DELETE FROM blocklist WHERE player_uuid = ? and blocked_player_uuid = ?;" ;
127+ try (Connection connection = getConnection ()) {
128+ PreparedStatement statement = connection .prepareStatement (queryString );
129+ statement .setString (1 , String .valueOf (playerUUID ));
130+ statement .setString (2 , String .valueOf (blockedPlayerUUID ));
131+ statement .executeUpdate ();
132+ } catch (SQLException e ) {
133+ Logger .getLogger ().warn ("Failed to remove blocked player: {}" , e .getMessage (), e );
134+ }
135+ }
136+
137+ public void updateSettings (UUID playerUUID , boolean socialSpyEnabled , boolean messagesDisabled ) {
138+ String queryString = "REPLACE INTO settings (player_uuid, socialspy_enabled, messages_disabled) VALUES (?, ?, ?);" ;
139+ try (Connection connection = getConnection ()) {
140+ PreparedStatement statement = connection .prepareStatement (queryString );
141+ statement .setString (1 , String .valueOf (playerUUID ));
142+ statement .setBoolean (2 , socialSpyEnabled );
143+ statement .setBoolean (3 , messagesDisabled );
144+ statement .executeUpdate ();
145+ } catch (SQLException e ) {
146+ Logger .getLogger ().warn ("Failed to update settings to database: {}" , e .getMessage (), e );
147+ }
148+
149+ }
150+
151+ public void updateDatabaseColumns () {
152+ String tableName = "blocklist" ;
153+ String columnName = "blocked_player_name" ;
154+ boolean columnExists ;
155+ try {
156+ if (usingMySQL ) {
157+ columnExists = doesMysqlColumnExist (tableName , columnName );
158+ } else {
159+ columnExists = doesSqliteColumnExist (tableName , columnName );
160+ }
161+ if (!columnExists ) {
162+ addColumn (tableName , columnName , "VARCHAR(256)" , "" );
163+ }
164+ } catch (Exception e ) {
165+ Logger .getLogger ().warn ("Unable to update database table: {} column: {}, error: {}" , tableName , columnName , e .getMessage (), e );
166+ }
167+ }
168+
169+ private Boolean doesSqliteColumnExist (String tableName , String columnName ) {
170+ String query = "PRAGMA table_info(" + tableName + ")" ;
171+ try (Connection connection = getConnection ()) {
172+ PreparedStatement statement = connection .prepareStatement (query );
173+ ResultSet resultSet = statement .executeQuery ();
174+ while (resultSet .next ()) {
175+ if (columnName .equalsIgnoreCase (resultSet .getString ("name" ))) {
176+ return true ;
177+ }
178+ }
179+ } catch (SQLException e ) {
180+ Logger .getLogger ().warn ("Failed to to check for column {} in table {}: {}" , columnName , tableName , e .getMessage (), e );
181+ }
182+ return false ;
183+ }
184+
185+ private Boolean doesMysqlColumnExist (String tableName , String columnName ) {
186+ try (Connection connection = getConnection ()) {
187+ DatabaseMetaData metaData = connection .getMetaData ();
188+ ResultSet resultSet = metaData .getColumns (null , null , tableName , columnName );
189+ return resultSet .next ();
190+ } catch (SQLException e ) {
191+ Logger .getLogger ().warn ("Failed to check for column {} in table {}: {}" , columnName , tableName , e .getMessage (), e );
192+ }
193+ return false ;
194+ }
195+
196+ // Possibly extremely cursed way to do this :cackle:
197+
198+ public void addColumn (String tableName , String columnName , String dataType , String constraints ) {
199+ String query = String .format ("ALTER TABLE %s ADD COLUMN %s %s %s;" , tableName , columnName , dataType , constraints );
200+ try (Connection connection = getConnection ()) {
201+ PreparedStatement statement = connection .prepareStatement (query );
202+ statement .executeUpdate ();
203+ Logger .getLogger ().info ("Added new column '{}' to table '{}'" , columnName , tableName );
204+ } catch (SQLException e ) {
205+ Logger .getLogger ().warn ("Failed to add new column {} to table {}: {}" , columnName , tableName , e .getMessage (), e );
206+ }
207+ }
208+
209+ private static void loadHikariDataSource () {
210+ if (hikariConfig == null ) return ; // TODO: Logger for missing hikariConfig
211+ dataSource = new HikariDataSource (hikariConfig );
212+ }
213+
214+ public void setupConfig (String jdbcUrl , String username , String password ) {
215+ if (!usingMySQL ) {
216+ hikariConfig .setJdbcUrl (jdbcUrl );
217+ hikariConfig .setConnectionTestQuery ("PRAGMA journal_mode = WAL;" );
218+ loadHikariDataSource ();
219+ return ;
220+ }
221+ hikariConfig .setJdbcUrl (jdbcUrl );
222+ hikariConfig .setUsername (username );
223+ hikariConfig .setPassword (password );
224+ loadHikariDataSource ();
225+ }
226+
227+ public static void setUsingMySQL (boolean usingMySQL ) {
228+ SQLHandler .usingMySQL = usingMySQL ;
229+ }
230+
231+ public static Connection getConnection () throws SQLException {
232+ return dataSource .getConnection ();
233+ }
234+
235+ public void shutdownConnection () {
236+ if (dataSource == null || dataSource .isClosed ()) return ;
237+ dataSource .close ();
238+ dataSource = null ;
239+ Logger .getLogger ().info ("Closed existing database connection" );
240+ }
241+ }
0 commit comments