From cec266d39658a8e2cf0dc942c1b26d8416c21b45 Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Mon, 17 Apr 2017 16:14:35 +0900 Subject: [PATCH] modular first test --- assets/modules/LoadOrder.csv | 9 ++ .../colourmap/black_body_how_to_use.txt | 0 .../basegame}/tapestries/the_world | Bin assets/modules/basegame/tiles/tileid.csv | 2 - assets/modules/basegame/tiles/tty.lua | 1 - assets/modules/modules_doc.md | 15 +--- src/net/torvald/colourutil/ColourTemp.kt | 3 +- src/net/torvald/terrarum/ModuleManager.kt | 77 ++++++++++++++++++ src/net/torvald/terrarum/Terrarum.kt | 5 ++ .../terrarum/gameactors/CreatureBuilder.kt | 4 +- .../terrarum/gameactors/InjectCreatureRaw.kt | 6 +- .../terrarum/gameactors/PlayerBuilder.kt | 2 +- .../gameactors/PlayerBuilderCynthia.kt | 5 +- .../gameactors/PlayerBuilderSigrid.kt | 7 +- .../gameactors/PlayerBuilderTestSubject1.kt | 5 +- .../gameactors/faction/FactionFactory.kt | 7 +- .../torvald/terrarum/mapdrawer/TilesDrawer.kt | 19 ++--- .../torvald/terrarum/weather/WeatherMixer.kt | 17 ++-- 18 files changed, 128 insertions(+), 56 deletions(-) create mode 100644 assets/modules/LoadOrder.csv rename assets/{graphics => modules/basegame}/colourmap/black_body_how_to_use.txt (100%) rename assets/{graphics => modules/basegame}/tapestries/the_world (100%) delete mode 100644 assets/modules/basegame/tiles/tileid.csv delete mode 100644 assets/modules/basegame/tiles/tty.lua create mode 100644 src/net/torvald/terrarum/ModuleManager.kt diff --git a/assets/modules/LoadOrder.csv b/assets/modules/LoadOrder.csv new file mode 100644 index 000000000..c7cc161b3 --- /dev/null +++ b/assets/modules/LoadOrder.csv @@ -0,0 +1,9 @@ +# Load order +# Modules are loaded from top to bottom. +# And yes, you can disable basegame, but we don't recommend. +# Acceptable formats: +# module_name,description_in_English_no_comma, +# module_name,description_in_English_no_comma,(external Jar 1);(external Jar 2); ... + +basegame,The base game, +dwarventech,Logic gates and machines, diff --git a/assets/graphics/colourmap/black_body_how_to_use.txt b/assets/modules/basegame/colourmap/black_body_how_to_use.txt similarity index 100% rename from assets/graphics/colourmap/black_body_how_to_use.txt rename to assets/modules/basegame/colourmap/black_body_how_to_use.txt diff --git a/assets/graphics/tapestries/the_world b/assets/modules/basegame/tapestries/the_world similarity index 100% rename from assets/graphics/tapestries/the_world rename to assets/modules/basegame/tapestries/the_world diff --git a/assets/modules/basegame/tiles/tileid.csv b/assets/modules/basegame/tiles/tileid.csv deleted file mode 100644 index 607bc9106..000000000 --- a/assets/modules/basegame/tiles/tileid.csv +++ /dev/null @@ -1,2 +0,0 @@ - "id";"filename" -"12345";"tty.lua" \ No newline at end of file diff --git a/assets/modules/basegame/tiles/tty.lua b/assets/modules/basegame/tiles/tty.lua deleted file mode 100644 index 8b1378917..000000000 --- a/assets/modules/basegame/tiles/tty.lua +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/modules/modules_doc.md b/assets/modules/modules_doc.md index 9968fa45e..b622f55ee 100644 --- a/assets/modules/modules_doc.md +++ b/assets/modules/modules_doc.md @@ -1,14 +1,3 @@ -Modules may have following subdirectories: +## Register modules -- creatures -- factions -- items -- tiles -- weathers - - -Modules must be packed with Terrarum Virtual Disk format. The packer is located in - - lib/TerranVirtualDisk.jar - -For more information, please refer to [this link](https://github.com/minjaesong/TerranVirtualDisk) \ No newline at end of file +Only the modules that enlisted in LoadOrder.csv will be loaded. diff --git a/src/net/torvald/colourutil/ColourTemp.kt b/src/net/torvald/colourutil/ColourTemp.kt index 32b305158..299aa7fac 100644 --- a/src/net/torvald/colourutil/ColourTemp.kt +++ b/src/net/torvald/colourutil/ColourTemp.kt @@ -5,13 +5,14 @@ import net.torvald.terrarum.weather.toColor import org.newdawn.slick.Color import org.newdawn.slick.Image import net.torvald.colourutil.CIEXYZUtil.toColor +import net.torvald.terrarum.ModuleManager /** * RGB-modeled CCT calculator * Created by minjaesong on 16-07-26. */ object ColourTemp { - private var envOverlayColourmap = Image("./assets/graphics/colourmap/black_body_col_1000_40000_K.tga") + private var envOverlayColourmap = Image(ModuleManager.getPath("basegame", "colourmap/black_body_col_1000_40000_K.tga")) private fun colTempToImagePos(K: Int): Int { if (K < 1000 || K >= 40000) throw IllegalArgumentException("K: out of range. ($K)") diff --git a/src/net/torvald/terrarum/ModuleManager.kt b/src/net/torvald/terrarum/ModuleManager.kt new file mode 100644 index 000000000..e69986f64 --- /dev/null +++ b/src/net/torvald/terrarum/ModuleManager.kt @@ -0,0 +1,77 @@ +package net.torvald.terrarum + +import org.apache.commons.csv.CSVFormat +import org.apache.commons.csv.CSVParser +import java.io.File +import java.io.FileNotFoundException +import java.nio.file.FileSystems + +/** + * Modules Resource Manager + * + * Created by SKYHi14 on 2017-04-17. + */ +object ModuleManager { + + data class ModuleMetadata(val order: Int, val isDir: Boolean, val desc: String, val libraries: Array) { + override fun toString() = + "\tModule #$order -- $desc\n" + + "\tExternal libraries: ${libraries.joinToString(", ")}" + } + const val modDir = "./assets/modules" + + val moduleInfo = HashMap() + + init { + // load modules + val loadOrderCSVparser = CSVParser.parse( + FileSystems.getDefault().getPath("$modDir/LoadOrder.csv").toFile(), + Charsets.UTF_8, + CSVFormat.DEFAULT.withCommentMarker('#') + ) + val loadOrder = loadOrderCSVparser.records + loadOrderCSVparser.close() + + + loadOrder.forEachIndexed { index, it -> + val moduleName = it[0] + println("[ModuleManager] Loading module $moduleName") + + val description = it[1] + val libs = it[2].split(';').toTypedArray() + val isDir = FileSystems.getDefault().getPath("$modDir/$moduleName").toFile().isDirectory + moduleInfo[moduleName] = ModuleMetadata(index, isDir, description, libs) + + println(moduleInfo[moduleName]) + } + } + + private fun checkExistence(module: String) { + if (!moduleInfo.containsKey(module)) + throw FileNotFoundException("No such module: $module") + } + private fun String.sanitisePath() = if (this[0] == '/' || this[0] == '\\') + this.substring(1..this.lastIndex) + else this + + + + fun getPath(module: String, path: String): String { + checkExistence(module) + return "$modDir/$module/${path.sanitisePath()}" + } + fun getFile(module: String, path: String): File { + checkExistence(module) + return FileSystems.getDefault().getPath(getPath(module, path)).toFile() + } + fun getFiles(module: String, path: String): Array { + checkExistence(module) + val dir = FileSystems.getDefault().getPath(getPath(module, path)).toFile() + if (!dir.isDirectory) { + throw FileNotFoundException("The path is not a directory") + } + else { + return dir.listFiles() + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index ded03e15e..a12fe515a 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -311,6 +311,11 @@ object Terrarum : StateBasedGame(GAME_NAME) { } } + + // load modules + ModuleManager + + gc.graphics.clear() // clean up any 'dust' in the buffer //addState(StateVTTest()) diff --git a/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt b/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt index 156d88ef5..8811a7eab 100644 --- a/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt +++ b/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt @@ -18,9 +18,9 @@ object CreatureBuilder { * @Param jsonFileName with extension */ @Throws(IOException::class, SlickException::class) - operator fun invoke(jsonFileName: String): ActorWithSprite { + operator fun invoke(module: String, jsonFileName: String): ActorWithSprite { val actor = ActorWithSprite(Actor.RenderOrder.MIDDLE) - InjectCreatureRaw(actor.actorValue, jsonFileName) + InjectCreatureRaw(actor.actorValue, module, jsonFileName) return actor } diff --git a/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt b/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt index 1862ac106..9c45674ea 100644 --- a/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt +++ b/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt @@ -5,6 +5,7 @@ import net.torvald.random.Fudge3 import net.torvald.terrarum.langpack.Lang import com.google.gson.JsonObject import net.torvald.terrarum.ActorValue +import net.torvald.terrarum.ModuleManager import net.torvald.terrarum.gameactors.ActorHumanoid import org.newdawn.slick.SlickException import java.io.IOException @@ -15,7 +16,6 @@ import java.security.SecureRandom */ object InjectCreatureRaw { - const val JSONPATH = "./assets/raw/creatures/" private const val JSONMULT = "mult" // one appears in JSON files /** @@ -24,8 +24,8 @@ object InjectCreatureRaw { * @param actorValueRef ActorValue object to be injected. * @param jsonFileName with extension */ - operator fun invoke(actorValueRef: ActorValue, jsonFileName: String) { - val jsonObj = JsonFetcher(JSONPATH + jsonFileName) + operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) { + val jsonObj = JsonFetcher(ModuleManager.getPath(module, "creatures/$jsonFileName")) val elementsInt = arrayOf(AVKey.BASEHEIGHT, AVKey.TOOLSIZE, AVKey.ENCUMBRANCE) val elementsString = arrayOf(AVKey.RACENAME, AVKey.RACENAMEPLURAL) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt index 537ce9e3e..348d9e5cb 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt @@ -13,7 +13,7 @@ object PlayerBuilder { operator fun invoke(): Actor { val p: Actor = Player(Terrarum.ingame!!.world.time.currentTimeAsGameDate) - InjectCreatureRaw(p.actorValue, "CreatureHuman.json") + InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json") // attach sprite diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt index c838d6079..da664ce63 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.gameactors import net.torvald.spriteanimation.SpriteAnimation +import net.torvald.terrarum.ModuleManager import net.torvald.terrarum.gameactors.ActorHumanoid import net.torvald.terrarum.gameactors.ai.LuaAIWrapper import net.torvald.terrarum.mapdrawer.FeaturesDrawer @@ -15,14 +16,14 @@ object PlayerBuilderCynthia { val p: HumanoidNPC = HumanoidNPC( LuaAIWrapper("/net/torvald/terrarum/gameactors/ai/scripts/PokemonNPCAI.lua"), GameDate(100, 143)) // random value thrown - InjectCreatureRaw(p.actorValue, "CreatureHuman.json") + InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json") p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 p.actorValue[AVKey.NAME] = "Cynthia" - p.makeNewSprite(26, 42, "assets/graphics/sprites/test_player_2.tga") + p.makeNewSprite(26, 42, ModuleManager.getPath("basegame", "sprites/test_player_2.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(1, 1) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt index 446529b55..0201eddfd 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt @@ -5,6 +5,7 @@ import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.spriteanimation.SpriteAnimation import com.google.gson.JsonObject import net.torvald.terrarum.ActorValue +import net.torvald.terrarum.ModuleManager import net.torvald.terrarum.gameactors.ActorHumanoid import net.torvald.terrarum.gameactors.faction.FactionFactory import net.torvald.terrarum.itemproperties.ItemCodex @@ -27,11 +28,11 @@ object PlayerBuilderSigrid { p.referenceID = 0x51621D // the only constant of this procedural universe - p.makeNewSprite(28, 51, "assets/graphics/sprites/test_player.tga") + p.makeNewSprite(28, 51, ModuleManager.getPath("basegame", "sprites/test_player.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(1, 1) - p.makeNewSpriteGlow(28, 51, "assets/graphics/sprites/test_player_glow.tga") + p.makeNewSpriteGlow(28, 51, ModuleManager.getPath("basegame", "sprites/test_player_glow.tga")) p.spriteGlow!!.delay = 200 p.spriteGlow!!.setRowsAndFrames(1, 1) @@ -72,7 +73,7 @@ object PlayerBuilderSigrid { p.setPosition((4096 * FeaturesDrawer.TILE_SIZE).toDouble(), (300 * 16).toDouble()) - p.faction.add(FactionFactory.create("FactionSigrid.json")) + p.faction.add(FactionFactory.create("basegame", "factions/FactionSigrid.json")) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt index 2e12e86fb..09678eeaf 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt @@ -1,5 +1,6 @@ package net.torvald.terrarum.gameactors +import net.torvald.terrarum.ModuleManager import net.torvald.terrarum.gameactors.ai.LuaAIWrapper import net.torvald.terrarum.mapdrawer.FeaturesDrawer @@ -9,14 +10,14 @@ import net.torvald.terrarum.mapdrawer.FeaturesDrawer object PlayerBuilderTestSubject1 { operator fun invoke(): Player { val p: Player = Player(GameDate(100, 143)) // random value thrown - InjectCreatureRaw(p.actorValue, "CreatureHuman.json") + InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json") p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 p.actorValue[AVKey.NAME] = "Test Subject 1" - p.makeNewSprite(48, 52, "assets/graphics/sprites/npc_template_anim_prototype.tga") + p.makeNewSprite(48, 52, ModuleManager.getPath("basegame", "sprites/npc_template_anim_prototype.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(2, 4) diff --git a/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt b/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt index 187536a9e..556bc24fc 100644 --- a/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt +++ b/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt @@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors.faction import net.torvald.JsonFetcher import com.google.gson.JsonObject +import net.torvald.terrarum.ModuleManager import java.io.IOException @@ -10,14 +11,12 @@ import java.io.IOException */ object FactionFactory { - const val JSONPATH = "./assets/raw/factions/" - /** * @param filename with extension */ @Throws(IOException::class) - fun create(filename: String): Faction { - val jsonObj = JsonFetcher(JSONPATH + filename) + fun create(module: String, path: String): Faction { + val jsonObj = JsonFetcher(ModuleManager.getPath(module, path)) val factionObj = Faction(jsonObj.get("factionname").asString) jsonObj.get("factionamicable").asJsonArray.forEach { factionObj.addFactionAmicable(it.asString) } diff --git a/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt b/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt index f07c781e7..4a6355d7a 100644 --- a/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt +++ b/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt @@ -2,14 +2,11 @@ package net.torvald.terrarum.mapdrawer import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.PairedMapLayer -import net.torvald.terrarum.Terrarum import net.torvald.terrarum.tileproperties.Tile import net.torvald.terrarum.tileproperties.TileCodex import com.jme3.math.FastMath -import net.torvald.terrarum.blendAlphaMap +import net.torvald.terrarum.* import net.torvald.terrarum.concurrent.ThreadParallel -import net.torvald.terrarum.blendMul -import net.torvald.terrarum.blendNormal import net.torvald.terrarum.mapdrawer.FeaturesDrawer.TILE_SIZE import net.torvald.terrarum.mapdrawer.LightmapRenderer.normaliseToColour import net.torvald.terrarum.mapdrawer.MapCamera.x @@ -28,9 +25,9 @@ object TilesDrawer { private val TILE_SIZE = FeaturesDrawer.TILE_SIZE private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat() - var tilesTerrain: SpriteSheet = SpriteSheet("./assets/graphics/terrain/terrain.tga", TILE_SIZE, TILE_SIZE) + var tilesTerrain: SpriteSheet = SpriteSheet(ModuleManager.getPath("basegame", "tiles/terrain.tga"), TILE_SIZE, TILE_SIZE) private set // Slick has some weird quirks with PNG's transparency. I'm using 32-bit targa here. - var tilesWire: SpriteSheet = SpriteSheet("./assets/graphics/terrain/wire.tga", TILE_SIZE, TILE_SIZE) + var tilesWire: SpriteSheet = SpriteSheet(ModuleManager.getPath("basegame", "tiles/wire.tga"), TILE_SIZE, TILE_SIZE) private set val WALL = GameWorld.WALL @@ -52,7 +49,7 @@ object TilesDrawer { * It holds different shading rule to discriminate with group 02, index 0 is single tile. * These are the tiles that only connects to itself, will not connect to colour variants */ - val TILES_CONNECT_SELF = arrayOf( + var TILES_CONNECT_SELF = arrayOf( Tile.ICE_MAGICAL, Tile.GLASS_CRUDE, Tile.GLASS_CLEAN, @@ -101,7 +98,7 @@ object TilesDrawer { * Connectivity group 02 : natural tiles * It holds different shading rule to discriminate with group 01, index 0 is middle tile. */ - val TILES_CONNECT_MUTUAL = arrayOf( + var TILES_CONNECT_MUTUAL = arrayOf( Tile.STONE, Tile.STONE_QUARRIED, Tile.STONE_TILE_WHITE, @@ -166,7 +163,7 @@ object TilesDrawer { /** * Torches, levers, switches, ... */ - val TILES_WALL_STICKER = arrayOf( + var TILES_WALL_STICKER = arrayOf( Tile.TORCH, Tile.TORCH_FROST, Tile.TORCH_OFF, @@ -176,7 +173,7 @@ object TilesDrawer { /** * platforms, ... */ - val TILES_WALL_STICKER_CONNECT_SELF = arrayOf( + var TILES_WALL_STICKER_CONNECT_SELF = arrayOf( Tile.PLATFORM_BIRCH, Tile.PLATFORM_BLOODROSE, Tile.PLATFORM_EBONY, @@ -189,7 +186,7 @@ object TilesDrawer { * will blend colour using colour multiplication * i.e. red hues get lost if you dive into the water */ - val TILES_BLEND_MUL = arrayOf( + var TILES_BLEND_MUL = arrayOf( Tile.WATER, Tile.WATER_1, Tile.WATER_2, diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt index 48a4174b5..be685c2a5 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -4,14 +4,11 @@ import com.jme3.math.FastMath import net.torvald.JsonFetcher import net.torvald.colourutil.* import net.torvald.random.HQRNG -import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.blendMul -import net.torvald.terrarum.blendNormal +import net.torvald.terrarum.* import net.torvald.terrarum.gameactors.ParticleTestRain import net.torvald.terrarum.gamecontroller.Key import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gameworld.WorldTime -import net.torvald.terrarum.getPixel import org.newdawn.slick.Color import org.newdawn.slick.GameContainer import org.newdawn.slick.Graphics @@ -47,14 +44,12 @@ object WeatherMixer { const val WEATHER_GENERIC_RAIN = "genericrain" // TODO add weather classification indices manually - const val RAW_DIR = "./assets/raw/weathers" - init { weatherList = HashMap>() // read weather descriptions from assets/weather (modular weather) val weatherRawValidList = ArrayList() - val weatherRaws = File(RAW_DIR).listFiles() + val weatherRaws = ModuleManager.getFiles("basegame", "weathers") weatherRaws.forEach { if (!it.isDirectory && it.name.endsWith(".json")) weatherRawValidList.add(it) @@ -191,7 +186,7 @@ object WeatherMixer { ] } */ - val pathToImage = "./assets/graphics/weathers" + val pathToImage = "weathers" val JSON = JsonFetcher(path) @@ -212,7 +207,7 @@ object WeatherMixer { // parse globalLight if (globalLightInJson.isString) - globalLight = Image("$pathToImage/${globalLightInJson.asString}") + globalLight = Image(ModuleManager.getPath("basegame", "$pathToImage/${globalLightInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x1 image with specified colour globalLight = Image(1, 1) @@ -224,7 +219,7 @@ object WeatherMixer { // parse skyboxGradColourMap if (skyboxInJson.isString) - skybox = Image("$pathToImage/${skyboxInJson.asString}") + skybox = Image(ModuleManager.getPath("basegame", "$pathToImage/${skyboxInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x2 image with specified colour skybox = Image(1, 2) @@ -236,7 +231,7 @@ object WeatherMixer { // get extra images for (i in extraImagesPath) - extraImages.add(Image("$pathToImage/${i.asString}")) + extraImages.add(Image(ModuleManager.getPath("basegame", "$pathToImage/${i.asString}"))) // get mix from