Verwendung und Grenzen
Beschreibt Rezepte mit Fähigkeit, exakten Eingaben, garantierten Ausgaben, Dauer und FE je aktivem Tick. Nebenprodukte gehören dazu. Ein Template-Resolver ist rein und begrenzt; Zufallsmittelwerte sind keine Garantien.
- EntdeckenFähigkeiten und Rezepte lesen
- PlanenExakte Ressourcen reservieren
- AusführenAn realen Besitzer übertragen
- FortsetzenGespeicherten Beleg lesen
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) }
}
Quelle
src/main/kotlin/dev/nebulastorage/api/production/ProductionModels.kt; editorial/de.json
43b4d12d69e6033c24fa8c39dac383ef2429b446