package com.dowstats.service.w40k import com.dowstats.data.entities.* import com.dowstats.data.repositories.* import com.dowstats.data.rgd.RgdData import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service import java.lang.Exception import jakarta.transaction.Transactional import org.slf4j.LoggerFactory import java.io.File import java.nio.file.Files import kotlin.io.path.Path import kotlin.io.path.name @Service class ModParserService @Autowired constructor( val rgdParserService: RgdParserService, val unitRgdExtractService: UnitRgdExtractService, val raceRepository: RaceRepository, val armorTypeRepository: ArmorTypeRepository, val unitRepository: UnitRepository, val sergeantRepository: SergeantRepository, val weaponRgdExtractService: WeaponRgdExtractService, val weaponRepository: WeaponRepository, val modAttribPathService: ModAttribPathService, val buildingRepository: BuildingRepository, val buildingExtractService: BuildingRgdExtractService, ) { val defaultDictionary: MutableMap = mutableMapOf() init { RgdParserService::class.java.getClassLoader().getResourceAsStream("DXP2.ucs").bufferedReader(Charsets.UTF_8).lines().forEach { val kv = it.split("\\s+".toRegex()) if (kv.size < 2) return@forEach val key = kv.first().filter { it.isDigit() }.toInt() val value = kv.drop(1).joinToString(" ").replace("\u0000","") defaultDictionary[key] = value } RgdParserService::class.java.getClassLoader().getResourceAsStream("W40k.ucs").bufferedReader(Charsets.UTF_8).lines().forEach { val kv = it.split("\\s+".toRegex()) if (kv.size < 2) return@forEach val key = kv.first().filter { it.isDigit() }.toInt() val value = kv.drop(1).joinToString(" ").replace("\u0000","") defaultDictionary[key] = value } } val log = LoggerFactory.getLogger(ModParserService::class.java) @Transactional fun parseModFilesAndSaveToDb(mod: Mod) { log.info("Start parse mod files ${mod.technicalName}:${mod.version}") val modFolderData = modAttribPathService.getModFolderData(mod.technicalName!!, mod.version!!) val racesList = Files.walk(Path(modAttribPathService.getSbpsAttribsFolderPath(modFolderData)), 1) .toList() .drop(1) .filter { Files.isDirectory(it) }.map { it.name }.toList() racesList.forEach{ unitRepository.deleteAllByModIdAndRaceId(mod.id!!, it) } val modDictionary: Map = getModDictionary(mod, modFolderData) log.info("Extract dictionaries from $modFolderData") 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) { log.warn("Cant' get key ${kv.first()} for dict") 0 } val value = kv.drop(1).joinToString(" ").replace("\u0000","") modDictionary[key] = value } } return modDictionary } private fun saveUnits(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { val races = raceRepository.findAll().toList() val armorTypes = armorTypeRepository.findAll().toList() racesList.forEach { raceFolder -> val classicRgdDataSquads = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getSbpsAttribsPath(modAttribPathService.pathToWanilaData, raceFolder)) val modRgdDataSquads = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getSbpsAttribsPath(modFolderData, raceFolder)) val classicRgdDataUnits = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getEbpsTroopsAttribsPath(modAttribPathService.pathToWanilaData, raceFolder)) val modRgdDataUnits = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getEbpsTroopsAttribsPath(modFolderData, raceFolder)) val modSquadsFull = classicRgdDataSquads + modRgdDataSquads val modUnitsFull = classicRgdDataUnits + modRgdDataUnits modSquadsFull.forEach { squadRgdData -> val baseUnitName = unitRgdExtractService.getUnitRgdFileNameFromSquadData(squadRgdData.value) ?: throw Exception("Can't extract unit name from squad ${squadRgdData.key}") log.info("Start extracting $raceFolder: $baseUnitName") val unitRgdData: List = modUnitsFull[baseUnitName] ?: emptyList() if(unitRgdData.isEmpty()){ log.warn("Can't find rgd data for unit $baseUnitName") return@forEach } val unitDataToSave = try { unitRgdExtractService.extractToUnitEntity( squadRgdData.key, modDictionary, squadRgdData.value, unitRgdData, weapons, raceFolder, modFolderData, mod, races, armorTypes, modUnitsFull ) } catch (e: Exception) { log.error("Can't extract $baseUnitName", e) return@forEach } try { val unit = unitRepository.save(unitDataToSave.unit) unit.weapons = unitDataToSave.unitWeapons?.map {weapon -> UnitWeapon().also { it.unit = unit it.weapon = weapon.weapon it.unitWeaponKey = UnitWeaponKey().also { it.unitId = unit.id it.weaponId = weapon.weapon?.id it.hardpoint = weapon.hardpoint it.hardpointOrder = weapon.hardpointOrder } } }?.toMutableSet() unitRepository.save(unit) unitDataToSave.sergeants?.forEach { sergeantToSave -> val sergeant = sergeantRepository.save(sergeantToSave.first.also { it.unit = unit }) sergeant.weapons = sergeantToSave.second.map {weapon -> SergeantWeapon().also { it.sergeant = sergeant it.weapon = weapon it.sergeantWeaponKey = SergeantWeaponKey().also { swk -> swk.sergeantId = sergeant.id swk.weaponId = weapon.id swk.hardpoint = weapon.hardpoint swk.hardpointOrder = weapon.hardpointOrder } } }.toMutableSet() sergeantRepository.save(sergeant) } } catch (e: Exception) { throw e } } } } private fun saveBuildings(modFolderData: String, weapons: Set, racesList: List, mod: Mod, modDictionary: Map) { val races = raceRepository.findAll().toList() val armorTypes = armorTypeRepository.findAll().toList() val addonsRgdData = getAddonsRgdData(modFolderData) racesList.forEach { raceFolder -> val classicRgdDataStructures = rgdParserService.parseFolderToRgdFiles(modAttribPathService.geBuildingAttribsPath(modAttribPathService.pathToWanilaData, raceFolder)) val modRgdDataStructures = rgdParserService.parseFolderToRgdFiles(modAttribPathService.geBuildingAttribsPath(modFolderData, raceFolder)) val modStructuresFull = classicRgdDataStructures + modRgdDataStructures modStructuresFull.forEach { structure -> val structureDataToSave = try { buildingExtractService.extractToBuildingEntity( structure.key, modDictionary, structure.value, weapons, raceFolder, modFolderData, mod, races, armorTypes, addonsRgdData, ) } catch (e: Exception) { log.error("Can't extract ${structure.key}", e) return@forEach } try { val building = buildingRepository.save(structureDataToSave.building) building.weapons = structureDataToSave.buildingWeapons.map {weapon -> BuildingWeapon().also { it.building = building it.weapon = weapon it.buildingWeaponKey = BuildingWeaponKey().also { it.buildingId = building.id it.weaponId = weapon.id it.hardpoint = weapon.hardpoint it.hardpointOrder = weapon.hardpointOrder } } }.toMutableSet() buildingRepository.save(building) } catch (e: Exception) { throw e } } } } private fun getAddonsRgdData(modFolderData: String): Map> { val classicAddons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/addons") val modAddons = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getAddonAttribsPath(modFolderData)) return classicAddons + modAddons } private fun saveWeapons(modFolderData: String, mod: Mod, modDictionary: Map): Set { val classicWeapons = rgdParserService.parseFolderToRgdFiles("${modAttribPathService.pathToWanilaData}/attrib/weapon") val modWeapons = rgdParserService.parseFolderToRgdFiles(modAttribPathService.getWeaponAttribsPath(modFolderData)) val allWeapons = classicWeapons + modWeapons val weaponsToSave = allWeapons.mapNotNull { weaponRgdExtractService.extractToWeaponEntity(it.key, it.value, mod, modFolderData, modDictionary) } return try { weaponRepository.saveAll(weaponsToSave).toSet() } catch (e: Exception) { throw e } } }