From 44a61e9c6f2b96408cac6995f2693b7f3f6cffc9 Mon Sep 17 00:00:00 2001 From: Arno Hovhannisyan Date: Tue, 5 Jan 2021 15:07:22 -0800 Subject: [PATCH] initial --- .gitignore | 6 + LICENSE | 7 + README.md | 66 ++++++++ build.gradle.kts | 69 ++++++++ gradle.properties | 20 +++ settings.gradle.kts | 11 ++ .../AbstractFurnaceBlockEntityMixin.java | 29 ++++ .../io/glossnyx/vibes/mixin/BlockMixin.java | 24 +++ .../mixin/EnderChestInventoryAccessor.java | 12 ++ .../io/glossnyx/vibes/mixin/EntityMixin.java | 26 +++ .../vibes/mixin/HopperBlockEntityMixin.java | 41 +++++ .../mixin/ItemDispenserBehaviorMixin.java | 20 +++ .../glossnyx/vibes/mixin/ItemEntityMixin.java | 49 ++++++ .../vibes/mixin/ItemFrameEntityMixin.java | 39 +++++ .../LootableContainerBlockEntityMixin.java | 29 ++++ .../vibes/mixin/PlayerEntityMixin.java | 22 +++ .../vibes/mixin/ScreenHandlerMixin.java | 33 ++++ .../mixin/ServerPlayNetworkHandlerMixin.java | 24 +++ .../vibes/mixin/ServerPlayerEntityMixin.java | 27 ++++ .../vibes/mixin/ShulkerBoxBlockMixin.java | 21 +++ .../vibes/mixin/SimpleInventoryMixin.java | 18 +++ .../mixin/StorageMinecartEntityMixin.java | 35 +++++ .../kotlin/io/glossnyx/vibes/Entrypoint.kt | 21 +++ .../kotlin/io/glossnyx/vibes/item/Vibe.kt | 26 +++ .../vibes/network/ClientNetworking.kt | 115 ++++++++++++++ .../vibes/network/ServerNetworking.kt | 148 ++++++++++++++++++ .../network/packet/ChangePositionBlock.kt | 22 +++ .../network/packet/ChangePositionEntity.kt | 21 +++ .../glossnyx/vibes/network/packet/Packets.kt | 45 ++++++ .../io/glossnyx/vibes/network/packet/Play.kt | 24 +++ .../vibes/network/packet/RightClickPlay.kt | 24 +++ .../vibes/network/packet/RightClickStop.kt | 14 ++ .../io/glossnyx/vibes/network/packet/Stop.kt | 15 ++ .../vibes/sound/BlockPositionProvider.kt | 13 ++ .../vibes/sound/EntityPositionProvider.kt | 9 ++ .../glossnyx/vibes/sound/PositionProvider.kt | 7 + .../io/glossnyx/vibes/sound/VibeInstance.kt | 18 +++ .../kotlin/io/glossnyx/vibes/util/Items.kt | 43 +++++ .../io/glossnyx/vibes/util/Networking.kt | 8 + .../kotlin/io/glossnyx/vibes/util/Tags.kt | 26 +++ src/main/resources/assets/vibes/icon.png | Bin 0 -> 4512 bytes .../resources/assets/vibes/lang/en_us.json | 3 + .../assets/vibes/models/item/vibe.json | 6 + .../assets/vibes/textures/item/vibe.png | Bin 0 -> 4512 bytes .../resources/data/vibes/recipes/vibe.json | 19 +++ src/main/resources/fabric.mod.json | 26 +++ src/main/resources/vibes.mixins.json | 28 ++++ 47 files changed, 1309 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 settings.gradle.kts create mode 100644 src/main/java/io/glossnyx/vibes/mixin/AbstractFurnaceBlockEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/BlockMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/EnderChestInventoryAccessor.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/EntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/HopperBlockEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ItemDispenserBehaviorMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ItemEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ItemFrameEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/LootableContainerBlockEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/PlayerEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ScreenHandlerMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ServerPlayNetworkHandlerMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ServerPlayerEntityMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/ShulkerBoxBlockMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/SimpleInventoryMixin.java create mode 100644 src/main/java/io/glossnyx/vibes/mixin/StorageMinecartEntityMixin.java create mode 100644 src/main/kotlin/io/glossnyx/vibes/Entrypoint.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/item/Vibe.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/ClientNetworking.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/ServerNetworking.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionBlock.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionEntity.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/Packets.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/Play.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickPlay.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickStop.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/network/packet/Stop.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/sound/BlockPositionProvider.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/sound/EntityPositionProvider.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/sound/PositionProvider.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/sound/VibeInstance.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/util/Items.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/util/Networking.kt create mode 100644 src/main/kotlin/io/glossnyx/vibes/util/Tags.kt create mode 100644 src/main/resources/assets/vibes/icon.png create mode 100644 src/main/resources/assets/vibes/lang/en_us.json create mode 100644 src/main/resources/assets/vibes/models/item/vibe.json create mode 100644 src/main/resources/assets/vibes/textures/item/vibe.png create mode 100644 src/main/resources/data/vibes/recipes/vibe.json create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/vibes.mixins.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..846d52b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.gradle +/.idea +/build +/run +/gradle +/gradlew* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3a82986 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright © 2020 Arno Hovhannisyan + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..74a0b48 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Vibes + +Vibes adds a new item, the Vibe, that lets you play music discs on the go without needing a jukebox. + +Vibes try to position their music logically, which means that putting a Vibe inside a chest while it's still playing will move the sound source to the chest's position. This not only works with chests, but with all vanilla storage blocks, storage minecarts, shulker boxes, and item frames. + +Dropping a Vibe on the ground will move the sound source to the item entity, and picking it up will move the sound source to the position of the player that picked it up. + +### Dependencies + +- [Fabric Loader] +- [Fabric API] +- [Fabric Language Kotlin] + +## Installation + +You have three options for downloading Vibes. + +- [CurseForge] +- [Modrinth] +- [GitHub Releases] + +## Crafting + +A Vibe can be crafted with **1 Jukebox** and **4 Iron Ingots**. + +![crafting recipe](https://i.imgur.com/maHXzce.png) + +## Usage + +The mechanics for Vibe are very similar to how the bundle is used. + +### Playing Discs + +1. Pick up the Vibe +1. Hover over the disc +1. Right click the disc with the Vibe + +### Ejecting Discs + +1. Pick up the Vibe +1. Right click an empty inventory slot with the Vibe + +## Compatibility + +Vibes is compatible with mods that introduce new music discs into the game, so you can enjoy your favorite modded music on the go. + +## Suggestions + +- [Disco] - Modular music disc loader + + + +[fabric loader]: https://fabricmc.net/use +[fabric api]: https://www.curseforge.com/minecraft/mc-mods/fabric-api +[fabric language kotlin]: https://www.curseforge.com/minecraft/mc-mods/fabric-language-kotlin + + + +[curseforge]: https://www.curseforge.com/minecraft/mc-mods/vibes +[modrinth]: https://modrinth.com/mod/vibes +[github releases]: https://github.com/glossnyx/vibes/releases + + + +[disco]: https://github.com/glossnyx/disco diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..f3da60f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,69 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import com.modrinth.minotaur.TaskModrinthUpload + +java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.targetCompatibility = JavaVersion.VERSION_1_8 + +fun getArtifactPath(): String { + return tasks.named("jar").get().archiveFile.get().toString() +} + +fun p(name: String): String { + return project.property(name).toString() +} + +plugins { + id("fabric-loom") + id("com.modrinth.minotaur") version "1.1.0" + kotlin("jvm") version "1.4.21" +} + +repositories { + maven("https://maven.fabricmc.net/") +} + +dependencies { + minecraft("com.mojang:minecraft:${p("minecraft.target")}") + mappings("net.fabricmc:yarn:${p("yarn")}:v2") + modImplementation("net.fabricmc:fabric-loader:${p("loader")}") + modImplementation("net.fabricmc.fabric-api:fabric-api:${p("fabric_api")}") + modImplementation("net.fabricmc:fabric-language-kotlin:${p("fabric_kotlin")}") +} + +tasks.named("processResources") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + + from(sourceSets["main"].resources.srcDirs) { + include("fabric.mod.json") + expand("version" to project.version) + } + + from(sourceSets["main"].resources.srcDirs) { + exclude("fabric.mod.json") + } +} + +tasks.withType { + kotlinOptions.jvmTarget = "1.8" +} + +task("modrinth") { + token = System.getenv("MODRINTH_TOKEN") + projectId = p("id") + + versionNumber = p("version") + versionName = "${project.property("name")} v$versionNumber" + + uploadFile = getArtifactPath() + + addGameVersion(p("minecraft.target")) + + p("minecraft.compatible").split(", ").forEach { addGameVersion(it) } + + addLoader("fabric") +} + +task("publish") { + dependsOn("build") + dependsOn("modrinth") +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8786fab --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +kotlin.code.style=official +org.gradle.jvmargs=-Xmx1G + +# Project +id=vibes +name=Vibes +version=1.0.0 + +# Curse + +curse.id=?????? + +# Versions (https://modmuss50.me/fabric.html) +minecraft.target=1.16.4 +minecraft.compatible=1.16.3, 1.16.2 +yarn=1.16.4+build.7 +loader=0.10.8 + +fabric_api=0.29.2+1.16 +fabric_kotlin=1.4.21+build.1 diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..5305335 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://maven.fabricmc.net") + } + + plugins { + id("fabric-loom") version "0.5-SNAPSHOT" + kotlin("jvm") version embeddedKotlinVersion + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/AbstractFurnaceBlockEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/AbstractFurnaceBlockEntityMixin.java new file mode 100644 index 0000000..a15d52f --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/AbstractFurnaceBlockEntityMixin.java @@ -0,0 +1,29 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(AbstractFurnaceBlockEntity.class) +class AbstractFurnaceBlockEntityMixin extends BlockEntity { + public AbstractFurnaceBlockEntityMixin(BlockEntityType type) { + super(type); + } + + @Inject(method = "setStack", at = @At("HEAD")) + private void onSetStack(int slot, ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(stack, this); + } + + @Inject(method = "removeStack(II)Lnet/minecraft/item/ItemStack;", at = @At("RETURN")) + private void onRemoveStack(int slot, int amount, CallbackInfoReturnable cir) { + ServerNetworking.INSTANCE.changePositionProvider(cir.getReturnValue(), this.world); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/BlockMixin.java b/src/main/java/io/glossnyx/vibes/mixin/BlockMixin.java new file mode 100644 index 0000000..860d4d4 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/BlockMixin.java @@ -0,0 +1,24 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.block.Block; +import net.minecraft.entity.Entity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Block.class) +class BlockMixin { + @Redirect( + method = "dropStack", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z" + ) + ) + private static boolean onSpawnEntity(World world, Entity entity) { + ServerNetworking.INSTANCE.onBreakShulkerBox(entity); + return world.spawnEntity(entity); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/EnderChestInventoryAccessor.java b/src/main/java/io/glossnyx/vibes/mixin/EnderChestInventoryAccessor.java new file mode 100644 index 0000000..ad2804e --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/EnderChestInventoryAccessor.java @@ -0,0 +1,12 @@ +package io.glossnyx.vibes.mixin; + +import net.minecraft.block.entity.EnderChestBlockEntity; +import net.minecraft.inventory.EnderChestInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EnderChestInventory.class) +public interface EnderChestInventoryAccessor { + @Accessor("activeBlockEntity") + EnderChestBlockEntity getActiveBlockEntity(); +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/EntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/EntityMixin.java new file mode 100644 index 0000000..39d7d67 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/EntityMixin.java @@ -0,0 +1,26 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Entity.class) +class EntityMixin { + @Shadow public World world; + + @Inject(method = "remove", at = @At("HEAD")) + private void onRemove(CallbackInfo ci) { + if (this.world.isClient) return; + + //noinspection ConstantConditions + if (!ItemEntity.class.isInstance(this)) return; + + ServerNetworking.INSTANCE.stopPlaying(ItemEntity.class.cast(this).getStack(), world); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/HopperBlockEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/HopperBlockEntityMixin.java new file mode 100644 index 0000000..34bdce1 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/HopperBlockEntityMixin.java @@ -0,0 +1,41 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import io.glossnyx.vibes.util.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.HopperBlockEntity; +import net.minecraft.entity.ItemEntity; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(HopperBlockEntity.class) +class HopperBlockEntityMixin extends BlockEntity { + public HopperBlockEntityMixin(BlockEntityType type) { + super(type); + } + + @Inject(method = "setStack", at = @At("HEAD")) + private void onSetStack(int slot, ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(stack, this); + } + + @Inject(method = "removeStack(II)Lnet/minecraft/item/ItemStack;", at = @At("RETURN")) + private void onRemoveStack(int slot, int amount, CallbackInfoReturnable cir) { + ServerNetworking.INSTANCE.changePositionProvider(cir.getReturnValue(), this.world); + } + + @Redirect( + method = "extract(Lnet/minecraft/inventory/Inventory;Lnet/minecraft/entity/ItemEntity;)Z", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;remove()V") + ) + private static void onRemoveItem(ItemEntity entity) { + if (!entity.world.isClient && ItemsKt.vibeTypeOf(entity.getStack()) == VibeType.VIBE) entity.removed = true; + else entity.remove(); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/ItemDispenserBehaviorMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ItemDispenserBehaviorMixin.java new file mode 100644 index 0000000..9a8b40f --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ItemDispenserBehaviorMixin.java @@ -0,0 +1,20 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.block.dispenser.ItemDispenserBehavior; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ItemDispenserBehavior.class) +class ItemDispenserBehaviorMixin { + @Redirect(method = "spawnItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z")) + private static boolean onSpawnEntity(World world, Entity entity) { + ItemEntity itemEntity = (ItemEntity) entity; + ServerNetworking.INSTANCE.changePositionProvider(itemEntity.getStack(), itemEntity); + return world.spawnEntity(entity); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/ItemEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ItemEntityMixin.java new file mode 100644 index 0000000..054250b --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ItemEntityMixin.java @@ -0,0 +1,49 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.util.ItemsKt; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ItemEntity.class) +abstract +class ItemEntityMixin { + @Shadow private int age; + @Shadow public abstract ItemStack getStack(); + + @Redirect( + method = "onPlayerCollision", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/ItemEntity;getStack()Lnet/minecraft/item/ItemStack;" + ) + ) + private ItemStack onGetStack(ItemEntity entity) { + if (ItemsKt.isPlaying(entity.getStack())) return entity.getStack().copy(); + return entity.getStack(); + } + + @Redirect( + method = "onPlayerCollision", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/player/PlayerEntity;sendPickup(Lnet/minecraft/entity/Entity;I)V" + ) + ) + private void onSendPickup(PlayerEntity player, Entity item, int count) { + player.sendPickup(item, count); + ItemStack stack = ((ItemEntity) item).getStack(); + if (ItemsKt.isPlaying(stack)) stack.setCount(0); + } + + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;remove()V")) + private void onRemove(ItemEntity entity) { + if (ItemsKt.isPlaying(entity.getStack())) age = 0; + else entity.remove(); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/ItemFrameEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ItemFrameEntityMixin.java new file mode 100644 index 0000000..1f5642e --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ItemFrameEntityMixin.java @@ -0,0 +1,39 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.decoration.AbstractDecorationEntity; +import net.minecraft.entity.decoration.ItemFrameEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemFrameEntity.class) +abstract class ItemFrameEntityMixin extends AbstractDecorationEntity { + protected ItemFrameEntityMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "setHeldItemStack(Lnet/minecraft/item/ItemStack;)V", at = @At("HEAD")) + private void onSetStack(ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(stack, this); + } + + @Redirect( + method = "dropHeldStack", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/decoration/ItemFrameEntity;dropStack(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/entity/ItemEntity;" + ) + ) + private ItemEntity onDropStack(ItemFrameEntity entity, ItemStack stack) { + ItemEntity itemEntity = entity.dropStack(stack); + ServerNetworking.INSTANCE.changePositionProvider(stack, itemEntity); + return itemEntity; + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/LootableContainerBlockEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/LootableContainerBlockEntityMixin.java new file mode 100644 index 0000000..7095f5f --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/LootableContainerBlockEntityMixin.java @@ -0,0 +1,29 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LootableContainerBlockEntity; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(LootableContainerBlockEntity.class) +class LootableContainerBlockEntityMixin extends BlockEntity { + public LootableContainerBlockEntityMixin(BlockEntityType type) { + super(type); + } + + @Inject(method = "setStack", at = @At("HEAD")) + private void onSetStack(int slot, ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(stack, this); + } + + @Inject(method = "removeStack(II)Lnet/minecraft/item/ItemStack;", at = @At("RETURN")) + private void onRemoveStack(int slot, int amount, CallbackInfoReturnable cir) { + ServerNetworking.INSTANCE.changePositionProvider(cir.getReturnValue(), this.world); + } +} \ No newline at end of file diff --git a/src/main/java/io/glossnyx/vibes/mixin/PlayerEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/PlayerEntityMixin.java new file mode 100644 index 0000000..4bed234 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/PlayerEntityMixin.java @@ -0,0 +1,22 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.ItemEntity; +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; + +@Mixin(PlayerEntity.class) +class PlayerEntityMixin { + @Inject(method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", at = @At("RETURN")) + private void onDropItem(ItemStack stack, boolean throwRandomly, boolean retainOwnership, CallbackInfoReturnable cir) { + ItemEntity entity = cir.getReturnValue(); + + if (entity == null) return; + + ServerNetworking.INSTANCE.changePositionProvider(entity.getStack(), entity); + } +} \ No newline at end of file diff --git a/src/main/java/io/glossnyx/vibes/mixin/ScreenHandlerMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ScreenHandlerMixin.java new file mode 100644 index 0000000..51728b4 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ScreenHandlerMixin.java @@ -0,0 +1,33 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ClientNetworking; +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.List; + +@Mixin(ScreenHandler.class) +class ScreenHandlerMixin { + @Shadow @Final public List slots; + + @Inject(method = "method_30010", at = @At("HEAD"), cancellable = true) + private void onRemoveStack(int slotIndex, int clickType, SlotActionType actionType, PlayerEntity player, CallbackInfoReturnable cir) { + if (slotIndex < 0) return; + + Slot slot = slots.get(slotIndex); + if (slot == null) return; + + ServerNetworking.INSTANCE.onQuickMove(slot, actionType, player); + ClientNetworking.INSTANCE.onRightClick(slot, clickType, actionType, player, cir); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/ServerPlayNetworkHandlerMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ServerPlayNetworkHandlerMixin.java new file mode 100644 index 0000000..d9ca378 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ServerPlayNetworkHandlerMixin.java @@ -0,0 +1,24 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayNetworkHandler.class) +class ServerPlayNetworkHandlerMixin { + @Shadow public ServerPlayerEntity player; + @Shadow @Final private MinecraftServer server; + + @Inject(method = "onDisconnected", at = @At("HEAD")) + private void onDisconnect(Text reason, CallbackInfo ci) { + server.execute(() -> ServerNetworking.INSTANCE.onDisconnect(player)); + } +} \ No newline at end of file diff --git a/src/main/java/io/glossnyx/vibes/mixin/ServerPlayerEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ServerPlayerEntityMixin.java new file mode 100644 index 0000000..771ba62 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ServerPlayerEntityMixin.java @@ -0,0 +1,27 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ServerPlayerEntity.class) +class ServerPlayerEntityMixin { + @Inject(method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", at = @At("RETURN")) + private void onDropItem(ItemStack stack, boolean throwRandomly, boolean retainOwnership, CallbackInfoReturnable cir) { + ItemEntity entity = cir.getReturnValue(); + if (entity == null) return; + ServerNetworking.INSTANCE.changePositionProvider(entity.getStack(), entity); + } + + @Inject(method = "sendPickup", at = @At("HEAD")) + private void onPickup(Entity item, int count, CallbackInfo ci) { + ServerNetworking.INSTANCE.onPickup(ServerPlayerEntity.class.cast(this), item); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/ShulkerBoxBlockMixin.java b/src/main/java/io/glossnyx/vibes/mixin/ShulkerBoxBlockMixin.java new file mode 100644 index 0000000..8fa4a1c --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/ShulkerBoxBlockMixin.java @@ -0,0 +1,21 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.CallbackInfo; + +@Mixin(ShulkerBoxBlock.class) +class ShulkerBoxBlockMixin { + @Inject(method = "onPlaced", at = @At("HEAD")) + private void onPlaced(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(itemStack, world.getBlockEntity(pos)); + } +} diff --git a/src/main/java/io/glossnyx/vibes/mixin/SimpleInventoryMixin.java b/src/main/java/io/glossnyx/vibes/mixin/SimpleInventoryMixin.java new file mode 100644 index 0000000..3d84620 --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/SimpleInventoryMixin.java @@ -0,0 +1,18 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SimpleInventory; +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.CallbackInfo; + +@Mixin(SimpleInventory.class) +class SimpleInventoryMixin { + @Inject(method = "setStack", at = @At("HEAD")) + private void onSetStack(int slot, ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.handleEnderChest((Inventory) this, stack); + } +} \ No newline at end of file diff --git a/src/main/java/io/glossnyx/vibes/mixin/StorageMinecartEntityMixin.java b/src/main/java/io/glossnyx/vibes/mixin/StorageMinecartEntityMixin.java new file mode 100644 index 0000000..173c1ca --- /dev/null +++ b/src/main/java/io/glossnyx/vibes/mixin/StorageMinecartEntityMixin.java @@ -0,0 +1,35 @@ +package io.glossnyx.vibes.mixin; + +import io.glossnyx.vibes.network.ServerNetworking; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.vehicle.AbstractMinecartEntity; +import net.minecraft.entity.vehicle.StorageMinecartEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(StorageMinecartEntity.class) +class StorageMinecartEntityMixin extends AbstractMinecartEntity { + protected StorageMinecartEntityMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "setStack", at = @At("HEAD")) + private void onSetStack(int slot, ItemStack stack, CallbackInfo ci) { + ServerNetworking.INSTANCE.changePositionProvider(stack, this); + } + + @Inject(method = "removeStack(II)Lnet/minecraft/item/ItemStack;", at = @At("RETURN")) + private void onRemoveStack(int slot, int amount, CallbackInfoReturnable cir) { + ServerNetworking.INSTANCE.changePositionProvider(cir.getReturnValue(), this.world); + } + + @Override + public Type getMinecartType() { + return null; + } +} diff --git a/src/main/kotlin/io/glossnyx/vibes/Entrypoint.kt b/src/main/kotlin/io/glossnyx/vibes/Entrypoint.kt new file mode 100644 index 0000000..cb19813 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/Entrypoint.kt @@ -0,0 +1,21 @@ +package io.glossnyx.vibes + +import io.glossnyx.vibes.item.Vibe +import io.glossnyx.vibes.network.ClientNetworking +import io.glossnyx.vibes.network.ServerNetworking +import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry + +object Vibes { + fun id(path: String) = Identifier("vibes", path) +} + +fun init() { + Registry.register(Registry.ITEM, Vibe.id, Vibe) + + ServerNetworking.init() +} + +fun initClient() { + ClientNetworking.init() +} diff --git a/src/main/kotlin/io/glossnyx/vibes/item/Vibe.kt b/src/main/kotlin/io/glossnyx/vibes/item/Vibe.kt new file mode 100644 index 0000000..ba6b50c --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/item/Vibe.kt @@ -0,0 +1,26 @@ +package io.glossnyx.vibes.item + +import io.glossnyx.vibes.util.Tags +import io.glossnyx.vibes.Vibes +import net.minecraft.client.item.TooltipContext +import net.minecraft.item.Item +import net.minecraft.item.ItemGroup +import net.minecraft.item.ItemStack +import net.minecraft.text.Text +import net.minecraft.world.World + +private val settings: Item.Settings = Item.Settings() + .group(ItemGroup.MISC) + .maxCount(1) + +object Vibe : Item(settings) { + val id = Vibes.id("vibe") + + override fun appendTooltip(stack: ItemStack?, world: World?, tooltip: MutableList?, context: TooltipContext?) { + if (tooltip == null || stack == null) return + + val tag = stack.getSubTag(Tags.DISC) ?: return + + ItemStack.fromTag(tag).item.appendTooltip(stack, world, tooltip, context) + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/ClientNetworking.kt b/src/main/kotlin/io/glossnyx/vibes/network/ClientNetworking.kt new file mode 100644 index 0000000..2ae8b5a --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/ClientNetworking.kt @@ -0,0 +1,115 @@ +package io.glossnyx.vibes.network + +import io.glossnyx.vibes.network.packet.* +import io.glossnyx.vibes.sound.* +import io.glossnyx.vibes.util.* +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import net.minecraft.client.MinecraftClient +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.Entity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.ItemStack +import net.minecraft.item.Items +import net.minecraft.item.MusicDiscItem +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.sound.SoundEvent +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable +import java.util.UUID + +object ClientNetworking { + private val vibeInstances = mutableListOf() + + private fun getInstance(uuid: UUID): VibeInstance? { + return vibeInstances.find { it.uuid == uuid } + } + + private fun getEntity(world: ClientWorld, uuid: UUID): Entity { + return world.players.find { it.uuid == uuid } + ?: world.entities.find { it.uuid == uuid } + ?: throw Exception("entity with uuid $uuid not found") + } + + fun onRightClick(slot: Slot, clickType: Int, actionType: SlotActionType, player: PlayerEntity, cir: CallbackInfoReturnable) { + if (!player.world.isClient) return + if (clickType != 1) return + if (actionType != SlotActionType.PICKUP) return + + val cursorStack = player.inventory.cursorStack + if (vibeTypeOf(cursorStack) != VibeType.VIBE) return + + when (val item = slot.stack.item) { + is MusicDiscItem -> { + val uuid = uuidOf(cursorStack) ?: UUID.randomUUID() + val newSlotStack = discOf(cursorStack) ?: ItemStack.EMPTY + setDiscOf(cursorStack, slot.stack) + + if (player.isCreative) slot.stack = newSlotStack + + send(RightClickPlay(slot.id, uuid, item.sound.id)) + } + + Items.AIR -> { + if (player.isCreative) slot.stack = discOf(cursorStack) + + setDiscOf(cursorStack, ItemStack.EMPTY) + + send(RightClickStop(slot.id)) + } + + else -> return + } + + cir.returnValue = ItemStack.EMPTY + cir.cancel() + } + + fun init() { + register { data -> + val client = MinecraftClient.getInstance() + + getInstance(data.uuid)?.let { + client.soundManager.stop(it) + vibeInstances.remove(it) + } + + val world = client.world ?: throw Exception("no world") + val entity = getEntity(world, data.entityUUID) + + val instance = VibeInstance(data.uuid, EntityPositionProvider(entity), SoundEvent(data.identifier)) + vibeInstances.add(instance) + + client.soundManager.play(instance) + } + + register { data -> + getInstance(data.uuid)?.let { + MinecraftClient.getInstance().soundManager.stop(it) + vibeInstances.remove(it) + } + } + + register { data -> + getInstance(data.uuid)?.let { + GlobalScope.launch { + val world = MinecraftClient.getInstance().world ?: throw Exception("no world") + + val entity = runCatching { getEntity(world, data.entityUUID) }.getOrElse { + delay(100) + getEntity(world, data.entityUUID) + } + + it.position = EntityPositionProvider(entity) + } + } + } + + register { data -> + getInstance(data.uuid)?.let { + it.position = BlockPositionProvider(data.blockPos) + } + } + } +} diff --git a/src/main/kotlin/io/glossnyx/vibes/network/ServerNetworking.kt b/src/main/kotlin/io/glossnyx/vibes/network/ServerNetworking.kt new file mode 100644 index 0000000..7ae17e1 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/ServerNetworking.kt @@ -0,0 +1,148 @@ +package io.glossnyx.vibes.network + +import io.glossnyx.vibes.network.packet.* +import io.glossnyx.vibes.mixin.EnderChestInventoryAccessor +import io.glossnyx.vibes.util.* +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import net.minecraft.block.entity.BlockEntity +import net.minecraft.entity.Entity +import net.minecraft.entity.ItemEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.inventory.EnderChestInventory +import net.minecraft.inventory.Inventory +import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.world.World + +object ServerNetworking { + /** Stops sounds when source player disconnects */ + fun onDisconnect(player: PlayerEntity) { + player.inventory.main.forEach { stopPlaying(it, player.world) } + } + + /** Stops playing sounds */ + fun stopPlaying(stack: ItemStack, world: World) { + forEachVibe(stack) { + world.sendAll(Stop(uuidOf(it)!!)) + } + } + + /** When vibe is placed into ender chest */ + fun handleEnderChest(inventory: Inventory, stack: ItemStack) { + if (inventory !is EnderChestInventory) return + if (!isPlaying(stack)) return + + val world = (inventory as? EnderChestInventoryAccessor)?.activeBlockEntity?.world ?: return + if (world.isClient) return + + stopPlaying(stack, world) + } + + fun onBreakShulkerBox(entity: Entity) { + if (entity !is ItemEntity) return + if (vibeTypeOf(entity.stack) != VibeType.SHULKER) return + + changePositionProvider(entity.stack, entity) + } + + /** When vibe is picked by a player */ + fun onPickup(player: PlayerEntity, entity: Entity?) { + if (entity == null) return + if (entity !is ItemEntity) return + if (player.world.isClient) return + + changePositionProvider(entity.stack, player) + } + + /** When player quick moves vibe out of block */ + fun onQuickMove(slot: Slot, actionType: SlotActionType, player: PlayerEntity) { + if (player.world.isClient) return + if (actionType != SlotActionType.QUICK_MOVE) return + if (!isPlaying(slot.stack)) return + if (player.inventory.main.find { it == slot.stack } != null) return + + changePositionProvider(slot.stack, player) + } + + /** When player removes vibe from block */ + fun changePositionProvider(stack: ItemStack, world: World?) { + if (world == null) return + if (world.isClient) return + if (!isPlaying(stack)) return + + GlobalScope.launch { + delay(100) + val player = world.players.find { it.inventory.cursorStack == stack } ?: return@launch + changePositionProvider(stack, player) + } + } + + /** When player puts vibe into block */ + fun changePositionProvider(stack: ItemStack, block: BlockEntity?) { + if (block == null) return + if (block.world!!.isClient) return + + forEachVibe(stack) { + block.world!!.sendAll(ChangePositionBlock(uuidOf(it)!!, block.pos)) + } + } + + /** When player puts vibe into entity */ + fun changePositionProvider(stack: ItemStack, entity: Entity?) { + if (entity == null) return + if (entity.world.isClient) return + + forEachVibe(stack) { + entity.world.sendAll(ChangePositionEntity(uuidOf(it)!!, entity.uuid)) + } + } + + fun init() { + register { data, player -> + val slots = player.currentScreenHandler.slots + val slot = if (data.slotID >= slots.size - 1) slots[data.slotID - 9] else slots[data.slotID] + val cursorStack = player.inventory.cursorStack + + if (uuidOf(cursorStack) == null) setUUIDOf(cursorStack, data.uuid) + + val newCursorStack = discOf(cursorStack) ?: ItemStack.EMPTY + + setDiscOf(cursorStack, slot.stack) + + if (player.isCreative) { + slot.stack = newCursorStack + } else { + slot.stack = cursorStack.copy() + player.inventory.cursorStack = newCursorStack + } + + slot.markDirty() + + player.world.sendAll(Play(data.uuid, player.uuid, data.identifier)) + } + + register { data, player -> + val slots = player.currentScreenHandler.slots + val slot = if (data.slotID >= slots.size - 1) slots[data.slotID - 9] else slots[data.slotID] + val cursorStack = player.inventory.cursorStack + + val uuid = uuidOf(cursorStack) ?: return@register + + if (player.isCreative) { + slot.stack = discOf(cursorStack) + setDiscOf(cursorStack, ItemStack.EMPTY) + } else { + player.inventory.cursorStack = discOf(cursorStack) + slot.stack = cursorStack.copy() + setDiscOf(slot.stack, ItemStack.EMPTY) + } + + slot.markDirty() + + player.world.sendAll(Stop(uuid)) + } + } +} diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionBlock.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionBlock.kt new file mode 100644 index 0000000..d7c40d3 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionBlock.kt @@ -0,0 +1,22 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf +import net.minecraft.util.math.BlockPos +import java.util.UUID + +data class ChangePositionBlock(val uuid: UUID, val blockPos: BlockPos) : Packet { + companion object : PacketCompanion { + override val id = Vibes.id("change-position-block") + + override fun fromBuf(buf: PacketByteBuf) = ChangePositionBlock( + buf.readUuid(), + buf.readBlockPos() + ) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create() + .writeUuid(uuid) + .writeBlockPos(blockPos) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionEntity.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionEntity.kt new file mode 100644 index 0000000..9dd2a51 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/ChangePositionEntity.kt @@ -0,0 +1,21 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf +import java.util.UUID + +data class ChangePositionEntity(val uuid: UUID, val entityUUID: UUID) : Packet { + companion object : PacketCompanion { + override val id = Vibes.id("change-position-entity") + + override fun fromBuf(buf: PacketByteBuf) = ChangePositionEntity( + buf.readUuid(), + buf.readUuid() + ) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create() + .writeUuid(uuid) + .writeUuid(entityUUID) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/Packets.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/Packets.kt new file mode 100644 index 0000000..2db52b8 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/Packets.kt @@ -0,0 +1,45 @@ +package io.glossnyx.vibes.network.packet + +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking +import net.minecraft.network.PacketByteBuf +import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.util.Identifier +import kotlin.reflect.full.companionObjectInstance + +interface Packet { + fun toBuf(): PacketByteBuf +} + +interface PacketCompanion { + val id: Identifier + fun fromBuf(buf: PacketByteBuf): Packet +} + +inline fun register(crossinline handler: (packet: T) -> Unit) { + val companion = T::class.companionObjectInstance as PacketCompanion + + ClientPlayNetworking.registerGlobalReceiver(companion.id) { client, _, buf, _ -> + val packet = companion.fromBuf(buf) as T + client.execute { handler(packet) } + } +} + +inline fun register(crossinline handler: (packet: T, player: ServerPlayerEntity) -> Unit) { + val companion = T::class.companionObjectInstance as PacketCompanion + + ServerPlayNetworking.registerGlobalReceiver(companion.id) { server, player, _, buf, _ -> + val packet = companion.fromBuf(buf) as T + server.execute { handler(packet, player) } + } +} + +fun send(player: ServerPlayerEntity, packet: Packet) { + val companion = packet::class.companionObjectInstance as PacketCompanion + ServerPlayNetworking.send(player, companion.id, packet.toBuf()) +} + +fun send(packet: Packet) { + val companion = packet::class.companionObjectInstance as PacketCompanion + ClientPlayNetworking.send(companion.id, packet.toBuf()) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/Play.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/Play.kt new file mode 100644 index 0000000..d798f43 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/Play.kt @@ -0,0 +1,24 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf +import net.minecraft.util.Identifier +import java.util.UUID + +data class Play(val uuid: UUID, val entityUUID: UUID, val identifier: Identifier) : Packet { + companion object : PacketCompanion { + override val id = Vibes.id("play") + + override fun fromBuf(buf: PacketByteBuf) = Play( + buf.readUuid(), + buf.readUuid(), + buf.readIdentifier() + ) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create() + .writeUuid(uuid) + .writeUuid(entityUUID) + .writeIdentifier(identifier) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickPlay.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickPlay.kt new file mode 100644 index 0000000..84840d1 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickPlay.kt @@ -0,0 +1,24 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf +import net.minecraft.util.Identifier +import java.util.UUID + +data class RightClickPlay(val slotID: Int, val uuid: UUID, val identifier: Identifier) : Packet { + companion object : PacketCompanion { + override val id = Vibes.id("right-click-play") + + override fun fromBuf(buf: PacketByteBuf) = RightClickPlay( + buf.readVarInt(), + buf.readUuid(), + buf.readIdentifier() + ) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create() + .writeVarInt(slotID) + .writeUuid(uuid) + .writeIdentifier(identifier) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickStop.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickStop.kt new file mode 100644 index 0000000..85e924e --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/RightClickStop.kt @@ -0,0 +1,14 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf + +data class RightClickStop(val slotID: Int) : Packet { + companion object: PacketCompanion { + override val id = Vibes.id("right-click-stop") + override fun fromBuf(buf: PacketByteBuf) = RightClickStop(buf.readVarInt()) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create().writeVarInt(slotID) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/network/packet/Stop.kt b/src/main/kotlin/io/glossnyx/vibes/network/packet/Stop.kt new file mode 100644 index 0000000..4e98972 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/network/packet/Stop.kt @@ -0,0 +1,15 @@ +package io.glossnyx.vibes.network.packet + +import io.glossnyx.vibes.Vibes +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.minecraft.network.PacketByteBuf +import java.util.UUID + +data class Stop(val uuid: UUID) : Packet { + companion object : PacketCompanion { + override val id = Vibes.id("stop") + override fun fromBuf(buf: PacketByteBuf) = Stop(buf.readUuid()) + } + + override fun toBuf(): PacketByteBuf = PacketByteBufs.create().writeUuid(uuid) +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/sound/BlockPositionProvider.kt b/src/main/kotlin/io/glossnyx/vibes/sound/BlockPositionProvider.kt new file mode 100644 index 0000000..39a44dc --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/sound/BlockPositionProvider.kt @@ -0,0 +1,13 @@ +package io.glossnyx.vibes.sound + +import net.minecraft.util.math.BlockPos + +class BlockPositionProvider(blockPos: BlockPos) : PositionProvider { + private val x = blockPos.x.toDouble() + 0.5 + private val y = blockPos.y.toDouble() + 0.5 + private val z = blockPos.z.toDouble() + 0.5 + + override fun getX() = x + override fun getY() = y + override fun getZ() = z +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/sound/EntityPositionProvider.kt b/src/main/kotlin/io/glossnyx/vibes/sound/EntityPositionProvider.kt new file mode 100644 index 0000000..f37bbf9 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/sound/EntityPositionProvider.kt @@ -0,0 +1,9 @@ +package io.glossnyx.vibes.sound + +import net.minecraft.entity.Entity + +class EntityPositionProvider(private val entity: Entity) : PositionProvider { + override fun getX() = entity.x + override fun getY() = entity.y + override fun getZ() = entity.z +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/sound/PositionProvider.kt b/src/main/kotlin/io/glossnyx/vibes/sound/PositionProvider.kt new file mode 100644 index 0000000..41142ce --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/sound/PositionProvider.kt @@ -0,0 +1,7 @@ +package io.glossnyx.vibes.sound + +interface PositionProvider { + fun getX(): Double + fun getY(): Double + fun getZ(): Double +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/sound/VibeInstance.kt b/src/main/kotlin/io/glossnyx/vibes/sound/VibeInstance.kt new file mode 100644 index 0000000..e45b66b --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/sound/VibeInstance.kt @@ -0,0 +1,18 @@ +package io.glossnyx.vibes.sound + +import net.minecraft.client.sound.MovingSoundInstance +import net.minecraft.sound.SoundCategory +import net.minecraft.sound.SoundEvent +import java.util.UUID + +class VibeInstance(val uuid: UUID, var position: PositionProvider, event: SoundEvent) : MovingSoundInstance(event, SoundCategory.RECORDS) { + init { + tick() + } + + override fun tick() { + x = position.getX() + y = position.getY() + z = position.getZ() + } +} diff --git a/src/main/kotlin/io/glossnyx/vibes/util/Items.kt b/src/main/kotlin/io/glossnyx/vibes/util/Items.kt new file mode 100644 index 0000000..35dee26 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/util/Items.kt @@ -0,0 +1,43 @@ +package io.glossnyx.vibes.util + +import io.glossnyx.vibes.item.Vibe +import net.minecraft.block.ShulkerBoxBlock +import net.minecraft.inventory.Inventories +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemStack +import net.minecraft.util.collection.DefaultedList + +enum class VibeType { + VIBE, + SHULKER +} + +fun getShulkerInventory(stack: ItemStack): DefaultedList? { + val tag = stack.getSubTag("BlockEntityTag") ?: return null + val inventory = DefaultedList.ofSize(27, ItemStack.EMPTY) + Inventories.fromTag(tag, inventory) + return inventory +} + +fun isPlaying(stack: ItemStack): Boolean { + return when (vibeTypeOf(stack)) { + VibeType.VIBE -> uuidOf(stack) != null && discOf(stack) != null + VibeType.SHULKER -> getShulkerInventory(stack)?.find { isPlaying(it) } != null + else -> false + } +} + +fun vibeTypeOf(stack: ItemStack): VibeType? { + return when (val item = stack.item) { + is Vibe -> VibeType.VIBE + is BlockItem -> if (item.block is ShulkerBoxBlock) VibeType.SHULKER else null + else -> null + } +} + +fun forEachVibe(stack: ItemStack, fn: (stack: ItemStack) -> Unit) { + if (!isPlaying(stack)) return + if (vibeTypeOf(stack) == VibeType.VIBE) return fn(stack) + + getShulkerInventory(stack)?.filter { isPlaying(it) }?.forEach { forEachVibe(it, fn) } +} \ No newline at end of file diff --git a/src/main/kotlin/io/glossnyx/vibes/util/Networking.kt b/src/main/kotlin/io/glossnyx/vibes/util/Networking.kt new file mode 100644 index 0000000..4c89be2 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/util/Networking.kt @@ -0,0 +1,8 @@ +package io.glossnyx.vibes.util + +import io.glossnyx.vibes.network.packet.Packet +import io.glossnyx.vibes.network.packet.send +import net.minecraft.server.world.ServerWorld +import net.minecraft.world.World + +fun World.sendAll(packet: Packet) = (this as? ServerWorld)?.players?.forEach { send(it, packet) } diff --git a/src/main/kotlin/io/glossnyx/vibes/util/Tags.kt b/src/main/kotlin/io/glossnyx/vibes/util/Tags.kt new file mode 100644 index 0000000..23bd5f8 --- /dev/null +++ b/src/main/kotlin/io/glossnyx/vibes/util/Tags.kt @@ -0,0 +1,26 @@ +package io.glossnyx.vibes.util + +import net.minecraft.item.ItemStack +import net.minecraft.nbt.CompoundTag +import java.util.UUID + +object Tags { + const val DISC = "Disc" + const val UUID = "VibeUUID" +} + +fun uuidOf(stack: ItemStack): UUID? { + return if (stack.orCreateTag.containsUuid(Tags.UUID)) stack.orCreateTag.getUuid(Tags.UUID) else null +} + +fun setUUIDOf(stack: ItemStack, uuid: UUID) = stack.orCreateTag.putUuid(Tags.UUID, uuid) + +fun discOf(stack: ItemStack): ItemStack? { + val tag = stack.getSubTag(Tags.DISC) ?: return null + return ItemStack.fromTag(tag) +} + +fun setDiscOf(stack: ItemStack, disc: ItemStack) { + if (disc == ItemStack.EMPTY) return stack.removeSubTag(Tags.DISC) + stack.putSubTag(Tags.DISC, disc.toTag(CompoundTag())) +} diff --git a/src/main/resources/assets/vibes/icon.png b/src/main/resources/assets/vibes/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bfbabb894027a94df44a6736e1ca5fe341c74238 GIT binary patch literal 4512 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/vibes/lang/en_us.json b/src/main/resources/assets/vibes/lang/en_us.json new file mode 100644 index 0000000..5bc92ee --- /dev/null +++ b/src/main/resources/assets/vibes/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "item.vibes.vibe": "Vibe" +} diff --git a/src/main/resources/assets/vibes/models/item/vibe.json b/src/main/resources/assets/vibes/models/item/vibe.json new file mode 100644 index 0000000..40711a3 --- /dev/null +++ b/src/main/resources/assets/vibes/models/item/vibe.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "vibes:item/vibe" + } +} diff --git a/src/main/resources/assets/vibes/textures/item/vibe.png b/src/main/resources/assets/vibes/textures/item/vibe.png new file mode 100644 index 0000000000000000000000000000000000000000..bfbabb894027a94df44a6736e1ca5fe341c74238 GIT binary patch literal 4512 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/data/vibes/recipes/vibe.json b/src/main/resources/data/vibes/recipes/vibe.json new file mode 100644 index 0000000..f68e6d0 --- /dev/null +++ b/src/main/resources/data/vibes/recipes/vibe.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " I ", + "IJI", + " I " + ], + "key": { + "J": { + "item": "minecraft:jukebox" + }, + "I": { + "item": "minecraft:iron_ingot" + } + }, + "result": { + "item": "vibes:vibe" + } +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..a7857bc --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "vibes", + "version": "${version}", + "name": "Vibes", + "description": "Listen to music discs on the go with the Vibe", + "authors": ["glossnyx"], + "contact": { + "homepage": "https://github.com/glossnyx/vibes", + "sources": "https://github.com/glossnyx/vibes" + }, + "license": "MIT", + "icon": "assets/vibes/icon.png", + "environment": "*", + "entrypoints": { + "main": ["io.glossnyx.vibes.EntrypointKt::init"], + "client": ["io.glossnyx.vibes.EntrypointKt::initClient"] + }, + "mixins": ["vibes.mixins.json"], + "depends": { + "fabricloader": ">=0.8.7", + "fabric": "*", + "fabric-language-kotlin": "*", + "minecraft": "1.16.x" + } +} diff --git a/src/main/resources/vibes.mixins.json b/src/main/resources/vibes.mixins.json new file mode 100644 index 0000000..5b3a865 --- /dev/null +++ b/src/main/resources/vibes.mixins.json @@ -0,0 +1,28 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.glossnyx.vibes.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "ServerPlayNetworkHandlerMixin", + "ScreenHandlerMixin", + "EntityMixin", + "ServerPlayerEntityMixin", + "ItemEntityMixin", + "HopperBlockEntityMixin", + "LootableContainerBlockEntityMixin", + "AbstractFurnaceBlockEntityMixin", + "StorageMinecartEntityMixin", + "ItemDispenserBehaviorMixin", + "SimpleInventoryMixin", + "EnderChestInventoryAccessor", + "ItemFrameEntityMixin", + "BlockMixin", + "ShulkerBoxBlockMixin" + ], + "client": [], + "server": [], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file