Using BlockSets. Added filtering and several replace settings.
Overhauled build.gradle based on Create. Added flywheel as jarInJar, no external dependency anymore. New mappings channel from parchment. Added replace modes: air, air and solid, solid, offhand filtered. Offhand filtering looks in randomizer bag as well. Protecting tile entities by default. Other mods can cancel placement through BlockEvent.OnBlockPlaced, such as FTBChunks. BlockPreviews.drawOutlinesIfNoBlockInHand overhaul.
This commit is contained in:
249
build.gradle
249
build.gradle
@@ -1,85 +1,66 @@
|
||||
plugins {
|
||||
id 'eclipse'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.gradle' version '5.1.+'
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://maven.minecraftforge.net' }
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url = 'https://repo.spongepowered.org/repository/maven-public' }
|
||||
maven { url = 'https://maven.parchmentmc.org' }
|
||||
}
|
||||
dependencies {
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: "${forgegradle_version}", changing: false
|
||||
classpath "org.spongepowered:mixingradle:${mixingradle_version}"
|
||||
classpath "org.parchmentmc:librarian:${librarian_version}"
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id 'com.matthewprenger.cursegradle' version "${cursegradle_version}"
|
||||
}
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
apply plugin: 'org.parchmentmc.librarian.forgegradle'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'org.spongepowered.mixin'
|
||||
|
||||
version = '1.19-2.40'
|
||||
jarJar.enable()
|
||||
|
||||
boolean flywheelInWorkspace = findProject(':Flywheel') != null
|
||||
|
||||
ext.buildNumber = System.getenv('BUILD_NUMBER')
|
||||
|
||||
version = mod_version
|
||||
group = 'nl.requios.effortlessbuilding'
|
||||
archivesBaseName = 'effortlessbuilding'
|
||||
archivesBaseName = "effortlessbuilding-${artifact_minecraft_version}"
|
||||
|
||||
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||
|
||||
println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
|
||||
minecraft {
|
||||
// The mappings can be changed at any time, and must be in the following format.
|
||||
// snapshot_YYYYMMDD Snapshot are built nightly.
|
||||
// stable_# Stables are built at the discretion of the MCP team.
|
||||
// Use non-default mappings at your own risk. they may not always work.
|
||||
// Simply re-run your setup task after changing the mappings to update your workspace.
|
||||
mappings channel: 'official', version: '1.19.2'
|
||||
mappings channel: 'parchment', version: "${parchment_version}-${minecraft_version}"
|
||||
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
|
||||
// Default run configurations.
|
||||
// These can be tweaked, removed, or duplicated as needed.
|
||||
runs {
|
||||
client {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
// Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP)
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
// Recommended logging level for the console
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||
property 'forge.enabledGameTestNamespaces', 'effortlessbuilding'
|
||||
|
||||
// Flywheel
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"
|
||||
arg '-mixin.config=flywheel.mixins.json'
|
||||
//jvmArgs '-XX:+UnlockCommercialFeatures' // uncomment for profiling
|
||||
property 'forge.logging.console.level', 'info'
|
||||
|
||||
mods {
|
||||
effortlessbuilding {
|
||||
source sourceSets.main
|
||||
}
|
||||
|
||||
if (flywheelInWorkspace) {
|
||||
flywheel {
|
||||
source project(":Flywheel").sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
property 'forge.enabledGameTestNamespaces', 'effortlessbuilding'
|
||||
|
||||
// Flywheel
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"
|
||||
|
||||
mods {
|
||||
effortlessbuilding {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
||||
// By default, the server will crash when no gametests are provided.
|
||||
// The gametest system is also enabled by default for other run configs under the /test command.
|
||||
gameTestServer {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
property 'forge.enabledGameTestNamespaces', 'effortlessbuilding'
|
||||
|
||||
workingDirectory project.file('run/server')
|
||||
property 'forge.logging.console.level', 'info'
|
||||
mods {
|
||||
effortlessbuilding {
|
||||
source sourceSets.main
|
||||
@@ -89,73 +70,107 @@ minecraft {
|
||||
|
||||
data {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
property 'forge.logging.markers', 'REGISTRIES'
|
||||
|
||||
property 'forge.logging.markers', 'REGISTRIES,REGISTRYDUMP'
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||
args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||
|
||||
args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources')
|
||||
mods {
|
||||
effortlessbuilding {
|
||||
source sourceSets.main
|
||||
}
|
||||
|
||||
if (flywheelInWorkspace) {
|
||||
flywheel {
|
||||
source project(":Flywheel").sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include resources generated by data generators.
|
||||
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||
|
||||
repositories {
|
||||
// Put repositories for dependencies here
|
||||
// ForgeGradle automatically adds the Forge maven and Maven Central for you
|
||||
|
||||
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so:
|
||||
// flatDir {
|
||||
// dir 'libs'
|
||||
// }
|
||||
|
||||
//Flywheel
|
||||
maven {
|
||||
name "tterrag maven"
|
||||
url "https://maven.tterrag.com/"
|
||||
// Location of the maven that hosts JEI files (and TiC)
|
||||
name 'Progwml6 maven'
|
||||
url 'https://dvs1.progwml6.com/files/maven'
|
||||
}
|
||||
/*maven {
|
||||
// Location of a maven mirror for JEI files, as a fallback
|
||||
name 'ModMaven'
|
||||
url 'https://modmaven.k-4u.nl'
|
||||
}*/
|
||||
maven {
|
||||
// Location of the maven for vazkii's mods
|
||||
name 'blamejared'
|
||||
url 'https://maven.blamejared.com'
|
||||
}
|
||||
maven {
|
||||
// Location of the maven for mixed mappings, Registrate, and Flywheel
|
||||
name 'tterrag maven'
|
||||
url 'https://maven.tterrag.com'
|
||||
}
|
||||
maven {
|
||||
url 'https://www.cursemaven.com'
|
||||
content {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
//location of the maven for dynamic trees
|
||||
url 'https://harleyoconnor.com/maven'
|
||||
}
|
||||
maven {
|
||||
//location of the maven for curios api
|
||||
url = "https://maven.theillusivec4.top/"
|
||||
}
|
||||
maven {
|
||||
name = "Modrinth"
|
||||
url = "https://api.modrinth.com/maven"
|
||||
content {
|
||||
includeGroup "maven.modrinth"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Specify the version of Minecraft to use. If this is any group other than 'net.minecraft', it is assumed
|
||||
// that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied.
|
||||
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
|
||||
minecraft 'net.minecraftforge:forge:1.19.2-43.1.47'
|
||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||
|
||||
// Real mod deobf dependency examples - these get remapped to your current mappings
|
||||
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency
|
||||
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency
|
||||
// implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency
|
||||
jarJar(group: 'com.jozufozu.flywheel', name: "flywheel-forge-${flywheel_minecraft_version}", version: '[0.6.8,0.6.9)') {
|
||||
jarJar.pin(it, project.flywheel_version)
|
||||
}
|
||||
|
||||
// Examples using mod jars from ./libs
|
||||
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
|
||||
if (flywheelInWorkspace) {
|
||||
implementation project(':Flywheel')
|
||||
} else {
|
||||
implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}")
|
||||
}
|
||||
|
||||
// For more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
|
||||
//Flywheel
|
||||
//Versions: https://maven.tterrag.com/com/jozufozu/flywheel/flywheel-forge-1.19.2/
|
||||
implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-1.19.2:0.6.8-13")
|
||||
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings
|
||||
// This allows 'Settings > Build, Execution, and Deployment > Build Tools > Gradle > Build and run using' set to IntelliJ to work correctly
|
||||
if (System.getProperty('idea.sync.active') != 'true') {
|
||||
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main.resources {
|
||||
srcDir 'src/generated/resources'
|
||||
exclude '.cache/'
|
||||
}
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs = ['-Xdiags:verbose']
|
||||
}
|
||||
|
||||
// Example for how to get properties into the manifest for reading by the runtime..
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
"Specification-Title": "effortlessbuilding",
|
||||
"Specification-Vendor": "requios",
|
||||
"Specification-Version": "1", // We are version 1 of ourselves
|
||||
"Implementation-Title": project.name,
|
||||
"Specification-Version": "1",
|
||||
"Implementation-Title": project.jar.baseName,
|
||||
"Implementation-Version": project.jar.archiveVersion,
|
||||
"Implementation-Vendor" :"requios",
|
||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
@@ -163,25 +178,37 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
// Example configuration to allow publishing using the maven-publish plugin
|
||||
// This is the preferred method to reobfuscate your jar file
|
||||
task jarJarRelease {
|
||||
doLast {
|
||||
tasks.jarJar {
|
||||
classifier = ''
|
||||
}
|
||||
}
|
||||
finalizedBy tasks.jarJar
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
jar.finalizedBy('reobfJar')
|
||||
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
|
||||
// publish.dependsOn('reobfJar')
|
||||
tasks.jarJar.finalizedBy('reobfJarJar')
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact jar
|
||||
artifactId = archivesBaseName
|
||||
|
||||
from components.java
|
||||
fg.component(it)
|
||||
jarJar.component(it)
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "file://${project.projectDir}/mcmodsrepo"
|
||||
if (project.hasProperty('mavendir')) {
|
||||
maven { url mavendir }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
@@ -2,3 +2,19 @@
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
|
||||
mod_version = 3.0
|
||||
artifact_minecraft_version = 1.19.2
|
||||
|
||||
minecraft_version = 1.19.2
|
||||
forge_version = 43.1.47
|
||||
|
||||
forgegradle_version = 5.1.53
|
||||
mixingradle_version = 0.7-SNAPSHOT
|
||||
mixin_version = 0.8.5
|
||||
librarian_version = 1.+
|
||||
cursegradle_version = 1.4.0
|
||||
parchment_version = 2022.11.06
|
||||
|
||||
flywheel_minecraft_version = 1.19.2
|
||||
flywheel_version = 0.6.8.a-14
|
||||
@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
@@ -13,7 +12,6 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
@@ -27,12 +25,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
|
||||
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||
import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui;
|
||||
import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu;
|
||||
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui;
|
||||
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||
import nl.requios.effortlessbuilding.render.RenderHandler;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
import nl.requios.effortlessbuilding.network.*;
|
||||
import nl.requios.effortlessbuilding.render.BuildRenderTypes;
|
||||
@@ -161,7 +156,7 @@ public class ClientEvents {
|
||||
|
||||
//QuickReplace toggle
|
||||
if (keyBindings[1].consumeClick()) {
|
||||
EffortlessBuildingClient.QUICK_REPLACE.toggleQuickReplacing();
|
||||
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
|
||||
}
|
||||
|
||||
//Radial menu
|
||||
@@ -241,7 +236,7 @@ public class ClientEvents {
|
||||
keyBindings[keybindIndex].getKey().getValue());
|
||||
}
|
||||
|
||||
public static BlockHitResult getLookingAt(Player player) {
|
||||
public static BlockHitResult getLookingAtFar(Player player) {
|
||||
Level world = player.level;
|
||||
|
||||
//base distance off of player ability (config)
|
||||
|
||||
@@ -17,11 +17,11 @@ import net.minecraftforge.event.level.BlockEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
import nl.requios.effortlessbuilding.systems.ServerBuildState;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
import nl.requios.effortlessbuilding.network.AddUndoMessage;
|
||||
import nl.requios.effortlessbuilding.network.ClearUndoMessage;
|
||||
@@ -55,7 +55,7 @@ public class CommonEvents {
|
||||
EffortlessBuilding.DELAYED_BLOCK_PLACER.tick();
|
||||
}
|
||||
|
||||
//Cancel event if necessary. Nothing more, rest is handled on mouse right click
|
||||
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
|
||||
@SubscribeEvent
|
||||
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
|
||||
if (event.getLevel().isClientSide()) return; //Never called clientside anyway, but just to be sure
|
||||
@@ -65,9 +65,7 @@ public class CommonEvents {
|
||||
//Don't cancel event if our custom logic is breaking blocks
|
||||
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
|
||||
|
||||
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
|
||||
|
||||
if (buildMode != BuildModeEnum.DISABLED || EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing()) {
|
||||
if (!ServerBuildState.isLikeVanilla(player)) {
|
||||
|
||||
//Only cancel if itemblock in hand
|
||||
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
|
||||
@@ -83,6 +81,7 @@ public class CommonEvents {
|
||||
}
|
||||
}
|
||||
|
||||
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
|
||||
@SubscribeEvent
|
||||
public static void onBlockBroken(BlockEvent.BreakEvent event) {
|
||||
if (event.getLevel().isClientSide()) return;
|
||||
@@ -92,10 +91,7 @@ public class CommonEvents {
|
||||
//Don't cancel event if our custom logic is breaking blocks
|
||||
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
|
||||
|
||||
//Cancel event if necessary
|
||||
//If cant break far then dont cancel event ever
|
||||
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
|
||||
if (buildMode != BuildModeEnum.DISABLED && ReachHelper.canBreakFar(player)) {
|
||||
if (!ServerBuildState.isLikeVanilla(player) && ReachHelper.canBreakFar(player)) {
|
||||
event.setCanceled(true);
|
||||
} else {
|
||||
//NORMAL mode, let vanilla handle block breaking
|
||||
|
||||
@@ -10,14 +10,14 @@ import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen;
|
||||
import nl.requios.effortlessbuilding.gui.RandomizerBagScreen;
|
||||
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||
import nl.requios.effortlessbuilding.systems.BuilderChain;
|
||||
import nl.requios.effortlessbuilding.systems.QuickReplace;
|
||||
import nl.requios.effortlessbuilding.systems.BuildSettings;
|
||||
|
||||
public class EffortlessBuildingClient {
|
||||
|
||||
public static final BuilderChain BUILDER_CHAIN = new BuilderChain();
|
||||
public static final BuildModes BUILD_MODES = new BuildModes();
|
||||
public static final BuildModifiers BUILD_MODIFIERS = new BuildModifiers();
|
||||
public static final QuickReplace QUICK_REPLACE = new QuickReplace();
|
||||
public static final BuildSettings BUILD_SETTINGS = new BuildSettings();
|
||||
public static final BlockPreviews BLOCK_PREVIEWS = new BlockPreviews();
|
||||
|
||||
public static void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) {
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
package nl.requios.effortlessbuilding.buildmode;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
|
||||
import java.util.List;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
public abstract class BaseBuildMode implements IBuildMode {
|
||||
|
||||
@@ -18,7 +12,7 @@ public abstract class BaseBuildMode implements IBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(List<BlockEntry> blocks) {
|
||||
public boolean onClick(BlockSet blocks) {
|
||||
clicks++;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.network.IsUsingBuildModePacket;
|
||||
import nl.requios.effortlessbuilding.network.PacketHandler;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
|
||||
import java.util.*;
|
||||
@@ -17,14 +18,8 @@ import java.util.*;
|
||||
public class BuildModes {
|
||||
private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
|
||||
|
||||
public void findCoordinates(List<BlockEntry> blocks, Player player, BuildModeEnum buildMode) {
|
||||
public void findCoordinates(BlockSet blocks, Player player, BuildModeEnum buildMode) {
|
||||
buildMode.instance.findCoordinates(blocks);
|
||||
|
||||
//Limit number of blocks you can place
|
||||
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
|
||||
while (blocks.size() > limit) {
|
||||
blocks.remove(blocks.size()-1);
|
||||
}
|
||||
}
|
||||
|
||||
public BuildModeEnum getBuildMode() {
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
package nl.requios.effortlessbuilding.buildmode;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
|
||||
import java.util.List;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
public interface IBuildMode {
|
||||
|
||||
@@ -14,7 +8,7 @@ public interface IBuildMode {
|
||||
void initialize();
|
||||
|
||||
//Returns if we should place blocks now
|
||||
boolean onClick(List<BlockEntry> blocks);
|
||||
boolean onClick(BlockSet blocks);
|
||||
|
||||
void findCoordinates(List<BlockEntry> blocks);
|
||||
void findCoordinates(BlockSet blocks);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package nl.requios.effortlessbuilding.buildmode;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import nl.requios.effortlessbuilding.ClientEvents;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||
|
||||
public class ModeOptions {
|
||||
@@ -73,7 +71,7 @@ public class ModeOptions {
|
||||
break;
|
||||
case REPLACE:
|
||||
if (player.level.isClientSide)
|
||||
EffortlessBuildingClient.QUICK_REPLACE.toggleQuickReplacing();
|
||||
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
|
||||
break;
|
||||
case OPEN_MODIFIER_SETTINGS:
|
||||
if (player.level.isClientSide)
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -23,7 +24,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(List<BlockEntry> blocks) {
|
||||
public boolean onClick(BlockSet blocks) {
|
||||
super.onClick(blocks);
|
||||
|
||||
if (clicks == 1) {
|
||||
@@ -35,7 +36,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
return false;
|
||||
}
|
||||
|
||||
firstBlockEntry = blocks.get(0);
|
||||
firstBlockEntry = blocks.getFirstBlockEntry();
|
||||
} else if (clicks == 2) {
|
||||
//Second click, find second position
|
||||
|
||||
@@ -57,7 +58,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findCoordinates(List<BlockEntry> blocks) {
|
||||
public void findCoordinates(BlockSet blocks) {
|
||||
if (clicks == 0) return;
|
||||
|
||||
if (clicks == 1) {
|
||||
@@ -85,6 +86,8 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
for (BlockPos pos : getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)) {
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
blocks.lastPos = secondPos;
|
||||
} else {
|
||||
var player = Minecraft.getInstance().player;
|
||||
BlockPos firstPos = firstBlockEntry.blockPos;
|
||||
@@ -118,6 +121,8 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
for (BlockPos pos : getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)) {
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
blocks.lastPos = thirdPos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,7 +20,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(List<BlockEntry> blocks) {
|
||||
public boolean onClick(BlockSet blocks) {
|
||||
super.onClick(blocks);
|
||||
|
||||
if (clicks == 1) {
|
||||
@@ -31,7 +32,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
|
||||
return false;
|
||||
}
|
||||
|
||||
firstBlockEntry = blocks.get(0);
|
||||
firstBlockEntry = blocks.getFirstBlockEntry();
|
||||
} else {
|
||||
//Second click, place blocks
|
||||
clicks = 0;
|
||||
@@ -41,7 +42,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findCoordinates(List<BlockEntry> blocks) {
|
||||
public void findCoordinates(BlockSet blocks) {
|
||||
if (clicks == 0) return;
|
||||
|
||||
var player = Minecraft.getInstance().player;
|
||||
@@ -68,6 +69,8 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
|
||||
for (BlockPos pos : getAllBlocks(player, x1, y1, z1, x2, y2, z2)) {
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
blocks.lastPos = secondPos;
|
||||
}
|
||||
|
||||
//Finds the place of the second block pos based on criteria (floor must be on same height as first click, wall on same plane etc)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package nl.requios.effortlessbuilding.buildmode.buildmodes;
|
||||
|
||||
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
|
||||
import java.util.List;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
public class Disabled implements IBuildMode {
|
||||
|
||||
@@ -13,12 +11,12 @@ public class Disabled implements IBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(List<BlockEntry> blocks) {
|
||||
public boolean onClick(BlockSet blocks) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findCoordinates(List<BlockEntry> blocks) {
|
||||
public void findCoordinates(BlockSet blocks) {
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package nl.requios.effortlessbuilding.buildmode.buildmodes;
|
||||
|
||||
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
|
||||
import java.util.List;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
public class Single implements IBuildMode {
|
||||
|
||||
@@ -13,12 +11,12 @@ public class Single implements IBuildMode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(List<BlockEntry> blocks) {
|
||||
public boolean onClick(BlockSet blocks) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findCoordinates(List<BlockEntry> blocks) {
|
||||
public void findCoordinates(BlockSet blocks) {
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
import nl.requios.effortlessbuilding.systems.DelayedBlockPlacer;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -30,7 +30,7 @@ import java.util.List;
|
||||
|
||||
public class BuildModifiers {
|
||||
|
||||
public void findCoordinates(List<BlockEntry> blocks, LocalPlayer player, ModifierSettingsManager.ModifierSettings modifierSettings) {
|
||||
public void findCoordinates(BlockSet blocks, LocalPlayer player, ModifierSettingsManager.ModifierSettings modifierSettings) {
|
||||
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public class BuildModifiers {
|
||||
//add to undo stack
|
||||
BlockPos firstPos = startCoordinates.get(0);
|
||||
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
|
||||
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
|
||||
UndoRedo.addUndo(player, new UndoRedoBlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,13 @@ import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import nl.requios.effortlessbuilding.CommonConfig;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.utilities.FixedStack;
|
||||
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -23,14 +20,14 @@ public class UndoRedo {
|
||||
|
||||
//Undo and redo stacks per player
|
||||
//Gets added to twice in singleplayer (server and client) if not careful. So separate stacks.
|
||||
private static final Map<UUID, FixedStack<BlockSet>> undoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<BlockSet>> undoStacksServer = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<BlockSet>> redoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<BlockSet>> redoStacksServer = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksServer = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksServer = new HashMap<>();
|
||||
|
||||
//add to undo stack
|
||||
public static void addUndo(Player player, BlockSet blockSet) {
|
||||
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
public static void addUndo(Player player, UndoRedoBlockSet blockSet) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
|
||||
//Assert coordinates is as long as previous and new blockstate lists
|
||||
if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() ||
|
||||
@@ -50,35 +47,35 @@ public class UndoRedo {
|
||||
|
||||
//If no stack exists, make one
|
||||
if (!undoStacks.containsKey(player.getUUID())) {
|
||||
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||
undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||
}
|
||||
|
||||
undoStacks.get(player.getUUID()).push(blockSet);
|
||||
}
|
||||
|
||||
private static void addRedo(Player player, BlockSet blockSet) {
|
||||
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
private static void addRedo(Player player, UndoRedoBlockSet blockSet) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
|
||||
//(No asserts necessary, it's private)
|
||||
|
||||
//If no stack exists, make one
|
||||
if (!redoStacks.containsKey(player.getUUID())) {
|
||||
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||
redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||
}
|
||||
|
||||
redoStacks.get(player.getUUID()).push(blockSet);
|
||||
}
|
||||
|
||||
public static boolean undo(Player player) {
|
||||
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
|
||||
if (!undoStacks.containsKey(player.getUUID())) return false;
|
||||
|
||||
FixedStack<BlockSet> undoStack = undoStacks.get(player.getUUID());
|
||||
FixedStack<UndoRedoBlockSet> undoStack = undoStacks.get(player.getUUID());
|
||||
|
||||
if (undoStack.isEmpty()) return false;
|
||||
|
||||
BlockSet blockSet = undoStack.pop();
|
||||
UndoRedoBlockSet blockSet = undoStack.pop();
|
||||
List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
@@ -129,15 +126,15 @@ public class UndoRedo {
|
||||
}
|
||||
|
||||
public static boolean redo(Player player) {
|
||||
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
|
||||
if (!redoStacks.containsKey(player.getUUID())) return false;
|
||||
|
||||
FixedStack<BlockSet> redoStack = redoStacks.get(player.getUUID());
|
||||
FixedStack<UndoRedoBlockSet> redoStack = redoStacks.get(player.getUUID());
|
||||
|
||||
if (redoStack.isEmpty()) return false;
|
||||
|
||||
BlockSet blockSet = redoStack.pop();
|
||||
UndoRedoBlockSet blockSet = redoStack.pop();
|
||||
List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
@@ -187,8 +184,8 @@ public class UndoRedo {
|
||||
}
|
||||
|
||||
public static void clear(Player player) {
|
||||
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
if (undoStacks.containsKey(player.getUUID())) {
|
||||
undoStacks.get(player.getUUID()).clear();
|
||||
}
|
||||
|
||||
@@ -2,19 +2,18 @@ package nl.requios.effortlessbuilding.buildmodifier;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//Used only for Undo
|
||||
public class BlockSet {
|
||||
public class UndoRedoBlockSet {
|
||||
private final List<BlockPos> coordinates;
|
||||
private final List<BlockState> previousBlockStates;
|
||||
private final List<BlockState> newBlockStates;
|
||||
private final BlockPos firstPos;
|
||||
private final BlockPos secondPos;
|
||||
|
||||
public BlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates,
|
||||
public UndoRedoBlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates,
|
||||
BlockPos firstPos, BlockPos secondPos) {
|
||||
this.coordinates = coordinates;
|
||||
this.previousBlockStates = previousBlockStates;
|
||||
@@ -1,10 +1,14 @@
|
||||
package nl.requios.effortlessbuilding.compatibility;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.ItemHandlerHelper;
|
||||
import nl.requios.effortlessbuilding.create.foundation.item.ItemHelper;
|
||||
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||
|
||||
public class CompatHelper {
|
||||
@@ -64,4 +68,21 @@ public class CompatHelper {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public static boolean containsBlock(ItemStack stack, Block block) {
|
||||
if (stack == null || stack.isEmpty() || !isItemBlockProxy(stack)) {
|
||||
return block == null || block == Blocks.AIR;
|
||||
}
|
||||
|
||||
if (stack.getItem() instanceof BlockItem) {
|
||||
return ((BlockItem) stack.getItem()).getBlock() == block;
|
||||
}
|
||||
|
||||
if (stack.getItem() instanceof AbstractRandomizerBagItem randomizerBagItem) {
|
||||
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
|
||||
ItemStack firstMatch = ItemHelper.findFirstMatch(bagInventory, s -> s.getItem() instanceof BlockItem);
|
||||
return firstMatch != null && !firstMatch.isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraftforge.common.IPlantable;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.BlockSnapshot;
|
||||
import net.minecraftforge.event.level.BlockEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -144,7 +145,7 @@ public class BlockHelper {
|
||||
destroyBlockAs(world, pos, null, ItemStack.EMPTY, effectChance, droppedItemCallback);
|
||||
}
|
||||
|
||||
public static void destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool,
|
||||
public static boolean destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool,
|
||||
float effectChance, Consumer<ItemStack> droppedItemCallback) {
|
||||
FluidState fluidState = world.getFluidState(pos);
|
||||
BlockState state = world.getBlockState(pos);
|
||||
@@ -157,7 +158,7 @@ public class BlockHelper {
|
||||
BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
if (event.isCanceled())
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (event.getExpToDrop() > 0 && world instanceof ServerLevel)
|
||||
state.getBlock()
|
||||
@@ -178,19 +179,20 @@ public class BlockHelper {
|
||||
if (state.getBlock() instanceof IceBlock && usedTool.getEnchantmentLevel(Enchantments.SILK_TOUCH) == 0) {
|
||||
if (world.dimensionType()
|
||||
.ultraWarm())
|
||||
return;
|
||||
return false;
|
||||
|
||||
Material material = world.getBlockState(pos.below())
|
||||
.getMaterial();
|
||||
if (material.blocksMotion() || material.isLiquid())
|
||||
world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState());
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
|
||||
}
|
||||
|
||||
world.setBlockAndUpdate(pos, fluidState.createLegacyBlock());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isSolidWall(BlockGetter reader, BlockPos fromPos, Direction toDirection) {
|
||||
@@ -223,7 +225,7 @@ public class BlockHelper {
|
||||
.getBlock(), target.below());
|
||||
}
|
||||
|
||||
public static void placeSchematicBlock(Level world, BlockState state, BlockPos target, ItemStack stack,
|
||||
public static boolean placeSchematicBlock(Level world, Player player, BlockState state, BlockPos target, ItemStack stack,
|
||||
@Nullable CompoundTag data) {
|
||||
BlockEntity existingTile = world.getBlockEntity(target);
|
||||
|
||||
@@ -253,7 +255,7 @@ public class BlockHelper {
|
||||
0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
Block.dropResources(state, world, target);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.getBlock() instanceof BaseRailBlock) {
|
||||
@@ -287,6 +289,7 @@ public class BlockHelper {
|
||||
.setPlacedBy(world, target, state, null, stack);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static double getBounceMultiplier(Block block) {
|
||||
|
||||
@@ -23,7 +23,6 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||
import nl.requios.effortlessbuilding.systems.ServerBuildState;
|
||||
import nl.requios.effortlessbuilding.capability.ItemHandlerCapabilityProvider;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
@@ -133,7 +132,7 @@ public abstract class AbstractRandomizerBagItem extends Item {
|
||||
if (world.isClientSide) return InteractionResult.SUCCESS;
|
||||
|
||||
//Only place manually if in normal vanilla mode
|
||||
if (ServerBuildState.isUsingBuildMode(player) || ServerBuildState.isQuickReplacing(player)) {
|
||||
if (!ServerBuildState.isLikeVanilla(player)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,13 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -84,7 +83,7 @@ public class AddUndoMessage {
|
||||
Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx);
|
||||
//Add to undo stack clientside
|
||||
//Only the appropriate player that needs to add this to the undo stack gets this message
|
||||
UndoRedo.addUndo(player, new BlockSet(
|
||||
UndoRedo.addUndo(player, new UndoRedoBlockSet(
|
||||
new ArrayList<BlockPos>() {{
|
||||
add(message.getCoordinate());
|
||||
}},
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
@@ -14,25 +15,24 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public class ServerBreakBlocksPacket {
|
||||
|
||||
private List<BlockEntry> blocks;
|
||||
private BlockSet blocks;
|
||||
|
||||
public ServerBreakBlocksPacket() {}
|
||||
|
||||
public ServerBreakBlocksPacket(List<BlockEntry> blocks) {
|
||||
public ServerBreakBlocksPacket(BlockSet blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
public static void encode(ServerBreakBlocksPacket message, FriendlyByteBuf buf) {
|
||||
buf.writeCollection(message.blocks, BlockEntry::encode);
|
||||
BlockSet.encode(buf, message.blocks);
|
||||
}
|
||||
|
||||
public static ServerBreakBlocksPacket decode(FriendlyByteBuf buf) {
|
||||
ServerBreakBlocksPacket message = new ServerBreakBlocksPacket();
|
||||
message.blocks = buf.readList(BlockEntry::decode);
|
||||
message.blocks = BlockSet.decode(buf);
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
public static class Handler {
|
||||
public static void handle(ServerBreakBlocksPacket message, Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
@@ -14,25 +15,24 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public class ServerPlaceBlocksPacket {
|
||||
|
||||
private List<BlockEntry> blocks;
|
||||
private BlockSet blocks;
|
||||
|
||||
public ServerPlaceBlocksPacket() {}
|
||||
|
||||
public ServerPlaceBlocksPacket(List<BlockEntry> blocks) {
|
||||
public ServerPlaceBlocksPacket(BlockSet blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
public static void encode(ServerPlaceBlocksPacket message, FriendlyByteBuf buf) {
|
||||
buf.writeCollection(message.blocks, BlockEntry::encode);
|
||||
BlockSet.encode(buf, message.blocks);
|
||||
}
|
||||
|
||||
public static ServerPlaceBlocksPacket decode(FriendlyByteBuf buf) {
|
||||
ServerPlaceBlocksPacket message = new ServerPlaceBlocksPacket();
|
||||
message.blocks = buf.readList(BlockEntry::decode);
|
||||
message.blocks = BlockSet.decode(buf);
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
public static class Handler {
|
||||
public static void handle(ServerPlaceBlocksPacket message, Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
package nl.requios.effortlessbuilding.render;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -17,20 +10,17 @@ import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.*;
|
||||
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
import nl.requios.effortlessbuilding.create.AllSpecialTextures;
|
||||
import nl.requios.effortlessbuilding.create.CreateClient;
|
||||
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
|
||||
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||
import nl.requios.effortlessbuilding.systems.BuilderChain;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class BlockPreviews {
|
||||
@@ -69,16 +59,15 @@ public class BlockPreviews {
|
||||
!ClientConfig.visuals.alwaysShowBlockPreview.get()) return;
|
||||
|
||||
var blocks = EffortlessBuildingClient.BUILDER_CHAIN.getBlocks();
|
||||
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState();
|
||||
|
||||
if (blocks.size() == 0) return;
|
||||
|
||||
var coordinates = EffortlessBuildingClient.BUILDER_CHAIN.getCoordinates();
|
||||
var coordinates = blocks.getCoordinates();
|
||||
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState();
|
||||
|
||||
//Dont fade out the outline if we are still determining where to place
|
||||
//Every outline with same ID will not fade out (because it gets replaced)
|
||||
Object outlineID = "single";
|
||||
if (blocks.size() > 1) outlineID = blocks.get(0).blockPos;
|
||||
if (blocks.size() > 1) outlineID = blocks.firstPos;
|
||||
|
||||
if (state != BuilderChain.State.BREAKING) {
|
||||
//Use fancy shader if config allows, otherwise outlines
|
||||
@@ -138,57 +127,44 @@ public class BlockPreviews {
|
||||
}
|
||||
|
||||
public void drawOutlinesIfNoBlockInHand(Player player) {
|
||||
ItemStack mainhand = player.getMainHandItem();
|
||||
HitResult lookingAt = ClientEvents.getLookingAt(player);
|
||||
if (EffortlessBuildingClient.BUILD_MODES.getBuildMode() == BuildModeEnum.DISABLED)
|
||||
lookingAt = Minecraft.getInstance().hitResult;
|
||||
var builderChain = EffortlessBuildingClient.BUILDER_CHAIN;
|
||||
if (builderChain.isBlockInHand()) return;
|
||||
if (builderChain.getState() != BuilderChain.State.IDLE) return;
|
||||
|
||||
boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand));
|
||||
if (!noBlockInHand) return;
|
||||
|
||||
//Draw outlines if no block in hand
|
||||
//Find proper raytrace: either normal range or increased range depending on canBreakFar
|
||||
HitResult objectMouseOver = Minecraft.getInstance().hitResult;
|
||||
HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver;
|
||||
|
||||
if (player.isCreative() && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) {
|
||||
BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace;
|
||||
List<BlockPos> breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos());
|
||||
var blocks = new ArrayList<>(builderChain.getBlocks().values());
|
||||
if (blocks.size() == 0) return;
|
||||
|
||||
//Only render first outline if further than normal reach
|
||||
if (objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK)
|
||||
breakCoordinates.remove(0);
|
||||
var lookingAtNear = Minecraft.getInstance().hitResult;
|
||||
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK)
|
||||
blocks.remove(0);
|
||||
|
||||
breakCoordinates.removeIf(pos -> {
|
||||
BlockState blockState = player.level.getBlockState(pos);
|
||||
if (blockState.isAir() || blockState.getMaterial().isLiquid()) return true;
|
||||
return !SurvivalHelper.canBreak(player.level, player, pos);
|
||||
});
|
||||
//Only render outlines if there is a block, like vanilla
|
||||
blocks.removeIf(blockEntry -> blockEntry.existingBlockState == null ||
|
||||
blockEntry.existingBlockState.isAir() ||
|
||||
blockEntry.existingBlockState.getMaterial().isLiquid());
|
||||
|
||||
if (!breakCoordinates.isEmpty()) {
|
||||
CreateClient.OUTLINER.showCluster("break", breakCoordinates)
|
||||
if (!blocks.isEmpty()) {
|
||||
var coordinates = blocks.stream().map(blockEntry -> blockEntry.blockPos).collect(Collectors.toList());
|
||||
CreateClient.OUTLINER.showCluster("break", coordinates)
|
||||
.disableNormals()
|
||||
.lineWidth(1 / 64f)
|
||||
.colored(0x222222);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderBlockPreviews(List<BlockEntry> blocks, boolean breaking, float dissolve) {
|
||||
|
||||
var firstPos = blocks.get(0).blockPos;
|
||||
var lastPos = blocks.get(blocks.size() - 1).blockPos;
|
||||
protected void renderBlockPreviews(BlockSet blocks, boolean breaking, float dissolve) {
|
||||
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
renderBlockPreview(blockEntry, breaking, dissolve, firstPos, lastPos);
|
||||
renderBlockPreview(blockEntry, breaking, dissolve, blocks.firstPos, blocks.lastPos);
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderBlockPreview(BlockEntry blockEntry, boolean breaking, float dissolve, BlockPos firstPos, BlockPos lastPos) {
|
||||
if (blockEntry.blockState == null) return;
|
||||
if (blockEntry.newBlockState == null) return;
|
||||
|
||||
var blockPos = blockEntry.blockPos;
|
||||
var blockState = blockEntry.blockState;
|
||||
var blockState = blockEntry.newBlockState;
|
||||
|
||||
float scale = 0.5f;
|
||||
float alpha = 0.7f;
|
||||
@@ -247,22 +223,22 @@ public class BlockPreviews {
|
||||
return (ay * t3) + (by * t2) + (cy * t) + 0;
|
||||
}
|
||||
|
||||
public void onBlocksPlaced(List<BlockEntry> blocks) {
|
||||
public void onBlocksPlaced(BlockSet blocks) {
|
||||
if (!ClientConfig.visuals.showBlockPreviews.get()) return;
|
||||
if (blocks.size() <= 1 || blocks.size() > ClientConfig.visuals.maxBlockPreviews.get()) return;
|
||||
|
||||
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new ArrayList<>(blocks)));
|
||||
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new BlockSet(blocks)));
|
||||
|
||||
CreateClient.OUTLINER.keep(blocks.get(0).blockPos, CommonConfig.visuals.appearAnimationLength.get());
|
||||
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.appearAnimationLength.get());
|
||||
}
|
||||
|
||||
public void onBlocksBroken(List<BlockEntry> blocks) {
|
||||
public void onBlocksBroken(BlockSet blocks) {
|
||||
if (!ClientConfig.visuals.showBlockPreviews.get()) return;
|
||||
if (blocks.size() <= 1 || blocks.size() > ClientConfig.visuals.maxBlockPreviews.get()) return;
|
||||
|
||||
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new ArrayList<>(blocks)));
|
||||
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new BlockSet(blocks)));
|
||||
|
||||
CreateClient.OUTLINER.keep(blocks.get(0).blockPos, CommonConfig.visuals.breakAnimationLength.get());
|
||||
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.breakAnimationLength.get());
|
||||
}
|
||||
|
||||
private void sortOnDistanceToPlayer(List<BlockPos> coordinates, Player player) {
|
||||
@@ -279,9 +255,9 @@ public class BlockPreviews {
|
||||
public static class PlacedBlocksEntry {
|
||||
float time;
|
||||
boolean breaking;
|
||||
List<BlockEntry> blocks;
|
||||
BlockSet blocks;
|
||||
|
||||
public PlacedBlocksEntry(float time, boolean breaking, List<BlockEntry> blocks) {
|
||||
public PlacedBlocksEntry(float time, boolean breaking, BlockSet blocks) {
|
||||
this.time = time;
|
||||
this.breaking = breaking;
|
||||
this.blocks = blocks;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package nl.requios.effortlessbuilding.systems;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket;
|
||||
import nl.requios.effortlessbuilding.network.PacketHandler;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class BuildSettings {
|
||||
public enum ReplaceMode {
|
||||
ONLY_AIR,
|
||||
SOLID_AND_AIR,
|
||||
SOLID_ONLY,
|
||||
FILTERED_BY_OFFHAND
|
||||
}
|
||||
|
||||
private boolean quickReplace = false;
|
||||
public ReplaceMode replaceMode = ReplaceMode.ONLY_AIR;
|
||||
private boolean replaceTileEntities;
|
||||
|
||||
public boolean isQuickReplacing() {
|
||||
return quickReplace;
|
||||
}
|
||||
|
||||
public void toggleQuickReplace() {
|
||||
setQuickReplace(!quickReplace);
|
||||
}
|
||||
|
||||
public void setQuickReplace(boolean quickReplace) {
|
||||
this.quickReplace = quickReplace;
|
||||
|
||||
EffortlessBuilding.log(Minecraft.getInstance().player, "Set " + ChatFormatting.GOLD + "Quick Replace " +
|
||||
ChatFormatting.RESET + (this.quickReplace ? "on" : "off"));
|
||||
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(this.quickReplace));
|
||||
}
|
||||
|
||||
public void setReplaceMode(ReplaceMode replaceMode) {
|
||||
this.replaceMode = replaceMode;
|
||||
}
|
||||
|
||||
public void setReplaceTileEntities(boolean replaceTileEntities) {
|
||||
this.replaceTileEntities = replaceTileEntities;
|
||||
}
|
||||
|
||||
public boolean shouldReplaceAir() {
|
||||
return replaceMode == ReplaceMode.ONLY_AIR || replaceMode == ReplaceMode.SOLID_AND_AIR;
|
||||
}
|
||||
|
||||
public boolean shouldReplaceSolid() {
|
||||
return replaceMode == ReplaceMode.SOLID_ONLY || replaceMode == ReplaceMode.SOLID_AND_AIR;
|
||||
}
|
||||
|
||||
public boolean shouldReplaceFiltered() {
|
||||
return replaceMode == ReplaceMode.FILTERED_BY_OFFHAND;
|
||||
}
|
||||
|
||||
public boolean shouldReplaceTileEntities() {
|
||||
return replaceTileEntities;
|
||||
}
|
||||
|
||||
public boolean shouldOffsetStartPosition() {
|
||||
return shouldReplaceSolid() || shouldReplaceFiltered();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package nl.requios.effortlessbuilding.systems;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
@@ -12,10 +10,8 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -29,11 +25,10 @@ import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||
import nl.requios.effortlessbuilding.network.PacketHandler;
|
||||
import nl.requios.effortlessbuilding.network.ServerBreakBlocksPacket;
|
||||
import nl.requios.effortlessbuilding.network.ServerPlaceBlocksPacket;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.ReachHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
// Receives block placed events, then finds additional blocks we want to place through various systems,
|
||||
@@ -42,10 +37,11 @@ import java.util.List;
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class BuilderChain {
|
||||
|
||||
private final List<BlockEntry> blocks = new ArrayList<>();
|
||||
private final List<BlockPos> coordinates = new ArrayList<>();
|
||||
private int soundTime = 0;
|
||||
private final BlockSet blocks = new BlockSet();
|
||||
private boolean blockInHand;
|
||||
private boolean lookingAtInteractiveObject;
|
||||
private Item previousHeldItem;
|
||||
private int soundTime = 0;
|
||||
|
||||
public enum State {
|
||||
IDLE,
|
||||
@@ -56,6 +52,7 @@ public class BuilderChain {
|
||||
private State state = State.IDLE;
|
||||
|
||||
public void onRightClick() {
|
||||
if (lookingAtInteractiveObject) return;
|
||||
var player = Minecraft.getInstance().player;
|
||||
|
||||
if (state == State.BREAKING) {
|
||||
@@ -63,9 +60,6 @@ public class BuilderChain {
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if we have a BlockItem in hand
|
||||
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack);
|
||||
if (!blockInHand) {
|
||||
if (state == State.PLACING) cancel();
|
||||
return;
|
||||
@@ -83,13 +77,15 @@ public class BuilderChain {
|
||||
|
||||
if (!blocks.isEmpty()) {
|
||||
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksPlaced(blocks);
|
||||
playSoundIfFurtherThanNormal(player, blocks.get(0), false);
|
||||
BlockUtilities.playSoundIfFurtherThanNormal(player, blocks.getLastBlockEntry(), false);
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksPacket(blocks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onLeftClick() {
|
||||
if (lookingAtInteractiveObject) return;
|
||||
var player = Minecraft.getInstance().player;
|
||||
|
||||
if (state == State.PLACING) {
|
||||
@@ -114,53 +110,55 @@ public class BuilderChain {
|
||||
|
||||
if (!blocks.isEmpty()) {
|
||||
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksBroken(blocks);
|
||||
playSoundIfFurtherThanNormal(player, blocks.get(0), true);
|
||||
BlockUtilities.playSoundIfFurtherThanNormal(player, blocks.getLastBlockEntry(), true);
|
||||
player.swing(InteractionHand.MAIN_HAND);
|
||||
PacketHandler.INSTANCE.sendToServer(new ServerBreakBlocksPacket(blocks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onTick() {
|
||||
var previousCoordinates = new ArrayList<>(coordinates);
|
||||
var previousCoordinates = new HashSet<>(blocks.getCoordinates());
|
||||
blocks.clear();
|
||||
|
||||
var mc = Minecraft.getInstance();
|
||||
var player = mc.player;
|
||||
var level = mc.level;
|
||||
var world = mc.level;
|
||||
|
||||
//Check if we have a BlockItem in hand
|
||||
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack);
|
||||
blockInHand = CompatHelper.isItemBlockProxy(itemStack);
|
||||
|
||||
//Cancel placing as soon as we aren't holding a block anymore
|
||||
// if (!blockInHand && state == State.PLACING) {
|
||||
// state = State.IDLE;
|
||||
// }
|
||||
lookingAtInteractiveObject = BlockUtilities.determineIfLookingAtInteractiveObject(mc, world);
|
||||
if (lookingAtInteractiveObject) return;
|
||||
|
||||
var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||
var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
|
||||
var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||
|
||||
|
||||
BlockHitResult lookingAt = ClientEvents.getLookingAt(player);
|
||||
if (state == State.IDLE) {
|
||||
//Find start position
|
||||
BlockHitResult lookingAt = ClientEvents.getLookingAtFar(player);
|
||||
BlockEntry startEntry = findStartPosition(player, lookingAt);
|
||||
if (startEntry != null) {
|
||||
blocks.add(startEntry);
|
||||
blocks.firstPos = startEntry.blockPos;
|
||||
blocks.lastPos = startEntry.blockPos;
|
||||
}
|
||||
}
|
||||
|
||||
EffortlessBuildingClient.BUILD_MODES.findCoordinates(blocks, player, buildMode);
|
||||
EffortlessBuildingClient.BUILD_MODIFIERS.findCoordinates(blocks, player, modifierSettings);
|
||||
|
||||
removeDuplicateCoordinates();
|
||||
BuilderFilter.filterOnCoordinates(blocks, player);
|
||||
|
||||
coordinates.clear();
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
coordinates.add(blockEntry.blockPos);
|
||||
}
|
||||
findExistingBlockStates(world);
|
||||
BuilderFilter.filterOnExistingBlockStates(blocks, player);
|
||||
|
||||
findBlockStates(player, itemStack);
|
||||
findNewBlockStates(player, itemStack);
|
||||
BuilderFilter.filterOnNewBlockStates(blocks, player);
|
||||
|
||||
//Check if any changes are made
|
||||
if (previousHeldItem != itemStack.getItem() || !previousCoordinates.equals(coordinates)) {
|
||||
if (previousHeldItem != itemStack.getItem() || !previousCoordinates.equals(blocks.getCoordinates())) {
|
||||
onBlocksChanged(player);
|
||||
}
|
||||
|
||||
@@ -176,9 +174,9 @@ public class BuilderChain {
|
||||
if (blocks.size() > 1 && soundTime < ClientEvents.ticksInGame) {
|
||||
soundTime = ClientEvents.ticksInGame;
|
||||
|
||||
var firstBlockState = blocks.get(0).blockState;
|
||||
if (firstBlockState != null) {
|
||||
SoundType soundType = firstBlockState.getBlock().getSoundType(firstBlockState, player.level, blocks.get(0).blockPos, player);
|
||||
if (blocks.getLastBlockEntry() != null && blocks.getLastBlockEntry().newBlockState != null) {
|
||||
var lastBlockState = blocks.getLastBlockEntry().newBlockState;
|
||||
SoundType soundType = lastBlockState.getBlock().getSoundType(lastBlockState, player.level, blocks.lastPos, player);
|
||||
SoundEvent soundEvent = state == BuilderChain.State.BREAKING ? soundType.getBreakSound() : soundType.getPlaceSound();
|
||||
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.3f, 0.8f);
|
||||
}
|
||||
@@ -192,34 +190,45 @@ public class BuilderChain {
|
||||
Minecraft.getInstance().player.playSound(SoundEvents.UI_TOAST_OUT, 4, 1);
|
||||
}
|
||||
|
||||
private BlockEntry findStartPosition(Player player, BlockHitResult lookingAt) {
|
||||
if (lookingAt == null || lookingAt.getType() == HitResult.Type.MISS) return null;
|
||||
private BlockEntry findStartPosition(Player player, BlockHitResult lookingAtFar) {
|
||||
if (lookingAtFar == null || lookingAtFar.getType() == HitResult.Type.MISS) return null;
|
||||
|
||||
var startPos = lookingAt.getBlockPos();
|
||||
var startPos = lookingAtFar.getBlockPos();
|
||||
|
||||
//Check if out of reach
|
||||
int maxReach = ReachHelper.getMaxReach(player);
|
||||
if (player.blockPosition().distSqr(startPos) > maxReach * maxReach) return null;
|
||||
|
||||
//TODO we are always at IDLE state here, find another way to check if we are breaking
|
||||
if (state != State.BREAKING) {
|
||||
//Offset in direction of sidehit if not quickreplace and not replaceable
|
||||
boolean isQuickReplacing = EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing();
|
||||
boolean shouldOffsetStartPosition = EffortlessBuildingClient.BUILD_SETTINGS.shouldOffsetStartPosition();
|
||||
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
|
||||
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos);
|
||||
if (!isQuickReplacing && !replaceable && !becomesDoubleSlab) {
|
||||
startPos = startPos.relative(lookingAt.getDirection());
|
||||
if (!shouldOffsetStartPosition && !replaceable && !becomesDoubleSlab) {
|
||||
startPos = startPos.relative(lookingAtFar.getDirection());
|
||||
}
|
||||
|
||||
//Get under tall grass and other replaceable blocks
|
||||
if (isQuickReplacing && replaceable) {
|
||||
if (shouldOffsetStartPosition && replaceable) {
|
||||
startPos = startPos.below();
|
||||
}
|
||||
} else {
|
||||
//Do not break far if we are not allowed to
|
||||
if (!ReachHelper.canBreakFar(player)) {
|
||||
boolean startPosIsNear = false;
|
||||
var lookingAtNear = Minecraft.getInstance().hitResult;
|
||||
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK) {
|
||||
startPosIsNear = ((BlockHitResult) lookingAtNear).getBlockPos().equals(startPos);
|
||||
}
|
||||
if (!startPosIsNear) return null;
|
||||
}
|
||||
}
|
||||
|
||||
var blockEntry = new BlockEntry(startPos);
|
||||
|
||||
//Place upside-down stairs if we aim high at block
|
||||
var hitVec = lookingAt.getLocation();
|
||||
var hitVec = lookingAtFar.getLocation();
|
||||
//Format hitvec to 0.x
|
||||
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z)));
|
||||
if (hitVec.y > 0.5) {
|
||||
@@ -229,32 +238,19 @@ public class BuilderChain {
|
||||
return blockEntry;
|
||||
}
|
||||
|
||||
private void removeDuplicateCoordinates() {
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
BlockEntry blockEntry = blocks.get(i);
|
||||
for (int j = i + 1; j < blocks.size(); j++) {
|
||||
BlockEntry blockEntry2 = blocks.get(j);
|
||||
if (blockEntry.blockPos.equals(blockEntry2.blockPos)) {
|
||||
blocks.remove(j);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findBlockStates(Player player, ItemStack itemStack) {
|
||||
|
||||
if (state == State.BREAKING) {
|
||||
private void findExistingBlockStates(Level world) {
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
blockEntry.blockState = Minecraft.getInstance().level.getBlockState(blockEntry.blockPos);
|
||||
blockEntry.existingBlockState = world.getBlockState(blockEntry.blockPos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void findNewBlockStates(Player player, ItemStack itemStack) {
|
||||
if (state == State.BREAKING) return;
|
||||
|
||||
if (itemStack.getItem() instanceof BlockItem) {
|
||||
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
blockEntry.blockState = getBlockState(player, InteractionHand.MAIN_HAND, itemStack, blockEntry);
|
||||
blockEntry.newBlockState = BlockUtilities.getBlockState(player, InteractionHand.MAIN_HAND, itemStack, blockEntry);
|
||||
}
|
||||
|
||||
} else if (CompatHelper.isItemBlockProxy(itemStack, false)) {
|
||||
@@ -263,44 +259,25 @@ public class BuilderChain {
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
ItemStack itemBlockStack = CompatHelper.getItemBlockFromStack(itemStack);
|
||||
if (itemBlockStack == null || itemBlockStack.isEmpty()) continue;
|
||||
blockEntry.blockState = getBlockState(player, InteractionHand.MAIN_HAND, itemBlockStack, blockEntry);
|
||||
blockEntry.newBlockState = BlockUtilities.getBlockState(player, InteractionHand.MAIN_HAND, itemBlockStack, blockEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) {
|
||||
Block block = Block.byItem(blockItemStack.getItem());
|
||||
//TODO convert lookingAt hitvec to relative hitvec
|
||||
var blockHitResult = new BlockHitResult(Vec3.ZERO, Direction.UP, blockEntry.blockPos, false);
|
||||
return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult));
|
||||
}
|
||||
|
||||
private void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
|
||||
|
||||
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
|
||||
return;
|
||||
|
||||
if (blockEntry == null || blockEntry.blockState == null)
|
||||
return;
|
||||
|
||||
SoundType soundType = blockEntry.blockState.getBlock().getSoundType(blockEntry.blockState, player.level, blockEntry.blockPos, player);
|
||||
SoundEvent soundEvent = breaking ? soundType.getBreakSound() : soundType.getPlaceSound();
|
||||
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.6f, soundType.getPitch());
|
||||
}
|
||||
|
||||
private void swingHand(Player player, InteractionHand hand) {
|
||||
player.swing(hand);
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public List<BlockEntry> getBlocks() {
|
||||
public BlockSet getBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public List<BlockPos> getCoordinates() {
|
||||
return coordinates;
|
||||
public boolean isBlockInHand() {
|
||||
return blockInHand;
|
||||
}
|
||||
|
||||
public boolean isLookingAtInteractiveObject() {
|
||||
return lookingAtInteractiveObject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package nl.requios.effortlessbuilding.systems;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.PlaceChecker;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class BuilderFilter {
|
||||
public static void filterOnCoordinates(BlockSet blocks, Player player) {
|
||||
|
||||
}
|
||||
|
||||
public static void filterOnExistingBlockStates(BlockSet blocks, Player player) {
|
||||
var buildSettings = EffortlessBuildingClient.BUILD_SETTINGS;
|
||||
boolean placing = EffortlessBuildingClient.BUILDER_CHAIN.getState() == BuilderChain.State.PLACING;
|
||||
|
||||
var iter = blocks.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
var blockEntry = iter.next().getValue();
|
||||
var blockState = blockEntry.existingBlockState;
|
||||
boolean remove = false;
|
||||
|
||||
if (!buildSettings.shouldReplaceTileEntities() && blockState.hasBlockEntity()) remove = true;
|
||||
|
||||
if (placing) {
|
||||
if (!buildSettings.shouldReplaceAir() && blockState.isAir()) remove = true;
|
||||
boolean isSolid = blockState.isRedstoneConductor(player.level, blockEntry.blockPos);
|
||||
if (!buildSettings.shouldReplaceSolid() && isSolid) remove = true;
|
||||
}
|
||||
|
||||
if (buildSettings.shouldReplaceFiltered()) {
|
||||
var offhandItem = player.getOffhandItem();
|
||||
if (!CompatHelper.containsBlock(offhandItem, blockState.getBlock())) remove = true;
|
||||
}
|
||||
|
||||
if (remove) iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public static void filterOnNewBlockStates(BlockSet blocks, Player player) {
|
||||
|
||||
var iter = blocks.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
var blockEntry = iter.next().getValue();
|
||||
boolean remove = false;
|
||||
|
||||
if (!PlaceChecker.shouldPlaceBlock(player.level, blockEntry)) remove = true;
|
||||
|
||||
if (remove) iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
@@ -97,7 +97,7 @@ public class DelayedBlockPlacer {
|
||||
//add to undo stack
|
||||
BlockPos firstPos = coordinates.get(0);
|
||||
BlockPos secondPos = coordinates.get(coordinates.size() - 1);
|
||||
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
|
||||
UndoRedo.addUndo(player, new UndoRedoBlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package nl.requios.effortlessbuilding.systems;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket;
|
||||
import nl.requios.effortlessbuilding.network.PacketHandler;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class QuickReplace {
|
||||
private boolean isQuickReplacing = false;
|
||||
|
||||
public boolean isQuickReplacing() {
|
||||
return isQuickReplacing;
|
||||
}
|
||||
|
||||
public void toggleQuickReplacing() {
|
||||
setIsQuickReplacing(!isQuickReplacing);
|
||||
}
|
||||
|
||||
public void setIsQuickReplacing(boolean isQuickReplacing) {
|
||||
this.isQuickReplacing = isQuickReplacing;
|
||||
|
||||
EffortlessBuilding.log(Minecraft.getInstance().player, "Set " + ChatFormatting.GOLD + "Quick Replace " +
|
||||
ChatFormatting.RESET + (this.isQuickReplacing ? "on" : "off"));
|
||||
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(this.isQuickReplacing));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,27 @@
|
||||
package nl.requios.effortlessbuilding.systems;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BucketItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
|
||||
import net.minecraftforge.common.util.BlockSnapshot;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.items.ItemHandlerHelper;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -15,7 +29,7 @@ import java.util.List;
|
||||
public class ServerBlockPlacer {
|
||||
private boolean isPlacingOrBreakingBlocks = false;
|
||||
|
||||
public void placeBlocks(Player player, List<BlockEntry> blocks) {
|
||||
public void placeBlocks(Player player, BlockSet blocks) {
|
||||
EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
|
||||
|
||||
for (BlockEntry block : blocks) {
|
||||
@@ -28,11 +42,11 @@ public class ServerBlockPlacer {
|
||||
if (!world.isLoaded(block.blockPos)) return;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
BlockHelper.placeSchematicBlock(world, block.blockState, block.blockPos, block.itemStack, null);
|
||||
boolean placedBlock = onPlaceItemIntoWorld(player, block) == InteractionResult.SUCCESS;
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
}
|
||||
|
||||
public void breakBlocks(Player player, List<BlockEntry> blocks) {
|
||||
public void breakBlocks(Player player, BlockSet blocks) {
|
||||
EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
|
||||
|
||||
for (BlockEntry block : blocks) {
|
||||
@@ -45,7 +59,7 @@ public class ServerBlockPlacer {
|
||||
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
|
||||
boolean brokeBlock = BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
|
||||
if (!player.isCreative()) {
|
||||
ItemHandlerHelper.giveItemToPlayer(player, stack);
|
||||
}
|
||||
@@ -56,4 +70,95 @@ public class ServerBlockPlacer {
|
||||
public boolean isPlacingOrBreakingBlocks() {
|
||||
return isPlacingOrBreakingBlocks;
|
||||
}
|
||||
|
||||
//ForgeHooks::onPlaceItemIntoWorld
|
||||
private InteractionResult onPlaceItemIntoWorld(Player player, BlockEntry block) {
|
||||
|
||||
ItemStack itemstack = block.itemStack;
|
||||
Level level = player.level;
|
||||
|
||||
if (player != null && !player.getAbilities().mayBuild && !itemstack.hasAdventureModePlaceTagForBlock(level.registryAccess().registryOrThrow(Registry.BLOCK_REGISTRY), new BlockInWorld(level, block.blockPos, false)))
|
||||
return InteractionResult.PASS;
|
||||
|
||||
// handle all placement events here
|
||||
Item item = itemstack.getItem();
|
||||
int size = itemstack.getCount();
|
||||
CompoundTag nbt = null;
|
||||
if (itemstack.getTag() != null)
|
||||
nbt = itemstack.getTag().copy();
|
||||
|
||||
if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket
|
||||
level.captureBlockSnapshots = true;
|
||||
|
||||
ItemStack copy = itemstack.copy();
|
||||
BlockHelper.placeSchematicBlock(level, player, block.newBlockState, block.blockPos, block.itemStack, null);
|
||||
InteractionResult ret = InteractionResult.SUCCESS;
|
||||
if (itemstack.isEmpty())
|
||||
ForgeEventFactory.onPlayerDestroyItem(player, copy, InteractionHand.MAIN_HAND);
|
||||
|
||||
level.captureBlockSnapshots = false;
|
||||
|
||||
if (ret.consumesAction())
|
||||
{
|
||||
// save new item data
|
||||
int newSize = itemstack.getCount();
|
||||
CompoundTag newNBT = null;
|
||||
if (itemstack.getTag() != null)
|
||||
{
|
||||
newNBT = itemstack.getTag().copy();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BlockSnapshot> blockSnapshots = (List<BlockSnapshot>)level.capturedBlockSnapshots.clone();
|
||||
level.capturedBlockSnapshots.clear();
|
||||
|
||||
// make sure to set pre-placement item data for event
|
||||
itemstack.setCount(size);
|
||||
itemstack.setTag(nbt);
|
||||
|
||||
Direction side = Direction.UP;
|
||||
|
||||
boolean eventResult = false;
|
||||
if (blockSnapshots.size() > 1)
|
||||
{
|
||||
eventResult = ForgeEventFactory.onMultiBlockPlace(player, blockSnapshots, side);
|
||||
}
|
||||
else if (blockSnapshots.size() == 1)
|
||||
{
|
||||
eventResult = ForgeEventFactory.onBlockPlace(player, blockSnapshots.get(0), side);
|
||||
}
|
||||
|
||||
if (eventResult)
|
||||
{
|
||||
ret = InteractionResult.FAIL; // cancel placement
|
||||
// revert back all captured blocks
|
||||
for (BlockSnapshot blocksnapshot : Lists.reverse(blockSnapshots))
|
||||
{
|
||||
level.restoringBlockSnapshots = true;
|
||||
blocksnapshot.restore(true, false);
|
||||
level.restoringBlockSnapshots = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change the stack to its new content
|
||||
itemstack.setCount(newSize);
|
||||
itemstack.setTag(newNBT);
|
||||
|
||||
for (BlockSnapshot snap : blockSnapshots)
|
||||
{
|
||||
int updateFlag = snap.getFlag();
|
||||
BlockState oldBlock = snap.getReplacedBlock();
|
||||
BlockState newBlock = level.getBlockState(snap.getPos());
|
||||
newBlock.onPlace(level, snap.getPos(), oldBlock, false);
|
||||
|
||||
level.markAndNotifyBlock(snap.getPos(), level.getChunkAt(snap.getPos()), oldBlock, newBlock, updateFlag, 512);
|
||||
}
|
||||
if (player != null)
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
}
|
||||
}
|
||||
level.capturedBlockSnapshots.clear();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,8 @@ public class ServerBuildState {
|
||||
player.getPersistentData().remove(IS_QUICK_REPLACING_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLikeVanilla(Player player) {
|
||||
return !isUsingBuildMode(player) && !isQuickReplacing(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Objects;
|
||||
|
||||
public class BlockEntry {
|
||||
public final BlockPos blockPos;
|
||||
@@ -16,7 +15,9 @@ public class BlockEntry {
|
||||
public boolean mirrorY;
|
||||
public boolean mirrorZ;
|
||||
public Rotation rotation;
|
||||
public BlockState blockState;
|
||||
//BlockState that is currently in the world
|
||||
public BlockState existingBlockState;
|
||||
public BlockState newBlockState;
|
||||
public ItemStack itemStack = ItemStack.EMPTY;
|
||||
|
||||
public BlockEntry(BlockPos blockPos) {
|
||||
@@ -24,7 +25,7 @@ public class BlockEntry {
|
||||
}
|
||||
|
||||
public boolean meansBreakBlock() {
|
||||
return blockState == null || blockState.isAir();
|
||||
return newBlockState == null || newBlockState.isAir();
|
||||
}
|
||||
|
||||
public BitSet getMirrorBitSet() {
|
||||
@@ -43,13 +44,13 @@ public class BlockEntry {
|
||||
|
||||
public static void encode(FriendlyByteBuf buf, BlockEntry block) {
|
||||
buf.writeBlockPos(block.blockPos);
|
||||
buf.writeNullable(block.blockState, (buffer, blockState) -> buffer.writeNbt(NbtUtils.writeBlockState(blockState)));
|
||||
buf.writeNullable(block.newBlockState, (buffer, blockState) -> buffer.writeNbt(NbtUtils.writeBlockState(blockState)));
|
||||
buf.writeItem(block.itemStack);
|
||||
}
|
||||
|
||||
public static BlockEntry decode(FriendlyByteBuf buf) {
|
||||
BlockEntry block = new BlockEntry(buf.readBlockPos());
|
||||
block.blockState = buf.readNullable(buffer -> {
|
||||
block.newBlockState = buf.readNullable(buffer -> {
|
||||
var nbt = buf.readNbt();
|
||||
if (nbt == null) return null;
|
||||
return NbtUtils.readBlockState(nbt);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package nl.requios.effortlessbuilding.utilities;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<BlockEntry> {
|
||||
public static boolean logging = true;
|
||||
|
||||
public BlockPos firstPos;
|
||||
public BlockPos lastPos;
|
||||
|
||||
public BlockSet() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BlockSet(BlockSet blockSet) {
|
||||
super(blockSet);
|
||||
this.firstPos = blockSet.firstPos;
|
||||
this.lastPos = blockSet.lastPos;
|
||||
}
|
||||
|
||||
public BlockSet(List<BlockEntry> blockEntries) {
|
||||
super();
|
||||
for (BlockEntry blockEntry : blockEntries) {
|
||||
add(blockEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(BlockEntry blockEntry) {
|
||||
if (!containsKey(blockEntry.blockPos)) {
|
||||
|
||||
//Limit number of blocks you can place
|
||||
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(Minecraft.getInstance().player);
|
||||
if (size() >= limit) {
|
||||
if (logging) EffortlessBuilding.log("BlockSet limit reached, not adding block at " + blockEntry.blockPos);
|
||||
return;
|
||||
}
|
||||
|
||||
put(blockEntry.blockPos, blockEntry);
|
||||
|
||||
} else {
|
||||
|
||||
if (logging) EffortlessBuilding.log("BlockSet already contains block at " + blockEntry.blockPos);
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<BlockPos> getCoordinates() {
|
||||
return new HashSet<>(keySet());
|
||||
}
|
||||
|
||||
public BlockEntry getFirstBlockEntry() {
|
||||
return get(firstPos);
|
||||
}
|
||||
|
||||
public BlockEntry getLastBlockEntry() {
|
||||
return get(lastPos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<BlockEntry> iterator() {
|
||||
return this.values().iterator();
|
||||
}
|
||||
|
||||
public static void encode(FriendlyByteBuf buf, BlockSet block) {
|
||||
buf.writeCollection(block.values(), BlockEntry::encode);
|
||||
}
|
||||
|
||||
public static BlockSet decode(FriendlyByteBuf buf) {
|
||||
return new BlockSet(buf.readList(BlockEntry::decode));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package nl.requios.effortlessbuilding.utilities;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class BlockUtilities {
|
||||
|
||||
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) {
|
||||
Block block = Block.byItem(blockItemStack.getItem());
|
||||
//TODO convert lookingAt hitvec to relative hitvec
|
||||
var blockHitResult = new BlockHitResult(Vec3.ZERO, Direction.UP, blockEntry.blockPos, false);
|
||||
return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult));
|
||||
}
|
||||
|
||||
public static boolean determineIfLookingAtInteractiveObject(Minecraft mc, ClientLevel level) {
|
||||
//Check if we are looking at an interactive object
|
||||
var result = false;
|
||||
if (mc.hitResult != null) {
|
||||
if (mc.hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
var blockHitResult = (BlockHitResult) mc.hitResult;
|
||||
var blockState = level.getBlockState(blockHitResult.getBlockPos());
|
||||
if (blockState.hasBlockEntity()) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
if (mc.hitResult.getType() == HitResult.Type.ENTITY) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
|
||||
|
||||
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
|
||||
return;
|
||||
|
||||
if (blockEntry == null || blockEntry.newBlockState == null)
|
||||
return;
|
||||
|
||||
SoundType soundType = blockEntry.newBlockState.getBlock().getSoundType(blockEntry.newBlockState, player.level, blockEntry.blockPos, player);
|
||||
SoundEvent soundEvent = breaking ? soundType.getBreakSound() : soundType.getPlaceSound();
|
||||
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.6f, soundType.getPitch());
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class InventoryHelper {
|
||||
|
||||
@Deprecated //Use BlockHelper.findAndRemoveInInventory instead
|
||||
public static ItemStack findItemStackInInventory(Player player, Block block) {
|
||||
for (ItemStack invStack : player.getInventory().items) {
|
||||
if (!invStack.isEmpty() && invStack.getItem() instanceof BlockItem &&
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package nl.requios.effortlessbuilding.utilities;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.piston.PistonHeadBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BedPart;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class PlaceChecker {
|
||||
|
||||
//SchematicPrinter::shouldPlaceBlock
|
||||
public static boolean shouldPlaceBlock(Level world, BlockEntry blockEntry) {
|
||||
if (world == null)
|
||||
return false;
|
||||
|
||||
var pos = blockEntry.blockPos;
|
||||
var state = blockEntry.newBlockState;
|
||||
BlockEntity tileEntity = null;
|
||||
|
||||
BlockState toReplace = world.getBlockState(pos);
|
||||
BlockEntity toReplaceTE = world.getBlockEntity(pos);
|
||||
BlockState toReplaceOther = null;
|
||||
|
||||
if (state.hasProperty(BlockStateProperties.BED_PART) && state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)
|
||||
&& state.getValue(BlockStateProperties.BED_PART) == BedPart.FOOT)
|
||||
toReplaceOther = world.getBlockState(pos.relative(state.getValue(BlockStateProperties.HORIZONTAL_FACING)));
|
||||
if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
|
||||
&& state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER)
|
||||
toReplaceOther = world.getBlockState(pos.above());
|
||||
|
||||
if (!world.isLoaded(pos))
|
||||
return false;
|
||||
if (!world.getWorldBorder().isWithinBounds(pos))
|
||||
return false;
|
||||
if (toReplace == state)
|
||||
return false;
|
||||
if (toReplace.getDestroySpeed(world, pos) == -1
|
||||
|| (toReplaceOther != null && toReplaceOther.getDestroySpeed(world, pos) == -1))
|
||||
return false;
|
||||
|
||||
boolean isNormalCube = state.isRedstoneConductor(world, pos);
|
||||
return shouldPlace(world, pos, state, tileEntity, toReplace, toReplaceOther, isNormalCube);
|
||||
}
|
||||
|
||||
//SchematicannonTileEntity::shouldPlace
|
||||
private static boolean shouldPlace(Level level, BlockPos pos, BlockState state, BlockEntity tileEntity, BlockState toReplace,
|
||||
BlockState toReplaceOther, boolean isNormalCube) {
|
||||
return true;
|
||||
// if (!replaceTileEntities
|
||||
// && (toReplace.hasBlockEntity() || (toReplaceOther != null && toReplaceOther.hasBlockEntity())))
|
||||
// return false;
|
||||
//
|
||||
// if (shouldIgnoreBlockState(state))
|
||||
// return false;
|
||||
//
|
||||
// boolean placingAir = state.isAir();
|
||||
//
|
||||
// if (replaceMode == 3)
|
||||
// return true;
|
||||
// if (replaceMode == 2 && !placingAir)
|
||||
// return true;
|
||||
// if (replaceMode == 1 && (isNormalCube || (!toReplace.isRedstoneConductor(level, pos)
|
||||
// && (toReplaceOther == null || !toReplaceOther.isRedstoneConductor(level, pos)))) && !placingAir)
|
||||
// return true;
|
||||
// if (replaceMode == 0 && !toReplace.isRedstoneConductor(level, pos)
|
||||
// && (toReplaceOther == null || !toReplaceOther.isRedstoneConductor(level, pos)) && !placingAir)
|
||||
// return true;
|
||||
//
|
||||
// return false;
|
||||
}
|
||||
|
||||
//SchematicannonTileEntity::shouldIgnoreBlockState
|
||||
private static boolean shouldIgnoreBlockState(BlockState state) {
|
||||
// Block doesn't have a mapping (Water, lava, etc)
|
||||
if (state.getBlock() == Blocks.STRUCTURE_VOID)
|
||||
return true;
|
||||
|
||||
// ItemRequirement requirement = ItemRequirement.of(state, te);
|
||||
// if (requirement.isEmpty())
|
||||
// return false;
|
||||
// if (requirement.isInvalid())
|
||||
// return false;
|
||||
|
||||
// Block doesn't need to be placed twice (Doors, beds, double plants)
|
||||
if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
|
||||
&& state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER)
|
||||
return true;
|
||||
if (state.hasProperty(BlockStateProperties.BED_PART)
|
||||
&& state.getValue(BlockStateProperties.BED_PART) == BedPart.HEAD)
|
||||
return true;
|
||||
if (state.getBlock() instanceof PistonHeadBlock)
|
||||
return true;
|
||||
// if (AllBlocks.BELT.has(state))
|
||||
// return state.getValue(BeltBlock.PART) == BeltPart.MIDDLE;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding.utilities;
|
||||
|
||||
import net.minecraft.advancements.CriteriaTriggers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
@@ -20,12 +19,10 @@ import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import nl.requios.effortlessbuilding.CommonConfig;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
|
||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
import nl.requios.effortlessbuilding.systems.ServerBuildState;
|
||||
|
||||
@@ -273,11 +270,9 @@ public class SurvivalHelper {
|
||||
}
|
||||
|
||||
//Check quickreplace
|
||||
boolean isQuickReplacing = false;
|
||||
if (placer instanceof Player player) {
|
||||
if (world.isClientSide) EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing();
|
||||
else isQuickReplacing = ServerBuildState.isQuickReplacing(player);
|
||||
|
||||
boolean isQuickReplacing = world.isClientSide ? EffortlessBuildingClient.BUILD_SETTINGS.isQuickReplacing()
|
||||
: ServerBuildState.isQuickReplacing(player);
|
||||
if (isQuickReplacing) return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user