2023-10-24 01:51:02 +02:00
package com.craftaro.epicanchors.files ;
2023-10-24 03:00:27 +02:00
import com.craftaro.core.SongodaPlugin ;
2023-10-24 01:51:02 +02:00
import com.craftaro.core.database.DatabaseConnector ;
import com.craftaro.epicanchors.AnchorImpl ;
import com.craftaro.epicanchors.api.Anchor ;
2023-10-24 03:00:27 +02:00
import com.craftaro.epicanchors.files.migration.LegacyYamlAnchorsMigrator ;
2023-10-24 01:51:02 +02:00
import com.craftaro.epicanchors.utils.Callback ;
import com.craftaro.epicanchors.utils.UpdateCallback ;
import com.craftaro.epicanchors.utils.Utils ;
import org.bukkit.Bukkit ;
import org.bukkit.Location ;
import org.bukkit.World ;
import org.jetbrains.annotations.NotNull ;
import org.jetbrains.annotations.Nullable ;
import java.sql.PreparedStatement ;
import java.sql.ResultSet ;
import java.sql.SQLException ;
import java.sql.Statement ;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.List ;
import java.util.Objects ;
import java.util.UUID ;
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Executors ;
import java.util.concurrent.TimeUnit ;
2023-10-24 03:00:27 +02:00
public class AnchorsDataManager {
2023-10-24 01:52:18 +02:00
private final ExecutorService thread = Executors . newSingleThreadExecutor ( ) ;
2023-10-24 03:00:27 +02:00
private final SongodaPlugin plugin ;
2023-10-24 01:52:18 +02:00
2023-10-24 03:00:27 +02:00
public AnchorsDataManager ( SongodaPlugin plugin ) {
this . plugin = plugin ;
2023-10-24 01:52:18 +02:00
}
public void close ( ) {
2023-10-24 03:00:27 +02:00
if ( this . thread . isShutdown ( ) ) {
return ;
}
2023-10-24 01:52:18 +02:00
2023-10-24 03:00:27 +02:00
this . thread . shutdown ( ) ;
try {
if ( ! this . thread . awaitTermination ( 60 , TimeUnit . SECONDS ) ) {
// Try stopping the thread forcefully (there is basically no hope left for the data)
this . thread . shutdownNow ( ) ;
2023-10-24 01:52:18 +02:00
}
2023-10-24 03:00:27 +02:00
} catch ( InterruptedException ex ) {
Utils . logException ( this . plugin , ex ) ;
2023-10-24 01:52:18 +02:00
}
2023-10-24 03:00:27 +02:00
this . plugin . getDataManager ( ) . shutdown ( ) ;
2023-10-24 01:52:18 +02:00
}
public void exists ( @NotNull String worldName , int x , int y , int z , @NotNull Callback < Boolean > callback ) {
2023-10-24 03:00:27 +02:00
getDatabaseConnector ( ) . connect ( ( con ) - > {
try ( PreparedStatement ps = con . prepareStatement ( " SELECT id FROM " + getAnchorTable ( ) + " WHERE world_name =? AND x =? AND y =? AND z=?; " ) ) {
2023-10-24 01:52:18 +02:00
ps . setString ( 1 , worldName ) ;
ps . setInt ( 2 , x ) ;
ps . setInt ( 3 , y ) ;
ps . setInt ( 4 , z ) ;
ResultSet rs = ps . executeQuery ( ) ;
callback . accept ( null , rs . next ( ) ) ;
} catch ( Exception ex ) {
resolveCallback ( callback , ex ) ;
}
} ) ;
}
public void getAnchors ( @Nullable World world , @NotNull Callback < List < Anchor > > callback ) {
List < Anchor > result = new ArrayList < > ( ) ;
2023-10-24 03:00:27 +02:00
getDatabaseConnector ( ) . connect ( ( con ) - > {
try ( PreparedStatement ps = con . prepareStatement ( " SELECT * FROM " + getAnchorTable ( ) + ( world ! = null ? " WHERE world_name =? " : " " ) + " ; " ) ) {
2023-10-24 01:52:18 +02:00
if ( world ! = null ) {
ps . setString ( 1 , world . getName ( ) ) ;
}
ResultSet rs = ps . executeQuery ( ) ;
while ( rs . next ( ) ) {
result . add ( extractAnchor ( rs ) ) ;
}
callback . accept ( null , result ) ;
} catch ( Exception ex ) {
resolveCallback ( callback , ex ) ;
}
} ) ;
}
public void insertAnchorAsync ( Location loc , UUID owner , int ticks , Callback < Anchor > callback ) {
this . thread . execute ( ( ) - > insertAnchor ( loc , owner , ticks , callback ) ) ;
}
public void insertAnchor ( Location loc , UUID owner , int ticks , Callback < Anchor > callback ) {
2023-10-24 03:00:27 +02:00
getDatabaseConnector ( ) . connect ( ( con ) - > {
try ( PreparedStatement ps = con . prepareStatement ( " INSERT INTO " + getAnchorTable ( ) + " (owner, world_name,x,y,z, ticks_left) VALUES (?,?,?,?,?, ?); " ) ; // Future SQLite version might support 'RETURNING *'
PreparedStatement psFetch = con . prepareStatement ( " SELECT * FROM " + getAnchorTable ( ) + " WHERE world_name =? AND x =? AND y =? AND z=?; " )
) {
2023-10-24 01:52:18 +02:00
ps . setString ( 1 , owner ! = null ? owner . toString ( ) : null ) ;
ps . setString ( 2 , Objects . requireNonNull ( loc . getWorld ( ) ) . getName ( ) ) ;
psFetch . setString ( 1 , Objects . requireNonNull ( loc . getWorld ( ) ) . getName ( ) ) ;
ps . setInt ( 3 , loc . getBlockX ( ) ) ;
psFetch . setInt ( 2 , loc . getBlockX ( ) ) ;
ps . setInt ( 4 , loc . getBlockY ( ) ) ;
psFetch . setInt ( 3 , loc . getBlockY ( ) ) ;
ps . setInt ( 5 , loc . getBlockZ ( ) ) ;
psFetch . setInt ( 4 , loc . getBlockZ ( ) ) ;
ps . setInt ( 6 , ticks ) ;
ps . executeUpdate ( ) ;
if ( callback ! = null ) {
ResultSet rs = psFetch . executeQuery ( ) ;
rs . next ( ) ;
callback . accept ( null , extractAnchor ( rs ) ) ;
}
} catch ( Exception ex ) {
resolveCallback ( callback , ex ) ;
}
} ) ;
}
2023-10-24 03:00:27 +02:00
public void migrateAnchor ( List < LegacyYamlAnchorsMigrator . LegacyAnchorEntry > anchorEntries , UpdateCallback callback ) {
getDatabaseConnector ( ) . connect ( ( con ) - > {
2023-10-24 01:52:18 +02:00
con . setAutoCommit ( false ) ;
SQLException err = null ;
2023-10-24 03:00:27 +02:00
try ( PreparedStatement ps = con . prepareStatement ( " INSERT INTO " + getAnchorTable ( ) + " (world_name,x,y,z, ticks_left) VALUES (?,?,?,?, ?); " ) ) {
for ( LegacyYamlAnchorsMigrator . LegacyAnchorEntry entry : anchorEntries ) {
2023-10-24 01:52:18 +02:00
ps . setString ( 1 , entry . worldName ) ;
ps . setInt ( 2 , entry . x ) ;
ps . setInt ( 3 , entry . y ) ;
ps . setInt ( 4 , entry . z ) ;
ps . setInt ( 5 , entry . ticksLeft ) ;
ps . addBatch ( ) ;
}
int [ ] batchRes = ps . executeBatch ( ) ;
for ( int i : batchRes ) {
if ( i < 0 & & i ! = Statement . SUCCESS_NO_INFO ) {
throw new AssertionError ( " Batch-INSERT failed for at least one statement with code " + i ) ;
}
}
} catch ( SQLException ex ) {
err = ex ;
}
if ( err = = null ) {
con . commit ( ) ;
resolveUpdateCallback ( callback , null ) ;
} else {
con . rollback ( ) ;
resolveUpdateCallback ( callback , err ) ;
}
con . setAutoCommit ( true ) ;
} ) ;
}
public void updateAnchors ( Collection < Anchor > anchors , UpdateCallback callback ) {
2023-10-24 03:00:27 +02:00
getDatabaseConnector ( ) . connect ( ( con ) - > {
2023-10-24 01:52:18 +02:00
con . setAutoCommit ( false ) ;
SQLException err = null ;
for ( Anchor anchor : anchors ) {
2023-10-24 03:00:27 +02:00
try ( PreparedStatement ps = con . prepareStatement ( " UPDATE " + getAnchorTable ( ) + " SET ticks_left =? WHERE id =?; " ) ) {
2023-10-24 01:52:18 +02:00
ps . setInt ( 1 , anchor . getTicksLeft ( ) ) ;
ps . setInt ( 2 , anchor . getDbId ( ) ) ;
ps . executeUpdate ( ) ;
} catch ( SQLException ex ) {
err = ex ;
break ;
}
}
if ( err = = null ) {
con . commit ( ) ;
resolveUpdateCallback ( callback , null ) ;
} else {
con . rollback ( ) ;
resolveUpdateCallback ( callback , err ) ;
}
con . setAutoCommit ( true ) ;
} ) ;
}
public void deleteAnchorAsync ( Anchor anchor ) {
deleteAnchorAsync ( anchor , null ) ;
}
public void deleteAnchorAsync ( Anchor anchor , UpdateCallback callback ) {
this . thread . execute ( ( ) - >
2023-10-24 03:00:27 +02:00
getDatabaseConnector ( ) . connect ( ( con ) - > {
try ( PreparedStatement ps = con . prepareStatement ( " DELETE FROM " + getAnchorTable ( ) + " WHERE id =?; " ) ) {
2023-10-24 01:52:18 +02:00
ps . setInt ( 1 , anchor . getDbId ( ) ) ;
ps . executeUpdate ( ) ;
resolveUpdateCallback ( callback , null ) ;
} catch ( Exception ex ) {
resolveUpdateCallback ( callback , ex ) ;
}
} )
) ;
}
2023-10-24 03:00:27 +02:00
public String getAnchorTable ( ) {
return getAnchorTable ( this . plugin . getDataManager ( ) . getTablePrefix ( ) ) ;
}
2023-10-24 01:52:18 +02:00
2023-10-24 03:00:27 +02:00
public static String getAnchorTable ( String prefix ) {
return prefix + " anchors " ;
}
2023-10-24 01:52:18 +02:00
2023-10-24 03:00:27 +02:00
private DatabaseConnector getDatabaseConnector ( ) {
return this . plugin . getDataManager ( ) . getDatabaseConnector ( ) ;
2023-10-24 01:52:18 +02:00
}
private Anchor extractAnchor ( ResultSet rs ) throws SQLException {
String ownerStr = rs . getString ( " owner " ) ;
return new AnchorImpl ( rs . getInt ( " id " ) ,
ownerStr ! = null ? UUID . fromString ( ownerStr ) : null ,
new Location ( Bukkit . getWorld ( rs . getString ( " world_name " ) ) ,
rs . getInt ( " x " ) ,
rs . getInt ( " y " ) ,
rs . getInt ( " z " ) ) ,
rs . getInt ( " ticks_left " ) ) ;
}
private void resolveUpdateCallback ( @Nullable UpdateCallback callback , @Nullable Exception ex ) {
if ( callback ! = null ) {
callback . accept ( ex ) ;
} else if ( ex ! = null ) {
2023-10-24 03:00:27 +02:00
Utils . logException ( this . plugin , ex , " H2 " ) ;
2023-10-24 01:52:18 +02:00
}
}
private void resolveCallback ( @Nullable Callback < ? > callback , @NotNull Exception ex ) {
if ( callback ! = null ) {
callback . accept ( ex , null ) ;
} else {
2023-10-24 03:00:27 +02:00
Utils . logException ( this . plugin , ex , " H2 " ) ;
2023-10-24 01:52:18 +02:00
}
}
2023-10-24 01:51:02 +02:00
}