Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DraftSchematicLoader that parses all the json files under server_data… #1533

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/***********************************************************************************
* Copyright (c) 2023 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* --------------------------------------------------------------------------------*
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
***********************************************************************************/
package com.projectswg.holocore.resources.support.data.server_info.loader

import com.projectswg.common.data.CRC
import com.projectswg.common.data.encodables.oob.StringId
import com.projectswg.common.data.schematic.DraftSchematic
import com.projectswg.common.data.schematic.DraftSlotDataOption
import com.projectswg.common.data.schematic.IngridientSlot
import com.projectswg.common.data.schematic.IngridientSlot.IngridientType
import com.projectswg.common.data.swgfile.ClientFactory
import com.projectswg.common.data.swgfile.visitors.ObjectData
import com.projectswg.holocore.resources.support.data.server_info.StandardLog
import me.joshlarson.json.JSON
import me.joshlarson.json.JSONObject
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.text.Charsets.UTF_8

class DraftSchematicLoader : DataLoader() {

private val draftSchematics: MutableMap<String, DraftSchematic> = HashMap()

fun getDraftSchematic(draftSchematicIff: String): DraftSchematic? {
return draftSchematics[draftSchematicIff]
}

override fun load() {
val what = "draft schematics"
val start = StandardLog.onStartLoad(what)

loadAllDraftSchematics()

StandardLog.onEndLoad(draftSchematics.size, what, start)
}

private fun loadAllDraftSchematics() {
val files = findAllDraftSchematicJsonFiles()

for (file in files) {
val jsonFilePath = file.path
val iffDraftSchematicPath = jsonFilePath.replace("\\", "/").replaceFirst("serverdata/", "object/").replace(".json", ".iff")
val fileToJsonString = fileToJsonString(file)
val sharedIffDraftSchematicPath = ClientFactory.formatToSharedFile(iffDraftSchematicPath)
val draftSchematic = jsonToDraftSchematic(fileToJsonString, sharedIffDraftSchematicPath)

draftSchematics[sharedIffDraftSchematicPath] = draftSchematic
}
}

private fun findAllDraftSchematicJsonFiles(): List<File> {
val base = Paths.get("serverdata/draft_schematic")
val pathStream = Files.find(base, 10, { path, _ -> path.toString().endsWith(".json") })

return pathStream.map { it.toFile() }.toList()
}

private fun fileToJsonString(file: File): String {
return file.readText(charset = UTF_8)
}

private fun jsonToDraftSchematic(json: String, iffDraftSchematicPath: String): DraftSchematic {
val draftSchematic = DraftSchematic()
val jsonObject = JSON.readObject(json)

setItemsPerContainer(jsonObject, draftSchematic)
setCraftedSharedTemplate(jsonObject, draftSchematic)
setCombinedCrc(iffDraftSchematicPath, draftSchematic)
setVolume(jsonObject, draftSchematic)
setComplexity(jsonObject, draftSchematic)
setSlots(jsonObject, draftSchematic)

return draftSchematic
}

private fun setSlots(jsonObject: JSONObject, draftSchematic: DraftSchematic) {
if (jsonObject.containsKey("slots")) {
val array = jsonObject.getArray("slots")
for (any in array) {
val slotObject = any as Map<*, *>
val name = stringIdName(slotObject)
val optional = slotObject["optional"] as Boolean
val slot = IngridientSlot(name, optional)
draftSchematic.ingridientSlot.add(slot)

setOptions(slotObject, slot)
}
}
}

private fun setOptions(slotObject: Map<*, *>, slot: IngridientSlot) {
val options = slotObject["options"] as List<Map<*, *>>
for (option in options) {
val ingredientType = ingridientType(option)
setIngredients(option, slot, ingredientType)
}
}

private fun ingridientType(option: Map<*, *>) : IngridientType {
val value = option["ingredientType"] as String

return when (value) {
"IT_none" -> IngridientType.IT_NONE
"IT_resourceType" -> IngridientType.IT_RESOURCE_TYPE
"IT_resourceClass" -> IngridientType.IT_RESOURCE_CLASS
"IT_template" -> IngridientType.IT_TEMPLATE
"IT_templateGeneric" -> IngridientType.IT_TEMPLATE
"IT_schematic" -> IngridientType.IT_SCHEMATIC
else -> throw IllegalArgumentException("Unknown ingredient type: $value. Maybe it just needs to be mapped?")
}
}

private fun setIngredients(option: Map<*, *>, slot: IngridientSlot, ingredientType: IngridientType) {
val ingredients = option["ingredients"] as List<Map<*, *>>

for (ingredient in ingredients) {
val name = stringIdName(ingredient)
val ingredientName = ingredient["ingredient"] as String
val amount = (ingredient["count"] as Long).toInt()
slot.addSlotDataOption(DraftSlotDataOption(name, resolveIngredientName(ingredientName), ingredientType.slotType, amount))
}
}

private fun setComplexity(jsonObject: JSONObject, draftSchematic: DraftSchematic) {
val complexity = jsonObject["complexity"] as Long?
if (complexity != null) {
draftSchematic.complexity = complexity.toInt()
}
}

private fun setVolume(jsonObject: JSONObject, draftSchematic: DraftSchematic) {
val volume = jsonObject["volume"] as Long?
if (volume != null) {
draftSchematic.volume = volume.toInt()
}
}

private fun setItemsPerContainer(jsonObject: JSONObject, draftSchematic: DraftSchematic) {
val itemsPerContainer = jsonObject["itemsPerContainer"] as Long?
if (itemsPerContainer != null) {
draftSchematic.itemsPerContainer = itemsPerContainer.toInt()
draftSchematic.isCanManufacture = itemsPerContainer > 0
}
}

private fun setCraftedSharedTemplate(jsonObject: JSONObject, draftSchematic: DraftSchematic) {
val craftedObjectTemplate = jsonObject["craftedObjectTemplate"] as String?
if (!craftedObjectTemplate.isNullOrEmpty()) {
draftSchematic.craftedSharedTemplate = ClientFactory.formatToSharedFile(craftedObjectTemplate)
}
}

private fun setCombinedCrc(iffDraftSchematicPath: String, draftSchematic: DraftSchematic) {
val serverCrc = getDraftSchematicServerCrc(iffDraftSchematicPath)
val clientCrc = getDraftSchematicClientCrc(iffDraftSchematicPath)
val combinedCrc = combinedCrc(serverCrc = serverCrc, clientCrc = clientCrc)
draftSchematic.combinedCrc = combinedCrc
}

private fun stringIdName(map: Map<*, *>): StringId {
val nameStrings = map["name"] as List<String>
return StringId(nameStrings[0], nameStrings[1])
}

private fun getDraftSchematicServerCrc(schematicInGroupShared: String): Int {
return CRC.getCrc(schematicInGroupShared)
}

private fun getDraftSchematicClientCrc(schematicInGroupShared: String): Int {
val templateWithoutPrefix = schematicInGroupShared.replace("object/draft_schematic/", "")
return CRC.getCrc(templateWithoutPrefix)
}

private fun combinedCrc(serverCrc: Int, clientCrc: Int): Long {
return serverCrc.toLong() shl 32 and -0x100000000L or (clientCrc.toLong() and 0x00000000FFFFFFFFL)
}

private fun resolveIngredientName(ingredientName: String): String {
if (ingredientName.endsWith(".iff")) {
val attributes = ServerData.objectData.getAttributes(ClientFactory.formatToSharedFile(ingredientName))
if (attributes != null) {
val stringId = attributes[ObjectData.ObjectDataAttribute.OBJECT_NAME] as StringId
return stringId.toString()
}
}

return ingredientName
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ object ServerData {
val staticCityPoints by SoftDataLoaderDelegate(::StaticCityPointLoader)
val npcEquipment by SoftDataLoaderDelegate(::NpcEquipmentLoader)
val schematicGroups by SoftDataLoaderDelegate(::SchematicGroupLoader)
val draftSchematics by SoftDataLoaderDelegate(::DraftSchematicLoader)

private class WeakDataLoaderDelegate<T: DataLoader>(loaderCreator: () -> T): DataLoaderDelegate<T>(::WeakReference, loaderCreator)
private class SoftDataLoaderDelegate<T: DataLoader>(loaderCreator: () -> T): DataLoaderDelegate<T>(::SoftReference, loaderCreator)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/***********************************************************************************
* Copyright (c) 2023 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* --------------------------------------------------------------------------------*
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
***********************************************************************************/
package com.projectswg.holocore.resources.support.data.server_info.loader

import com.projectswg.common.data.encodables.oob.StringId
import com.projectswg.common.data.schematic.SlotType
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test

class DraftSchematicLoaderTest {

@Test
fun craftedTemplateIsTransformedIntoSharedTemplate() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals("object/tangible/deed/camp_deed/shared_camp_luxury_deed.iff", draftSchematic.craftedSharedTemplate)
}

@Test
fun itemsPerContainer() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(10, draftSchematic.itemsPerContainer)
}

@Test
fun volume() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(1, draftSchematic.volume)
}

@Test
fun complexity() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(17, draftSchematic.complexity)
}

