Add name and description to the master_indexes table

Signed-off-by: Graham <gpe@openrs2.org>
Graham 4 years ago
parent dc9852e77c
commit a19a69b21e
  1. 8
      archive/src/main/kotlin/org/openrs2/archive/cache/CacheExporter.kt
  2. 66
      archive/src/main/kotlin/org/openrs2/archive/cache/CacheImporter.kt
  3. 4
      archive/src/main/kotlin/org/openrs2/archive/cache/ImportCommand.kt
  4. 4
      archive/src/main/kotlin/org/openrs2/archive/cache/ImportMasterIndexCommand.kt
  5. 3
      archive/src/main/kotlin/org/openrs2/archive/cache/Js5ChannelHandler.kt
  6. 4
      archive/src/main/resources/org/openrs2/archive/V1__init.sql
  7. 2
      archive/src/main/resources/org/openrs2/archive/templates/caches/index.html

@ -20,14 +20,15 @@ public class CacheExporter @Inject constructor(
val whirlpool: ByteArray, val whirlpool: ByteArray,
val game: String, val game: String,
val build: Int?, val build: Int?,
val timestamp: Instant? val timestamp: Instant?,
val name: String?
) )
public suspend fun list(): List<Cache> { public suspend fun list(): List<Cache> {
return database.execute { connection -> return database.execute { connection ->
connection.prepareStatement( connection.prepareStatement(
""" """
SELECT c.id, c.whirlpool, g.name, m.build, m.timestamp SELECT c.id, c.whirlpool, g.name, m.build, m.timestamp, m.name
FROM master_indexes m FROM master_indexes m
JOIN games g ON g.id = m.game_id JOIN games g ON g.id = m.game_id
JOIN containers c ON c.id = m.container_id JOIN containers c ON c.id = m.container_id
@ -48,8 +49,9 @@ public class CacheExporter @Inject constructor(
} }
val timestamp = rows.getTimestamp(5)?.toInstant() val timestamp = rows.getTimestamp(5)?.toInstant()
val name = rows.getString(6)
caches += Cache(id, whirlpool, game, build, timestamp) caches += Cache(id, whirlpool, game, build, timestamp, name)
} }
caches caches

@ -60,7 +60,14 @@ public class CacheImporter @Inject constructor(
encrypted: Boolean encrypted: Boolean
) : Container(data, encrypted) ) : Container(data, encrypted)
public suspend fun import(store: Store, game: String, build: Int?, timestamp: Instant?) { public suspend fun import(
store: Store,
game: String,
build: Int?,
timestamp: Instant?,
name: String?,
description: String?
) {
database.execute { connection -> database.execute { connection ->
prepare(connection) prepare(connection)
@ -69,7 +76,7 @@ public class CacheImporter @Inject constructor(
// import master index // import master index
val masterIndex = createMasterIndex(store) val masterIndex = createMasterIndex(store)
try { try {
addMasterIndex(connection, masterIndex, gameId, build, timestamp) addMasterIndex(connection, masterIndex, gameId, build, timestamp, name, description)
} finally { } finally {
masterIndex.release() masterIndex.release()
} }
@ -118,7 +125,14 @@ public class CacheImporter @Inject constructor(
} }
} }
public suspend fun importMasterIndex(buf: ByteBuf, game: String, build: Int?, timestamp: Instant?) { public suspend fun importMasterIndex(
buf: ByteBuf,
game: String,
build: Int?,
timestamp: Instant?,
name: String?,
description: String?
) {
Js5Compression.uncompress(buf.slice()).use { uncompressed -> Js5Compression.uncompress(buf.slice()).use { uncompressed ->
val masterIndex = MasterIndex(Js5MasterIndex.read(uncompressed.slice()), buf) val masterIndex = MasterIndex(Js5MasterIndex.read(uncompressed.slice()), buf)
@ -126,7 +140,7 @@ public class CacheImporter @Inject constructor(
prepare(connection) prepare(connection)
val gameId = getGameId(connection, game) val gameId = getGameId(connection, game)
addMasterIndex(connection, masterIndex, gameId, build, timestamp) addMasterIndex(connection, masterIndex, gameId, build, timestamp, name, description)
} }
} }
} }
@ -136,7 +150,8 @@ public class CacheImporter @Inject constructor(
buf: ByteBuf, buf: ByteBuf,
gameId: Int, gameId: Int,
build: Int, build: Int,
timestamp: Instant timestamp: Instant,
name: String,
): List<ByteBuf?> { ): List<ByteBuf?> {
return database.execute { connection -> return database.execute { connection ->
prepare(connection) prepare(connection)
@ -154,7 +169,7 @@ public class CacheImporter @Inject constructor(
stmt.execute() stmt.execute()
} }
addMasterIndex(connection, MasterIndex(masterIndex, buf), gameId, build, timestamp) addMasterIndex(connection, MasterIndex(masterIndex, buf), gameId, build, timestamp, name, null)
connection.prepareStatement( connection.prepareStatement(
""" """
@ -302,21 +317,32 @@ public class CacheImporter @Inject constructor(
connection: Connection, connection: Connection,
masterIndex: MasterIndex, masterIndex: MasterIndex,
gameId: Int, gameId: Int,
build: Int? = null, build: Int?,
timestamp: Instant? = null timestamp: Instant?,
name: String?,
description: String?
) { ) {
val containerId = addContainer(connection, masterIndex) val containerId = addContainer(connection, masterIndex)
val savepoint = connection.setSavepoint()
// TODO(gpe): override game_id/build/timestamp if they're null? // TODO(gpe): combine name/description instead of overwriting one with the other?
// or a mechanism to control priority?
connection.prepareStatement( connection.prepareStatement(
""" """
INSERT INTO master_indexes (container_id, game_id, build, timestamp) INSERT INTO master_indexes (container_id, game_id, build, timestamp, name, description)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (container_id) DO UPDATE SET
build = COALESCE(EXCLUDED.build, master_indexes.build),
timestamp = COALESCE(
LEAST(EXCLUDED.timestamp, master_indexes.timestamp),
EXCLUDED.timestamp,
master_indexes.timestamp
),
name = COALESCE(EXCLUDED.name, master_indexes.name),
description = COALESCE(EXCLUDED.description, master_indexes.description)
""".trimIndent() """.trimIndent()
).use { stmt -> ).use { stmt ->
stmt.setLong(1, containerId) stmt.setLong(1, containerId)
stmt.setObject(2, gameId, Types.INTEGER) stmt.setInt(2, gameId)
stmt.setObject(3, build, Types.INTEGER) stmt.setObject(3, build, Types.INTEGER)
if (timestamp != null) { if (timestamp != null) {
@ -326,21 +352,17 @@ public class CacheImporter @Inject constructor(
stmt.setNull(4, Types.TIMESTAMP_WITH_TIMEZONE) stmt.setNull(4, Types.TIMESTAMP_WITH_TIMEZONE)
} }
try { stmt.setString(5, name)
stmt.execute() stmt.setString(6, description)
} catch (ex: SQLException) {
if (ex.sqlState == PSQLState.UNIQUE_VIOLATION.state) { stmt.execute()
connection.rollback(savepoint)
return@addMasterIndex
}
throw ex
}
} }
connection.prepareStatement( connection.prepareStatement(
""" """
INSERT INTO master_index_archives (container_id, archive_id, crc32, version) INSERT INTO master_index_archives (container_id, archive_id, crc32, version)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
ON CONFLICT DO NOTHING
""".trimIndent() """.trimIndent()
).use { stmt -> ).use { stmt ->
for ((i, entry) in masterIndex.index.entries.withIndex()) { for ((i, entry) in masterIndex.index.entries.withIndex()) {

@ -14,6 +14,8 @@ import org.openrs2.cli.instant
public class ImportCommand : CliktCommand(name = "import") { public class ImportCommand : CliktCommand(name = "import") {
private val build by option().int() private val build by option().int()
private val timestamp by option().instant() private val timestamp by option().instant()
private val name by option()
private val description by option()
private val game by argument() private val game by argument()
private val input by argument().path( private val input by argument().path(
@ -27,7 +29,7 @@ public class ImportCommand : CliktCommand(name = "import") {
val importer = injector.getInstance(CacheImporter::class.java) val importer = injector.getInstance(CacheImporter::class.java)
Store.open(input).use { store -> Store.open(input).use { store ->
importer.import(store, game, build, timestamp) importer.import(store, game, build, timestamp, name, description)
} }
} }
} }

@ -16,6 +16,8 @@ import java.nio.file.Files
public class ImportMasterIndexCommand : CliktCommand(name = "import-master-index") { public class ImportMasterIndexCommand : CliktCommand(name = "import-master-index") {
private val build by option().int() private val build by option().int()
private val timestamp by option().instant() private val timestamp by option().instant()
private val name by option()
private val description by option()
private val game by argument() private val game by argument()
private val input by argument().path( private val input by argument().path(
@ -29,7 +31,7 @@ public class ImportMasterIndexCommand : CliktCommand(name = "import-master-index
val importer = injector.getInstance(CacheImporter::class.java) val importer = injector.getInstance(CacheImporter::class.java)
Unpooled.wrappedBuffer(Files.readAllBytes(input)).use { buf -> Unpooled.wrappedBuffer(Files.readAllBytes(input)).use { buf ->
importer.importMasterIndex(buf, game, build, timestamp) importer.importMasterIndex(buf, game, build, timestamp, name, description)
} }
} }
} }

@ -158,7 +158,8 @@ public class Js5ChannelHandler(
} }
val rawIndexes = runBlocking { val rawIndexes = runBlocking {
importer.importMasterIndexAndGetIndexes(masterIndex!!, buf, gameId, build, Instant.now()) val name = "Downloaded from $hostname:$port"
importer.importMasterIndexAndGetIndexes(masterIndex!!, buf, gameId, build, Instant.now(), name)
} }
try { try {
indexes = arrayOfNulls(rawIndexes.size) indexes = arrayOfNulls(rawIndexes.size)

@ -86,7 +86,9 @@ CREATE TABLE master_indexes (
container_id BIGINT PRIMARY KEY NOT NULL REFERENCES containers (id), container_id BIGINT PRIMARY KEY NOT NULL REFERENCES containers (id),
game_id INTEGER NOT NULL REFERENCES games (id), game_id INTEGER NOT NULL REFERENCES games (id),
build INTEGER NULL, build INTEGER NULL,
timestamp TIMESTAMPTZ NULL timestamp TIMESTAMPTZ NULL,
name TEXT NULL,
description TEXT NULL
); );
CREATE TABLE master_index_archives ( CREATE TABLE master_index_archives (

@ -8,6 +8,7 @@
<th>Game</th> <th>Game</th>
<th>Build</th> <th>Build</th>
<th>Timestamp</th> <th>Timestamp</th>
<th>Name</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -18,6 +19,7 @@
<td th:text="${cache.game}">runescape</td> <td th:text="${cache.game}">runescape</td>
<td th:text="${cache.build}">550</td> <td th:text="${cache.build}">550</td>
<td th:text="${#temporals.formatISO(cache.timestamp)}"></td> <td th:text="${#temporals.formatISO(cache.timestamp)}"></td>
<td th:text="${cache.name}"></td>
<td> <td>
<a th:href="${'/caches/' + cache.id + '.zip'}">Download</a> <a th:href="${'/caches/' + cache.id + '.zip'}">Download</a>
</td> </td>

Loading…
Cancel
Save