@ -169,9 +169,10 @@ public class CacheImporter @Inject constructor(
uncompressed : ByteBuf ,
uncompressed : ByteBuf ,
gameId : Int ,
gameId : Int ,
build : Int ,
build : Int ,
previousId : Int ? ,
timestamp : Instant ,
timestamp : Instant ,
name : String ,
name : String ,
) : List < ByteBuf ? > {
) : Pair < Int , List < ByteBuf ? > > {
return database . execute { connection ->
return database . execute { connection ->
prepare ( connection )
prepare ( connection )
@ -199,17 +200,28 @@ public class CacheImporter @Inject constructor(
overwrite = true
overwrite = true
)
)
/ *
* In order to defend against ( crc32 , version ) collisions , we only
* use a cached index if its checksum / version haven ' t changed
* between the previously downloaded version of the cache and the
* current version . This emulates the behaviour of a client always
* using the latest version of the cache - so if there is a
* collision , real players of the game would experience problems .
* /
connection . prepareStatement (
connection . prepareStatement (
"""
"""
SELECT c . data
SELECT c . data
FROM master _index _archives a
FROM master _index _archives a
LEFT JOIN containers c ON c . crc32 = a . crc32
LEFT JOIN master _index _archives a2 ON a2 . master _index _id = ? AND a2 . archive _id = a . archive _id AND
LEFT JOIN indexes i ON i . version = a . version AND i . container _id = c . id
a2 . crc32 = a . crc32 AND a2 . version = a . version
LEFT JOIN containers c ON c . crc32 = a2 . crc32
LEFT JOIN indexes i ON i . version = a2 . version AND i . container _id = c . id
WHERE a . master _index _id = ?
WHERE a . master _index _id = ?
ORDER BY a . archive _id ASC
ORDER BY a . archive _id ASC
""" .trimIndent()
""" .trimIndent()
) . use { stmt ->
) . use { stmt ->
stmt . setInt ( 1 , id )
stmt . setObject ( 1 , previousId , Types . INTEGER )
stmt . setInt ( 2 , id )
stmt . executeQuery ( ) . use { rows ->
stmt . executeQuery ( ) . use { rows ->
val indexes = mutableListOf < ByteBuf ? > ( )
val indexes = mutableListOf < ByteBuf ? > ( )
@ -224,7 +236,7 @@ public class CacheImporter @Inject constructor(
}
}
indexes . filterNotNull ( ) . forEach ( ByteBuf :: retain )
indexes . filterNotNull ( ) . forEach ( ByteBuf :: retain )
return @execute indexes
return @execute Pair ( id , indexes )
} finally {
} finally {
indexes . filterNotNull ( ) . forEach ( ByteBuf :: release )
indexes . filterNotNull ( ) . forEach ( ByteBuf :: release )
}
}
@ -237,7 +249,8 @@ public class CacheImporter @Inject constructor(
archive : Int ,
archive : Int ,
index : Js5Index ,
index : Js5Index ,
buf : ByteBuf ,
buf : ByteBuf ,
uncompressed : ByteBuf
uncompressed : ByteBuf ,
previousMasterIndexId : Int ?
) : List < Int > {
) : List < Int > {
return database . execute { connection ->
return database . execute { connection ->
prepare ( connection )
prepare ( connection )
@ -273,22 +286,33 @@ public class CacheImporter @Inject constructor(
}
}
/ *
/ *
* We deliberately ignore groups with truncated versions here and
* In order to defend against ( crc32 , version ) collisions , we only
* re - download them , just in case there ' s a ( crc32 , truncated version )
* use a cached group if its checksum / version haven ' t changed
* collision .
* between the previously downloaded version of the cache and the
* current version . This emulates the behaviour of a client always
* using the latest version of the cache - so if there is a
* collision , real players of the game would experience problems .
*
* We never use cached groups with a truncated version , as these
* are even more likely to be prone to collisions .
* /
* /
connection . prepareStatement (
connection . prepareStatement (
"""
"""
SELECT t . group _id
SELECT t . group _id
FROM tmp _groups t
FROM tmp _groups t
LEFT JOIN groups g ON g . archive _id = ? AND g . group _id = t . group _id AND g . version = t . version AND
LEFT JOIN master _index _valid _indexes i ON i . master _index _id = ? AND
NOT g . version _truncated
i . archive _id = ?
LEFT JOIN containers c ON c . id = g . container _id AND c . crc32 = t . crc32
LEFT JOIN index _groups ig ON ig . container _id = i . container _id AND ig . group _id = t . group _id AND
ig . crc32 = t . crc32 AND ig . version = t . version
LEFT JOIN groups g ON g . archive _id = i . archive _id AND g . group _id = ig . group _id AND
g . version = ig . version AND NOT g . version _truncated
LEFT JOIN containers c ON c . id = g . container _id AND c . crc32 = ig . crc32
WHERE g . container _id IS NULL
WHERE g . container _id IS NULL
ORDER BY t . group _id ASC
ORDER BY t . group _id ASC
""" .trimIndent()
""" .trimIndent()
) . use { stmt ->
) . use { stmt ->
stmt . setInt ( 1 , archive )
stmt . setObject ( 1 , previousMasterIndexId , Types . INTEGER )
stmt . setInt ( 2 , archive )
stmt . executeQuery ( ) . use { rows ->
stmt . executeQuery ( ) . use { rows ->
val groups = mutableListOf < Int > ( )
val groups = mutableListOf < Int > ( )
@ -770,6 +794,21 @@ public class CacheImporter @Inject constructor(
}
}
}
}
public suspend fun setMasterIndexId ( gameId : Int , masterIndexId : Int ) {
database . execute { connection ->
connection . prepareStatement (
"""
UPDATE games SET master _index _id = ? WHERE id = ?
""" .trimIndent()
) . use { stmt ->
stmt . setInt ( 1 , masterIndexId )
stmt . setInt ( 2 , gameId )
stmt . execute ( )
}
}
}
public companion object {
public companion object {
public const val BATCH _SIZE : Int = 1024
public const val BATCH _SIZE : Int = 1024
}
}