@Test
fun combinedCrc() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/instrument/shared_instrument_slitherhorn.iff") ?: fail("Could not find draft schematic")

assertEquals(8706505225174593761, draftSchematic.combinedCrc)
}

@Nested
inner class Slots {

@Test
fun allSlotsAreLoaded() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(10, draftSchematic.ingridientSlot.size)
}

@Test
fun optional() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertFalse(draftSchematic.ingridientSlot[0].isOptional)
}

@Test
fun name() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(StringId("craft_item_ingredients_n", "shelter_panels"), draftSchematic.ingridientSlot[0].name)
}

@Nested
inner class Ingredients {

@Test
fun ingredientType() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(SlotType.RESOURCES, draftSchematic.ingridientSlot[0].fromSlotDataOption[0].slotType)
}

@Test
fun allIngredientsAreLoaded() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(1, draftSchematic.ingridientSlot[0].fromSlotDataOption.size)
}

@Test
fun name() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(StringId("craft_item_ingredients_n", "shelter_panels"), draftSchematic.ingridientSlot[0].fromSlotDataOption[0].stfName)
}

@Test
fun ingredient() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals("aluminum", draftSchematic.ingridientSlot[0].fromSlotDataOption[0].ingredientName)
}

@Test
fun ingredientIffResolved() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/weapon/shared_bowcaster_assault.iff") ?: fail("Could not find draft schematic")

assertEquals("@craft_weapon_ingredients_n:blaster_power_handler_advanced", draftSchematic.ingridientSlot[4].fromSlotDataOption[0].ingredientName)
}

@Test
fun count() {
val draftSchematic = ServerData.draftSchematics.getDraftSchematic("object/draft_schematic/camp/shared_camp_luxury.iff") ?: fail("Could not find draft schematic")

assertEquals(250, draftSchematic.ingridientSlot[0].fromSlotDataOption[0].amount)
}
}
}
}