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

SBX-6 Custom Blocks #9

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
19 changes: 0 additions & 19 deletions src/main/java/org/sandboxpowered/fabric/mixin/impl/MixinBlock.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import net.minecraft.state.property.*
import org.graalvm.polyglot.HostAccess.Export

class PolyglotStateProperty private constructor(
private val property: Property<*>?,
val property: Property<*>?,
@JvmField @Export val name: String,
@JvmField @Export val type: String,
@JvmField @Export val values: Array<*>
Expand All @@ -27,12 +27,20 @@ class PolyglotStateProperty private constructor(
)

fun from(name: String, type: String, extra: Array<*> = emptyArray<Any>()) = PolyglotStateProperty(
property = null,
property = createVanillaProperty(name, type, extra),
name = name,
type = type,
values = extra
)

private fun createVanillaProperty(name: String, type: String, extra: Array<*> = emptyArray<Any>()): Property<*>? {
return when (type) {
INT -> IntProperty.of(name, extra.first() as Int, extra.last() as Int)
BOOLEAN -> BooleanProperty.of(name)
else -> null
}
}

const val BOOLEAN = "boolean"
const val INT = "int"
const val DIRECTION = "direction"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class SandboxResourcePolyglotContext(private val resource: String, private val s
@Export
fun emit(event: String, vararg args: Any) {
if (event.contains(':')) scriptLoader.emitEventToAll(event, *args)
else scriptLoader.emitEventTo(resource, event, *args)
else scriptLoader.emitEventTo(resource, event, args = args)
}

@Export
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.sandboxpowered.fabric.api.block

import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.state.StateManager
import org.graalvm.polyglot.Value
import org.sandboxpowered.fabric.api.PolyglotStateProperty

class PolyglotBlock(settings: Settings, private val possibleStates: List<PolyglotStateProperty>?, value: Value) : Block(settings) {
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
hackOverwrite?.forEach {
if (it.property != null) {
builder.add(it.property)
}
}
}

companion object {
var hackOverwrite: List<PolyglotStateProperty>? = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sandboxpowered.fabric.api.block

import net.minecraft.util.Identifier
import org.graalvm.polyglot.HostAccess.Export
import org.graalvm.polyglot.Value

class PolyglotBlockManager(private val domain: String, private val global: PolyglotGlobalBlockManager) {
@Export
fun add(id: String, value: Value) {
require(value.hasMembers()) { "Unsupported value for block registration" }
global.addBlock(Identifier(domain, id), value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.sandboxpowered.fabric.api.block

import com.google.common.collect.ImmutableMap
import net.minecraft.block.AbstractBlock
import net.minecraft.block.Block
import net.minecraft.block.Material
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import org.graalvm.polyglot.Value
import org.sandboxpowered.fabric.api.PolyglotStateProperty
import org.sandboxpowered.fabric.util.*

class PolyglotGlobalBlockManager {
private val archetypeMap: Map<String, (Value) -> Block> = ImmutableMap.builder<String, (Value) -> Block>()
.apply {
this["block"] = {
val settings: AbstractBlock.Settings
var possibleStates: List<PolyglotStateProperty>? = null
if (!it.hasMember("properties")) settings = AbstractBlock.Settings.of(Material.AIR)
else {
val properties = it.getMember("properties")
settings = itemPropertiesToSettings(properties)
possibleStates = properties.getMemberValue("state")?.let { state -> state.asArray().map { value -> value.convert() } }
}
PolyglotBlock.hackOverwrite = possibleStates
PolyglotBlock(settings, possibleStates ?: emptyList(), it)
}
}
.build()

private fun itemPropertiesToSettings(properties: Value): AbstractBlock.Settings {
val settings = AbstractBlock.Settings.of(materialFromString(properties.getMemberValue("material", "air")))
properties.getMemberValueFloat("hardness")?.let(settings::hardness)
properties.getMemberValueFloat("resistance")?.let(settings::resistance)
return settings
}

private fun materialFromString(input: String): Material {
return when (input) {
"stone" -> Material.STONE
"ice" -> Material.ICE
"soil", "dirt" -> Material.SOIL
else -> Material.AIR
}
}

fun addBlock(id: Identifier, value: Value) {
val archetype = value.getMemberValue("archetype", "block")
require(archetype in archetypeMap) { "Unknown block archetype [$archetype]" }
val block = archetypeMap[archetype]!!(value)
Registry.register(Registry.BLOCK, id, block)
//TODO register itemblock
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class SandboxLoader {

log.info("Loaded ${addons.size} resources")

resourceContent.keys.forEach {
polyglotLoader.emitEventTo(it, "blocks", false, polyglotLoader.getBlockManager(it))
}

if (side == Side.SERVER) {
val json = JsonObject()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import org.graalvm.polyglot.Context
import org.graalvm.polyglot.HostAccess
import org.graalvm.polyglot.Source
import org.sandboxpowered.fabric.api.SandboxResourcePolyglotContext
import org.sandboxpowered.fabric.api.block.PolyglotBlockManager
import org.sandboxpowered.fabric.api.block.PolyglotGlobalBlockManager
import org.sandboxpowered.fabric.scripting.polyglot.PolyglotFileSystem
import org.sandboxpowered.fabric.util.TimingUtil
import java.util.concurrent.Executors
Expand All @@ -12,6 +14,8 @@ class PolyglotScriptLoader {
private val executor = Executors.newSingleThreadExecutor()
private val scriptContext: MutableMap<String, MutableMap<String, Context>> = HashMap()
private val polyglotContext: MutableMap<String, SandboxResourcePolyglotContext> = HashMap()
private val globalBlockManager = PolyglotGlobalBlockManager()
private val blockManagers: MutableMap<String, PolyglotBlockManager> = hashMapOf()

private fun buildContext(): Context = Context.newBuilder("js", "python")
.allowExperimentalOptions(true)
Expand All @@ -33,9 +37,10 @@ class PolyglotScriptLoader {
}
}

fun emitEventTo(resource: String, event: String, vararg args: Any) {
fun emitEventTo(resource: String, event: String, emitToAll: Boolean = true, vararg args: Any) {
polyglotContext[resource]?.event(event) { it(args) }
emitEventToAll(resource, "$resource:$event", *args)
if (emitToAll)
emitEventToAll(resource, "$resource:$event", *args)
}

fun loadScriptContext(resource: String, scriptSource: Source) {
Expand Down Expand Up @@ -69,6 +74,9 @@ class PolyglotScriptLoader {
fun markEventAsNetCapable(string: String) {

}

fun getBlockManager(it: String): PolyglotBlockManager =
blockManagers.computeIfAbsent(it) { PolyglotBlockManager(it, globalBlockManager) }
}

private inline fun <reified T : Annotation> HostAccess.Builder.allowAccessAnnotatedBy(): HostAccess.Builder {
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/org/sandboxpowered/fabric/util/map.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.sandboxpowered.fabric.util

import com.google.common.collect.ImmutableMap

/**
* Removes elements from this map if the predicate returns true
*/
Expand All @@ -9,4 +11,8 @@ inline fun <K, V> MutableMap<K, V>.removeIf(predicate: (K, V) -> Boolean) {
val (k, v) = iter.next()
if (predicate(k, v)) iter.remove()
}
}

operator fun <K, V> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> {
return put(key, value)
}
15 changes: 14 additions & 1 deletion src/main/kotlin/org/sandboxpowered/fabric/util/polyglot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ fun Value.getMemberValue(member: String): Value? = if (hasMember(member)) getMem

fun Value.getMemberValueStr(member: String): String? = getMemberValue(member)?.asString()
fun Value.getMemberValueInt(member: String): Int? = getMemberValue(member)?.asInt()
fun Value.getMemberValueFloat(member: String): Float? = getMemberValue(member)?.asFloat()

fun Value.getMemberValue(member: String, default: String): String = getMemberValueStr(member) ?: default
fun Value.getMemberValue(member: String, default: Int): Int = getMemberValueInt(member) ?: default
fun Value.getMemberValue(member: String, default: Float): Float = getMemberValueFloat(member) ?: default

fun Value.toJSON(): JsonElement = when {
hasArrayElements() -> JsonArray().apply {
Expand All @@ -25,4 +27,15 @@ fun Value.toJSON(): JsonElement = when {
isBoolean -> JsonPrimitive(asBoolean())
isNumber -> JsonPrimitive(asInt())
else -> JsonNull.INSTANCE
}
}

inline fun <reified T> Value.convert(): T {
return `as`(T::class.java)
}

fun Value.asArray(): Array<Value> {
if (hasArrayElements()) {
return (0 until this.arraySize).map { getArrayElement(it) }.toTypedArray()
}
return emptyArray()
}
5 changes: 2 additions & 3 deletions src/main/resources/sandbox.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
"compatibilityLevel": "JAVA_16",
"mixins": [
"MixinBootstrap",
"MixinMinecraftClient",
"MixinMinecraftServer",
"MixinRecipeManager",
"MixinServerResourceManager",
"impl.MixinBlock"
"MixinServerResourceManager"
],
"client": [
"MixinMinecraftClient",
"MixinClientBrandRetriever"
],
"injectors": {
Expand Down