From b2c3a14efe7b6304e89de07daf877b2fce596dab Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Sun, 17 Nov 2024 13:31:25 +0100 Subject: [PATCH] Initial --- .gitignore | 119 ++++++++++++++++++ LICENSE.txt | 21 ++++ build.gradle | 95 ++++++++++++++ gradle.properties | 15 +++ gradle/wrapper/gradle-wrapper.properties | 1 + settings.gradle | 9 ++ .../java/me/alexdevs/claimcore/ClaimCore.java | 55 ++++++++ .../api/event/PlayerEquipmentChange.java | 15 +++ .../me/alexdevs/claimcore/core/Claim.java | 66 ++++++++++ .../alexdevs/claimcore/core/ClaimManager.java | 29 +++++ .../claimcore/core/InteractionManager.java | 116 +++++++++++++++++ .../mixin/EquipmentChangesMixin.java | 23 ++++ src/main/resources/claimcore.mixins.json | 14 +++ src/main/resources/fabric.mod.json | 25 ++++ 14 files changed, 603 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle create mode 100644 src/main/java/me/alexdevs/claimcore/ClaimCore.java create mode 100644 src/main/java/me/alexdevs/claimcore/api/event/PlayerEquipmentChange.java create mode 100644 src/main/java/me/alexdevs/claimcore/core/Claim.java create mode 100644 src/main/java/me/alexdevs/claimcore/core/ClaimManager.java create mode 100644 src/main/java/me/alexdevs/claimcore/core/InteractionManager.java create mode 100644 src/main/java/me/alexdevs/claimcore/mixin/EquipmentChangesMixin.java create mode 100644 src/main/resources/claimcore.mixins.json create mode 100644 src/main/resources/fabric.mod.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5f737e --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..75f844b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 Alessandro "AlexDevs" Proto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..0d78b8f --- /dev/null +++ b/build.gradle @@ -0,0 +1,95 @@ +plugins { + id 'fabric-loom' version '1.8-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name +} + + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. + maven { + name = "EngineHub" + url = "https://maven.enginehub.org/repo/" + } +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + modImplementation "com.sk89q.worldedit:worldedit-fabric-mc1.20:7.3.0-SNAPSHOT" +} + +processResources { + inputs.property "version", project.version + inputs.property "minecraft_version", project.minecraft_version + inputs.property "loader_version", project.loader_version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version, + "minecraft_version": project.minecraft_version, + "loader_version": project.loader_version + } +} + +def targetJavaVersion = 17 +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release.set(targetJavaVersion) + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}" } + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava", MavenPublication) { + artifactId = project.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..95290e4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.20.1 +yarn_mappings=1.20.1+build.10 +loader_version=0.16.9 +# Mod Properties +mod_version=1.0-SNAPSHOT +maven_group=me.alexdevs +archives_base_name=claimcore +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.92.2+1.20.1 +worldeditapi_version= \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..de9161e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/me/alexdevs/claimcore/ClaimCore.java b/src/main/java/me/alexdevs/claimcore/ClaimCore.java new file mode 100644 index 0000000..de5804a --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/ClaimCore.java @@ -0,0 +1,55 @@ +package me.alexdevs.claimcore; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.internal.cui.CUIEvent; +import com.sk89q.worldedit.session.SessionOwner; +import me.alexdevs.claimcore.core.ClaimManager; +import me.alexdevs.claimcore.core.InteractionManager; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.MinecraftServer; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.TypedActionResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClaimCore implements ModInitializer { + public static final String MOD_ID = "claimcore"; + public static final Logger LOGGER = LoggerFactory.getLogger(ClaimCore.class); + public static String claimToolName = "minecraft:golden_shovel"; + public static Item claimTool; + public static String virtualBlockName = "minecraft:gold_block"; + public static Block virtualBlock; + + public static MinecraftServer server; + + public static InteractionManager interactionManager; + public static final ClaimManager claimManager = new ClaimManager(); + + @Override + public void onInitialize() { + LOGGER.info("Initializing ClaimCore"); + + interactionManager = new InteractionManager(); + + ServerLifecycleEvents.SERVER_STARTING.register((server) -> { + ClaimCore.server = server; + claimTool = server.getRegistryManager().get(RegistryKeys.ITEM).get(Identifier.tryParse(claimToolName)); + virtualBlock = server.getRegistryManager().get(RegistryKeys.BLOCK).get(Identifier.tryParse(virtualBlockName)); + }); + + + } +} diff --git a/src/main/java/me/alexdevs/claimcore/api/event/PlayerEquipmentChange.java b/src/main/java/me/alexdevs/claimcore/api/event/PlayerEquipmentChange.java new file mode 100644 index 0000000..f608e10 --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/api/event/PlayerEquipmentChange.java @@ -0,0 +1,15 @@ +package me.alexdevs.claimcore.api.event; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.entity.player.PlayerEntity; + +public interface PlayerEquipmentChange { + Event EVENT = EventFactory.createArrayBacked(PlayerEquipmentChange.class, listeners -> (player) -> { + for (PlayerEquipmentChange listener : listeners) { + listener.changeEquipment(player); + } + }); + + void changeEquipment(PlayerEntity player); +} diff --git a/src/main/java/me/alexdevs/claimcore/core/Claim.java b/src/main/java/me/alexdevs/claimcore/core/Claim.java new file mode 100644 index 0000000..18da2f2 --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/core/Claim.java @@ -0,0 +1,66 @@ +package me.alexdevs.claimcore.core; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.UUID; + +public class Claim { + public enum ClaimType { + FLAT, + CUBIC, + } + + public UUID owner; + public final BlockPos pos1; + public final BlockPos pos2; + public ClaimType type; + public World world; + public final Volume volume; + + public Claim(UUID owner, BlockPos pos1, BlockPos pos2, World world, ClaimType type) { + this.owner = owner; + this.pos1 = pos1; + this.pos2 = pos2; + this.world = world; + this.type = type; + volume = new Volume(pos1, pos2); + } + + public static Claim createFlatClaim(UUID owner, BlockPos pos1, BlockPos pos2, World world) { + // The engine says 2,032 in both +Y and -Y is the max possible value. + pos1 = new BlockPos(pos1.getX(), -2048, pos1.getZ()); + pos2 = new BlockPos(pos2.getX(), +2048, pos2.getZ()); + return new Claim(owner, pos1, pos2, world, ClaimType.FLAT); + } + + public static Claim createCubicClaim(UUID owner, BlockPos pos1, BlockPos pos2, World world) { + return new Claim(owner, pos1, pos2, world, ClaimType.CUBIC); + } + + public static class Volume { + public final int minX, minY, minZ; + public final int maxX, maxY, maxZ; + + public Volume(BlockPos pos1, BlockPos pos2) { + this.minX = Math.min(pos1.getX(), pos2.getX()); + this.maxX = Math.max(pos1.getX(), pos2.getX()); + this.minY = Math.min(pos1.getY(), pos2.getY()); + this.maxY = Math.max(pos1.getY(), pos2.getY()); + this.minZ = Math.min(pos1.getZ(), pos2.getZ()); + this.maxZ = Math.max(pos1.getZ(), pos2.getZ()); + } + + public boolean contains(BlockPos target) { + return target.getX() >= minX && target.getX() <= maxX && + target.getY() >= minY && target.getY() <= maxY && + target.getZ() >= minZ && target.getZ() <= maxZ; + } + + public boolean intersects(Volume other) { + return (this.maxX >= other.minX && other.maxX >= this.minX) + && (this.maxY >= other.minY && other.maxY >= this.minY) + && (this.maxZ >= other.minZ && other.maxZ >= this.minZ); + } + } +} diff --git a/src/main/java/me/alexdevs/claimcore/core/ClaimManager.java b/src/main/java/me/alexdevs/claimcore/core/ClaimManager.java new file mode 100644 index 0000000..060efd6 --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/core/ClaimManager.java @@ -0,0 +1,29 @@ +package me.alexdevs.claimcore.core; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +public class ClaimManager { + public final ArrayList claims = new ArrayList<>(); + + + public ClaimManager() { + + } + + public void createClaim(Claim claim) { + claims.add(claim); + } + + @Nullable + public Claim getClaimAtPosition(World world, BlockPos pos) { + for (Claim claim : claims) { + if(claim.volume.contains(pos)) + return claim; + } + return null; + } +} diff --git a/src/main/java/me/alexdevs/claimcore/core/InteractionManager.java b/src/main/java/me/alexdevs/claimcore/core/InteractionManager.java new file mode 100644 index 0000000..f06e176 --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/core/InteractionManager.java @@ -0,0 +1,116 @@ +package me.alexdevs.claimcore.core; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.internal.cui.SelectionPointEvent; +import me.alexdevs.claimcore.ClaimCore; +import me.alexdevs.claimcore.api.event.PlayerEquipmentChange; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.event.player.AttackBlockCallback; +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.block.Block; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.math.BlockPos; + +import java.util.*; + +public class InteractionManager { + private final HashMap areaSelections = new HashMap<>(); + private final Queue fakeBlockQueue = new LinkedList<>(); + + public InteractionManager() { + + UseBlockCallback.EVENT.register((playerEntity, world, hand, hitResult) -> { + var currentItem = playerEntity.getStackInHand(hand).getItem(); + if(!currentItem.equals(ClaimCore.claimTool)) { + return ActionResult.PASS; + } + + var playerUuid = playerEntity.getUuid(); + var player = ClaimCore.server.getPlayerManager().getPlayer(playerUuid); + assert player != null; + + var blockPos = hitResult.getBlockPos(); + var playerWE = FabricAdapter.adaptPlayer(player); + var blockVector = FabricAdapter.adapt(blockPos); + var session = WorldEdit.getInstance().getSessionManager().get(playerWE); + if(areaSelections.containsKey(playerUuid)) { + session.dispatchCUIEvent(playerWE, new SelectionPointEvent( + 1, blockVector, 100L + )); + tryClaim(player, player.getServerWorld(), areaSelections.get(playerUuid), blockPos); + areaSelections.remove(playerUuid); + return ActionResult.FAIL; + } + + areaSelections.put(playerUuid, blockPos); + player.sendMessage( + Text.of("Selected first position: " + blockPos.toShortString()) + ); + + session.dispatchCUIEvent(playerWE, new SelectionPointEvent( + 0, blockVector, 1L + )); + + return ActionResult.FAIL; + }); + + PlayerEquipmentChange.EVENT.register(player -> { + if(!player.getMainHandStack().getItem().equals(ClaimCore.claimTool)) { + areaSelections.remove(player.getUuid()); + } + }); + + ServerTickEvents.END_SERVER_TICK.register(server -> { + while(!fakeBlockQueue.isEmpty()) { + var fakeBlock = fakeBlockQueue.poll(); + fakeBlock.player.networkHandler.sendPacket(new BlockUpdateS2CPacket( + fakeBlock.pos, + fakeBlock.block.getDefaultState() + )); + } + }); + + AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { + var claim = ClaimCore.claimManager.getClaimAtPosition(world, pos); + if(claim == null) { + return ActionResult.PASS; + } + + player.sendMessage(Text.of("You cannot grief this mf")); + return ActionResult.FAIL; + }); + + + PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> { + var claim = ClaimCore.claimManager.getClaimAtPosition(world, pos); + return claim == null; + }); + } + + public void tryClaim(PlayerEntity player, ServerWorld world, BlockPos pos1, BlockPos pos2) { + var volume = new Claim.Volume(pos1, pos2); + for(var claim : ClaimCore.claimManager.claims) { + if(claim.volume.intersects(volume)) { + player.sendMessage(Text.of("Intersects with existing claim!")); + return; + } + } + ClaimCore.claimManager.createClaim(Claim.createFlatClaim( + player.getUuid(), + pos1, + pos2, + world + )); + + player.sendMessage(Text.of("Created claim!")); + } + + public record FakeBlock(ServerPlayerEntity player, BlockPos pos, Block block) {} +} diff --git a/src/main/java/me/alexdevs/claimcore/mixin/EquipmentChangesMixin.java b/src/main/java/me/alexdevs/claimcore/mixin/EquipmentChangesMixin.java new file mode 100644 index 0000000..72d2057 --- /dev/null +++ b/src/main/java/me/alexdevs/claimcore/mixin/EquipmentChangesMixin.java @@ -0,0 +1,23 @@ +package me.alexdevs.claimcore.mixin; + +import me.alexdevs.claimcore.api.event.PlayerEquipmentChange; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; + +@Mixin(LivingEntity.class) +public class EquipmentChangesMixin { + @Inject(method = "getEquipmentChanges", at = @At(target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", value = "INVOKE")) + private void claimCore$getEquipmentChanges(CallbackInfoReturnable> cir) { + if((Object)this instanceof PlayerEntity player) { + PlayerEquipmentChange.EVENT.invoker().changeEquipment(player); + } + } +} diff --git a/src/main/resources/claimcore.mixins.json b/src/main/resources/claimcore.mixins.json new file mode 100644 index 0000000..fdf2f64 --- /dev/null +++ b/src/main/resources/claimcore.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "me.alexdevs.claimcore.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "EquipmentChangesMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..3cdac7e --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": 1, + "id": "claimcore", + "version": "${version}", + "name": "ClaimCore", + "description": "Fabric mod to protect player structures", + "authors": [], + "contact": {}, + "license": "MIT", + "icon": "assets/claimcore/icon.png", + "environment": "server", + "entrypoints": { + "main": [ + "me.alexdevs.claimcore.ClaimCore" + ] + }, + "mixins": [ + "claimcore.mixins.json" + ], + "depends": { + "fabricloader": ">=${loader_version}", + "fabric": "*", + "minecraft": "${minecraft_version}" + } +}