diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2ca3795 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +# Automatically build the project and run any configured tests for every push +# and submitted pull request. This can help catch issues that only occur on +# certain platforms or Java versions, and provides a first line of defence +# against bad commits. + +name: build +on: [pull_request, push] + +jobs: + build: + strategy: + matrix: + # Use these Java versions + java: [ + 17, # Current Java LTS & minimum supported by Minecraft + ] + # and run on both Linux and Windows + os: [ubuntu-22.04, windows-2022] + runs-on: ${{ matrix.os }} + steps: + - name: checkout repository + uses: actions/checkout@v3 + - name: validate gradle wrapper + uses: gradle/wrapper-validation-action@v1 + - name: setup jdk ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'microsoft' + - name: make gradle wrapper executable + if: ${{ runner.os != 'Windows' }} + run: chmod +x ./gradlew + - name: build + run: ./gradlew build + - name: capture build artifacts + if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS + uses: actions/upload-artifact@v3 + with: + name: Artifacts + path: build/libs/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index ccb0c56..c476faf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,40 @@ -build/ -*.ipr -run/ -*.iws -out/ -*.iml -.gradle/ -output/ -bin/ -libs/ +# gradle +.gradle/ +build/ +out/ +classes/ + +# eclipse + +*.launch + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ .classpath .project -.idea/ -classes/ -.metadata -.vscode -.settings -*.launch \ No newline at end of file + +# macos + +*.DS_Store + +# fabric + +run/ + +# java + +hs_err_*.log +replay_*.log +*.hprof +*.jfr diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1625c17 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/build.gradle b/build.gradle index df015f7..ac4bfa3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,52 +1,88 @@ plugins { - id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false + id 'fabric-loom' version '1.4-SNAPSHOT' + id 'maven-publish' } -architectury { - minecraft = rootProject.minecraft_version +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name } -subprojects { - apply plugin: "dev.architectury.loom" - - loom { - silentMojangMappingsLicense() - } - - dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - // The following line declares the yarn mappings you may select this one as well. - mappings "net.fabricmc:yarn:1.20.1+build.10:v2" - } +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. } -allprojects { - apply plugin: "java" - apply plugin: "architectury-plugin" - apply plugin: "maven-publish" +loom { + splitEnvironmentSourceSets() - base { - archivesName = rootProject.archives_base_name - } + mods { + "knockdowns" { + sourceSet sourceSets.main + sourceSet sourceSets.client + } + } - version = rootProject.mod_version - group = rootProject.maven_group - - 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. - } - - tasks.withType(JavaCompile).configureEach { - options.encoding = "UTF-8" - options.release = 17 - } - - java { - withSourcesJar() - } } + +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}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.2.0"))) +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 17 +} + +java { + // 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() + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +jar { + from("LICENSE") { + rename { "${it}_${project.base.archivesName.get()}"} + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + 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. + } +} \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle deleted file mode 100644 index a545d0d..0000000 --- a/common/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -architectury { - common(rootProject.enabled_platforms.split(",")) -} - -loom { - accessWidenerPath = file("src/main/resources/knockdowns.accesswidener") -} - -dependencies { - // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies - // Do NOT use other classes from fabric loader - modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury:${rootProject.architectury_version}" - - compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.3")) -} - -publishing { - publications { - mavenCommon(MavenPublication) { - artifactId = rootProject.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. - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java deleted file mode 100644 index 1b4272f..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java +++ /dev/null @@ -1,63 +0,0 @@ -package ru.octol1ttle.knockdowns.common; - -import dev.architectury.event.EventResult; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.hit.EntityHitResult; -import net.minecraft.util.hit.HitResult; -import net.minecraft.util.math.Vec3d; -import org.jetbrains.annotations.Nullable; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; -import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket; -import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket; -import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; -import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; - -public class KnockdownsClient { - @Nullable - public static Entity reviving; - - public static void init() { - KnockdownsClientEvents.registerCallbacks(); - } - - public static void playKnockedDownSound(Vec3d pos) { - MinecraftClient.getInstance().getSoundManager().play( - new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos) - ); - } - - public static EventResult onEntityUse(PlayerEntity player, Entity entity) { - if (KnockdownsUtils.isKnockedOrReviving(player) || !(entity instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) { - return EventResult.pass(); - } - - KnockdownsNetwork.sendToServer(new RequestStartRevivingC2SPacket(entity.getUuid())); - reviving = entity; - - return EventResult.interruptTrue(); - } - - public static void onPlayerTick(PlayerEntity player) { - MinecraftClient client = MinecraftClient.getInstance(); - if (!player.equals(client.player) || reviving == null) { - return; - } - - boolean playerKnocked = ((IKnockableDown) player).is_KnockedDown(); - boolean revivingTargeted = client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.ENTITY - && ((EntityHitResult) client.crosshairTarget).getEntity().equals(reviving); - - if (!(reviving instanceof IKnockableDown knockable)) { - return; - } - - if (!knockable.is_KnockedDown() || playerKnocked || !revivingTargeted) { - KnockdownsNetwork.sendToServer(new StopRevivingC2SPacket(reviving.getUuid())); - reviving = null; - } - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java deleted file mode 100644 index d36f5d7..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.octol1ttle.knockdowns.common; - -import net.minecraft.SharedConstants; -import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; -import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; - -public class KnockdownsCommon { - public static final String MOD_ID = "knockdowns"; - public static final int REVIVE_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; - - public static void init() { - KnockdownsSoundEvents.register(); - KnockdownsNetwork.registerPackets(); - KnockdownsEvents.registerCallbacks(); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java deleted file mode 100644 index 8715bd8..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.octol1ttle.knockdowns.common; - -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.server.MinecraftServer; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class KnockdownsUtils { - public static boolean isKnockedOrReviving(PlayerEntity player) { - return player instanceof IKnockableDown knockable && (knockable.is_KnockedDown() || knockable.is_Reviving()); - } - - public static boolean allTeammatesKnocked(MinecraftServer server, PlayerEntity player) { - for (PlayerEntity teammate : server.getPlayerManager().getPlayerList()) { - if (teammate.equals(player)) { - continue; - } - IKnockableDown knockable = (IKnockableDown) teammate; - if (!knockable.is_KnockedDown() && !player.isDead()) { - return false; - } - } - return true; - } - - public static void hurtTenacity(PlayerEntity player, float damage) { - player.setInvulnerable(false); - //DamageSource recent = player.getRecentDamageSource(); - player.damage(/*Objects.requireNonNullElse(recent, */player.getDamageSources().generic()/*)*/, damage); - player.velocityModified = false; - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java deleted file mode 100644 index 17c4308..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.octol1ttle.knockdowns.common.api; - -public interface IKnockableDown { - boolean is_KnockedDown(); - - void set_KnockedDown(boolean knockedDown); - - int get_ReviverCount(); - - void set_ReviverCount(int reviverCount); - - boolean is_Reviving(); - - void set_Reviving(boolean reviving); - - int get_ReviveTimer(); - - void set_ReviveTimer(int reviveTimer); - - int get_KnockedAge(); - - void set_KnockedAge(int knockedAge); -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java deleted file mode 100644 index 47ec31d..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.octol1ttle.knockdowns.common.events; - -import dev.architectury.event.events.client.ClientGuiEvent; -import net.minecraft.SharedConstants; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.util.Formatting; -import ru.octol1ttle.knockdowns.common.KnockdownsClient; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class KnockdownsClientEvents { - public static void registerCallbacks() { - registerOnHudRender(); - } - - private static void registerOnHudRender() { - ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> { - IKnockableDown reviving = (IKnockableDown) KnockdownsClient.reviving; - MinecraftClient client = MinecraftClient.getInstance(); - if (reviving == null) { - reviving = (IKnockableDown) client.player; - if (reviving == null || reviving.get_ReviverCount() == 0) { - return; - } - } - - TextRenderer renderer = client.textRenderer; - - String timerText = String.format("%.1f", reviving.get_ReviveTimer() / (float) SharedConstants.TICKS_PER_SECOND); - int timerX = (drawContext.getScaledWindowWidth() - renderer.getWidth(timerText)) / 2; - - int reviverCount = reviving.get_ReviverCount(); - Integer color = reviverCount > 1 ? Formatting.GREEN.getColorValue() : Formatting.WHITE.getColorValue(); - - String reviverCountText = "x" + reviverCount; - int reviveCountX = (drawContext.getScaledWindowWidth() - renderer.getWidth(reviverCountText)) / 2; - - if (color != null) { - drawContext.drawTextWithShadow(renderer, timerText, timerX, drawContext.getScaledWindowHeight() / 2 + 5, color); - drawContext.drawTextWithShadow(renderer, reviverCountText, reviveCountX, drawContext.getScaledWindowHeight() / 2 + 14, color); - } - }); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java deleted file mode 100644 index 8f46b96..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ /dev/null @@ -1,155 +0,0 @@ -package ru.octol1ttle.knockdowns.common.events; - -import dev.architectury.event.CompoundEventResult; -import dev.architectury.event.EventResult; -import dev.architectury.event.events.common.EntityEvent; -import dev.architectury.event.events.common.InteractionEvent; -import dev.architectury.event.events.common.PlayerEvent; -import dev.architectury.event.events.common.TickEvent; -import net.minecraft.SharedConstants; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableTextContent; -import net.minecraft.util.Hand; -import net.minecraft.util.math.MathHelper; -import ru.octol1ttle.knockdowns.common.KnockdownsClient; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; -import ru.octol1ttle.knockdowns.common.KnockdownsUtils; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; -import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; - -public class KnockdownsEvents { - private static final float KNOCKED_INVULNERABILITY_TICKS = 3.0f * SharedConstants.TICKS_PER_SECOND; - private static final float KNOCKED_HURT_PERIOD = 1.2f; - private static final float KNOCKED_TENACITY = 60.0f; - - public static void registerCallbacks() { - registerOnLivingDeath(); - registerOnPlayerTick(); - registerOnPlayerInteractions(); - registerOnEntityUse(); - } - - private static void registerOnLivingDeath() { - EntityEvent.LIVING_DEATH.register((entity, source) -> { - MinecraftServer server = entity.getServer(); - if (server == null || !(entity instanceof IKnockableDown knockable)) { - return EventResult.pass(); - } - - ServerPlayerEntity player = (ServerPlayerEntity) entity; - - if (KnockdownsUtils.allTeammatesKnocked(server, player)) { - return EventResult.pass(); - } - - if (knockable.is_KnockedDown()) { - knockable.set_KnockedDown(false); - knockable.set_ReviverCount(0); - knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); - knockable.set_KnockedAge(0); - - return EventResult.pass(); - } - - entity.clearStatusEffects(); - entity.setInvulnerable(true); - entity.setGlowing(true); - entity.setHealth(entity.getMaxHealth()); - entity.extinguish(); - entity.setAir(entity.getMaxAir()); - entity.setFrozenTicks(0); - player.stopFallFlying(); - - knockable.set_KnockedDown(true); - knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); - knockable.set_KnockedAge(0); - - KnockdownsNetwork.sendToWorld(player.getServerWorld(), new PlayKnockedDownSoundS2CPacket(player.getX(), player.getY(), player.getZ())); - - Text deathMessage = entity.getDamageTracker().getDeathMessage(); - TranslatableTextContent content = (TranslatableTextContent) deathMessage.getContent(); - Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), deathMessage.getString(), content.getArgs()); - - server.getPlayerManager().broadcast(replaced, false); - - return EventResult.interruptFalse(); - }); - } - - private static void registerOnPlayerTick() { - TickEvent.PLAYER_POST.register(player -> { - MinecraftServer server = player.getServer(); - if (server == null) { - KnockdownsClient.onPlayerTick(player); - return; - } - if (!(player instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) { - return; - } - - if (KnockdownsUtils.allTeammatesKnocked(server, player)) { - KnockdownsUtils.hurtTenacity(player, player.getMaxHealth()); - return; - } - - if (knockable.get_ReviverCount() > 0) { - knockable.set_ReviveTimer(knockable.get_ReviveTimer() - knockable.get_ReviverCount()); - - if (knockable.get_ReviveTimer() <= 0) { - knockable.set_KnockedDown(false); - knockable.set_ReviverCount(0); - knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); - knockable.set_KnockedAge(0); - - player.setInvulnerable(false); - player.setGlowing(false); - player.setHealth(6.0f); - } - return; - } - knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 1)); - - knockable.set_KnockedAge(knockable.get_KnockedAge() + 1); - - int period = MathHelper.floor(KNOCKED_HURT_PERIOD * SharedConstants.TICKS_PER_SECOND); - if (knockable.get_KnockedAge() >= KNOCKED_INVULNERABILITY_TICKS && knockable.get_KnockedAge() % period == 0) { - KnockdownsUtils.hurtTenacity(player, player.getMaxHealth() / (KNOCKED_TENACITY / KNOCKED_HURT_PERIOD)); - } - }); - } - - private static void registerOnPlayerInteractions() { - InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> { - if (KnockdownsUtils.isKnockedOrReviving(player)) { - return EventResult.interruptFalse(); - } - return EventResult.pass(); - }); - PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { - if (KnockdownsUtils.isKnockedOrReviving(player)) { - return EventResult.interruptFalse(); - } - return EventResult.pass(); - }); - InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { - if (KnockdownsUtils.isKnockedOrReviving(player)) { - return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); - } - return CompoundEventResult.pass(); - }); - InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> { - if (KnockdownsUtils.isKnockedOrReviving(player)) { - return EventResult.interruptFalse(); - } - return EventResult.pass(); - }); - } - - private static void registerOnEntityUse() { - InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) - -> player.getWorld().isClient() ? KnockdownsClient.onEntityUse(player, entity) : EventResult.pass()); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/LivingEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/LivingEntityMixin.java deleted file mode 100644 index 369a8b4..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/LivingEntityMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.octol1ttle.knockdowns.common.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import net.minecraft.entity.LivingEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -@Mixin(LivingEntity.class) -public abstract class LivingEntityMixin { - @ModifyReturnValue(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("RETURN")) - private boolean dontTargetKnockedPlayers(boolean original, LivingEntity target) { - return original && !(target instanceof IKnockableDown knockable && knockable.is_KnockedDown()); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/MobEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/MobEntityMixin.java deleted file mode 100644 index 17540a0..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/MobEntityMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.octol1ttle.knockdowns.common.mixin; - -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.mob.MobEntity; -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 ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -@Mixin(MobEntity.class) -public abstract class MobEntityMixin { - @Inject(method = "setTarget", at = @At("HEAD"), cancellable = true) - private void setTarget(LivingEntity target, CallbackInfo ci) { - if (target instanceof IKnockableDown knockable && knockable.is_KnockedDown()) { - ci.cancel(); - } - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java deleted file mode 100644 index ce8f9f1..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java +++ /dev/null @@ -1,129 +0,0 @@ -package ru.octol1ttle.knockdowns.common.mixin; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.data.TrackedData; -import net.minecraft.entity.data.TrackedDataHandlerRegistry; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -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; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -@SuppressWarnings("WrongEntityDataParameterClass") -@Mixin(PlayerEntity.class) -public abstract class PlayerEntityMixin extends Entity implements IKnockableDown { - @Unique - private static final TrackedData KNOCKED_DOWN = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN); - @Unique - private static final TrackedData IS_REVIVING = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN); - @Unique - private static final TrackedData REVIVER_COUNT = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER); - @Unique - private static final TrackedData REVIVE_TIMER = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER); - @Unique - public int knocked_age; - - private PlayerEntityMixin(EntityType type, World world) { - super(type, world); - throw new AssertionError(); - } - - @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) - private boolean enterSwimmingIfKnockedDown(boolean original) { - return original || this.is_KnockedDown(); - } - - @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) - private boolean dontHealIfKnockedDown(boolean original) { - return original && !this.is_KnockedDown(); - } - - @Inject(method = "checkFallFlying", at = @At("HEAD"), cancellable = true) - private void dontOpenElytraIfKnockedDown(CallbackInfoReturnable cir) { - if (this.is_KnockedDown()) { - cir.setReturnValue(false); - } - } - - @Inject(method = "initDataTracker", at = @At("TAIL")) - private void initCustomDataTracker(CallbackInfo ci) { - this.dataTracker.startTracking(KNOCKED_DOWN, false); - this.dataTracker.startTracking(IS_REVIVING, false); - this.dataTracker.startTracking(REVIVER_COUNT, 0); - this.dataTracker.startTracking(REVIVE_TIMER, KnockdownsCommon.REVIVE_WAIT_TIME); - } - - @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) - private void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { - this.set_KnockedDown(nbt.getBoolean("KnockedDown")); - this.set_ReviveTimer(nbt.getInt("ReviveTimer")); - this.set_KnockedAge(nbt.getInt("KnockedAge")); - } - - @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) - private void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { - nbt.putBoolean("KnockedDown", this.is_KnockedDown()); - nbt.putInt("ReviveTimer", this.get_ReviveTimer()); - nbt.putInt("KnockedAge", this.get_KnockedAge()); - } - - @Override - public boolean is_KnockedDown() { - return this.dataTracker.get(KNOCKED_DOWN); - } - - @Override - public void set_KnockedDown(boolean knockedDown) { - this.dataTracker.set(KNOCKED_DOWN, knockedDown); - } - - @Override - public boolean is_Reviving() { - return this.dataTracker.get(IS_REVIVING); - } - - @Override - public void set_Reviving(boolean reviving) { - this.dataTracker.set(IS_REVIVING, reviving); - } - - @Override - public int get_ReviverCount() { - return this.dataTracker.get(REVIVER_COUNT); - } - - @Override - public void set_ReviverCount(int reviverCount) { - this.dataTracker.set(REVIVER_COUNT, reviverCount); - } - - @Override - public int get_ReviveTimer() { - return this.dataTracker.get(REVIVE_TIMER); - } - - @Override - public void set_ReviveTimer(int reviveTimer) { - this.dataTracker.set(REVIVE_TIMER, reviveTimer); - } - - @Override - public int get_KnockedAge() { - return knocked_age; - } - - @Override - public void set_KnockedAge(int knockedAge) { - this.knocked_age = knockedAge; - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java deleted file mode 100644 index 8544efb..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java +++ /dev/null @@ -1,57 +0,0 @@ -package ru.octol1ttle.knockdowns.common.network; - -import dev.architectury.networking.NetworkChannel; -import dev.architectury.networking.NetworkManager; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.packet.Packet; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.Identifier; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; -import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; -import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket; -import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket; - -public class KnockdownsNetwork { - private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main")); - public static void registerPackets() { - CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply); - CHANNEL.register(RequestStartRevivingC2SPacket.class, RequestStartRevivingC2SPacket::encode, RequestStartRevivingC2SPacket::new, RequestStartRevivingC2SPacket::apply); - CHANNEL.register(StopRevivingC2SPacket.class, StopRevivingC2SPacket::encode, StopRevivingC2SPacket::new, StopRevivingC2SPacket::apply); - } - - public static void sendToServer(T message) { - if (CHANNEL.canServerReceive(message.getClass())) { - CHANNEL.sendToServer(message); - } - } - - public static void sendToPlayer(PlayerEntity player, T message) { - Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); - Class messageClass = message.getClass(); - - sendToPlayer(player, packet, messageClass); - } - - public static void sendToPlayer(PlayerEntity player, Packet packet, Class messageClass) { - if (!(player instanceof ServerPlayerEntity serverPlayer)) { - throw new IllegalArgumentException("Cannot send to client players"); - } - if (CHANNEL.canPlayerReceive(serverPlayer, messageClass)) { - serverPlayer.networkHandler.sendPacket(packet); - } - } - - public static void sendToWorld(ServerWorld world, T message) { - Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); - Class messageClass = message.getClass(); - - sendToWorld(world, packet, messageClass); - } - - private static void sendToWorld(ServerWorld world, Packet packet, Class messageClass) { - for (ServerPlayerEntity player : world.getPlayers()) { - sendToPlayer(player, packet, messageClass); - } - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java deleted file mode 100644 index f9288d7..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.octol1ttle.knockdowns.common.network.packets; - -import dev.architectury.networking.NetworkManager; -import java.util.function.Supplier; -import net.minecraft.network.PacketByteBuf; - -public abstract class KnockdownsPacket { - public KnockdownsPacket(/* args here */) { - // Message creation - } - - public abstract void encode(PacketByteBuf buf); - - public abstract void apply(Supplier contextSupplier); -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java deleted file mode 100644 index fa9f4fb..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.octol1ttle.knockdowns.common.network.packets; - -import dev.architectury.networking.NetworkManager; -import java.util.function.Supplier; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.math.Vec3d; -import ru.octol1ttle.knockdowns.common.KnockdownsClient; - -public class PlayKnockedDownSoundS2CPacket extends KnockdownsPacket { - private final double x; - private final double y; - private final double z; - - public PlayKnockedDownSoundS2CPacket(PacketByteBuf buf) { - this(buf.readDouble(), buf.readDouble(), buf.readDouble()); - } - - public PlayKnockedDownSoundS2CPacket(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeDouble(this.x); - buf.writeDouble(this.y); - buf.writeDouble(this.z); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> KnockdownsClient.playKnockedDownSound(new Vec3d(this.x, this.y, this.z))); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/RequestStartRevivingC2SPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/RequestStartRevivingC2SPacket.java deleted file mode 100644 index 31a7de0..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/RequestStartRevivingC2SPacket.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.octol1ttle.knockdowns.common.network.packets; - -import dev.architectury.networking.NetworkManager; -import java.util.UUID; -import java.util.function.Supplier; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.PacketByteBuf; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class RequestStartRevivingC2SPacket extends KnockdownsPacket { - private final UUID targetUuid; - - public RequestStartRevivingC2SPacket(PacketByteBuf buf) { - this(buf.readUuid()); - } - - public RequestStartRevivingC2SPacket(UUID targetUuid) { - this.targetUuid = targetUuid; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.targetUuid); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - PlayerEntity player = context.getPlayer(); - IKnockableDown playerKnockable = (IKnockableDown) player; - IKnockableDown targetKnockable = (IKnockableDown) player.getWorld().getPlayerByUuid(this.targetUuid); - if (!playerKnockable.is_Reviving() && targetKnockable != null && targetKnockable.is_KnockedDown()) { - playerKnockable.set_Reviving(true); - targetKnockable.set_ReviverCount(targetKnockable.get_ReviverCount() + 1); - } - }); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/StopRevivingC2SPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/StopRevivingC2SPacket.java deleted file mode 100644 index 260cc1e..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/StopRevivingC2SPacket.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.octol1ttle.knockdowns.common.network.packets; - -import dev.architectury.networking.NetworkManager; -import java.util.UUID; -import java.util.function.Supplier; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.PacketByteBuf; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class StopRevivingC2SPacket extends KnockdownsPacket { - private final UUID targetUuid; - - public StopRevivingC2SPacket(PacketByteBuf buf) { - this(buf.readUuid()); - } - - public StopRevivingC2SPacket(UUID targetUuid) { - this.targetUuid = targetUuid; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.targetUuid); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - PlayerEntity player = context.getPlayer(); - IKnockableDown playerKnockable = (IKnockableDown) player; - IKnockableDown targetKnockable = (IKnockableDown) player.getWorld().getPlayerByUuid(this.targetUuid); - if (playerKnockable.is_Reviving() && targetKnockable != null) { - playerKnockable.set_Reviving(false); - if (targetKnockable.is_KnockedDown()) { - targetKnockable.set_ReviverCount(targetKnockable.get_ReviverCount() - 1); - } - } - }); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java deleted file mode 100644 index a207925..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.octol1ttle.knockdowns.common.registries; - -import dev.architectury.registry.registries.DeferredRegister; -import dev.architectury.registry.registries.RegistrySupplier; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.sound.SoundEvent; -import net.minecraft.util.Identifier; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; - -public class KnockdownsSoundEvents { - private static final DeferredRegister SOUND_EVENTS = DeferredRegister.create(KnockdownsCommon.MOD_ID, RegistryKeys.SOUND_EVENT); - public static final RegistrySupplier KNOCKED_DOWN = SOUND_EVENTS.register(KnockdownsCommon.MOD_ID, - () -> SoundEvent.of(new Identifier(KnockdownsCommon.MOD_ID, "knocked_down"))); - - public static void register() { - SOUND_EVENTS.register(); - } -} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java deleted file mode 100644 index 7781165..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.octol1ttle.knockdowns.common.registries; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.sound.MovingSoundInstance; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.random.Random; - -public class KnockedDownSoundInstance extends MovingSoundInstance { - private final Vec3d pos; - - public KnockedDownSoundInstance(SoundEvent sound, Vec3d pos) { - super(sound, SoundCategory.MASTER, Random.create(0L)); - this.pos = pos; - this.relative = true; - } - - @Override - public void tick() { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) { - throw new IllegalStateException(); - } - Vec3d vec = pos.subtract(player.getPos()).normalize(); - this.x = vec.x; - this.y = vec.y; - this.z = vec.z; - } -} diff --git a/common/src/main/resources/architectury.common.json b/common/src/main/resources/architectury.common.json deleted file mode 100644 index 2fc0d72..0000000 --- a/common/src/main/resources/architectury.common.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "accessWidener": "knockdowns.accesswidener" -} \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg b/common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg deleted file mode 100644 index ccb17cc..0000000 Binary files a/common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg and /dev/null differ diff --git a/common/src/main/resources/knockdowns-common.mixins.json b/common/src/main/resources/knockdowns-common.mixins.json deleted file mode 100644 index b157b56..0000000 --- a/common/src/main/resources/knockdowns-common.mixins.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "required": true, - "package": "ru.octol1ttle.knockdowns.common.mixin", - "compatibilityLevel": "JAVA_17", - "minVersion": "0.8", - "client": [ - "client.ClientPlayerEntityMixin" - ], - "mixins": [ - "LivingEntityMixin", - "MobEntityMixin", - "PlayerEntityMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file diff --git a/common/src/main/resources/knockdowns.accesswidener b/common/src/main/resources/knockdowns.accesswidener deleted file mode 100644 index 13268c3..0000000 --- a/common/src/main/resources/knockdowns.accesswidener +++ /dev/null @@ -1 +0,0 @@ -accessWidener v2 named \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle deleted file mode 100644 index 5ca331b..0000000 --- a/fabric/build.gradle +++ /dev/null @@ -1,77 +0,0 @@ -plugins { - id "com.github.johnrengelman.shadow" version "7.1.2" -} - -architectury { - platformSetupLoomIde() - fabric() -} - -loom { - accessWidenerPath = project(":common").loom.accessWidenerPath -} - -configurations { - common - shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. - compileClasspath.extendsFrom common - runtimeClasspath.extendsFrom common - developmentFabric.extendsFrom common -} - -dependencies { - modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" - modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury-fabric:${rootProject.architectury_version}" - - common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false } -} - -processResources { - inputs.property "version", project.version - - filesMatching("fabric.mod.json") { - expand "version": project.version - } -} - -shadowJar { - exclude "architectury.common.json" - - configurations = [project.configurations.shadowCommon] - archiveClassifier = "dev-shadow" -} - -remapJar { - injectAccessWidener = true - input.set shadowJar.archiveFile - dependsOn shadowJar -} - -sourcesJar { - def commonSources = project(":common").sourcesJar - dependsOn commonSources - from commonSources.archiveFile.map { zipTree(it) } -} - -components.java { - withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { - skip() - } -} - -publishing { - publications { - mavenFabric(MavenPublication) { - artifactId = rootProject.archives_base_name + "-" + project.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. - } -} diff --git a/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabric.java b/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabric.java deleted file mode 100644 index 6a6fa40..0000000 --- a/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabric.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.octol1ttle.knockdowns.fabric; - -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; -import net.fabricmc.api.ModInitializer; - -public class KnockdownsFabric implements ModInitializer { - @Override - public void onInitialize() { - KnockdownsCommon.init(); - } -} diff --git a/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java b/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java deleted file mode 100644 index 2a651c4..0000000 --- a/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.octol1ttle.knockdowns.fabric; - -import net.fabricmc.api.ClientModInitializer; -import ru.octol1ttle.knockdowns.common.KnockdownsClient; - -public class KnockdownsFabricClient implements ClientModInitializer { - @Override - public void onInitializeClient() { - KnockdownsClient.init(); - } -} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json deleted file mode 100644 index 5e30f2e..0000000 --- a/fabric/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "schemaVersion": 1, - "id": "knockdowns", - "version": "${version}", - "name": "Knockdowns", - "description": "DBNO mechanic from Fortnite, ported to Minecraft", - "authors": [ - "Octol1ttle" - ], - "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" - }, - "license": "ARR", - "icon": "assets/knockdowns/icon.png", - "environment": "*", - "entrypoints": { - "main": [ - "ru.octol1ttle.knockdowns.fabric.KnockdownsFabric" - ], - "client": [ - "ru.octol1ttle.knockdowns.fabric.KnockdownsFabricClient" - ] - }, - "mixins": [ - "knockdowns.mixins.json", - "knockdowns-common.mixins.json" - ], - "depends": { - "fabric": "*", - "minecraft": ">=1.20.1", - "architectury": ">=9.1.12" - } -} \ No newline at end of file diff --git a/fabric/src/main/resources/knockdowns.mixins.json b/fabric/src/main/resources/knockdowns.mixins.json deleted file mode 100644 index 76a92ed..0000000 --- a/fabric/src/main/resources/knockdowns.mixins.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "required": true, - "package": "ru.octol1ttle.knockdowns.fabric.mixin", - "compatibilityLevel": "JAVA_17", - "minVersion": "0.8", - "client": [ - ], - "mixins": [ - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle deleted file mode 100644 index 490b2f1..0000000 --- a/forge/build.gradle +++ /dev/null @@ -1,86 +0,0 @@ -plugins { - id "com.github.johnrengelman.shadow" version "7.1.2" -} - -architectury { - platformSetupLoomIde() - forge() -} - -loom { - accessWidenerPath = project(":common").loom.accessWidenerPath - - forge { - convertAccessWideners = true - extraAccessWideners.add loom.accessWidenerPath.get().asFile.name - - mixinConfig "knockdowns-common.mixins.json" - mixinConfig "knockdowns.mixins.json" - } -} - -configurations { - common - shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. - compileClasspath.extendsFrom common - runtimeClasspath.extendsFrom common - developmentForge.extendsFrom common -} - -dependencies { - forge "net.minecraftforge:forge:${rootProject.forge_version}" - // Remove the next line if you don't want to depend on the API - modApi "dev.architectury:architectury-forge:${rootProject.architectury_version}" - - common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } - - implementation(include("io.github.llamalad7:mixinextras-forge:0.3.3")) -} - -processResources { - inputs.property "version", project.version - - filesMatching("META-INF/mods.toml") { - expand "version": project.version - } -} - -shadowJar { - exclude "fabric.mod.json" - exclude "architectury.common.json" - - configurations = [project.configurations.shadowCommon] - archiveClassifier = "dev-shadow" -} - -remapJar { - input.set shadowJar.archiveFile - dependsOn shadowJar -} - -sourcesJar { - def commonSources = project(":common").sourcesJar - dependsOn commonSources - from commonSources.archiveFile.map { zipTree(it) } -} - -components.java { - withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { - skip() - } -} - -publishing { - publications { - mavenForge(MavenPublication) { - artifactId = rootProject.archives_base_name + "-" + project.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. - } -} diff --git a/forge/gradle.properties b/forge/gradle.properties deleted file mode 100644 index 32f842a..0000000 --- a/forge/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=forge \ No newline at end of file diff --git a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java deleted file mode 100644 index 83a53a8..0000000 --- a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.octol1ttle.knockdowns.forge; - -import dev.architectury.platform.forge.EventBuses; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import ru.octol1ttle.knockdowns.common.KnockdownsClient; -import ru.octol1ttle.knockdowns.common.KnockdownsCommon; - -@SuppressWarnings("unused") -@Mod(KnockdownsCommon.MOD_ID) -public class KnockdownsForge { - public KnockdownsForge() { - // Submit our event bus to let architectury register our content on the right time - IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); - EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, modEventBus); - modEventBus.addListener(this::onInitializeClient); - - KnockdownsCommon.init(); - } - - public void onInitializeClient(FMLClientSetupEvent event) { - KnockdownsClient.init(); - } -} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml deleted file mode 100644 index 9e1ece6..0000000 --- a/forge/src/main/resources/META-INF/mods.toml +++ /dev/null @@ -1,35 +0,0 @@ -modLoader = "javafml" -loaderVersion = "[47,)" -#issueTrackerURL = "" -license = "ARR" - -[[mods]] -modId = "knockdowns" -version = "${version}" -displayName = "Knockdowns" -authors = "Octol1ttle" -description = ''' -DBNO mechanic from Fortnite, ported to Minecraft -''' -#logoFile = "" - -[[dependencies.knockdowns]] -modId = "forge" -mandatory = true -versionRange = "[47,)" -ordering = "NONE" -side = "BOTH" - -[[dependencies.knockdowns]] -modId = "minecraft" -mandatory = true -versionRange = "[1.20.1,)" -ordering = "NONE" -side = "BOTH" - -[[dependencies.knockdowns]] -modId = "architectury" -mandatory = true -versionRange = "[9.1.12,)" -ordering = "AFTER" -side = "BOTH" \ No newline at end of file diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta deleted file mode 100644 index edf178a..0000000 --- a/forge/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "Knockdowns", - "pack_format": 15 - } -} diff --git a/gradle.properties b/gradle.properties index f07c82c..f5d3061 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,17 @@ -org.gradle.jvmargs=-Xmx2048M +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true -minecraft_version=1.20.1 -enabled_platforms=fabric,forge +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.20.2 +yarn_mappings=1.20.2+build.4 +loader_version=0.14.24 -archives_base_name=knockdowns -mod_version=2.1.0 +# Mod Properties +mod_version=1.1.1 maven_group=ru.octol1ttle.knockdowns +archives_base_name=knockdowns -architectury_version=9.1.12 -fabric_api_version=0.90.4+1.20.1 - -fabric_loader_version=0.15.5 -forge_version=1.20.1-47.2.0 - - - +# Dependencies +fabric_version=0.90.4+1.20.2 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 744c64d..ac72c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d4..0adc8e1 100644 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/settings.gradle b/settings.gradle index 163e9f2..75c4d72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,14 +1,10 @@ pluginManagement { - repositories { - maven { url "https://maven.fabricmc.net/" } - maven { url "https://maven.architectury.dev/" } - maven { url "https://maven.minecraftforge.net/" } - gradlePluginPortal() - } -} - -include("common") -include("fabric") -include("forge") - -rootProject.name = "knockdowns-modern" + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + mavenCentral() + gradlePluginPortal() + } +} \ No newline at end of file diff --git a/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java b/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java new file mode 100644 index 0000000..25ff0de --- /dev/null +++ b/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java @@ -0,0 +1,166 @@ +package ru.octol1ttle.knockdowns; + +import java.util.UUID; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.fabricmc.fabric.api.event.player.UseEntityCallback; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.SharedConstants; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.hit.HitResult; +import ru.octol1ttle.knockdowns.api.IKnockableDown; +import ru.octol1ttle.knockdowns.network.KnockdownsNetworkingConstants; + +public class KnockdownsClient implements ClientModInitializer { + private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; + private static IKnockableDown reviving = null; + private static int revivalTimer = -1; + + @Override + public void onInitializeClient() { + registerOnReceiveKnockedDown(); + registerOnReceiveReviving(); + registerOnEntityLoad(); + registerOnEntityUse(); + registerOnStartWorldTick(); + registerOnHudRender(); + } + + private static void registerOnReceiveKnockedDown() { + ClientPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, (client, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + boolean knockedDown = buf.readBoolean(); + + client.execute(() -> { + IKnockableDown knockableDown = (IKnockableDown) handler.getWorld().getPlayerByUuid(uuid); + if (knockableDown != null) { + knockableDown.knockdowns$setKnockedDown(knockedDown); + } + }); + }); + } + + private static void registerOnReceiveReviving() { + ClientPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, (client, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + boolean beingRevived = buf.readBoolean(); + + client.execute(() -> { + IKnockableDown knockableDown = (IKnockableDown) handler.getWorld().getPlayerByUuid(uuid); + if (knockableDown != null) { + knockableDown.knockdowns$setBeingRevived(beingRevived); + + if (client.player != null && uuid.equals(client.player.getUuid())) { + if (knockableDown.knockdowns$isBeingRevived()) { + revivalTimer = REVIVAL_WAIT_TIME; + } else { + revivalTimer = -1; + } + } + + if (reviving != null && knockableDown.knockdowns$getUuid().equals(reviving.knockdowns$getUuid()) + && !knockableDown.knockdowns$isBeingRevived()) { + reviving = null; + revivalTimer = -1; + } + } + }); + }); + } + + private static void registerOnEntityLoad() { + ClientEntityEvents.ENTITY_LOAD.register((entity, world) -> { + if (entity instanceof IKnockableDown) { + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeUuid(entity.getUuid()); + + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, PacketByteBufs.copy(buf)); + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_REVIVING, buf); + } + }); + } + + private static void registerOnEntityUse() { + UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { + if (!(entity instanceof IKnockableDown knockableDown) || !knockableDown.knockdowns$isKnockedDown() + || knockableDown.knockdowns$isBeingRevived()) { + return ActionResult.PASS; + } + if (((IKnockableDown)player).knockdowns$isKnockedDown()) { + return ActionResult.FAIL; + } + + knockableDown.knockdowns$setBeingRevived(true); + + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeUuid(entity.getUuid()); + buf.writeBoolean(true); + + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, buf); + + reviving = knockableDown; + revivalTimer = REVIVAL_WAIT_TIME; + + return ActionResult.SUCCESS; + }); + } + + private static void registerOnStartWorldTick() { + ClientTickEvents.START_WORLD_TICK.register(world -> { + boolean revived = false; + revivalTimer--; + if (revivalTimer <= 0) { + revivalTimer = -1; + revived = true; + } + + if (reviving == null) { + return; + } + + HitResult crosshairTarget = MinecraftClient.getInstance().crosshairTarget; + if (revived || crosshairTarget == null || crosshairTarget.getType() != HitResult.Type.ENTITY + || !((EntityHitResult) crosshairTarget).getEntity().getUuid().equals(reviving.knockdowns$getUuid())) { + reviving.knockdowns$setBeingRevived(false); + + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeUuid(reviving.knockdowns$getUuid()); + buf.writeBoolean(false); + + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, buf); + if (revived) { + reviving.knockdowns$setKnockedDown(false); + + PacketByteBuf revivedBuf = PacketByteBufs.create(); + revivedBuf.writeUuid(reviving.knockdowns$getUuid()); + + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVED, revivedBuf); + } + + reviving = null; + revivalTimer = -1; + } + }); + } + + private static void registerOnHudRender() { + HudRenderCallback.EVENT.register((drawContext, tickDelta) -> { + if (revivalTimer == -1) { + return; + } + + TextRenderer renderer = MinecraftClient.getInstance().textRenderer; + String text = String.format("%.1f", revivalTimer / (float) SharedConstants.TICKS_PER_SECOND); + int x = (drawContext.getScaledWindowWidth() - renderer.getWidth(text)) / 2; + + drawContext.drawTextWithShadow(renderer, text, x, drawContext.getScaledWindowHeight() / 2 + 15, 0xFFFFFF); + }); + } +} \ No newline at end of file diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java b/src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java similarity index 61% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java rename to src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java index 440c2ef..aed34f1 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java +++ b/src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java @@ -1,15 +1,15 @@ -package ru.octol1ttle.knockdowns.common.mixin.client; +package ru.octol1ttle.knockdowns.mixin.client; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import net.minecraft.client.network.ClientPlayerEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; +import ru.octol1ttle.knockdowns.api.IKnockableDown; @Mixin(ClientPlayerEntity.class) -public abstract class ClientPlayerEntityMixin implements IKnockableDown { +public abstract class ClientPlayerEntityMixin { @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) private boolean shouldSlowDown(boolean original) { - return original || this.is_KnockedDown(); + return original || ((IKnockableDown) this).knockdowns$isKnockedDown(); } -} \ No newline at end of file +} diff --git a/forge/src/main/resources/knockdowns.mixins.json b/src/client/resources/knockdowns.client.mixins.json similarity index 54% rename from forge/src/main/resources/knockdowns.mixins.json rename to src/client/resources/knockdowns.client.mixins.json index bc08964..ccd33aa 100644 --- a/forge/src/main/resources/knockdowns.mixins.json +++ b/src/client/resources/knockdowns.client.mixins.json @@ -1,13 +1,11 @@ { "required": true, - "package": "ru.octol1ttle.knockdowns.forge.mixin", + "package": "ru.octol1ttle.knockdowns.mixin.client", "compatibilityLevel": "JAVA_17", - "minVersion": "0.8", "client": [ - ], - "mixins": [ + "ClientPlayerEntityMixin" ], "injectors": { "defaultRequire": 1 - } + } } \ No newline at end of file diff --git a/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java b/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java new file mode 100644 index 0000000..a59282c --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java @@ -0,0 +1,219 @@ +package ru.octol1ttle.knockdowns; + +import java.util.UUID; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; +import net.fabricmc.fabric.api.event.player.AttackBlockCallback; +import net.fabricmc.fabric.api.event.player.AttackEntityCallback; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.TypedActionResult; +import net.minecraft.world.GameRules; +import ru.octol1ttle.knockdowns.api.IKnockableDown; +import ru.octol1ttle.knockdowns.network.KnockdownsNetworkingConstants; + +public class Knockdowns implements ModInitializer { + public static final String MOD_ID = "knockdowns"; + private static final Identifier KNOCKED_DOWN_ID = new Identifier("knockdowns:knocked_down"); + private static final SoundEvent KNOCKED_DOWN = SoundEvent.of(KNOCKED_DOWN_ID); + + @Override + public void onInitialize() { + registerSoundEvents(); + registerOnDeath(); + registerOnRequestKnockedDown(); + registerOnRequestReviving(); + registerOnReceiveReviving(); + registerOnReceiveRevived(); + registerOnPlayerInteractions(); + } + + private static void registerSoundEvents() { + Registry.register(Registries.SOUND_EVENT, KNOCKED_DOWN_ID, KNOCKED_DOWN); + } + + private static void registerOnDeath() { + ServerLivingEntityEvents.ALLOW_DEATH.register((entity, damageSource, damageAmount) -> { + if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) { + return true; + } + + ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; + // TODO: timer + if (!serverPlayer.getWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) { + serverPlayer.getInventory().dropAll(); + } + entity.setHealth(1.0f); + entity.setInvulnerable(true); + entity.setGlowing(true); + entity.setAir(entity.getMaxAir()); + entity.extinguish(); + entity.setFrozenTicks(0); + entity.setOnFire(false); + entity.clearStatusEffects(); + + knockableDown.knockdowns$setKnockedDown(true); + + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeUuid(entity.getUuid()); + buf.writeBoolean(true); + + ServerPlayNetworking.send(serverPlayer, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, buf); + for (ServerPlayerEntity player : PlayerLookup.tracking(entity)) { + ServerPlayNetworking.send(player, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, buf); + } + + for (ServerPlayerEntity player : serverPlayer.getServerWorld().getPlayers()) { + player.networkHandler.sendPacket(new PlaySoundS2CPacket(Registries.SOUND_EVENT.getEntry(KNOCKED_DOWN), SoundCategory.PLAYERS, + player.getX(), player.getY(), player.getZ(), 1.0f, 1.0f, 0L)); + } + + TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); + Text replaced = Text.translatable(content.getKey().replace("death.", "knockdown."), content.getArgs()); + MinecraftServer server = serverPlayer.getServer(); + if (server != null) { + server.getPlayerManager().broadcast(replaced, false); + } + + return false; + }); + } + + private static void registerOnRequestKnockedDown() { + ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, (server, player, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + + server.execute(() -> { + IKnockableDown knockableDown = (IKnockableDown) player.getWorld().getPlayerByUuid(uuid); + if (knockableDown == null) { + return; + } + + PacketByteBuf responseBuf = PacketByteBufs.create(); + responseBuf.writeUuid(uuid); + responseBuf.writeBoolean(knockableDown.knockdowns$isKnockedDown()); + + responseSender.sendPacket(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, responseBuf); + }); + }); + } + + private static void registerOnRequestReviving() { + ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_REVIVING, (server, player, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + + server.execute(() -> { + IKnockableDown knockableDown = (IKnockableDown) player.getWorld().getPlayerByUuid(uuid); + if (knockableDown == null) { + return; + } + + PacketByteBuf responseBuf = PacketByteBufs.create(); + responseBuf.writeUuid(uuid); + responseBuf.writeBoolean(knockableDown.knockdowns$isBeingRevived()); + + responseSender.sendPacket(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, responseBuf); + }); + }); + } + + private static void registerOnReceiveReviving() { + ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, (server, player, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + boolean beingRevived = buf.readBoolean(); + + // TODO: revival by multiple players + server.execute(() -> { + PlayerEntity reviving = player.getWorld().getPlayerByUuid(uuid); + IKnockableDown knockableDown = (IKnockableDown) reviving; + if (knockableDown == null) { + return; + } + + knockableDown.knockdowns$setBeingRevived(beingRevived); + + PacketByteBuf broadcastBuf = PacketByteBufs.create(); + broadcastBuf.writeUuid(uuid); + broadcastBuf.writeBoolean(beingRevived); + + ServerPlayNetworking.send((ServerPlayerEntity) reviving, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, broadcastBuf); + for (ServerPlayerEntity entity : PlayerLookup.tracking(reviving)) { + ServerPlayNetworking.send(entity, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, broadcastBuf); + } + }); + }); + } + + private static void registerOnReceiveRevived() { + ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVED, (server, player, handler, buf, responseSender) -> { + UUID uuid = buf.readUuid(); + + server.execute(() -> { + PlayerEntity reviving = player.getWorld().getPlayerByUuid(uuid); + IKnockableDown knockableDown = (IKnockableDown) reviving; + if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) { + return; + } + + reviving.setInvulnerable(false); + reviving.setGlowing(false); + reviving.setHealth(reviving.getHealth() + 5.0f); + + knockableDown.knockdowns$setKnockedDown(false); + + PacketByteBuf sendBuf = PacketByteBufs.create(); + sendBuf.writeUuid(reviving.getUuid()); + sendBuf.writeBoolean(false); + + ServerPlayNetworking.send((ServerPlayerEntity) reviving, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, sendBuf); + for (ServerPlayerEntity entity : PlayerLookup.tracking(reviving)) { + ServerPlayNetworking.send(entity, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, sendBuf); + } + }); + }); + } + + private static void registerOnPlayerInteractions() { + AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return ActionResult.FAIL; + } + return ActionResult.PASS; + }); + AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return ActionResult.FAIL; + } + return ActionResult.PASS; + }); + UseItemCallback.EVENT.register((player, world, hand) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return TypedActionResult.fail(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); + } + return TypedActionResult.pass(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); + }); + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return ActionResult.FAIL; + } + return ActionResult.PASS; + }); + } +} \ No newline at end of file diff --git a/src/main/java/ru/octol1ttle/knockdowns/api/IKnockableDown.java b/src/main/java/ru/octol1ttle/knockdowns/api/IKnockableDown.java new file mode 100644 index 0000000..b06d4a4 --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/api/IKnockableDown.java @@ -0,0 +1,15 @@ +package ru.octol1ttle.knockdowns.api; + +import java.util.UUID; + +public interface IKnockableDown { + boolean knockdowns$isKnockedDown(); + + void knockdowns$setKnockedDown(boolean knockedDown); + + boolean knockdowns$isBeingRevived(); + + void knockdowns$setBeingRevived(boolean beingRevived); + + UUID knockdowns$getUuid(); +} diff --git a/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java b/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java new file mode 100644 index 0000000..3852bee --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java @@ -0,0 +1,71 @@ +package ru.octol1ttle.knockdowns.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import java.util.UUID; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import ru.octol1ttle.knockdowns.api.IKnockableDown; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin implements IKnockableDown { + @Unique + private boolean knockedDown; + @Unique + private boolean beingRevived; + + @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) + private boolean enterSwimmingIfKnockedDown(boolean original) { + PlayerEntity player = (PlayerEntity)(Object)this; + if (!(player instanceof IKnockableDown knockableDown)) { + throw new IllegalStateException(); + } + + return original || knockableDown.knockdowns$isKnockedDown(); + } + + @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) + private boolean dontHealIfKnockedDown(boolean original) { + return original && !this.knockdowns$isKnockedDown(); + } + + @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) + public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { + this.knockedDown = nbt.getBoolean("KnockedDown"); + } + + @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) + public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { + nbt.putBoolean("KnockedDown", this.knockedDown); + } + + @Override + public boolean knockdowns$isKnockedDown() { + return knockedDown; + } + + @Override + public void knockdowns$setKnockedDown(boolean knockedDown) { + this.knockedDown = knockedDown; + } + + @Override + public boolean knockdowns$isBeingRevived() { + return beingRevived; + } + + @Override + public void knockdowns$setBeingRevived(boolean beingRevived) { + this.beingRevived = beingRevived; + } + + @Override + public UUID knockdowns$getUuid() { + return ((PlayerEntity)(Object)this).getUuid(); + } +} diff --git a/src/main/java/ru/octol1ttle/knockdowns/network/KnockdownsNetworkingConstants.java b/src/main/java/ru/octol1ttle/knockdowns/network/KnockdownsNetworkingConstants.java new file mode 100644 index 0000000..a618797 --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/network/KnockdownsNetworkingConstants.java @@ -0,0 +1,13 @@ +package ru.octol1ttle.knockdowns.network; + +import net.minecraft.util.Identifier; +import ru.octol1ttle.knockdowns.Knockdowns; + +public class KnockdownsNetworkingConstants { + public static final Identifier S2C_SEND_PLAYER_KNOCKED_DOWN = new Identifier(Knockdowns.MOD_ID, "s2c_send_player_knocked_down"); + public static final Identifier S2C_SEND_PLAYER_REVIVING = new Identifier(Knockdowns.MOD_ID, "s2c_send_player_reviving"); + public static final Identifier C2S_REQUEST_PLAYER_KNOCKED_DOWN = new Identifier(Knockdowns.MOD_ID, "c2s_request_player_knocked_down"); + public static final Identifier C2S_REQUEST_PLAYER_REVIVING = new Identifier(Knockdowns.MOD_ID, "c2s_request_player_reviving"); + public static final Identifier C2S_SEND_PLAYER_REVIVING = new Identifier(Knockdowns.MOD_ID, "c2s_send_player_reviving"); + public static final Identifier C2S_SEND_PLAYER_REVIVED = new Identifier(Knockdowns.MOD_ID, "c2s_send_player_revived"); +} diff --git a/src/main/resources/assets/knockdowns/icon.png b/src/main/resources/assets/knockdowns/icon.png new file mode 100644 index 0000000..047b91f Binary files /dev/null and b/src/main/resources/assets/knockdowns/icon.png differ diff --git a/common/src/main/resources/assets/knockdowns/lang/en_us.json b/src/main/resources/assets/knockdowns/lang/en_us.json similarity index 99% rename from common/src/main/resources/assets/knockdowns/lang/en_us.json rename to src/main/resources/assets/knockdowns/lang/en_us.json index 57cacc4..256bd9b 100644 --- a/common/src/main/resources/assets/knockdowns/lang/en_us.json +++ b/src/main/resources/assets/knockdowns/lang/en_us.json @@ -1,6 +1,4 @@ { - "subtitles.knockdowns.knocked_down": "Player knocked down", - "knockdown.attack.anvil": "%1$s was knocked down by a falling anvil", "knockdown.attack.anvil.player": "%1$s was knocked down by a falling anvil while fighting %2$s", "knockdown.attack.arrow": "%1$s was knocked down due to an arrow fired by %2$s", @@ -99,5 +97,6 @@ "knockdown.fell.assist.item": "%1$s was doomed to get knocked down by %2$s using %3$s", "knockdown.fell.finish": "%1$s fell too far and was knocked down by %2$s", "knockdown.fell.finish.item": "%1$s fell too far and was knocked down by %2$s using %3$s", - "knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall" + "knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall", + "subtitles.knockdowns.knocked_down": "Player knocked down" } \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/lang/ru_ru.json b/src/main/resources/assets/knockdowns/lang/ru_ru.json similarity index 99% rename from common/src/main/resources/assets/knockdowns/lang/ru_ru.json rename to src/main/resources/assets/knockdowns/lang/ru_ru.json index 9fd0739..5cdd965 100644 --- a/common/src/main/resources/assets/knockdowns/lang/ru_ru.json +++ b/src/main/resources/assets/knockdowns/lang/ru_ru.json @@ -1,6 +1,4 @@ { - "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен", - "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", @@ -99,5 +97,6 @@ "knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s", "knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s", "knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s", - "knockdown.fell.killer": "%1$s был тяжело ранен падением" + "knockdown.fell.killer": "%1$s был тяжело ранен падением", + "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен" } \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/sounds.json b/src/main/resources/assets/knockdowns/sounds.json similarity index 100% rename from common/src/main/resources/assets/knockdowns/sounds.json rename to src/main/resources/assets/knockdowns/sounds.json diff --git a/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg b/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg new file mode 100644 index 0000000..a01bae8 Binary files /dev/null and b/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..c4330c1 --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": 1, + "id": "knockdowns", + "version": "${version}", + "name": "Knockdowns", + "description": "man hardcode is difficult", + "authors": [ + "Octol1ttle" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + "license": "ARR", + "icon": "assets/knockdowns/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "ru.octol1ttle.knockdowns.Knockdowns" + ], + "client": [ + "ru.octol1ttle.knockdowns.KnockdownsClient" + ] + }, + "mixins": [ + "knockdowns.mixins.json", + { + "config": "knockdowns.client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=0.14.23", + "minecraft": "~1.20.2", + "java": ">=17", + "fabric-api": "*" + } +} \ No newline at end of file diff --git a/src/main/resources/knockdowns.mixins.json b/src/main/resources/knockdowns.mixins.json new file mode 100644 index 0000000..81139b2 --- /dev/null +++ b/src/main/resources/knockdowns.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "ru.octol1ttle.knockdowns.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "PlayerEntityMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file