Compare commits

...

5 Commits

51 changed files with 831 additions and 440 deletions

View File

@ -12,6 +12,7 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/data" /> <option value="$PROJECT_DIR$/data" />
<option value="$PROJECT_DIR$/modules/debug" />
<option value="$PROJECT_DIR$/modules/system" /> <option value="$PROJECT_DIR$/modules/system" />
<option value="$PROJECT_DIR$/modules/util" /> <option value="$PROJECT_DIR$/modules/util" />
</set> </set>

View File

@ -60,6 +60,7 @@ idea {
["out", "src/main/kotlin", "src/test/kotlin"].each { ["out", "src/main/kotlin", "src/test/kotlin"].each {
excludeDirs += file(it) excludeDirs += file(it)
excludeDirs += file("modules/debug/" + it)
excludeDirs += file("modules/system/" + it) excludeDirs += file("modules/system/" + it)
excludeDirs += file("modules/util/" + it) excludeDirs += file("modules/util/" + it)
} }
@ -128,7 +129,6 @@ minecraft {
runs { runs {
client { client {
property "hee.debug", ""
property "mixin.env.remapRefMap", "true" property "mixin.env.remapRefMap", "true"
property "mixin.env.refMapRemappingFile", "${projectDir}/build/createSrgToMcp/output.srg" property "mixin.env.refMapRemappingFile", "${projectDir}/build/createSrgToMcp/output.srg"
arg "-mixin.config=hee.mixins.json" arg "-mixin.config=hee.mixins.json"
@ -138,6 +138,7 @@ minecraft {
mods { mods {
hee { hee {
source sourceSets.main source sourceSets.main
source project(":debug").sourceSets.main
source project(":system").sourceSets.main source project(":system").sourceSets.main
source project(":util").sourceSets.main source project(":util").sourceSets.main
} }
@ -145,7 +146,6 @@ minecraft {
} }
server { server {
property "hee.debug", ""
property "mixin.env.remapRefMap", "true" property "mixin.env.remapRefMap", "true"
property "mixin.env.refMapRemappingFile", "${projectDir}/build/createSrgToMcp/output.srg" property "mixin.env.refMapRemappingFile", "${projectDir}/build/createSrgToMcp/output.srg"
arg "-mixin.config=hee.mixins.json" arg "-mixin.config=hee.mixins.json"
@ -155,6 +155,7 @@ minecraft {
mods { mods {
hee { hee {
source sourceSets.main source sourceSets.main
source project(":debug").sourceSets.main
source project(":system").sourceSets.main source project(":system").sourceSets.main
source project(":util").sourceSets.main source project(":util").sourceSets.main
} }
@ -162,7 +163,6 @@ minecraft {
} }
data { data {
property "hee.debug", ""
args "--mod", "hee" args "--mod", "hee"
args "--all" args "--all"
args "--output", file("data/gen") args "--output", file("data/gen")
@ -174,6 +174,7 @@ minecraft {
mods { mods {
hee { hee {
source sourceSets.main source sourceSets.main
source project(":debug").sourceSets.main
source project(":system").sourceSets.main source project(":system").sourceSets.main
source project(":util").sourceSets.main source project(":util").sourceSets.main
source project(":datagen").sourceSets.main source project(":datagen").sourceSets.main
@ -190,12 +191,12 @@ mixin {
dependencies { dependencies {
minecraft "net.minecraftforge:forge:" + mc_version + "-" + forge_version minecraft "net.minecraftforge:forge:" + mc_version + "-" + forge_version
implementation project(":system")
implementation project(":util") implementation project(":util")
implementation project(":system")
implementation "thedarkcolour:kotlinforforge:" + kotlin_mod_version implementation "thedarkcolour:kotlinforforge:" + kotlin_mod_version
testImplementation project(":system")
testImplementation project(":util") testImplementation project(":util")
testImplementation project(":system")
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.0-RC1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.0-RC1"
if (System.getProperty("idea.sync.active") != "true") { if (System.getProperty("idea.sync.active") != "true") {

View File

@ -91,7 +91,7 @@ class LangEnglish(generator: DataGenerator, modid: String) : LanguageProvider(ge
} }
} }
for (command in ClientCommandHandler.nonHelpCommands.values) { for (command in ClientCommandHandler.all.values) {
val name = command.name val name = command.name
add("commands.hee.$name.info", command.description) add("commands.hee.$name.info", command.description)
} }

View File

@ -0,0 +1,33 @@
buildscript {
repositories {
mavenCentral()
maven { url = "https://files.minecraftforge.net/maven" }
}
dependencies {
classpath group: "net.minecraftforge.gradle", name: "ForgeGradle", version: forge_gradle_version, changing: true
classpath group: "org.jetbrains.kotlin", name: "kotlin-gradle-plugin", version: kotlin_version
}
}
apply plugin: "net.minecraftforge.gradle"
minecraft {
mappings channel: "snapshot", version: rootProject.mapping_version
setAccessTransformers(rootProject.access_transformers)
}
dependencies {
minecraft "net.minecraftforge:forge:" + mc_version + "-" + forge_version
implementation rootProject
implementation project(":system")
implementation project(":util")
}
jar {
manifest {
attributes([
"FMLModType": "LIBRARY"
])
}
}

View File

@ -0,0 +1,143 @@
package chylex.hee.client
import chylex.hee.client.render.RenderStateBuilder
import chylex.hee.client.render.util.DF_ONE
import chylex.hee.client.render.util.DF_ZERO
import chylex.hee.client.render.util.SF_ONE_MINUS_SRC_ALPHA
import chylex.hee.client.render.util.SF_SRC_ALPHA
import chylex.hee.client.util.MC
import chylex.hee.game.item.util.nbtOrNull
import chylex.hee.game.world.util.floodFill
import chylex.hee.game.world.util.getBlock
import chylex.hee.game.world.util.getState
import chylex.hee.game.world.util.isAir
import chylex.hee.game.world.util.removeBlock
import chylex.hee.game.world.util.setState
import chylex.hee.util.nbt.hasKey
import net.minecraft.block.BlockState
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.BlockItem
import net.minecraft.item.Items
import net.minecraft.util.Direction
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.EAST
import net.minecraft.util.Direction.NORTH
import net.minecraft.util.Direction.SOUTH
import net.minecraft.util.Direction.UP
import net.minecraft.util.Direction.WEST
import net.minecraft.util.Hand.MAIN_HAND
import net.minecraft.util.Hand.OFF_HAND
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.BlockRayTraceResult
import net.minecraft.util.math.shapes.ISelectionContext
import net.minecraft.world.IWorld
import net.minecraftforge.client.event.DrawHighlightEvent
import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickBlock
import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock
import net.minecraftforge.event.world.BlockEvent.BreakEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import org.lwjgl.opengl.GL11
object BuildStick {
private val RENDER_TYPE_LINE = with(RenderStateBuilder()) {
line(2.25)
blend(SF_SRC_ALPHA, DF_ONE, SF_ONE_MINUS_SRC_ALPHA, DF_ZERO)
layering(RenderStateBuilder.LAYERING_OFFSET_Z)
mask(RenderStateBuilder.MASK_COLOR)
buildType("hee:debug_line", DefaultVertexFormats.POSITION_COLOR, GL11.GL_LINES, bufferSize = 256)
}
private fun isHoldingBuildStick(player: PlayerEntity): Boolean {
val heldItem = player.getHeldItem(MAIN_HAND)
return heldItem.item === Items.STICK && heldItem.nbtOrNull.hasKey("HEE_BUILD")
}
private fun getBuildStickBlocks(world: IWorld, pos: BlockPos, state: BlockState, face: Direction): List<BlockPos> {
val floodFaces = when (face) {
UP, DOWN -> listOf(NORTH, SOUTH, EAST, WEST)
NORTH, SOUTH -> listOf(UP, DOWN, EAST, WEST)
EAST, WEST -> listOf(UP, DOWN, NORTH, SOUTH)
else -> emptyList()
}
val limit = 1000
val block = state.block
return pos.floodFill(floodFaces, limit) { it.getBlock(world) === block }.takeIf { it.size < limit }.orEmpty()
}
private var lastLeftClickHit: BlockRayTraceResult? = null
@SubscribeEvent
fun onLeftClickBlock(e: LeftClickBlock) {
val world = e.world
if (isHoldingBuildStick(e.player) && !world.isRemote) {
lastLeftClickHit = MC.instance.objectMouseOver as? BlockRayTraceResult
}
}
@SubscribeEvent
fun onBlockBreak(e: BreakEvent) {
val world = e.world
if (isHoldingBuildStick(e.player) && !world.isRemote) {
val hit = lastLeftClickHit ?: return
for (pos in getBuildStickBlocks(world, e.pos, e.state, hit.face)) {
pos.removeBlock(world)
}
}
}
@SubscribeEvent
fun onRightClickBlock(e: RightClickBlock) {
val world = e.world
val player = e.player
if (isHoldingBuildStick(player) && !world.isRemote) {
val state = e.pos.getState(world)
val face = e.face!!
val place = (player.getHeldItem(OFF_HAND).item as? BlockItem)?.block?.defaultState ?: state
for (pos in getBuildStickBlocks(world, e.pos, state, face)) {
val offset = pos.offset(face)
if (offset.isAir(world)) {
offset.setState(world, place)
}
}
}
}
@SubscribeEvent
fun onRenderOverlay(e: DrawHighlightEvent.HighlightBlock) {
val player = MC.player!!
if (isHoldingBuildStick(player)) {
val hit = MC.instance.objectMouseOver as? BlockRayTraceResult ?: return
val world = player.world
val center = hit.pos
val info = e.info
val matrix = e.matrix.last.matrix
val builder = e.buffers.getBuffer(RENDER_TYPE_LINE)
for (pos in getBuildStickBlocks(world, center, center.getState(world), hit.face)) {
val x = pos.x - info.projectedView.x
val y = pos.y - info.projectedView.y
val z = pos.z - info.projectedView.z
val shape = pos.getState(world).getShape(world, pos, ISelectionContext.forEntity(info.renderViewEntity))
shape.forEachEdge { x1, y1, z1, x2, y2, z2 ->
builder.pos(matrix, (x1 + x).toFloat(), (y1 + y).toFloat(), (z1 + z).toFloat()).color(1F, 1F, 1F, 1F).endVertex()
builder.pos(matrix, (x2 + x).toFloat(), (y2 + y).toFloat(), (z2 + z).toFloat()).color(1F, 1F, 1F, 1F).endVertex()
}
}
e.isCanceled = true
}
}
}

View File

@ -0,0 +1,42 @@
package chylex.hee.client
import chylex.hee.client.util.MC
import chylex.hee.debug.benchmark.TerritoryGenerationBenchmarkScreen
import chylex.hee.util.color.RGB
import com.mojang.blaze3d.matrix.MatrixStack
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.widget.button.Button
import net.minecraft.util.text.StringTextComponent
import net.minecraftforge.client.event.InputEvent.KeyInputEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import org.lwjgl.glfw.GLFW
object DebugMenu {
@SubscribeEvent
fun onKeyPressed(e: KeyInputEvent) {
if (e.action != GLFW.GLFW_RELEASE) {
return
}
if (e.key == GLFW.GLFW_KEY_F12) {
MC.instance.displayGuiScreen(DebugMenuScreen(MC.currentScreen))
}
}
private class DebugMenuScreen(private val parentScreen: Screen?) : Screen(StringTextComponent("HEE 2 Debug")) {
override fun init() {
addButton(Button(width / 2 - 100, 36, 200, 20, StringTextComponent("Territory Generation Benchmark")) { MC.instance.displayGuiScreen(TerritoryGenerationBenchmarkScreen(this)) })
addButton(Button(width / 2 - 100, height - 40, 200, 20, StringTextComponent("Close")) { closeScreen() })
}
override fun render(matrix: MatrixStack, mouseX: Int, mouseY: Int, partialTicks: Float) {
renderBackground(matrix)
drawCenteredString(matrix, font, title, width / 2, 15, RGB(255u).i)
super.render(matrix, mouseX, mouseY, partialTicks)
}
override fun closeScreen() {
MC.instance.displayGuiScreen(parentScreen)
}
}
}

View File

@ -0,0 +1,27 @@
package chylex.hee.client
import chylex.hee.client.util.MC
import net.minecraftforge.client.event.InputEvent.KeyInputEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import org.lwjgl.glfw.GLFW
object GameModeToggle {
@SubscribeEvent
fun onKeyPressed(e: KeyInputEvent) {
if (e.action != GLFW.GLFW_PRESS) {
return
}
if (e.key == GLFW.GLFW_KEY_GRAVE_ACCENT) {
val player = MC.player ?: return
if (player.isCreative) {
val ctrl = (e.modifiers and GLFW.GLFW_MOD_CONTROL) != 0
player.sendChatMessage(if (ctrl) "/gamemode spectator" else "/gamemode survival")
}
else {
player.sendChatMessage("/gamemode creative")
}
}
}
}

View File

@ -0,0 +1,19 @@
package chylex.hee.client
import chylex.hee.client.render.TerritoryRenderer
import chylex.hee.client.util.MC
import chylex.hee.game.world.isInEndDimension
import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
object TerritoryVoidDebug {
@SubscribeEvent
fun onRenderGameOverlayText(e: RenderGameOverlayEvent.Text) {
if (MC.settings.showDebugInfo && MC.player?.isInEndDimension == true) {
with(e.left) {
add("")
add("End Void Factor: ${"%.3f".format(TerritoryRenderer.currentVoidFactor)}")
}
}
}
}

View File

@ -0,0 +1,74 @@
package chylex.hee.debug
import chylex.hee.HEE
import chylex.hee.client.BuildStick
import chylex.hee.client.DebugMenu
import chylex.hee.client.GameModeToggle
import chylex.hee.client.TerritoryVoidDebug
import chylex.hee.game.block.BlockScaffoldingDebug
import chylex.hee.game.block.HeeBlock
import chylex.hee.game.block.properties.BlockBuilder
import chylex.hee.game.command.client.CommandClientDebugToggles
import chylex.hee.game.command.client.CommandClientScaffolding
import chylex.hee.game.command.server.CommandServerInstability
import chylex.hee.game.command.server.CommandServerStructure
import chylex.hee.game.command.server.CommandServerTerritory
import chylex.hee.game.command.server.CommandServerTestWorld
import chylex.hee.system.IDebugModule
import chylex.hee.util.forge.Side
import chylex.hee.util.forge.Sided
import chylex.hee.util.forge.SubscribeAllEvents
import chylex.hee.util.forge.SubscribeEvent
import net.minecraftforge.client.event.GuiOpenEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
@SubscribeAllEvents(modid = HEE.ID, bus = MOD)
internal object Debug : IDebugModule {
init {
HEE.debug = true
HEE.debugModule = this
}
override val clientCommands
get() = listOf(
CommandClientScaffolding,
CommandClientDebugToggles
)
override val serverCommands
get() = listOf(
CommandServerInstability,
CommandServerStructure,
CommandServerTerritory,
CommandServerTestWorld
)
override fun createScaffoldingBlock(builder: BlockBuilder): HeeBlock {
return BlockScaffoldingDebug(builder)
}
@SubscribeEvent
fun onClientSetup(@Suppress("UNUSED_PARAMETER") e: FMLClientSetupEvent) {
initializeClient()
}
@Sided(Side.CLIENT)
private fun initializeClient() {
if (HEE.debug) {
MinecraftForge.EVENT_BUS.register(DebugMenu)
MinecraftForge.EVENT_BUS.register(BuildStick)
MinecraftForge.EVENT_BUS.register(GameModeToggle)
MinecraftForge.EVENT_BUS.register(TerritoryVoidDebug)
MinecraftForge.EVENT_BUS.register(object : Any() {
@SubscribeEvent
fun onGuiOpen(@Suppress("UNUSED_PARAMETER") e: GuiOpenEvent) {
PowerShell.maximizeWindow()
MinecraftForge.EVENT_BUS.unregister(this)
}
})
}
}
}

View File

@ -0,0 +1,26 @@
package chylex.hee.debug
import chylex.hee.game.Environment
import chylex.hee.util.forge.Side
import org.apache.commons.lang3.SystemUtils
import java.io.File
import java.lang.management.ManagementFactory
internal object PowerShell {
private fun canExecutePowershell(scriptName: String): Boolean {
return SystemUtils.IS_OS_WINDOWS && Environment.side == Side.CLIENT && File(scriptName).exists()
}
fun setClipboardContents(file: File) {
if (canExecutePowershell("filecopy.ps1")) {
ProcessBuilder("powershell.exe", "-ExecutionPolicy", "Unrestricted", "-Sta", "-File", "filecopy.ps1", file.absolutePath).start()
}
}
fun maximizeWindow() {
if (canExecutePowershell("maximize.ps1")) {
val pid = ManagementFactory.getRuntimeMXBean().name.split("@")[0]
ProcessBuilder("powershell.exe", "-ExecutionPolicy", "Unrestricted", "-File", "maximize.ps1", pid).start()
}
}
}

View File

@ -0,0 +1,80 @@
package chylex.hee.debug.benchmark
import chylex.hee.HEE
import chylex.hee.client.util.MC
import chylex.hee.game.Environment
import chylex.hee.game.territory.TerritoryType
import chylex.hee.game.world.generation.structure.world.SegmentedWorld
import chylex.hee.util.color.RGB
import com.mojang.blaze3d.matrix.MatrixStack
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.widget.button.Button
import net.minecraft.util.text.StringTextComponent
import net.minecraft.util.text.TranslationTextComponent
import net.minecraft.world.World
import java.util.Random
class TerritoryGenerationBenchmarkScreen(private val parentScreen: Screen) : Screen(StringTextComponent("Territory Generation Benchmark")) {
private val generated = mutableListOf<SegmentedWorld>()
override fun init() {
try {
Environment.getDimension(World.OVERWORLD)
} catch (e: NullPointerException) {
println("Must be in a world!")
closeScreen()
return
}
val x = width / 2 - 100
addButton(Button(x, 36, 200, 20, StringTextComponent("(All)")) { runAll() })
for ((index, territory) in TerritoryType.ALL.withIndex()) {
val y = 36 + (22 * (index + 1))
addButton(Button(x, y, 200, 20, TranslationTextComponent(territory.translationKey)) { runOnce(territory) })
}
addButton(Button(x, height - 40, 200, 20, StringTextComponent("Close")) { closeScreen() })
}
override fun render(matrix: MatrixStack, mouseX: Int, mouseY: Int, partialTicks: Float) {
renderBackground(matrix)
drawCenteredString(matrix, font, title, width / 2, 15, RGB(255u).i)
super.render(matrix, mouseX, mouseY, partialTicks)
}
override fun closeScreen() {
MC.instance.displayGuiScreen(parentScreen)
}
private fun runAll() {
generated.clear()
for (territory in TerritoryType.ALL) {
if (territory.gen === TerritoryType.Companion.GeneratorDummy) {
continue
}
for (seed in 0L until 5L) {
runImpl(territory, seed)
}
}
HEE.log.info("[TerritoryGenerationBenchmarkScreen] done!")
}
private fun runOnce(territory: TerritoryType) {
generated.clear()
runImpl(territory, 0L)
}
private fun runImpl(territory: TerritoryType, seed: Long) {
HEE.log.info("[TerritoryGenerationBenchmarkScreen] generating " + territory.name)
val timeStart = System.currentTimeMillis()
generated.add(territory.generate(Random(seed)).first)
val timeEnd = System.currentTimeMillis()
HEE.log.info("[TerritoryGenerationBenchmarkScreen] finished in ${timeEnd - timeStart} ms")
}
}

View File

@ -0,0 +1,103 @@
package chylex.hee.game.block
import chylex.hee.debug.PowerShell
import chylex.hee.game.Environment
import chylex.hee.game.block.properties.BlockBuilder
import chylex.hee.game.command.client.CommandClientScaffolding
import chylex.hee.game.world.generation.structure.file.StructureFile
import chylex.hee.game.world.generation.util.WorldToStructureWorldAdapter
import chylex.hee.game.world.util.getBlock
import chylex.hee.game.world.util.offsetUntilExcept
import chylex.hee.util.math.BoundingBox
import chylex.hee.util.math.Pos
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.CompressedStreamTools
import net.minecraft.util.ActionResultType
import net.minecraft.util.ActionResultType.FAIL
import net.minecraft.util.ActionResultType.SUCCESS
import net.minecraft.util.Direction
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.EAST
import net.minecraft.util.Direction.NORTH
import net.minecraft.util.Direction.SOUTH
import net.minecraft.util.Direction.UP
import net.minecraft.util.Direction.WEST
import net.minecraft.util.Hand
import net.minecraft.util.Util
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.BlockRayTraceResult
import net.minecraft.util.text.StringTextComponent
import net.minecraft.util.text.TextFormatting
import net.minecraft.world.World
import java.nio.file.Files
class BlockScaffoldingDebug(builder: BlockBuilder) : BlockScaffolding(builder) {
override fun onBlockActivated(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockRayTraceResult): ActionResultType {
if (world.isRemote && player.isSneaking && !player.abilities.isFlying) {
val palette = CommandClientScaffolding.currentPalette
if (palette == null) {
player.sendMessage(StringTextComponent("No structure set."), Util.DUMMY_UUID)
return FAIL
}
val minPos = findMinPos(world, pos)?.let { findMinPos(world, it) } // double pass to find min from any side
val maxPos = minPos?.let { findMaxPos(world, it) }
if (minPos == null || maxPos == null) {
player.sendMessage(StringTextComponent("Could not find structure boundaries."), Util.DUMMY_UUID)
return FAIL
}
val box = BoundingBox(minPos, maxPos)
val serverWorld = Environment.getDimension(world.dimensionKey)
val (structureTag, missingMappings) = StructureFile.save(WorldToStructureWorldAdapter(serverWorld, serverWorld.rand, box.min), box.size, palette, this)
val structureFile = Files.createTempDirectory("HardcoreEnderExpansion_Structure_").resolve(CommandClientScaffolding.currentFile).toFile()
CompressedStreamTools.write(structureTag, structureFile)
PowerShell.setClipboardContents(structureFile)
if (missingMappings.isNotEmpty()) {
player.sendMessage(StringTextComponent("Missing mappings for states:"), Util.DUMMY_UUID)
for (missingMapping in missingMappings) {
player.sendMessage(StringTextComponent(" - ${TextFormatting.GRAY}$missingMapping"), Util.DUMMY_UUID)
}
}
player.sendMessage(StringTextComponent("Generated structure file of ${box.size}."), Util.DUMMY_UUID)
return SUCCESS
}
return FAIL
}
// Helpers
private fun find(world: World, pos: BlockPos?, direction: Direction): BlockPos? {
return pos?.offsetUntilExcept(direction, 0..255) { it.getBlock(world) === Blocks.AIR }
}
private fun findMinPos(world: World, pos: BlockPos): BlockPos? {
val bottomPos = find(world, pos, DOWN)
val y = bottomPos?.y
val x = find(world, bottomPos, WEST)?.x
val z = find(world, bottomPos, NORTH)?.z
return if (x == null || y == null || z == null) null else Pos(x, y, z)
}
private fun findMaxPos(world: World, pos: BlockPos): BlockPos? {
val topPos = find(world, pos, UP)
val y = topPos?.y
val x = find(world, topPos, EAST)?.x
val z = find(world, topPos, SOUTH)?.z
return if (x == null || y == null || z == null) null else Pos(x, y, z)
}
}

View File

@ -1,13 +1,13 @@
package chylex.hee.game.command.client package chylex.hee.game.command.client
import chylex.hee.client.render.TerritoryRenderer import chylex.hee.client.render.TerritoryRenderer
import chylex.hee.game.block.BlockScaffolding
import chylex.hee.game.command.IClientCommand import chylex.hee.game.command.IClientCommand
import chylex.hee.game.territory.TerritoryVoid import chylex.hee.game.territory.TerritoryVoid
import chylex.hee.init.ModBlocks
import net.minecraft.command.CommandSource import net.minecraft.command.CommandSource
import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.StringTextComponent
object CommandDebugToggles : IClientCommand { object CommandClientDebugToggles : IClientCommand {
override val name = "debug" override val name = "debug"
override val description = "access to debug toggles" override val description = "access to debug toggles"
@ -23,8 +23,8 @@ object CommandDebugToggles : IClientCommand {
sender.sendFeedback(StringTextComponent("Territory debugging ${if (debug) "enabled" else "disabled"}."), false) sender.sendFeedback(StringTextComponent("Territory debugging ${if (debug) "enabled" else "disabled"}."), false)
} }
else if (name == "scaffolding") { else if (name == "scaffolding") {
ModBlocks.SCAFFOLDING.enableShape = !ModBlocks.SCAFFOLDING.enableShape BlockScaffolding.enableShape = !BlockScaffolding.enableShape
sender.sendFeedback(StringTextComponent("Scaffolding shape ${if (ModBlocks.SCAFFOLDING.enableShape) "enabled" else "disabled"}."), false) sender.sendFeedback(StringTextComponent("Scaffolding shape ${if (BlockScaffolding.enableShape) "enabled" else "disabled"}."), false)
} }
} }
} }

View File

@ -1,7 +1,7 @@
package chylex.hee.game.command.client package chylex.hee.game.command.client
import chylex.hee.game.command.IClientCommand import chylex.hee.game.command.IClientCommand
import chylex.hee.game.command.server.CommandDebugStructure import chylex.hee.game.command.server.CommandServerStructure
import net.minecraft.command.CommandSource import net.minecraft.command.CommandSource
import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.StringTextComponent
import java.util.prefs.Preferences import java.util.prefs.Preferences
@ -14,7 +14,7 @@ object CommandClientScaffolding : IClientCommand {
get() = Preferences.userRoot().node("chylex-hee-scaffolding") get() = Preferences.userRoot().node("chylex-hee-scaffolding")
val currentPalette val currentPalette
get() = data.get("Structure", null)?.let(CommandDebugStructure.structureDescriptions::get)?.PALETTE get() = data.get("Structure", null)?.let(CommandServerStructure.structureDescriptions::get)?.PALETTE
val currentFile val currentFile
get() = data.get("File", "")!!.ifBlank { "structure.nbt" } get() = data.get("File", "")!!.ifBlank { "structure.nbt" }
@ -22,7 +22,7 @@ object CommandClientScaffolding : IClientCommand {
override fun executeCommand(sender: CommandSource, args: Array<String>) { override fun executeCommand(sender: CommandSource, args: Array<String>) {
val structure = args.getOrNull(0) ?: return val structure = args.getOrNull(0) ?: return
if (!CommandDebugStructure.structureDescriptions.containsKey(structure)) { if (!CommandServerStructure.structureDescriptions.containsKey(structure)) {
sender.sendFeedback(StringTextComponent("Unknown structure."), false) sender.sendFeedback(StringTextComponent("Unknown structure."), false)
return return
} }

View File

@ -15,7 +15,7 @@ import net.minecraft.command.Commands.argument
import net.minecraft.command.Commands.literal import net.minecraft.command.Commands.literal
import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.StringTextComponent
object CommandDebugInstability : ICommand { object CommandServerInstability : ICommand {
override val name = "instability" override val name = "instability"
override val description = "utilities for instability" override val description = "utilities for instability"

View File

@ -31,7 +31,7 @@ import net.minecraft.util.Rotation
import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.StringTextComponent
import java.util.Random import java.util.Random
object CommandDebugStructure : ICommand { object CommandServerStructure : ICommand {
val structureDescriptions = mapOf( val structureDescriptions = mapOf(
"stronghold" to StrongholdPieces, "stronghold" to StrongholdPieces,
"energyshrine" to EnergyShrinePieces, "energyshrine" to EnergyShrinePieces,

View File

@ -23,7 +23,7 @@ import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.text.StringTextComponent import net.minecraft.util.text.StringTextComponent
import java.util.Random import java.util.Random
object CommandDebugTerritory : ICommand { object CommandServerTerritory : ICommand {
override val name = "territory" override val name = "territory"
override val description = "utilities for territories" override val description = "utilities for territories"

View File

@ -7,7 +7,7 @@ import com.mojang.brigadier.builder.ArgumentBuilder
import com.mojang.brigadier.context.CommandContext import com.mojang.brigadier.context.CommandContext
import net.minecraft.command.CommandSource import net.minecraft.command.CommandSource
object CommandDebugTestWorld : ICommand, CommandExecutionFunction { object CommandServerTestWorld : ICommand, CommandExecutionFunction {
override val name = "testworld" override val name = "testworld"
override val description = "converts overworld into a test world" override val description = "converts overworld into a test world"

View File

@ -0,0 +1,7 @@
{
"pack": {
"description": "Hardcore Ender Expansion 2 (Debug Library)",
"pack_format": 6,
"_comment": ""
}
}

View File

@ -1,6 +1,7 @@
package chylex.hee package chylex.hee
import chylex.hee.game.Resource import chylex.hee.game.Resource
import chylex.hee.system.IDebugModule
import net.minecraft.util.RegistryKey import net.minecraft.util.RegistryKey
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.minecraft.world.World import net.minecraft.world.World
@ -12,6 +13,10 @@ object HEE {
lateinit var version: String lateinit var version: String
@JvmField
var debug = false
var debugModule: IDebugModule? = null
val log: Logger = LogManager.getLogger("HardcoreEnderExpansion") val log: Logger = LogManager.getLogger("HardcoreEnderExpansion")
val dim: RegistryKey<World> = RegistryKey.getOrCreateKey(Registry.WORLD_KEY, Resource.Custom("end")) val dim: RegistryKey<World> = RegistryKey.getOrCreateKey(Registry.WORLD_KEY, Resource.Custom("end"))
} }

View File

@ -1,5 +1,7 @@
package chylex.hee.game.world.generation.noise package chylex.hee.game.world.generation.noise
import chylex.hee.util.math.FloatRange
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.pow import kotlin.math.pow
@ -26,12 +28,12 @@ class NoiseValue(var value: Double) {
it.coerceIn(minimum, maximum) it.coerceIn(minimum, maximum)
} }
fun remap(oldRange: ClosedFloatingPointRange<Double>, newRange: ClosedFloatingPointRange<Double>) = then { fun remap(oldRange: FloatRange, newRange: FloatRange) = then {
remapRange(it, oldRange, newRange) remapRange(it, oldRange, newRange)
} }
fun remap(newRange: ClosedFloatingPointRange<Double>) = then { fun remap(newRange: FloatRange) = then {
remapRange(it, (0.0)..(1.0), newRange) remapRange(it, range(0F, 1F), newRange)
} }
inline fun ifNonZero(block: NoiseValue.() -> Unit) { inline fun ifNonZero(block: NoiseValue.() -> Unit) {

View File

@ -7,7 +7,6 @@ import chylex.hee.game.world.generation.structure.piece.IStructurePieceConnectio
import chylex.hee.game.world.generation.structure.piece.StructureBuild.AddMode.APPEND import chylex.hee.game.world.generation.structure.piece.StructureBuild.AddMode.APPEND
import chylex.hee.game.world.generation.structure.piece.StructureBuild.AddMode.MERGE import chylex.hee.game.world.generation.structure.piece.StructureBuild.AddMode.MERGE
import chylex.hee.game.world.generation.structure.world.OffsetStructureWorld import chylex.hee.game.world.generation.structure.world.OffsetStructureWorld
import chylex.hee.system.Debug
import chylex.hee.util.math.Size import chylex.hee.util.math.Size
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
@ -83,7 +82,7 @@ class StructureBuild<T : StructurePiece<*>.MutableInstance>(val size: Size) {
* [MERGE] mode allows intersection with [targetPiece] but no other pieces. * [MERGE] mode allows intersection with [targetPiece] but no other pieces.
*/ */
fun addPiece(newPiece: T, newPieceConnection: IStructurePieceConnection, targetPiece: PositionedPiece<T>, targetPieceConnection: IStructurePieceConnection, mode: AddMode = APPEND): PositionedPiece<T>? { fun addPiece(newPiece: T, newPieceConnection: IStructurePieceConnection, targetPiece: PositionedPiece<T>, targetPieceConnection: IStructurePieceConnection, mode: AddMode = APPEND): PositionedPiece<T>? {
if (Debug.enabled && !pieces.contains(targetPiece)) { if (HEE.debug && !pieces.contains(targetPiece)) {
HEE.log.error("[StructureBuild] attempted to connect to a piece that is not present in the structure") HEE.log.error("[StructureBuild] attempted to connect to a piece that is not present in the structure")
} }

View File

@ -6,59 +6,67 @@ import chylex.hee.game.world.generation.structure.IStructureWorld
import chylex.hee.game.world.generation.structure.world.segments.ISegment import chylex.hee.game.world.generation.structure.world.segments.ISegment
import chylex.hee.game.world.generation.structure.world.segments.ISegment.Companion.index import chylex.hee.game.world.generation.structure.world.segments.ISegment.Companion.index
import chylex.hee.game.world.util.Transform import chylex.hee.game.world.util.Transform
import chylex.hee.system.Debug
import chylex.hee.util.math.Pos import chylex.hee.util.math.Pos
import chylex.hee.util.math.Size import chylex.hee.util.math.Size
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.component1
import chylex.hee.util.math.component2
import chylex.hee.util.math.component3
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import java.util.Random import java.util.Random
open class SegmentedWorld(override val rand: Random, val worldSize: Size, private val segmentSize: Size, defaultSegmentFactory: (Size) -> ISegment) : IStructureWorld { open class SegmentedWorld(override val rand: Random, val worldSize: Size, segmentSize: Size, defaultSegmentFactory: (Size) -> ISegment) : IStructureWorld {
private val segmentSizeX = segmentSize.x
private val segmentSizeY = segmentSize.y
private val segmentSizeZ = segmentSize.z
private val segmentCounts = Size( private val segmentCounts = Size(
(worldSize.x.toFloat() / segmentSize.x).ceilToInt(), (worldSize.x.toFloat() / segmentSizeX).ceilToInt(),
(worldSize.y.toFloat() / segmentSize.y).ceilToInt(), (worldSize.y.toFloat() / segmentSizeY).ceilToInt(),
(worldSize.z.toFloat() / segmentSize.z).ceilToInt() (worldSize.z.toFloat() / segmentSizeZ).ceilToInt()
) )
private val segments = Array(segmentCounts.x * segmentCounts.y * segmentCounts.z) { defaultSegmentFactory(segmentSize) } private val segments = Array(segmentCounts.x * segmentCounts.y * segmentCounts.z) { defaultSegmentFactory(segmentSize) }
private val triggers = mutableListOf<Pair<BlockPos, IStructureTrigger>>() private val triggers = mutableListOf<Pair<BlockPos, IStructureTrigger>>()
private fun mapPos(pos: BlockPos): Pair<Int, BlockPos>? { private fun getSegmentIndex(pos: BlockPos): Int {
if (!isInside(pos)) { return index(pos.x / segmentSizeX, pos.y / segmentSizeY, pos.z / segmentSizeZ, segmentCounts)
HEE.log.warn("[SegmentedWorld] attempted to access position outside bounds: $pos is outside $worldSize") }
if (Debug.enabled) { private fun getSegmentOffset(pos: BlockPos): BlockPos {
Thread.dumpStack() return Pos(pos.x % segmentSizeX, pos.y % segmentSizeY, pos.z % segmentSizeZ)
}
return null
}
val (x, y, z) = pos
val (sizeX, sizeY, sizeZ) = segmentSize
val segmentIndex = index(x / sizeX, y / sizeY, z / sizeZ, segmentCounts)
val segmentOffset = Pos(x % sizeX, y % sizeY, z % sizeZ)
return Pair(segmentIndex, segmentOffset)
} }
fun isInside(pos: BlockPos): Boolean { fun isInside(pos: BlockPos): Boolean {
return pos.x in 0..worldSize.maxX && pos.y in 0..worldSize.maxY && pos.z in 0..worldSize.maxZ return pos.x in 0..worldSize.maxX && pos.y in 0..worldSize.maxY && pos.z in 0..worldSize.maxZ
} }
private fun warnNotInside(pos: BlockPos) {
HEE.log.warn("[SegmentedWorld] attempted to access position outside bounds: $pos is outside $worldSize")
if (HEE.debug) {
Thread.dumpStack()
}
}
override fun getState(pos: BlockPos): BlockState { override fun getState(pos: BlockPos): BlockState {
val (segmentIndex, segmentOffset) = mapPos(pos) ?: return Blocks.AIR.defaultState if (!isInside(pos)) {
warnNotInside(pos)
return Blocks.AIR.defaultState
}
val segmentIndex = getSegmentIndex(pos)
val segmentOffset = getSegmentOffset(pos)
return segments[segmentIndex].getState(segmentOffset) return segments[segmentIndex].getState(segmentOffset)
} }
override fun setState(pos: BlockPos, state: BlockState) { override fun setState(pos: BlockPos, state: BlockState) {
val (segmentIndex, segmentOffset) = mapPos(pos) ?: return if (!isInside(pos)) {
warnNotInside(pos)
return
}
val segmentIndex = getSegmentIndex(pos)
val segmentOffset = getSegmentOffset(pos)
segments[segmentIndex] = segments[segmentIndex].withState(segmentOffset, state) segments[segmentIndex] = segments[segmentIndex].withState(segmentOffset, state)
} }

View File

@ -1,11 +1,26 @@
package chylex.hee.game.world.generation.structure.world.segments package chylex.hee.game.world.generation.structure.world.segments
import chylex.hee.game.world.generation.structure.world.segments.ISegment.Companion.index import chylex.hee.game.world.generation.structure.world.segments.ISegment.Companion.index
import chylex.hee.util.math.MutablePos
import chylex.hee.util.math.Size import chylex.hee.util.math.Size
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
/**
* A segment that supports arbitrary block states in arbitrary positions.
*/
class SegmentFull(private val size: Size, fillState: BlockState) : ISegment { class SegmentFull(private val size: Size, fillState: BlockState) : ISegment {
constructor(size: Size, fillState: BlockState, posToStateMap: Long2ObjectOpenHashMap<BlockState>) : this(size, fillState) {
val pos = MutablePos()
val iter = posToStateMap.long2ObjectEntrySet().fastIterator()
while (iter.hasNext()) {
val entry = iter.next()
pos.setPos(entry.longKey)
data[index(pos, size)] = entry.value
}
}
private val data = Array(size.x * size.y * size.z) { fillState } private val data = Array(size.x * size.y * size.z) { fillState }
override fun getState(pos: BlockPos): BlockState { override fun getState(pos: BlockPos): BlockState {

View File

@ -0,0 +1,42 @@
package chylex.hee.game.world.generation.structure.world.segments
import chylex.hee.util.math.Size
import chylex.hee.util.math.floorToInt
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import kotlin.math.pow
/**
* A segment mostly filled with one type of block state, but allowing for a few [exceptions].
* When the amount of exceptions reaches a [threshold] based on the segment size, it gets converted to [SegmentFull].
*/
class SegmentMultiState(private val size: Size, private val fillState: BlockState) : ISegment {
constructor(size: Size, block: Block) : this(size, block.defaultState)
/**
* The threshold power was determined by careful experimentation and profiling.
*/
private val threshold = (size.x * size.y * size.z).toDouble().pow(0.69).floorToInt()
private val exceptions = Long2ObjectOpenHashMap<BlockState>(threshold, 0.75F).apply { defaultReturnValue(fillState) }
override fun getState(pos: BlockPos): BlockState {
return exceptions.get(pos.toLong())
}
override fun withState(pos: BlockPos, state: BlockState): ISegment {
if (state == fillState) {
exceptions.remove(pos.toLong())
return this
}
@Suppress("ReplacePutWithAssignment")
exceptions.put(pos.toLong(), state) // kotlin indexer boxes the values
return if (exceptions.size < threshold)
this
else
SegmentFull(size, fillState, exceptions)
}
}

View File

@ -5,6 +5,10 @@ import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
/**
* A segment completely filled with only one type of block state.
* When any block state is changed, gets converted to [SegmentMultiState].
*/
class SegmentSingleState(private val size: Size, private val fillState: BlockState) : ISegment { class SegmentSingleState(private val size: Size, private val fillState: BlockState) : ISegment {
constructor(size: Size, block: Block) : this(size, block.defaultState) constructor(size: Size, block: Block) : this(size, block.defaultState)
@ -16,6 +20,6 @@ class SegmentSingleState(private val size: Size, private val fillState: BlockSta
return if (state == fillState) return if (state == fillState)
this this
else else
SegmentFull(size, fillState).withState(pos, state) SegmentMultiState(size, fillState).withState(pos, state)
} }
} }

View File

@ -1,206 +0,0 @@
package chylex.hee.system
import chylex.hee.client.render.RenderStateBuilder
import chylex.hee.client.render.RenderStateBuilder.Companion.LAYERING_OFFSET_Z
import chylex.hee.client.render.RenderStateBuilder.Companion.MASK_COLOR
import chylex.hee.client.render.util.DF_ONE
import chylex.hee.client.render.util.DF_ZERO
import chylex.hee.client.render.util.SF_ONE_MINUS_SRC_ALPHA
import chylex.hee.client.render.util.SF_SRC_ALPHA
import chylex.hee.client.util.MC
import chylex.hee.game.Environment
import chylex.hee.game.item.util.nbtOrNull
import chylex.hee.game.world.util.floodFill
import chylex.hee.game.world.util.getBlock
import chylex.hee.game.world.util.getState
import chylex.hee.game.world.util.isAir
import chylex.hee.game.world.util.removeBlock
import chylex.hee.game.world.util.setState
import chylex.hee.util.forge.Side
import chylex.hee.util.forge.Sided
import chylex.hee.util.forge.SubscribeEvent
import chylex.hee.util.nbt.hasKey
import net.minecraft.block.BlockState
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.BlockItem
import net.minecraft.item.Items
import net.minecraft.util.Direction
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.EAST
import net.minecraft.util.Direction.NORTH
import net.minecraft.util.Direction.SOUTH
import net.minecraft.util.Direction.UP
import net.minecraft.util.Direction.WEST
import net.minecraft.util.Hand.MAIN_HAND
import net.minecraft.util.Hand.OFF_HAND
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.BlockRayTraceResult
import net.minecraft.util.math.shapes.ISelectionContext
import net.minecraft.world.IWorld
import net.minecraftforge.client.event.DrawHighlightEvent
import net.minecraftforge.client.event.GuiOpenEvent
import net.minecraftforge.client.event.InputEvent.KeyInputEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickBlock
import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock
import net.minecraftforge.event.world.BlockEvent.BreakEvent
import org.apache.commons.lang3.SystemUtils
import org.lwjgl.glfw.GLFW
import org.lwjgl.opengl.GL11
import java.io.File
import java.lang.management.ManagementFactory
object Debug {
@JvmField
val enabled = System.getProperty("hee.debug") != null
@Sided(Side.CLIENT)
fun initializeClient() {
if (enabled) {
MinecraftForge.EVENT_BUS.register(object : Any() {
@SubscribeEvent
fun onKeyPressed(e: KeyInputEvent) {
if (e.action != GLFW.GLFW_PRESS) {
return
}
if (e.key == GLFW.GLFW_KEY_GRAVE_ACCENT) {
val player = MC.player ?: return
if (player.isCreative) {
val ctrl = (e.modifiers and GLFW.GLFW_MOD_CONTROL) != 0
player.sendChatMessage(if (ctrl) "/gamemode spectator" else "/gamemode survival")
}
else {
player.sendChatMessage("/gamemode creative")
}
}
}
private fun isHoldingBuildStick(player: PlayerEntity): Boolean {
val heldItem = player.getHeldItem(MAIN_HAND)
return heldItem.item === Items.STICK && heldItem.nbtOrNull.hasKey("HEE_BUILD")
}
private fun getBuildStickBlocks(world: IWorld, pos: BlockPos, state: BlockState, face: Direction): List<BlockPos> {
val floodFaces = when (face) {
UP, DOWN -> listOf(NORTH, SOUTH, EAST, WEST)
NORTH, SOUTH -> listOf(UP, DOWN, EAST, WEST)
EAST, WEST -> listOf(UP, DOWN, NORTH, SOUTH)
else -> emptyList()
}
val limit = 1000
val block = state.block
return pos.floodFill(floodFaces, limit) { it.getBlock(world) === block }.takeIf { it.size < limit }.orEmpty()
}
private var lastLeftClickHit: BlockRayTraceResult? = null
@SubscribeEvent
fun onLeftClickBlock(e: LeftClickBlock) {
val world = e.world
if (isHoldingBuildStick(e.player) && !world.isRemote) {
lastLeftClickHit = MC.instance.objectMouseOver as? BlockRayTraceResult
}
}
@SubscribeEvent
fun onBlockBreak(e: BreakEvent) {
val world = e.world
if (isHoldingBuildStick(e.player) && !world.isRemote) {
val hit = lastLeftClickHit ?: return
for (pos in getBuildStickBlocks(world, e.pos, e.state, hit.face)) {
pos.removeBlock(world)
}
}
}
@SubscribeEvent
fun onRightClickBlock(e: RightClickBlock) {
val world = e.world
val player = e.player
if (isHoldingBuildStick(player) && !world.isRemote) {
val state = e.pos.getState(world)
val face = e.face!!
val place = (player.getHeldItem(OFF_HAND).item as? BlockItem)?.block?.defaultState ?: state
for (pos in getBuildStickBlocks(world, e.pos, state, face)) {
val offset = pos.offset(face)
if (offset.isAir(world)) {
offset.setState(world, place)
}
}
}
}
private val RENDER_TYPE_LINE = with(RenderStateBuilder()) {
line(2.25)
blend(SF_SRC_ALPHA, DF_ONE, SF_ONE_MINUS_SRC_ALPHA, DF_ZERO)
layering(LAYERING_OFFSET_Z)
mask(MASK_COLOR)
buildType("hee:debug_line", DefaultVertexFormats.POSITION_COLOR, GL11.GL_LINES, bufferSize = 256)
}
@SubscribeEvent
fun onRenderOverlay(e: DrawHighlightEvent.HighlightBlock) {
val player = MC.player!!
if (isHoldingBuildStick(player)) {
val hit = MC.instance.objectMouseOver as? BlockRayTraceResult ?: return
val world = player.world
val center = hit.pos
val info = e.info
val matrix = e.matrix.last.matrix
val builder = e.buffers.getBuffer(RENDER_TYPE_LINE)
for (pos in getBuildStickBlocks(world, center, center.getState(world), hit.face)) {
val x = pos.x - info.projectedView.x
val y = pos.y - info.projectedView.y
val z = pos.z - info.projectedView.z
val shape = pos.getState(world).getShape(world, pos, ISelectionContext.forEntity(info.renderViewEntity))
shape.forEachEdge { x1, y1, z1, x2, y2, z2 ->
builder.pos(matrix, (x1 + x).toFloat(), (y1 + y).toFloat(), (z1 + z).toFloat()).color(1F, 1F, 1F, 1F).endVertex()
builder.pos(matrix, (x2 + x).toFloat(), (y2 + y).toFloat(), (z2 + z).toFloat()).color(1F, 1F, 1F, 1F).endVertex()
}
}
e.isCanceled = true
}
}
})
if (canExecutePowershell("maximize.ps1")) {
MinecraftForge.EVENT_BUS.register(object : Any() {
@SubscribeEvent
fun onGuiOpen(@Suppress("UNUSED_PARAMETER") e: GuiOpenEvent) {
val pid = ManagementFactory.getRuntimeMXBean().name.split("@")[0]
ProcessBuilder("powershell.exe", "-ExecutionPolicy", "Unrestricted", "-File", "maximize.ps1", pid).start()
MinecraftForge.EVENT_BUS.unregister(this)
}
})
}
}
}
fun setClipboardContents(file: File) {
if (canExecutePowershell("filecopy.ps1")) {
ProcessBuilder("powershell.exe", "-ExecutionPolicy", "Unrestricted", "-Sta", "-File", "filecopy.ps1", file.absolutePath).start()
}
}
private fun canExecutePowershell(scriptName: String): Boolean {
return SystemUtils.IS_OS_WINDOWS && Environment.side == Side.CLIENT && File(scriptName).exists()
}
}

View File

@ -0,0 +1,13 @@
package chylex.hee.system
import chylex.hee.game.block.HeeBlock
import chylex.hee.game.block.properties.BlockBuilder
import chylex.hee.game.command.IClientCommand
import chylex.hee.game.command.ICommand
interface IDebugModule {
val clientCommands: List<IClientCommand>
val serverCommands: List<ICommand>
fun createScaffoldingBlock(builder: BlockBuilder): HeeBlock
}

View File

@ -19,7 +19,7 @@ object MinecraftForgeEventBus {
} }
for (listener in item.javaClass.methods.filter { !Modifier.isStatic(it.modifiers) && it.isAnnotationPresent(SubscribeEvent::class.java) }) { for (listener in item.javaClass.methods.filter { !Modifier.isStatic(it.modifiers) && it.isAnnotationPresent(SubscribeEvent::class.java) }) {
if (Debug.enabled) { if (HEE.debug) {
HEE.log.info("[MinecraftForgeEventBus] registering ${listener.parameterTypes.firstOrNull()?.name?.substringAfterLast('.')} for ${item.javaClass.simpleName}") HEE.log.info("[MinecraftForgeEventBus] registering ${listener.parameterTypes.firstOrNull()?.name?.substringAfterLast('.')} for ${item.javaClass.simpleName}")
} }

View File

@ -48,15 +48,15 @@ fun lerp(from: Double, to: Double, progress: Double): Double {
/** /**
* Maps a range of values in [from] range to values in [to] range using linear interpolation. * Maps a range of values in [from] range to values in [to] range using linear interpolation.
*/ */
fun remapRange(value: Float, from: ClosedFloatingPointRange<Float>, to: ClosedFloatingPointRange<Float>): Float { fun remapRange(value: Float, from: FloatRange, to: FloatRange): Float {
val remappedBetween0And1 = (value - from.start) / (from.endInclusive - from.start) val remappedBetween0And1 = (value - from.start) / (from.end - from.start)
return to.start + remappedBetween0And1 * (to.endInclusive - to.start) return to.start + remappedBetween0And1 * (to.end - to.start)
} }
/** /**
* Maps a range of values in [from] range to values in [to] range using linear interpolation. * Maps a range of values in [from] range to values in [to] range using linear interpolation.
*/ */
fun remapRange(value: Double, from: ClosedFloatingPointRange<Double>, to: ClosedFloatingPointRange<Double>): Double { fun remapRange(value: Double, from: FloatRange, to: FloatRange): Double {
val remappedBetween0And1 = (value - from.start) / (from.endInclusive - from.start) val remappedBetween0And1 = (value - from.start) / (from.end - from.start)
return to.start + remappedBetween0And1 * (to.endInclusive - to.start) return to.start + remappedBetween0And1 * (to.end - to.start)
} }

View File

@ -0,0 +1,14 @@
package chylex.hee.util.math
@JvmInline
value class FloatRange(private val combined: Long) {
constructor(start: Float, end: Float) : this((start.toRawBits() shlong 32) or (end.toRawBits().toLong() and 0xFFFF_FFFFL))
val start
get() = Float.fromBits((combined ushr 32).toInt())
val end
get() = Float.fromBits((combined and 0xFFFF_FFFFL).toInt())
}
fun range(start: Float, end: Float) = FloatRange(start, end)

View File

@ -1,5 +1,6 @@
package chylex.hee.util.random package chylex.hee.util.random
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import java.util.Random import java.util.Random
import kotlin.math.pow import kotlin.math.pow
@ -29,7 +30,7 @@ abstract class RandomDouble private constructor(val min: Double, val max: Double
fun Exp(min: Double, max: Double, exp: Double) = object : RandomDouble(min, max) { fun Exp(min: Double, max: Double, exp: Double) = object : RandomDouble(min, max) {
override fun invoke(rand: Random): Double { override fun invoke(rand: Random): Double {
return remapRange(rand.nextDouble().pow(exp), (0.0)..(1.0), min..max) return remapRange(rand.nextDouble().pow(exp), range(0F, 1F), range(min.toFloat(), max.toFloat()))
} }
} }
} }

View File

@ -1,7 +1,9 @@
include ":system"
include ":util" include ":util"
include ":system"
include ":debug"
include ":datagen" include ":datagen"
project(":system").projectDir = file("./modules/system")
project(":util").projectDir = file("./modules/util") project(":util").projectDir = file("./modules/util")
project(":system").projectDir = file("./modules/system")
project(":debug").projectDir = file("./modules/debug")
project(":datagen").projectDir = file("./data") project(":datagen").projectDir = file("./data")

View File

@ -19,7 +19,6 @@ import chylex.hee.init.ModPackets
import chylex.hee.init.ModPotions import chylex.hee.init.ModPotions
import chylex.hee.init.ModTileEntities import chylex.hee.init.ModTileEntities
import chylex.hee.network.NetworkManager import chylex.hee.network.NetworkManager
import chylex.hee.system.Debug
import chylex.hee.util.forge.Side import chylex.hee.util.forge.Side
import chylex.hee.util.forge.SubscribeAllEvents import chylex.hee.util.forge.SubscribeAllEvents
import chylex.hee.util.forge.SubscribeEvent import chylex.hee.util.forge.SubscribeEvent
@ -29,7 +28,6 @@ import net.minecraftforge.fml.DistExecutor.SafeRunnable
import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent
@ -41,6 +39,10 @@ object Mod {
HEE.version = activeContainer.modInfo.version.toString() HEE.version = activeContainer.modInfo.version.toString()
} }
try {
Class.forName("chylex.hee.debug.Debug")
} catch (e: ClassNotFoundException) {}
@Suppress("ConvertLambdaToReference") @Suppress("ConvertLambdaToReference")
DistExecutor.safeRunWhenOn(Side.CLIENT) { DistExecutor.safeRunWhenOn(Side.CLIENT) {
SafeRunnable { VanillaResourceOverrides.register() } SafeRunnable { VanillaResourceOverrides.register() }
@ -51,11 +53,6 @@ object Mod {
ModCreativeTabs ModCreativeTabs
} }
@SubscribeEvent
fun onClientSetup(@Suppress("UNUSED_PARAMETER") e: FMLClientSetupEvent) {
Debug.initializeClient()
}
@SubscribeEvent @SubscribeEvent
fun onCommonSetup(@Suppress("UNUSED_PARAMETER") e: FMLCommonSetupEvent) { fun onCommonSetup(@Suppress("UNUSED_PARAMETER") e: FMLCommonSetupEvent) {
NetworkManager.initialize(ModPackets.ALL) NetworkManager.initialize(ModPackets.ALL)

View File

@ -17,7 +17,6 @@ import chylex.hee.game.territory.TerritoryType
import chylex.hee.game.territory.TerritoryVoid import chylex.hee.game.territory.TerritoryVoid
import chylex.hee.game.territory.system.properties.TerritoryEnvironment import chylex.hee.game.territory.system.properties.TerritoryEnvironment
import chylex.hee.game.world.isInEndDimension import chylex.hee.game.world.isInEndDimension
import chylex.hee.system.Debug
import chylex.hee.util.color.IntColor import chylex.hee.util.color.IntColor
import chylex.hee.util.color.RGB import chylex.hee.util.color.RGB
import chylex.hee.util.forge.EventPriority import chylex.hee.util.forge.EventPriority
@ -26,6 +25,7 @@ import chylex.hee.util.forge.SubscribeAllEvents
import chylex.hee.util.forge.SubscribeEvent import chylex.hee.util.forge.SubscribeEvent
import chylex.hee.util.math.LerpedFloat import chylex.hee.util.math.LerpedFloat
import chylex.hee.util.math.floorToInt import chylex.hee.util.math.floorToInt
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.scale import chylex.hee.util.math.scale
import com.mojang.blaze3d.matrix.MatrixStack import com.mojang.blaze3d.matrix.MatrixStack
@ -36,7 +36,6 @@ import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.math.vector.Vector3f import net.minecraft.util.math.vector.Vector3f
import net.minecraftforge.client.event.EntityViewRenderEvent.RenderFogEvent import net.minecraftforge.client.event.EntityViewRenderEvent.RenderFogEvent
import net.minecraftforge.client.event.RenderGameOverlayEvent import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.TickEvent.ClientTickEvent import net.minecraftforge.event.TickEvent.ClientTickEvent
import net.minecraftforge.event.TickEvent.Phase import net.minecraftforge.event.TickEvent.Phase
import org.lwjgl.opengl.GL11.GL_GREATER import org.lwjgl.opengl.GL11.GL_GREATER
@ -119,7 +118,7 @@ object TerritoryRenderer {
// Fog rendering // Fog rendering
private val currentFogDensityMp private val currentFogDensityMp
get() = 1F + (9F * remapRange(currentVoidFactor, (-0.5F)..(1F), (0F)..(1F)).coerceIn(0F, 1F).pow(1.5F)) get() = 1F + (9F * remapRange(currentVoidFactor, range(-0.5F, 1F), range(0F, 1F)).coerceIn(0F, 1F).pow(1.5F))
private val currentRenderDistanceMp private val currentRenderDistanceMp
get() = MC.settings.renderDistanceChunks.let { if (it > 12) 0F else (1F - (it / 16.5F)).pow((it - 1) * 0.25F) } get() = MC.settings.renderDistanceChunks.let { if (it > 12) 0F else (1F - (it / 16.5F)).pow((it - 1) * 0.25F) }
@ -149,7 +148,7 @@ object TerritoryRenderer {
get() = Void.voidFactor.get(MC.partialTicks) get() = Void.voidFactor.get(MC.partialTicks)
val currentSkyAlpha val currentSkyAlpha
get() = remapRange(currentVoidFactor, (-1F)..(0.5F), (1F)..(0F)).coerceIn(0F, 1F) get() = remapRange(currentVoidFactor, range(-1F, 0.5F), range(1F, 0F)).coerceIn(0F, 1F)
private object Void { private object Void {
private val VOID_PARTICLE = ParticleSpawnerCustom( private val VOID_PARTICLE = ParticleSpawnerCustom(
@ -159,12 +158,6 @@ object TerritoryRenderer {
val voidFactor = LerpedFloat(TerritoryVoid.OUTSIDE_VOID_FACTOR) val voidFactor = LerpedFloat(TerritoryVoid.OUTSIDE_VOID_FACTOR)
init {
if (Debug.enabled) {
MinecraftForge.EVENT_BUS.register(this)
}
}
fun tick(player: PlayerEntity) { fun tick(player: PlayerEntity) {
val factor = TerritoryVoid.getVoidFactor(player).also(voidFactor::update) val factor = TerritoryVoid.getVoidFactor(player).also(voidFactor::update)
@ -186,16 +179,6 @@ object TerritoryRenderer {
fun reset() { fun reset() {
voidFactor.updateImmediately(TerritoryVoid.OUTSIDE_VOID_FACTOR) voidFactor.updateImmediately(TerritoryVoid.OUTSIDE_VOID_FACTOR)
} }
@SubscribeEvent
fun onRenderGameOverlayText(e: RenderGameOverlayEvent.Text) {
if (MC.settings.showDebugInfo && MC.player?.isInEndDimension == true) {
with(e.left) {
add("")
add("End Void Factor: ${"%.3f".format(voidFactor.currentValue)}")
}
}
}
} }
// Text rendering // Text rendering

View File

@ -1,48 +1,27 @@
package chylex.hee.game.block package chylex.hee.game.block
import chylex.hee.HEE
import chylex.hee.game.Environment import chylex.hee.game.Environment
import chylex.hee.game.block.properties.BlockBuilder import chylex.hee.game.block.properties.BlockBuilder
import chylex.hee.game.block.properties.BlockModel import chylex.hee.game.block.properties.BlockModel
import chylex.hee.game.block.properties.BlockRenderLayer.CUTOUT import chylex.hee.game.block.properties.BlockRenderLayer.CUTOUT
import chylex.hee.game.command.client.CommandClientScaffolding
import chylex.hee.game.world.generation.structure.file.StructureFile
import chylex.hee.game.world.generation.util.WorldToStructureWorldAdapter
import chylex.hee.game.world.util.getBlock
import chylex.hee.game.world.util.offsetUntilExcept
import chylex.hee.system.Debug
import chylex.hee.util.forge.Side import chylex.hee.util.forge.Side
import chylex.hee.util.forge.Sided import chylex.hee.util.forge.Sided
import chylex.hee.util.math.BoundingBox
import chylex.hee.util.math.Pos
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.CompressedStreamTools
import net.minecraft.util.ActionResultType
import net.minecraft.util.ActionResultType.FAIL
import net.minecraft.util.ActionResultType.SUCCESS
import net.minecraft.util.Direction
import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.EAST
import net.minecraft.util.Direction.NORTH
import net.minecraft.util.Direction.SOUTH
import net.minecraft.util.Direction.UP
import net.minecraft.util.Direction.WEST
import net.minecraft.util.Hand
import net.minecraft.util.Util
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.BlockRayTraceResult
import net.minecraft.util.math.shapes.ISelectionContext import net.minecraft.util.math.shapes.ISelectionContext
import net.minecraft.util.math.shapes.VoxelShape import net.minecraft.util.math.shapes.VoxelShape
import net.minecraft.util.math.shapes.VoxelShapes import net.minecraft.util.math.shapes.VoxelShapes
import net.minecraft.util.text.StringTextComponent
import net.minecraft.util.text.TextFormatting
import net.minecraft.world.IBlockReader import net.minecraft.world.IBlockReader
import net.minecraft.world.World
import java.nio.file.Files
class BlockScaffolding(builder: BlockBuilder) : HeeBlock(builder) { open class BlockScaffolding protected constructor(builder: BlockBuilder) : HeeBlock(builder) {
var enableShape = true companion object {
var enableShape = true
fun create(builder: BlockBuilder): HeeBlock {
return HEE.debugModule?.createScaffoldingBlock(builder) ?: BlockScaffolding(builder)
}
}
override val model override val model
get() = BlockModel.Manual get() = BlockModel.Manual
@ -50,73 +29,6 @@ class BlockScaffolding(builder: BlockBuilder) : HeeBlock(builder) {
override val renderLayer override val renderLayer
get() = CUTOUT get() = CUTOUT
override fun onBlockActivated(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockRayTraceResult): ActionResultType {
if (world.isRemote && player.isSneaking && !player.abilities.isFlying && Debug.enabled) {
val palette = CommandClientScaffolding.currentPalette
if (palette == null) {
player.sendMessage(StringTextComponent("No structure set."), Util.DUMMY_UUID)
return FAIL
}
val minPos = findMinPos(world, pos)?.let { findMinPos(world, it) } // double pass to find min from any side
val maxPos = minPos?.let { findMaxPos(world, it) }
if (minPos == null || maxPos == null) {
player.sendMessage(StringTextComponent("Could not find structure boundaries."), Util.DUMMY_UUID)
return FAIL
}
val box = BoundingBox(minPos, maxPos)
val serverWorld = Environment.getDimension(world.dimensionKey)
val (structureTag, missingMappings) = StructureFile.save(WorldToStructureWorldAdapter(serverWorld, serverWorld.rand, box.min), box.size, palette, this)
val structureFile = Files.createTempDirectory("HardcoreEnderExpansion_Structure_").resolve(CommandClientScaffolding.currentFile).toFile()
CompressedStreamTools.write(structureTag, structureFile)
Debug.setClipboardContents(structureFile)
if (missingMappings.isNotEmpty()) {
player.sendMessage(StringTextComponent("Missing mappings for states:"), Util.DUMMY_UUID)
for (missingMapping in missingMappings) {
player.sendMessage(StringTextComponent(" - ${TextFormatting.GRAY}$missingMapping"), Util.DUMMY_UUID)
}
}
player.sendMessage(StringTextComponent("Generated structure file of ${box.size}."), Util.DUMMY_UUID)
return SUCCESS
}
return FAIL
}
// Helpers
private fun find(world: World, pos: BlockPos?, direction: Direction): BlockPos? {
return pos?.offsetUntilExcept(direction, 0..255) { it.getBlock(world) === Blocks.AIR }
}
private fun findMinPos(world: World, pos: BlockPos): BlockPos? {
val bottomPos = find(world, pos, DOWN)
val y = bottomPos?.y
val x = find(world, bottomPos, WEST)?.x
val z = find(world, bottomPos, NORTH)?.z
return if (x == null || y == null || z == null) null else Pos(x, y, z)
}
private fun findMaxPos(world: World, pos: BlockPos): BlockPos? {
val topPos = find(world, pos, UP)
val y = topPos?.y
val x = find(world, topPos, EAST)?.x
val z = find(world, topPos, SOUTH)?.z
return if (x == null || y == null || z == null) null else Pos(x, y, z)
}
// Visuals and physics // Visuals and physics
override fun getShape(state: BlockState, world: IBlockReader, pos: BlockPos, context: ISelectionContext): VoxelShape { override fun getShape(state: BlockState, world: IBlockReader, pos: BlockPos, context: ISelectionContext): VoxelShape {

View File

@ -3,8 +3,6 @@ package chylex.hee.game.command
import chylex.hee.HEE import chylex.hee.HEE
import chylex.hee.client.util.MC import chylex.hee.client.util.MC
import chylex.hee.game.command.client.CommandClientHelp import chylex.hee.game.command.client.CommandClientHelp
import chylex.hee.game.command.client.CommandClientScaffolding
import chylex.hee.game.command.client.CommandDebugToggles
import chylex.hee.init.ModCommands import chylex.hee.init.ModCommands
import chylex.hee.util.forge.EventPriority import chylex.hee.util.forge.EventPriority
import chylex.hee.util.forge.Side import chylex.hee.util.forge.Side
@ -14,11 +12,15 @@ import net.minecraftforge.client.event.ClientChatEvent
@SubscribeAllEvents(Side.CLIENT, modid = HEE.ID) @SubscribeAllEvents(Side.CLIENT, modid = HEE.ID)
object ClientCommandHandler { // UPDATE object ClientCommandHandler { // UPDATE
val nonHelpCommands = listOf( private val client
CommandClientHelp, get() = listOf(
CommandClientScaffolding, CommandClientHelp
CommandDebugToggles )
).associateBy { it.name }
private val debug
get() = HEE.debugModule?.clientCommands.orEmpty()
val all = (client + debug).associateBy { it.name }
@SubscribeEvent(priority = EventPriority.LOWEST) @SubscribeEvent(priority = EventPriority.LOWEST)
fun onClientChat(e: ClientChatEvent) { fun onClientChat(e: ClientChatEvent) {
@ -34,7 +36,7 @@ object ClientCommandHandler { // UPDATE
val command = when { val command = when {
arguments.isEmpty() -> CommandClientHelp arguments.isEmpty() -> CommandClientHelp
arguments[0] == CommandClientHelp.name -> CommandClientHelp.takeIf { arguments.size < 2 || arguments[1] == "1" } ?: return arguments[0] == CommandClientHelp.name -> CommandClientHelp.takeIf { arguments.size < 2 || arguments[1] == "1" } ?: return
else -> nonHelpCommands[arguments[0]] ?: return else -> all[arguments[0]] ?: return
} }
command.executeCommand(source, arguments.drop(1).toTypedArray()) command.executeCommand(source, arguments.drop(1).toTypedArray())

View File

@ -10,6 +10,6 @@ object CommandClientHelp : IClientCommand {
override val description = CommandServerHelp.description override val description = CommandServerHelp.description
override fun executeCommand(sender: CommandSource, args: Array<String>) { override fun executeCommand(sender: CommandSource, args: Array<String>) {
CommandServerHelp.sendCommandListPage(sender, ClientCommandHandler.nonHelpCommands.keys, emptyMap(), "commands.hee.help.header.client", 1, null) CommandServerHelp.sendCommandListPage(sender, ClientCommandHandler.all.keys, emptyMap(), "commands.hee.help.header.client", 1, null)
} }
} }

View File

@ -27,6 +27,7 @@ import chylex.hee.system.random.nextItem
import chylex.hee.system.random.nextRounded import chylex.hee.system.random.nextRounded
import chylex.hee.util.math.Vec import chylex.hee.util.math.Vec
import chylex.hee.util.math.center import chylex.hee.util.math.center
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.nbt.TagCompound import chylex.hee.util.nbt.TagCompound
import chylex.hee.util.nbt.use import chylex.hee.util.nbt.use
@ -268,9 +269,9 @@ class EntityInfusedTNT : TNTEntity {
val waterRatio = foundWaterBlocks.size.toFloat() / totalCountedBlocks val waterRatio = foundWaterBlocks.size.toFloat() / totalCountedBlocks
val dropAmount = when { val dropAmount = when {
waterRatio < 0.1 -> remapRange(waterRatio, (0.0F)..(0.1F), (1.0F)..(1.6F)) waterRatio < 0.1 -> remapRange(waterRatio, range(0.0F, 0.1F), range(1.0F, 1.6F))
waterRatio < 0.4 -> remapRange(waterRatio, (0.1F)..(0.4F), (1.6F)..(4.0F)) waterRatio < 0.4 -> remapRange(waterRatio, range(0.1F, 0.4F), range(1.6F, 4.0F))
else -> remapRange(waterRatio, (0.4F)..(1.0F), (4.0F)..(5.8F)) else -> remapRange(waterRatio, range(0.4F, 1.0F), range(4.0F, 5.8F))
} }
val lootTable = Environment.getLootTable(LootTables.GAMEPLAY_FISHING) val lootTable = Environment.getLootTable(LootTables.GAMEPLAY_FISHING)

View File

@ -3,6 +3,7 @@ package chylex.hee.game.mechanics.instability.dimension
import chylex.hee.game.mechanics.instability.dimension.components.EndermiteSpawnLogic import chylex.hee.game.mechanics.instability.dimension.components.EndermiteSpawnLogic
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.floorToInt import chylex.hee.util.math.floorToInt
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.nbt.TagCompound import chylex.hee.util.nbt.TagCompound
import chylex.hee.util.nbt.use import chylex.hee.util.nbt.use
@ -26,7 +27,7 @@ open class DimensionInstabilityGlobal(private val world: World, private val ende
private fun calculateActionMultiplier(ticksSinceEndermiteSpawn: Long): Float { private fun calculateActionMultiplier(ticksSinceEndermiteSpawn: Long): Float {
return if (ticksSinceEndermiteSpawn < 300L) return if (ticksSinceEndermiteSpawn < 300L)
remapRange(ticksSinceEndermiteSpawn.toFloat(), (0F)..(300F), (0.2F)..(1F)) remapRange(ticksSinceEndermiteSpawn.toFloat(), range(0F, 300F), range(0.2F, 1F))
else else
1F 1F
} }

View File

@ -188,7 +188,7 @@ enum class TerritoryType(
} }
} }
private object GeneratorDummy : ITerritoryGenerator { object GeneratorDummy : ITerritoryGenerator {
override val segmentSize = Size(1) override val segmentSize = Size(1)
override fun provide(world: SegmentedWorld): TerritoryGenerationInfo { override fun provide(world: SegmentedWorld): TerritoryGenerationInfo {

View File

@ -28,6 +28,7 @@ import chylex.hee.util.math.Pos
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.directionTowards import chylex.hee.util.math.directionTowards
import chylex.hee.util.math.floorToInt import chylex.hee.util.math.floorToInt
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.square import chylex.hee.util.math.square
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
@ -128,7 +129,7 @@ object TerritoryVoid {
private const val PLAYER_NEXT_DAMAGE_TIME_TAG = "VoidNextDamageTime" private const val PLAYER_NEXT_DAMAGE_TIME_TAG = "VoidNextDamageTime"
private val FACTOR_DAMAGE_REMAP_FROM = (0.5F)..(3.0F) private val FACTOR_DAMAGE_REMAP_FROM = range(0.5F, 3F)
private val DAMAGE = Damage(DEAL_CREATIVE, IGNORE_INVINCIBILITY()) private val DAMAGE = Damage(DEAL_CREATIVE, IGNORE_INVINCIBILITY())
fun onWorldTick(world: ServerWorld) { fun onWorldTick(world: ServerWorld) {
@ -157,10 +158,10 @@ object TerritoryVoid {
val nextDamageTime = getLong(PLAYER_NEXT_DAMAGE_TIME_TAG) val nextDamageTime = getLong(PLAYER_NEXT_DAMAGE_TIME_TAG)
if (currentTime >= nextDamageTime) { if (currentTime >= nextDamageTime) {
val amount = remapRange(factor, FACTOR_DAMAGE_REMAP_FROM, (2F)..(6F)).ceilToInt().toFloat() val amount = remapRange(factor, FACTOR_DAMAGE_REMAP_FROM, range(2F, 6F)).ceilToInt().toFloat()
if (DAMAGE.dealTo(amount, entity, TITLE_WITHER)) { if (DAMAGE.dealTo(amount, entity, TITLE_WITHER)) {
val cooldown = min(30, remapRange(factor, FACTOR_DAMAGE_REMAP_FROM, (30F)..(6F)).floorToInt()) val cooldown = min(30, remapRange(factor, FACTOR_DAMAGE_REMAP_FROM, range(30F, 6F)).floorToInt())
putLong(PLAYER_NEXT_DAMAGE_TIME_TAG, currentTime + cooldown) putLong(PLAYER_NEXT_DAMAGE_TIME_TAG, currentTime + cooldown)
} }
} }

View File

@ -33,6 +33,7 @@ import chylex.hee.util.math.Size
import chylex.hee.util.math.component1 import chylex.hee.util.math.component1
import chylex.hee.util.math.component2 import chylex.hee.util.math.component2
import chylex.hee.util.math.component3 import chylex.hee.util.math.component3
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.square import chylex.hee.util.math.square
import chylex.hee.util.math.xz import chylex.hee.util.math.xz
@ -121,9 +122,9 @@ object Generator_ForgottenTombs : ITerritoryGenerator {
val noiseY = NoiseGenerator.OldPerlinNormalized(rand, scale = 48.0, octaves = 2) val noiseY = NoiseGenerator.OldPerlinNormalized(rand, scale = 48.0, octaves = 2)
for ((x, y, z) in BlockPos.ZERO.allInCenteredBoxMutable(RADIUS_XZ, RADIUS_Y, RADIUS_XZ)) { for ((x, y, z) in BlockPos.ZERO.allInCenteredBoxMutable(RADIUS_XZ, RADIUS_Y, RADIUS_XZ)) {
val normalizedY = remapRange(y.toFloat(), -radY..radY, 0F..1F) val normalizedY = remapRange(y.toFloat(), range(-radY, radY), range(0F, 1F))
val powerY = remapRange(sqrt(normalizedY), 0F..1F, (4.8F)..(1.6F)) val powerY = remapRange(sqrt(normalizedY), range(0F, 1F), range(4.8F, 1.6F))
val powerXZ = powerY * 0.8F val powerXZ = powerY * 0.8F
val corner = 1 + ((abs(x) + abs(z)) / (3F * radXZ)).pow(2F) val corner = 1 + ((abs(x) + abs(z)) / (3F * radXZ)).pow(2F)

View File

@ -56,6 +56,7 @@ import chylex.hee.util.math.addY
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.center import chylex.hee.util.math.center
import chylex.hee.util.math.floorToInt import chylex.hee.util.math.floorToInt
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.scale import chylex.hee.util.math.scale
import chylex.hee.util.math.scaleY import chylex.hee.util.math.scaleY
@ -207,7 +208,7 @@ object Generator_LostGarden : ITerritoryGenerator {
} }
val edgeMpXZ = if (distRatioXZ > 0.86) val edgeMpXZ = if (distRatioXZ > 0.86)
remapRange(distRatioXZ.coerceAtMost(1.0), (0.86)..(1.0), (1.0)..(0.86 * noiseXZ.getRawValue(-x * 3, -z * 3))) remapRange(distRatioXZ.coerceAtMost(1.0), range(0.86F, 1F), range(1F, 0.86F * noiseXZ.getRawValue(-x * 3, -z * 3).toFloat()))
else else
1.0 1.0
@ -217,10 +218,10 @@ object Generator_LostGarden : ITerritoryGenerator {
} }
val valueValley = 1.0 - noiseValley.getValue(x, z) { val valueValley = 1.0 - noiseValley.getValue(x, z) {
remap((0.5)..(1.0), (0.0)..(1.0)) remap(range(0.5F, 1F), range(0F, 1F))
coerce() coerce()
redistribute(0.5) redistribute(0.5)
remap((0.0)..(0.75)) remap(range(0F, 0.75F))
if (valueXZ < 0.6) { if (valueXZ < 0.6) {
multiply(valueXZ / 0.6) multiply(valueXZ / 0.6)
@ -228,7 +229,7 @@ object Generator_LostGarden : ITerritoryGenerator {
} }
val valueThreshold = noiseThreshold.getValue(x, z) { val valueThreshold = noiseThreshold.getValue(x, z) {
remap((0.14)..(0.29)) remap(range(0.14F, 0.29F))
} }
val valueTotalXZ = valueXZ * valueValley val valueTotalXZ = valueXZ * valueValley
@ -236,7 +237,7 @@ object Generator_LostGarden : ITerritoryGenerator {
val edgeMpY = (0.5 - (1.0 - edgeMpXZ)) val edgeMpY = (0.5 - (1.0 - edgeMpXZ))
val endersolY = -0.125 + (0.575 * noiseEndersol.getValue(x, z) { val endersolY = -0.125 + (0.575 * noiseEndersol.getValue(x, z) {
if (value > 0.6) { if (value > 0.6) {
remap((0.6)..(1.0), (0.6)..(5.0)) remap(range(0.6F, 1F), range(0.6F, 5F))
} }
}) })
@ -276,20 +277,20 @@ object Generator_LostGarden : ITerritoryGenerator {
private fun NoiseValue.distanceReshapeXZ(distance: Double) { private fun NoiseValue.distanceReshapeXZ(distance: Double) {
value = when (distance) { value = when (distance) {
in (0.00)..(0.40) -> value * remapRange(distance, (0.0)..(0.4), (0.8)..(1.0)) in (0.00)..(0.40) -> value * remapRange(distance, range(0F, 0.4F), range(0.8F, 1F))
in (0.40)..(0.85) -> value in (0.40)..(0.85) -> value
in (0.85)..(1.00) -> value * remapRange(distance, (0.85)..(1.0), (1.0)..(0.0)) in (0.85)..(1.00) -> value * remapRange(distance, range(0.85F, 1F), range(1F, 0F))
else -> 0.0 else -> 0.0
} }
} }
private fun NoiseValue.distanceReshapeY(distance: Double) { private fun NoiseValue.distanceReshapeY(distance: Double) {
value = when (distance) { value = when (distance) {
in (-1.0)..(-0.6) -> value * square(remapRange(distance, (-1.0)..(-0.5), (0.0)..(1.0))) in (-1.0)..(-0.6) -> value * square(remapRange(distance, range(-1F, -0.5F), range(0F, 1F)))
in (-0.6)..( 0.5) -> value in (-0.6)..( 0.5) -> value
in ( 0.5)..( 0.8) -> value * remapRange(distance, (0.5)..(0.8), (1.0)..(0.5)) in ( 0.5)..( 0.8) -> value * remapRange(distance, range(0.5F, 0.8F), range(1F, 0.5F))
in ( 0.8)..( 1.4) -> value * 0.5 in ( 0.8)..( 1.4) -> value * 0.5
in ( 1.4)..( 2.0) -> value * remapRange(distance, (1.4)..(2.0), (0.5)..(0.1)) in ( 1.4)..( 2.0) -> value * remapRange(distance, range(1.4F, 2F), range(0.5F, 0.1F))
else -> 0.0 else -> 0.0
} }
} }

View File

@ -51,6 +51,7 @@ import chylex.hee.util.math.Vec
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.center import chylex.hee.util.math.center
import chylex.hee.util.math.directionTowards import chylex.hee.util.math.directionTowards
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.square import chylex.hee.util.math.square
import chylex.hee.util.math.toRadians import chylex.hee.util.math.toRadians
@ -414,7 +415,7 @@ object Generator_ObsidianTowers : ITerritoryGenerator {
ENDIUM_ORES { ENDIUM_ORES {
override fun beforePillars(world: SegmentedWorld, rand: Random, island: Island) { override fun beforePillars(world: SegmentedWorld, rand: Random, island: Island) {
val piles = if (island.radius >= 5.0) val piles = if (island.radius >= 5.0)
rand.nextRounded(remapRange(island.radius, (5.0)..(7.0), (2.0)..(4.0)).toFloat()) rand.nextRounded(remapRange(island.radius, range(5F, 7F), range(2F, 4F)).toFloat())
else else
1 1

View File

@ -50,6 +50,7 @@ import chylex.hee.util.math.addY
import chylex.hee.util.math.ceilToInt import chylex.hee.util.math.ceilToInt
import chylex.hee.util.math.center import chylex.hee.util.math.center
import chylex.hee.util.math.floorToInt import chylex.hee.util.math.floorToInt
import chylex.hee.util.math.range
import chylex.hee.util.math.remapRange import chylex.hee.util.math.remapRange
import chylex.hee.util.math.scale import chylex.hee.util.math.scale
import chylex.hee.util.math.scaleY import chylex.hee.util.math.scaleY
@ -128,7 +129,7 @@ object Generator_TheHub : ITerritoryGenerator {
redistribute(0.4) redistribute(0.4)
ifNonZero { ifNonZero {
remap((0.2)..(1.0)) remap(range(0.2F, 1F))
multiply(ELEVATION_BOTTOM) multiply(ELEVATION_BOTTOM)
} }
} }
@ -154,7 +155,7 @@ object Generator_TheHub : ITerritoryGenerator {
private fun NoiseValue.distanceReshape(distance: Double) { private fun NoiseValue.distanceReshape(distance: Double) {
value = when (distance) { value = when (distance) {
in (0.00)..(0.85) -> value in (0.00)..(0.85) -> value
in (0.85)..(1.00) -> value * remapRange(distance, (0.85)..(1.0), (1.0)..(0.0)) in (0.85)..(1.00) -> value * remapRange(distance, range(0.85F, 1F), range(1F, 0F))
else -> 0.0 else -> 0.0
} }
} }

View File

@ -375,7 +375,7 @@ object ModBlocks {
// Blocks: Utilities // Blocks: Utilities
@JvmField val ETERNAL_FIRE = BlockEternalFire(buildEternalFire) named "eternal_fire" @JvmField val ETERNAL_FIRE = BlockEternalFire(buildEternalFire) named "eternal_fire"
@JvmField val SCAFFOLDING = BlockScaffolding(buildScaffolding) named "scaffolding" @JvmField val SCAFFOLDING = BlockScaffolding.create(buildScaffolding) named "scaffolding"
// Registry // Registry

View File

@ -3,17 +3,12 @@ package chylex.hee.init
import chylex.hee.HEE import chylex.hee.HEE
import chylex.hee.game.command.argument.EnumArgument import chylex.hee.game.command.argument.EnumArgument
import chylex.hee.game.command.argument.ValidatedStringArgument import chylex.hee.game.command.argument.ValidatedStringArgument
import chylex.hee.game.command.server.CommandDebugInstability
import chylex.hee.game.command.server.CommandDebugStructure
import chylex.hee.game.command.server.CommandDebugTerritory
import chylex.hee.game.command.server.CommandDebugTestWorld
import chylex.hee.game.command.server.CommandServerCausatum import chylex.hee.game.command.server.CommandServerCausatum
import chylex.hee.game.command.server.CommandServerHelp import chylex.hee.game.command.server.CommandServerHelp
import chylex.hee.game.command.server.CommandServerInfusions import chylex.hee.game.command.server.CommandServerInfusions
import chylex.hee.game.command.server.CommandServerLootChest import chylex.hee.game.command.server.CommandServerLootChest
import chylex.hee.game.command.server.CommandServerPortalToken import chylex.hee.game.command.server.CommandServerPortalToken
import chylex.hee.game.command.util.executes import chylex.hee.game.command.util.executes
import chylex.hee.system.Debug
import chylex.hee.util.forge.SubscribeAllEvents import chylex.hee.util.forge.SubscribeAllEvents
import chylex.hee.util.forge.SubscribeEvent import chylex.hee.util.forge.SubscribeEvent
import net.minecraft.command.Commands.literal import net.minecraft.command.Commands.literal
@ -32,13 +27,7 @@ object ModCommands {
CommandServerPortalToken CommandServerPortalToken
) )
val debug = if (Debug.enabled) listOf( val debug = HEE.debugModule?.serverCommands.orEmpty()
CommandDebugInstability,
CommandDebugStructure,
CommandDebugTerritory,
CommandDebugTestWorld
)
else emptyList()
init { init {
ArgumentTypes.register("hee:enum", EnumArgument::class.java, EnumArgument.Serializer) ArgumentTypes.register("hee:enum", EnumArgument::class.java, EnumArgument.Serializer)

View File

@ -1,7 +1,7 @@
package chylex.hee.mixin; package chylex.hee.mixin;
import chylex.hee.HEE;
import chylex.hee.client.util.MC; import chylex.hee.client.util.MC;
import chylex.hee.system.Debug;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.Minecraft.WorldSelectionType; import net.minecraft.client.Minecraft.WorldSelectionType;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class HookUselessExperimentalWorldWarning { // UPDATE remove when https://github.com/MinecraftForge/MinecraftForge/pull/7275 is pulled public abstract class HookUselessExperimentalWorldWarning { // UPDATE remove when https://github.com/MinecraftForge/MinecraftForge/pull/7275 is pulled
@Inject(method = "deleteWorld", at = @At("HEAD"), cancellable = true) @Inject(method = "deleteWorld", at = @At("HEAD"), cancellable = true)
private void ignoreUselessWarning(final WorldSelectionType selectionType, final String worldName, final boolean customized, final Runnable runnable, final CallbackInfo ci) { private void ignoreUselessWarning(final WorldSelectionType selectionType, final String worldName, final boolean customized, final Runnable runnable, final CallbackInfo ci) {
if (Debug.enabled) { if (HEE.debug) {
ci.cancel(); ci.cancel();
MC.instance.enqueue(runnable); MC.instance.enqueue(runnable);
} }

View File

@ -1,5 +1,6 @@
package chylex.hee.network.client package chylex.hee.network.client
import chylex.hee.HEE
import chylex.hee.game.block.BlockDragonEggOverride import chylex.hee.game.block.BlockDragonEggOverride
import chylex.hee.game.block.BlockEnderGooPurified import chylex.hee.game.block.BlockEnderGooPurified
import chylex.hee.game.block.BlockPuzzleLogic import chylex.hee.game.block.BlockPuzzleLogic
@ -35,7 +36,6 @@ import chylex.hee.game.mechanics.table.TableParticleHandler
import chylex.hee.game.potion.BanishmentEffect import chylex.hee.game.potion.BanishmentEffect
import chylex.hee.game.world.generation.feature.tombdungeon.piece.TombDungeonRoom_Tomb import chylex.hee.game.world.generation.feature.tombdungeon.piece.TombDungeonRoom_Tomb
import chylex.hee.network.BaseClientPacket import chylex.hee.network.BaseClientPacket
import chylex.hee.system.Debug
import chylex.hee.util.forge.Side import chylex.hee.util.forge.Side
import chylex.hee.util.forge.Sided import chylex.hee.util.forge.Sided
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
@ -114,7 +114,7 @@ class PacketClientFX<T : IFxData>() : BaseClientPacket() {
val index = buffer.readByte().toInt() val index = buffer.readByte().toInt()
if (index == -1) { if (index == -1) {
if (Debug.enabled) { if (HEE.debug) {
throw IndexOutOfBoundsException("could not find FX handler") throw IndexOutOfBoundsException("could not find FX handler")
} }
} }

View File

@ -0,0 +1,46 @@
package chylex.hee.test.main
import chylex.hee.game.world.generation.structure.world.segments.ISegment
import chylex.hee.game.world.generation.structure.world.segments.SegmentFull
import chylex.hee.game.world.generation.structure.world.segments.SegmentMultiState
import chylex.hee.game.world.util.allInBoxMutable
import chylex.hee.util.math.Pos
import chylex.hee.util.math.Size
import net.minecraft.block.Blocks
import net.minecraft.util.registry.Bootstrap
fun main() {
println("Initializing...")
Bootstrap.register()
val segments = mutableListOf<ISegment>()
val thresholdField = SegmentMultiState::class.java.getDeclaredField("threshold").also { it.isAccessible = true }
println("Waiting 20 seconds for data fixers...")
Thread.sleep(20000L)
println("Waiting 4 seconds to start memory profiling...")
Thread.sleep(4000L)
for (s in 8..128 step 12) {
var segment: ISegment = SegmentMultiState(Size(s), Blocks.AIR).also(segments::add)
val threshold = thresholdField.getInt(segment)
println("Testing size $s (total ${s * s * s} threshold $threshold)")
for ((index, pos) in Pos(0, 0, 0).allInBoxMutable(Pos(s - 1, s - 1, s - 1)).withIndex()) {
if (index < threshold - 1) {
segment = segment.withState(pos, Blocks.END_STONE.defaultState)
continue
}
check(segment is SegmentMultiState)
segment = segment.withState(pos, Blocks.END_STONE.defaultState).also(segments::add)
check(segment is SegmentFull)
break
}
}
println("Done!")
readLine()
segments.toString()
}