Uso e vincoli

Descrive capacità, ingressi esatti, uscite garantite, durata e FE per tick attivo. Include sottoprodotti. Il resolver è puro e limitato; una media casuale non è un risultato garantito.

  1. ScoprireLeggere capacità e ricette
  2. PianificareRiservare risorse esatte
  3. EseguireTrasferire al vero proprietario
  4. RiprendereLeggere la ricevuta salvata
Scoprire non modifica dati. Solo l’esecuzione trasferisce proprietà; il recupero non ricrea ingressi consumati.

ProductionModels

ProductionModels
package dev.nebulastorage.api.production

import dev.nebulastorage.api.resource.ResourceStack
import dev.nebulastorage.api.resource.ResourceKey
import dev.nebulastorage.api.resource.quantities
import java.util.Collections
import java.util.UUID

data class ProductionCapabilityId(val value: String) {
    init { require(Regex("[a-z0-9_.-]+:[a-z0-9/._-]+").matches(value)) }
}

data class ProductionRecipeId(val value: String) {
    init { require(Regex("[a-z0-9_.-]+:[a-z0-9/._-]+").matches(value)) }
}

/** Inputs and every secondary output count exact integral units per operation. Energy is FE per paid processing tick. */
class ProductionRecipe(
    override val id: ProductionRecipeId, override val capability: ProductionCapabilityId,
    inputs: List<ResourceStack>, outputs: List<ResourceStack>, override val durationTicks: Int = 1,
    override val priority: Int = 0, override val energyPerTick: Long = 0, override val materialYield: ProductionYield? = null
) : ProductionRecipeOffer {
    val inputs = quantities(inputs)
    override val outputs = quantities(outputs)
    init {
        require(inputs.isNotEmpty() && outputs.isNotEmpty() && durationTicks > 0 && energyPerTick >= 0)
        Math.multiplyExact(energyPerTick, durationTicks.toLong())
    }
}

sealed interface ProductionRecipeOffer {
    val id: ProductionRecipeId
    val capability: ProductionCapabilityId
    val outputs: Map<ResourceKey, Long>
    val durationTicks: Int
    val priority: Int
    val energyPerTick: Long
    val materialYield: ProductionYield?
}

/** Higher ranks mean better material yield only inside the same integration-defined comparison group. */
data class ProductionYield(val group: String, val rank: Long) {
    init { ProductionRecipeId(group); require(rank >= 0) }
}

/** One ingredient position, with exact interchangeable identities. Options are finite and never mutated by planning. */
class ProductionIngredient(options: Set<ResourceKey>, val amount: Long = 1) {
    val options: Set<ResourceKey> = Collections.unmodifiableSet(options.toSet())
    init { require(options.isNotEmpty() && amount > 0) }
}

/** Pure server-thread calculation, including every remainder. Null rejects this particular ingredient selection. */
fun interface RecipeOutputResolver {
    fun resolve(selected: List<ResourceKey>): List<ResourceStack>?
}

/** Indexed outputs are guaranteed per operation. Resolution may add secondary outputs, but cannot change the selected inputs. */
class ProductionRecipeTemplate(
    override val id: ProductionRecipeId, override val capability: ProductionCapabilityId,
    ingredients: List<ProductionIngredient>, outputs: List<ResourceStack>, override val durationTicks: Int = 1,
    override val priority: Int = 0, override val energyPerTick: Long = 0,
    private val resolver: RecipeOutputResolver? = null, override val materialYield: ProductionYield? = null
) : ProductionRecipeOffer {
    val ingredients: List<ProductionIngredient> = Collections.unmodifiableList(ingredients.toList())
    override val outputs = quantities(outputs)
    init {
        require(ingredients.isNotEmpty() && outputs.isNotEmpty() && durationTicks > 0 && energyPerTick >= 0)
        Math.multiplyExact(energyPerTick, durationTicks.toLong())
    }

    fun resolve(selected: List<ResourceKey>): ProductionRecipe? {
        require(selected.size == ingredients.size && selected.indices.all { selected[it] in ingredients[it].options })
        val result = if (resolver == null) outputs.map { (key, amount) -> ResourceStack(key, amount) }
            else resolver.resolve(Collections.unmodifiableList(selected.toList())) ?: return null
        val concrete = ProductionRecipe(id, capability, selected.mapIndexed { index, key -> ResourceStack(key, ingredients[index].amount) },
            result, durationTicks, priority, energyPerTick, materialYield)
        require(outputs.all { (key, amount) -> (concrete.outputs[key] ?: 0) >= amount }) { "Recipe resolution removed an indexed output" }
        return concrete
    }
}

enum class ProductionExecution { CONTROLLED, PHYSICAL }

/** [id] identifies one machine owner. An empty capability set represents installed equipment whose admission requirements are unmet. Physical execution requires durable transfer evidence. */
class ProductionMachine @JvmOverloads constructor(val id: UUID, val type: String, capabilities: Set<ProductionCapabilityId>, val parallelism: Int = 1,
                                                val execution: ProductionExecution = ProductionExecution.CONTROLLED) {
    val capabilities: Set<ProductionCapabilityId> = Collections.unmodifiableSet(capabilities.toSet())
    init { require(type.isNotBlank() && parallelism in 1..64) }
}
Fonte

src/main/kotlin/dev/nebulastorage/api/production/ProductionModels.kt; editorial/it.json

43b4d12d69e6033c24fa8c39dac383ef2429b446