Skip to content

Commit

Permalink
Expand /blueprint editor
Browse files Browse the repository at this point in the history
editor -> editor open
+ editor close
+ editor toolset
  • Loading branch information
andantet committed Nov 21, 2023
1 parent 10ef208 commit 62fa95c
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 14 deletions.
58 changes: 53 additions & 5 deletions src/main/kotlin/net/mcbrawls/blueprint/command/BlueprintCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package net.mcbrawls.blueprint.command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
import net.fabricmc.loader.api.FabricLoader
import net.fabricmc.loader.api.ModContainer
import net.fabricmc.loader.api.Version
import net.fabricmc.loader.api.metadata.ModMetadata
import net.mcbrawls.blueprint.BlueprintMod
import net.mcbrawls.blueprint.BlueprintMod.MOD_NAME
import net.mcbrawls.blueprint.editor.BlueprintEditorEnvironment
import net.mcbrawls.blueprint.editor.BlueprintEditors
import net.mcbrawls.blueprint.resource.BlueprintManager
import net.minecraft.command.argument.IdentifierArgumentType
Expand All @@ -23,6 +25,8 @@ import net.minecraft.util.math.Vec3d
object BlueprintCommand {
const val BLUEPRINT_KEY = "blueprint"

private val NOT_IN_EDITOR_ENVIRONMENT_EXCEPTION_TYPE = SimpleCommandExceptionType { "This command must be run in an editor environment" }
private val ALREADY_IN_EDITOR_ENVIRONMENT_EXCEPTION_TYPE = SimpleCommandExceptionType { "You are already in this editor environment" }
private val INVALID_BLUEPRINT_EXCEPTION_TYPE = DynamicCommandExceptionType { id -> Text.literal("There is no blueprint with id \"$id\"") }

fun register(dispatcher: CommandDispatcher<ServerCommandSource>) {
Expand All @@ -31,15 +35,32 @@ object BlueprintCommand {
builder.then(
literal("editor")
.then(
argument(BLUEPRINT_KEY, IdentifierArgumentType.identifier())
.suggests { _, suggestions -> BlueprintManager.suggestBlueprints(suggestions) }
.executes(::executeEditor)
literal("open")
.then(
argument(BLUEPRINT_KEY, IdentifierArgumentType.identifier())
.suggests { _, suggestions -> BlueprintManager.suggestBlueprints(suggestions) }
.executes(::executeEditorOpen)
)
)
.then(
literal("close")
.requires(::isEditorEnvironment)
.executes(::executeEditorClose)
)
.then(
literal("toolset")
.requires(::isEditorEnvironment)
.executes(::executeEditorToolset)
)
)

dispatcher.register(builder)
}

private fun isEditorEnvironment(source: ServerCommandSource): Boolean {
return source.world is BlueprintEditorEnvironment
}

private fun execute(context: CommandContext<ServerCommandSource>): Int {
// retrieve version
val loader = FabricLoader.getInstance()
Expand All @@ -55,7 +76,7 @@ object BlueprintCommand {
return 1
}

private fun executeEditor(context: CommandContext<ServerCommandSource>): Int {
private fun executeEditorOpen(context: CommandContext<ServerCommandSource>): Int {
val source = context.source
val player = source.playerOrThrow

Expand All @@ -64,9 +85,36 @@ object BlueprintCommand {
val environment = environmentManager[blueprintId]

if (player.world !== environment) {
player.teleport(environment, Vec3d.ZERO, Vec2f.ZERO)
player.teleport(environment, Vec3d.ofBottomCenter(BlueprintEditorEnvironment.ROOT_POSITION), Vec2f.ZERO)
source.sendFeedback({ Text.literal("Opened editor environment \"$blueprintId\"") }, false)
} else {
throw ALREADY_IN_EDITOR_ENVIRONMENT_EXCEPTION_TYPE.create()
}

return 1
}

private fun executeEditorClose(context: CommandContext<ServerCommandSource>): Int {
val source = context.source
val server = source.server

val world = source.world
if (world !is BlueprintEditorEnvironment) {
throw NOT_IN_EDITOR_ENVIRONMENT_EXCEPTION_TYPE.create()
}

val blueprintId = world.blueprintId
val editors = BlueprintEditors[server]
val editorEnvironment = editors.getNullable(blueprintId)
?: throw IllegalStateException("Editor environment was somehow null")

editors.remove(editorEnvironment)
source.sendFeedback({ Text.literal("Closed editor environment \"$blueprintId\"") }, false)

return 1
}

private fun executeEditorToolset(context: CommandContext<ServerCommandSource>): Int {
return 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dev.andante.bubble.world.BubbleWorld
import net.minecraft.registry.RegistryKey
import net.minecraft.server.MinecraftServer
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import net.minecraft.world.dimension.DimensionOptions

Expand All @@ -20,4 +21,7 @@ class BlueprintEditorEnvironment(
key: RegistryKey<World>,
options: DimensionOptions
) : BubbleWorld(server, key, options) {
companion object {
val ROOT_POSITION = BlockPos(0, 200, 0)
}
}
48 changes: 39 additions & 9 deletions src/main/kotlin/net/mcbrawls/blueprint/editor/BlueprintEditors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.mcbrawls.blueprint.editor

import com.google.common.base.Preconditions
import dev.andante.bubble.BubbleManager
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
import net.mcbrawls.blueprint.BlueprintMod
Expand All @@ -27,8 +28,8 @@ class BlueprintEditors private constructor(private val server: MinecraftServer)
}

/**
* Gets or creates a blueprint environment for the given blueprint id.
* @return a blueprint environment
* Gets or creates a blueprint editor for the given blueprint id.
* @return a blueprint editor environment
* @throws IllegalStateException if a world exists for the given blueprint id but that world is not an environment
*/
operator fun get(blueprintId: Identifier): BlueprintEditorEnvironment {
Expand All @@ -40,43 +41,72 @@ class BlueprintEditors private constructor(private val server: MinecraftServer)
val environmentWorld = server.getWorld(worldKey)

// get or create environment
val environment = if (environmentWorld != null) {
val editorEnvironment = if (environmentWorld != null) {
if (environmentWorld is BlueprintEditorEnvironment) {
environmentWorld
} else {
throw IllegalStateException("World existed for blueprint but was not environment: $blueprintId")
}
} else {
val bubbleManager = BubbleManager.getOrCreate(server)
val environment = bubbleManager.createAndInitialize(
val editorEnvironment = bubbleManager.createAndInitialize(
identifier = worldId,
factory = { server, key, options -> BlueprintEditorEnvironment(blueprintId, server, key, options) }
)

environments[blueprintId] = environment
environment
environments[blueprintId] = editorEnvironment
editorEnvironment
}

return environment
return editorEnvironment
}

/**
* Gets the blueprint editor with the given id if present.
* @return a nullable blueprint editor environment
*/
fun getNullable(blueprintId: Identifier): BlueprintEditorEnvironment? {
return environments[blueprintId]
}

/**
* Removes a blueprint editor from the editor manager and removes the dimension.
* @return whether the environment was removed
*/
fun remove(environment: BlueprintEditorEnvironment): Boolean {
return if (environments.remove(environment.blueprintId) != null) {
val bubbleManager = BubbleManager.getOrCreate(server)
bubbleManager.remove(environment)
true
} else {
false
}
}

companion object {
/**
* All blueprint environment managers.
* All blueprint editor managers.
*/
private val EDITORS = mutableMapOf<MinecraftServer, BlueprintEditors>()

init {
// register server events
ServerTickEvents.START_SERVER_TICK.register { server -> EDITORS[server]?.tick() }
ServerLifecycleEvents.SERVER_STOPPING.register { server -> EDITORS[server]?.clean() }

ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register { player, origin, destination ->
// send command tree on editor world change
if (origin is BlueprintEditorEnvironment || destination is BlueprintEditorEnvironment) {
player.server.commandManager.sendCommandTree(player)
}
}
}

/**
* Gets an environment manager for the server or creates one if not present.
*/
operator fun get(server: MinecraftServer): BlueprintEditors {
Preconditions.checkState(server.isOnThread, "Cannot create blueprint environment manager off-thread")
Preconditions.checkState(server.isOnThread, "Cannot create blueprint editor manager off-thread")
return EDITORS.computeIfAbsent(server, ::BlueprintEditors)
}

Expand Down

0 comments on commit 62fa95c

Please sign in to comment.