diff --git a/src/main/kotlin/com/dowstats/controllers/CustomModsController.kt b/src/main/kotlin/com/dowstats/controllers/CustomModsController.kt new file mode 100644 index 0000000..bdf969a --- /dev/null +++ b/src/main/kotlin/com/dowstats/controllers/CustomModsController.kt @@ -0,0 +1,29 @@ +package com.dowstats.controllers + +import com.dowstats.service.integrations.ModStorageIntegrationService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.* +import org.springframework.web.multipart.MultipartFile + + +@RestController +@RequestMapping("api/v1/custom-mod") +class CustomModsController @Autowired constructor( + val modStorageIntegrationService: ModStorageIntegrationService +) { + + @PostMapping("/reload/{modId}") + fun reloadMod(@PathVariable modId: Long): String { + modStorageIntegrationService.reloadMod(modId) + return "Successfully reload mod" + } + + @PostMapping("/upload") + fun uploadMod(@RequestParam("file") file: MultipartFile?, + @RequestParam("name") name: String, + @RequestParam("technicalName") technicalName: String, + @RequestParam("version") version: String?): String { + modStorageIntegrationService.saveModFromRequest(file, name, technicalName, version?:"1.0.0") + return "Successfully upload mod" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/controllers/UploadCustomModController.kt b/src/main/kotlin/com/dowstats/controllers/UploadCustomModController.kt deleted file mode 100644 index 13a27a9..0000000 --- a/src/main/kotlin/com/dowstats/controllers/UploadCustomModController.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.dowstats.controllers - -import com.dowstats.service.integrations.ModStorageIntegrationService -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import org.slf4j.LoggerFactory -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.multipart.MultipartFile - - -@RestController -@RequestMapping("api/v1/upload-mod") -class UploadCustomModController @Autowired constructor( - val modStorageIntegrationService: ModStorageIntegrationService -) { - - @PostMapping - fun uploadMod(@RequestParam("file") file: MultipartFile, - @RequestParam("name") name: String, - @RequestParam("technicalName") technicalName: String, - @RequestParam("version") version: String?): String { - - val log = LoggerFactory.getLogger(UploadCustomModController::class.java) - - modStorageIntegrationService.saveModFromRequest(file.bytes.inputStream(), name, technicalName, version?:"1.0.0") - log.info("${file.originalFilename} successfull uploaded. Name: $name, version: $version") - - return "Successfull upload mod" - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/data/repositories/ModRepository.kt b/src/main/kotlin/com/dowstats/data/repositories/ModRepository.kt index 3f3f730..6e868a3 100644 --- a/src/main/kotlin/com/dowstats/data/repositories/ModRepository.kt +++ b/src/main/kotlin/com/dowstats/data/repositories/ModRepository.kt @@ -1,8 +1,16 @@ package com.dowstats.data.repositories import com.dowstats.data.entities.Mod +import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.* interface ModRepository : CrudRepository{ fun findByTechnicalNameAndVersion(techName: String, version: String): Mod? + + @Query(""" + CALL delete_mod_data(:modId) + """, nativeQuery = true) + @Modifying + fun clearModData(modId: Long) } \ No newline at end of file diff --git a/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt b/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt index f753d92..db05562 100644 --- a/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt +++ b/src/main/kotlin/com/dowstats/service/integrations/ModStorageIntegrationService.kt @@ -9,7 +9,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import org.springframework.util.FileSystemUtils +import org.springframework.web.multipart.MultipartFile import java.io.File import java.io.FileOutputStream import java.io.InputStream @@ -70,9 +72,16 @@ class ModStorageIntegrationService( } } - fun saveModFromRequest(fileStream: InputStream, name: String, techName: String, version: String) { - val modDirectoryTo = "$name$version" - unzip(fileStream, modDirectoryTo) + @Transactional + fun reloadMod(modId: Long) { + val mod = modRepository.findById(modId).orElseThrow { IllegalArgumentException("Mod not found") } + modRepository.clearModData(modId) + modParserService.parceModFilesAndSaveToDb(mod) + } + + + fun saveModFromRequest(file: MultipartFile?, name: String, techName: String, version: String) { + val uploadingMod = modRepository.findByTechnicalNameAndVersion(techName, version) val savedMod = uploadingMod ?: modRepository.save(Mod().also { it.version = version @@ -80,7 +89,14 @@ class ModStorageIntegrationService( it.name = name it.technicalName = techName }) - modParserService.parceModFilesAndSaveToDb(savedMod) + + file?.let { + val modDirectoryTo = "$name$version" + val fileStream = file.bytes.inputStream() + unzip(fileStream, modDirectoryTo) + modParserService.parceModFilesAndSaveToDb(savedMod) + log.info("${file.originalFilename} successfull uploaded. Name: $name, version: $version") + } } private fun downloadAndExtractMod(modTechName: String, version: String) { diff --git a/src/main/kotlin/com/dowstats/service/schedulers/Schedulers.kt b/src/main/kotlin/com/dowstats/service/schedulers/Schedulers.kt index dd90189..f6505cb 100644 --- a/src/main/kotlin/com/dowstats/service/schedulers/Schedulers.kt +++ b/src/main/kotlin/com/dowstats/service/schedulers/Schedulers.kt @@ -11,8 +11,6 @@ class Schedulers ( val modStorageIntegrationService: ModStorageIntegrationService ) { - private val log = LogFactory.getLog(javaClass) - @Scheduled(fixedDelay = 600000) fun synchronizeLastMods() { modStorageIntegrationService.requestAvailableMods() diff --git a/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt b/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt index e5eb2f7..25e04e3 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/IconsService.kt @@ -22,11 +22,12 @@ class IconsService @Autowired constructor( */ fun convertTgaToJpegImage(iconPathInMod: String, pathToTgaIcon: String, modName: String? = null): String? { try{ - val image: BufferedImage = try { + + val image: BufferedImage = if(File(pathToTgaIcon).exists()) { ImageIO.read(File(pathToTgaIcon)) - } catch (e: Exception){ + } else if (File(pathToTgaIcon.lowercase()).exists()) { ImageIO.read(File(pathToTgaIcon.lowercase())) - } + } else return null val modFolder = modName?.let { "${File.separator}$modName" } ?: "" diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt index 195f1ef..9c1d1b8 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModAttribPathService.kt @@ -18,6 +18,9 @@ class ModAttribPathService @Autowired constructor( fun getUcsFolder(modFolderData: String): String = "${modFolderData.replace("Data", "")}Locale${File.separator}English${File.separator}" + fun getUcsFolderRus(modFolderData: String): String = + "${modFolderData.replace("Data", "")}Locale${File.separator}Russian${File.separator}" + fun getWeaponAttribsPath(modFolderData: String): String = "$modFolderData${File.separator}attrib${File.separator}weapon" diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt index 269d9dd..f0929de 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModParserService.kt @@ -65,11 +65,26 @@ class ModParserService @Autowired constructor( unitRepository.deleteAllByModIdAndRaceId(mod.id!!, it) } - val modDictionary: MutableMap = mutableMapOf() + val modDictionary: Map = getModDictionary(mod, modFolderData) log.info("Extract dictionaries from $modFolderData") - File(modAttribPathService.getUcsFolder(modFolderData)).listFiles().forEach { - it.bufferedReader(Charsets.UTF_8).lines().forEach { + + val enrichedModDictionary = defaultDictionary + modDictionary + + val weapons = saveWeapons(modFolderData, mod, enrichedModDictionary) + saveUnits(modFolderData, weapons, racesList, mod, enrichedModDictionary) + + saveBuildings(modFolderData, weapons, racesList, mod, enrichedModDictionary) + + } + + private fun getModDictionary(mod: Mod, modFolderData: String): Map{ + val folder = if (mod.technicalName == "multidungeon_rightpocalypse") modAttribPathService.getUcsFolderRus(modFolderData) else modAttribPathService.getUcsFolder(modFolderData) + + val modDictionary = mutableMapOf() + + File(folder).listFiles()?.forEach { + it.bufferedReader(Charsets.UTF_16).lines().forEach { val kv = it.split("\\s+".toRegex()) if (kv.size < 2) return@forEach val key = try {kv.first().filter { it.isDigit() }.toInt()} catch(e: Exception) { @@ -81,16 +96,10 @@ class ModParserService @Autowired constructor( } } - val enrichedModDictionary = modDictionary + defaultDictionary - - val weapons = saveWeapons(modFolderData, mod, enrichedModDictionary) - saveUnits(modFolderData, weapons, racesList, mod, enrichedModDictionary) - - saveBuildings(modFolderData, weapons, racesList, mod, enrichedModDictionary) - + return modDictionary } - fun saveUnits(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { + private fun saveUnits(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { val races = raceRepository.findAll().toList() val armorTypes = armorTypeRepository.findAll().toList() @@ -186,7 +195,7 @@ class ModParserService @Autowired constructor( } } - fun saveBuildings(modFolderData: String, + private fun saveBuildings(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { @@ -249,7 +258,7 @@ class ModParserService @Autowired constructor( } } - fun getAddonsRgdData(modFolderData: String): Map> { + private fun getAddonsRgdData(modFolderData: String): Map> { val classicAddons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/addons") @@ -259,7 +268,7 @@ class ModParserService @Autowired constructor( return classicAddons + modAddons } - fun saveWeapons(modFolderData: String, mod: Mod, modDictionary: Map): Set { + private fun saveWeapons(modFolderData: String, mod: Mod, modDictionary: Map): Set { val classicWeapons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon") diff --git a/src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt b/src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt index dff2e49..9316485 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/ModsDiffService.kt @@ -26,8 +26,6 @@ class ModsDiffService @Autowired constructor( setOf(spaceMarinesPath, chaosPath, eldarPath, orksPath, guardPath, necronPath, tauPath, sistersPath) - - fun getUnitsAndSquadsDiff(modFolderData: String, oldModFolderData: String? = null): String { val oldModFolderPath = diff --git a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt index d472129..efa98f8 100644 --- a/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt +++ b/src/main/kotlin/com/dowstats/service/w40k/WeaponRgdExtractService.kt @@ -115,23 +115,23 @@ class WeaponRgdExtractService @Autowired constructor( val armourDamage = areaEffect ?.getRgdTableByName("weapon_damage") - ?.getRgdTableByName("armour_damage")!! - val minDamage = armourDamage.getDoubleByName("min_damage")!! - val maxDamage = armourDamage.getDoubleByName("max_damage")!! - val minDamageValue = armourDamage.getDoubleByName("min_damage_value")!! - val moraleDamage = armourDamage.getDoubleByName("morale_damage")!! + ?.getRgdTableByName("armour_damage") + val minDamage = armourDamage?.getDoubleByName("min_damage") ?: 0.0 + val maxDamage = armourDamage?.getDoubleByName("max_damage") ?: 0.0 + val minDamageValue = armourDamage?.getDoubleByName("min_damage_value") ?: 0.0 + val moraleDamage = armourDamage?.getDoubleByName("morale_damage") ?: 0.0 - val defaultArmourPiercing = armourDamage.getDoubleByName("armour_piercing") + val defaultArmourPiercing = armourDamage?.getDoubleByName("armour_piercing") val weaponDmgMap: Map = - armourDamage.getRgdTableByName("armour_piercing_types")!!.mapNotNull { armour_piercing -> + armourDamage?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing -> if (armour_piercing.name.contains("entry")) { val entry = armour_piercing.value as List val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","") val dmgValue = entry.getDoubleByName("armour_piercing_value") dmgType!! to dmgValue!! } else null - }.toMap() + }?.toMap() ?: emptyMap() val armoursPiercing = armorTypes.map { val weaponArmourPiercing = WeaponArmorPiercing() diff --git a/src/main/resources/db/0.0.1/procedures/delete_mod_data_procedure.sql b/src/main/resources/db/0.0.1/procedures/delete_mod_data_procedure.sql new file mode 100644 index 0000000..720000a --- /dev/null +++ b/src/main/resources/db/0.0.1/procedures/delete_mod_data_procedure.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE PROCEDURE delete_mod_data(mod_id_p bigint) + LANGUAGE SQL +AS $$ +DELETE FROM units WHERE mod_id = mod_id_p; +DELETE FROM buildings WHERE mod_id = mod_id_p; +DELETE FROM weapons WHERE mod_id = mod_id_p; +$$; \ No newline at end of file diff --git a/src/main/resources/db/0.0.1/schema/addon_modifiers.json b/src/main/resources/db/0.0.1/schema/addon_modifiers.json index 2a86160..7ae48c6 100644 --- a/src/main/resources/db/0.0.1/schema/addon_modifiers.json +++ b/src/main/resources/db/0.0.1/schema/addon_modifiers.json @@ -53,7 +53,8 @@ "baseTableName": "addon_modifiers", "constraintName": "fk_building_addons_addon_modifiers", "referencedColumnNames": "id", - "referencedTableName": "building_addons" + "referencedTableName": "building_addons", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/addon_requires.json b/src/main/resources/db/0.0.1/schema/addon_requires.json index 4fe22a2..0448a38 100644 --- a/src/main/resources/db/0.0.1/schema/addon_requires.json +++ b/src/main/resources/db/0.0.1/schema/addon_requires.json @@ -53,7 +53,8 @@ "baseTableName": "addon_requires", "constraintName": "fk_building_addons_addon_requires", "referencedColumnNames": "id", - "referencedTableName": "building_addons" + "referencedTableName": "building_addons", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/building_addons.json b/src/main/resources/db/0.0.1/schema/building_addons.json index d380367..14f9c90 100644 --- a/src/main/resources/db/0.0.1/schema/building_addons.json +++ b/src/main/resources/db/0.0.1/schema/building_addons.json @@ -94,7 +94,8 @@ "baseTableName": "building_addons", "constraintName": "fk_building_addons_buildings", "referencedColumnNames": "id", - "referencedTableName": "buildings" + "referencedTableName": "buildings", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/buildings_weapons.json b/src/main/resources/db/0.0.1/schema/buildings_weapons.json index b34ef9d..03cc369 100644 --- a/src/main/resources/db/0.0.1/schema/buildings_weapons.json +++ b/src/main/resources/db/0.0.1/schema/buildings_weapons.json @@ -62,7 +62,8 @@ "baseTableName": "buildings_weapons", "constraintName": "fk_buildings_buildings_weapons", "referencedColumnNames": "id", - "referencedTableName": "buildings" + "referencedTableName": "buildings", + "onDelete": "CASCADE" } }, { @@ -72,7 +73,8 @@ "baseTableName": "buildings_weapons", "constraintName": "fk_weapons_buildings_weapons", "referencedColumnNames": "id", - "referencedTableName": "weapons" + "referencedTableName": "weapons", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/procedure_delete_mod_data.json b/src/main/resources/db/0.0.1/schema/procedure_delete_mod_data.json new file mode 100644 index 0000000..cb67492 --- /dev/null +++ b/src/main/resources/db/0.0.1/schema/procedure_delete_mod_data.json @@ -0,0 +1,17 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "Creade delete_mod_data_procedure", + "author": "anibus", + "changes": [ + { + "createProcedure": { + "path": "db/0.0.1/procedures/delete_mod_data_procedure.sql" + } + } + ] + } + } + ] +} diff --git a/src/main/resources/db/0.0.1/schema/sergants.json b/src/main/resources/db/0.0.1/schema/sergants.json index d579d62..19fe16d 100644 --- a/src/main/resources/db/0.0.1/schema/sergants.json +++ b/src/main/resources/db/0.0.1/schema/sergants.json @@ -149,7 +149,8 @@ "baseTableName": "sergeants", "constraintName": "fk_sergeants_units", "referencedColumnNames": "id", - "referencedTableName": "units" + "referencedTableName": "units", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/sergants_weapons.json b/src/main/resources/db/0.0.1/schema/sergants_weapons.json index 6111a91..b8af43d 100644 --- a/src/main/resources/db/0.0.1/schema/sergants_weapons.json +++ b/src/main/resources/db/0.0.1/schema/sergants_weapons.json @@ -62,7 +62,8 @@ "baseTableName": "sergeants_weapons", "constraintName": "fk_sergeants_sergeants_weapons", "referencedColumnNames": "id", - "referencedTableName": "sergeants" + "referencedTableName": "sergeants", + "onDelete": "CASCADE" } }, { @@ -72,7 +73,8 @@ "baseTableName": "sergeants_weapons", "constraintName": "fk_weapons_sergeants_weapons", "referencedColumnNames": "id", - "referencedTableName": "weapons" + "referencedTableName": "weapons", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/units_weapons.json b/src/main/resources/db/0.0.1/schema/units_weapons.json index 35d27d5..304d8ce 100644 --- a/src/main/resources/db/0.0.1/schema/units_weapons.json +++ b/src/main/resources/db/0.0.1/schema/units_weapons.json @@ -62,7 +62,8 @@ "baseTableName": "units_weapons", "constraintName": "fk_units_units_weapons", "referencedColumnNames": "id", - "referencedTableName": "units" + "referencedTableName": "units", + "onDelete": "CASCADE" } }, { @@ -72,7 +73,8 @@ "baseTableName": "units_weapons", "constraintName": "fk_weapons_units_weapons", "referencedColumnNames": "id", - "referencedTableName": "weapons" + "referencedTableName": "weapons", + "onDelete": "CASCADE" } } ] diff --git a/src/main/resources/db/0.0.1/schema/weapons_armors_damage.json b/src/main/resources/db/0.0.1/schema/weapons_armors_piercing.json similarity index 95% rename from src/main/resources/db/0.0.1/schema/weapons_armors_damage.json rename to src/main/resources/db/0.0.1/schema/weapons_armors_piercing.json index fd52807..cc9a848 100644 --- a/src/main/resources/db/0.0.1/schema/weapons_armors_damage.json +++ b/src/main/resources/db/0.0.1/schema/weapons_armors_piercing.json @@ -53,7 +53,8 @@ "baseColumnNames": "weapon_id", "baseTableName": "weapons_armors_damage", "referencedColumnNames": "id", - "referencedTableName": "weapons" + "referencedTableName": "weapons", + "onDelete": "CASCADE" } },{ "addForeignKeyConstraint": diff --git a/src/main/resources/db/changelog-master.json b/src/main/resources/db/changelog-master.json index b6d3c23..1277c9d 100644 --- a/src/main/resources/db/changelog-master.json +++ b/src/main/resources/db/changelog-master.json @@ -55,7 +55,7 @@ } },{ "include": { - "file": "db/0.0.1/schema/weapons_armors_damage.json" + "file": "db/0.0.1/schema/weapons_armors_piercing.json" } },{ "include": { @@ -69,6 +69,10 @@ "include": { "file": "db/0.0.1/data/races.json" } + },{ + "include": { + "file": "db/0.0.1/schema/procedure_delete_mod_data.json" + } } ] }