421 lines
16 KiB
Kotlin
421 lines
16 KiB
Kotlin
package com.dowstats.service.w40k
|
|
|
|
import com.dowstats.data.entities.*
|
|
import com.dowstats.data.entities.research.Research
|
|
import com.dowstats.data.repositories.*
|
|
import com.dowstats.data.rgd.RgdData
|
|
import com.dowstats.service.postparsing.PostParsingService
|
|
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 researchRgdExtractService: ResearchRgdExtractService,
|
|
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 postParsingService: PostParsingService,
|
|
private val researchRepository: ResearchRepository,
|
|
private val modRepository: ModRepository,
|
|
) {
|
|
|
|
val defaultDictionary: MutableMap<Int, String> = 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}")
|
|
modRepository.clearModData(mod.id!!)
|
|
|
|
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()
|
|
|
|
val armorTypes = if(mod.technicalName?.contains("UltimateApocalypse") == true)
|
|
armorTypeRepository.findByIsUa(true).toSet()
|
|
else armorTypeRepository.findByIsClassic(true).toSet()
|
|
|
|
racesList.forEach {
|
|
unitRepository.deleteAllByModIdAndRaceId(mod.id!!, it)
|
|
}
|
|
|
|
val modDictionary: Map<Int, String> = getModDictionary(mod, modFolderData)
|
|
|
|
log.info("Extract dictionaries from $modFolderData")
|
|
|
|
val enrichedModDictionary = defaultDictionary + modDictionary
|
|
|
|
val weapons = saveWeapons(modFolderData, mod, armorTypes, enrichedModDictionary)
|
|
val researches = saveResearches(modFolderData, mod, enrichedModDictionary)
|
|
saveUnits(modFolderData, weapons, racesList, armorTypes, mod, enrichedModDictionary)
|
|
saveBuildings(modFolderData, weapons, researches, racesList, armorTypes, mod, enrichedModDictionary)
|
|
|
|
modRepository.removeCampaignEntities(mod.id!!)
|
|
postParsingService.bindResearch(mod)
|
|
log.info("Complete parse mod ${mod.technicalName}:${mod.version}")
|
|
|
|
|
|
}
|
|
|
|
private fun getModDictionary(mod: Mod, modFolderData: String): Map<Int, String> {
|
|
val folder =
|
|
if (mod.technicalName == "multidungeon_rightpocalypse") modAttribPathService.getUcsFolderRus(modFolderData) else modAttribPathService.getUcsFolder(
|
|
modFolderData
|
|
)
|
|
|
|
val modDictionary = mutableMapOf<Int, String>()
|
|
|
|
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 saveResearches(
|
|
modFolderData: String,
|
|
mod: Mod,
|
|
modDictionary: Map<Int, String>
|
|
): Set<Research> {
|
|
|
|
val classicRgdDataResearches =
|
|
rgdParserService.parseFolderToRgdFiles(
|
|
modAttribPathService.getResearchAttribsPath(
|
|
modAttribPathService.pathToWanilaData,
|
|
)
|
|
)
|
|
val modRgdDataResearches =
|
|
rgdParserService.parseFolderToRgdFiles(
|
|
modAttribPathService.getResearchAttribsPath(
|
|
modFolderData,
|
|
)
|
|
)
|
|
|
|
val researchesFull = classicRgdDataResearches + modRgdDataResearches
|
|
|
|
val researches = researchesFull.mapNotNull { researchData ->
|
|
try {
|
|
researchRgdExtractService.extractToResearchEntity(
|
|
researchData.key,
|
|
modDictionary,
|
|
researchData.value,
|
|
modFolderData,
|
|
mod,
|
|
)
|
|
} catch (e: Exception) {
|
|
log.error("Can't extract ${researchData.key}", e)
|
|
null
|
|
}
|
|
}
|
|
return try {
|
|
researchRepository.saveAll(researches).toSet()
|
|
} catch (e: Exception) {
|
|
throw e
|
|
}
|
|
}
|
|
|
|
private fun saveUnits(
|
|
modFolderData: String,
|
|
weapons: Set<Weapon>,
|
|
racesList: List<String>,
|
|
armorTypes: Set<ArmorType>,
|
|
mod: Mod,
|
|
modDictionary: Map<Int, String>
|
|
) {
|
|
|
|
val races = raceRepository.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<RgdData> =
|
|
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,
|
|
baseUnitName,
|
|
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.flatMap { weapon ->
|
|
sergeant.weaponHardpoints
|
|
.filter { it.weaponId == weapon.id }
|
|
.map { hardpointOrder ->
|
|
SergeantWeapon().also {
|
|
it.sergeant = sergeant
|
|
it.weapon = weapon
|
|
|
|
it.sergeantWeaponKey = SergeantWeaponKey().also { swk ->
|
|
swk.sergeantId = sergeant.id
|
|
swk.weaponId = weapon.id
|
|
swk.hardpoint = hardpointOrder.hardpoint
|
|
swk.hardpointOrder = hardpointOrder.order
|
|
}
|
|
}
|
|
}
|
|
}.toMutableSet()
|
|
sergeantRepository.save(sergeant)
|
|
}
|
|
} catch (e: Exception) {
|
|
log.error("Cant save unit ${unitDataToSave.unit.name}", e)
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun saveBuildings(
|
|
modFolderData: String,
|
|
weapons: Set<Weapon>,
|
|
researches: Set<Research>,
|
|
racesList: List<String>,
|
|
armorTypes: Set<ArmorType>,
|
|
mod: Mod, modDictionary: Map<Int, String>
|
|
) {
|
|
|
|
val races = raceRepository.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
|
|
|
|
if(raceFolder == "npc") return@forEach
|
|
val race = races.find { it.id == raceFolder }
|
|
if(race == null){
|
|
log.warn("Can't find race $raceFolder")
|
|
return@forEach
|
|
}
|
|
val raceUnits = unitRepository.findByModIdAndRace(mod.id!!, race)
|
|
|
|
modStructuresFull.forEach { structure ->
|
|
val structureDataToSave =
|
|
try {
|
|
buildingExtractService.extractToBuildingEntity(
|
|
structure.key,
|
|
modDictionary,
|
|
structure.value,
|
|
weapons,
|
|
researches,
|
|
raceUnits,
|
|
race,
|
|
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.flatMap { weapon ->
|
|
building.weaponHardpoints
|
|
.filter { it.weaponId == weapon.id }
|
|
.map { hardpointOrder ->
|
|
BuildingWeapon().also {
|
|
it.building = building
|
|
it.weapon = weapon
|
|
it.buildingWeaponKey = BuildingWeaponKey().also {
|
|
it.buildingId = building.id
|
|
it.weaponId = weapon.id
|
|
it.hardpoint = hardpointOrder.hardpoint
|
|
it.hardpointOrder = hardpointOrder.order
|
|
}
|
|
}
|
|
}
|
|
}.toMutableSet()
|
|
buildingRepository.save(building)
|
|
|
|
} catch (e: Exception) {
|
|
throw e
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
private fun getAddonsRgdData(modFolderData: String): Map<String, List<RgdData>> {
|
|
|
|
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, armorTypes: Set<ArmorType>, modDictionary: Map<Int, String>): Set<Weapon> {
|
|
|
|
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, armorTypes, modFolderData, modDictionary)
|
|
}
|
|
|
|
return try {
|
|
weaponRepository.saveAll(weaponsToSave).toSet()
|
|
} catch (e: Exception) {
|
|
throw e
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|