From 5c1f88b2902a19bb6a16af60c05de80670f5cc2b Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Mon, 30 Oct 2023 12:54:40 +0500 Subject: [PATCH 01/13] Initial commit --- .github/workflows/build.yml | 40 +++ .gitignore | 40 +++ LICENSE | 121 +++++++++ build.gradle | 88 +++++++ gradle.properties | 17 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ settings.gradle | 10 + .../knockdowns/KnockdownsClient.java | 166 ++++++++++++ .../mixin/client/ClientPlayerEntityMixin.java | 15 ++ .../resources/knockdowns.client.mixins.json | 11 + .../ru/octol1ttle/knockdowns/Knockdowns.java | 215 +++++++++++++++ .../knockdowns/api/IKnockableDown.java | 15 ++ .../knockdowns/mixin/PlayerEntityMixin.java | 60 +++++ .../KnockdownsNetworkingConstants.java | 13 + src/main/resources/assets/knockdowns/icon.png | Bin 0 -> 453 bytes .../assets/knockdowns/lang/en_us.json | 102 +++++++ .../assets/knockdowns/lang/ru_ru.json | 102 +++++++ .../resources/assets/knockdowns/sounds.json | 10 + .../assets/knockdowns/sounds/knocked_down.ogg | Bin 0 -> 45653 bytes src/main/resources/fabric.mod.json | 38 +++ src/main/resources/knockdowns.mixins.json | 11 + 24 files changed, 1422 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java create mode 100644 src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java create mode 100644 src/client/resources/knockdowns.client.mixins.json create mode 100644 src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java create mode 100644 src/main/java/ru/octol1ttle/knockdowns/api/IKnockableDown.java create mode 100644 src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java create mode 100644 src/main/java/ru/octol1ttle/knockdowns/network/KnockdownsNetworkingConstants.java create mode 100644 src/main/resources/assets/knockdowns/icon.png create mode 100644 src/main/resources/assets/knockdowns/lang/en_us.json create mode 100644 src/main/resources/assets/knockdowns/lang/ru_ru.json create mode 100644 src/main/resources/assets/knockdowns/sounds.json create mode 100644 src/main/resources/assets/knockdowns/sounds/knocked_down.ogg create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/knockdowns.mixins.json 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 new file mode 100644 index 0000000..c476faf --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# gradle + +.gradle/ +build/ +out/ +classes/ + +# eclipse + +*.launch + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# 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 new file mode 100644 index 0000000..ac4bfa3 --- /dev/null +++ b/build.gradle @@ -0,0 +1,88 @@ +plugins { + id 'fabric-loom' version '1.4-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name +} + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +loom { + splitEnvironmentSourceSets() + + mods { + "knockdowns" { + sourceSet sourceSets.main + sourceSet sourceSets.client + } + } + +} + +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/gradle.properties b/gradle.properties new file mode 100644 index 0000000..0ddbc52 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# 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 + +# Mod Properties +mod_version=1.0.0 +maven_group=ru.octol1ttle.knockdowns +archives_base_name=knockdowns + +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..7f93135c49b765f8051ef9d0a6055ff8e46073d8 GIT binary patch literal 63721 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"gpYvgVMVsULOY3=lDL#2r|Jj~bidD9% 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 z '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# 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 + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + 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 +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..75c4d72 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + 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..889224a --- /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, 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/src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java b/src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java new file mode 100644 index 0000000..aed34f1 --- /dev/null +++ b/src/client/java/ru/octol1ttle/knockdowns/mixin/client/ClientPlayerEntityMixin.java @@ -0,0 +1,15 @@ +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.api.IKnockableDown; + +@Mixin(ClientPlayerEntity.class) +public abstract class ClientPlayerEntityMixin { + @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) + private boolean shouldSlowDown(boolean original) { + return original || ((IKnockableDown) this).knockdowns$isKnockedDown(); + } +} diff --git a/src/client/resources/knockdowns.client.mixins.json b/src/client/resources/knockdowns.client.mixins.json new file mode 100644 index 0000000..ccd33aa --- /dev/null +++ b/src/client/resources/knockdowns.client.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "ru.octol1ttle.knockdowns.mixin.client", + "compatibilityLevel": "JAVA_17", + "client": [ + "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..39c228f --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java @@ -0,0 +1,215 @@ +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 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; + } + + // TODO: timer + entity.setHealth(1.0f); + entity.setInvulnerable(true); + entity.setGlowing(true); + entity.setAir(entity.getMaxAir()); + entity.setFireTicks(0); + entity.setFrozenTicks(0); + entity.clearStatusEffects(); + + knockableDown.knockdowns$setKnockedDown(true); + + PacketByteBuf buf = PacketByteBufs.create(); + buf.writeUuid(entity.getUuid()); + buf.writeBoolean(true); + + ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; + + 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..28a70a1 --- /dev/null +++ b/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java @@ -0,0 +1,60 @@ +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 org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import ru.octol1ttle.knockdowns.api.IKnockableDown; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin implements IKnockableDown { + @Unique + private boolean knockedDown; + @Unique + private boolean beingRevived; + @Unique + private int reviveAt; + + @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) + private boolean isKnockedDown(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 canFoodHeal(boolean original) { + return original && !this.knockdowns$isKnockedDown(); + } + + @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 0000000000000000000000000000000000000000..047b91f2347de5cf95f23284476fddbe21ba23fe GIT binary patch literal 453 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykgMrH;EhMylkcQ7z8um$*pxH2#>{Qv)d>CO3f z85kI=JY5_^JdR(zyfHV%kcTaSi%Ti|TJ8eTpj}+~BK-x@9*$y@%`bB3L~{I!eX+NC zlHNB-j&%mwkF+BM{`*I?$?=)ih{~Me5!39upp((GtLc_m^NimC=^Eh|csG&)*Qrd==Y&n7UmRi%(N^JM_;TeSC#QK=Zw9+^jOG>IE8SNYJSk~k z5Y{~5u4B_KzVpl#mXe%1nZx7v{xLh$%INe`pm#$_heN5*Wsb9+XN1@{9Ex1z(lBBD zfu+n0IR}_qbms6I2yJTN%V+rDUK=z&u7V-ReL3?h|HUpQ3^T2mq-JhoG}|zP;mL%$ z02u{`Lo>b};n%wGII;gbLmq2&gV6#PbAj{<#d(dv5emOqPjS5xTT#|n#+=r;uYW?G zg5HF1jT-(wi;N%QEGzc2Uws>3-(cqWm+OF@xgGO=#y@{$DjL3dZ1~U27a%9M#WB_~ ze1i4@=WpBrGaNhA7u@UT+|V$WVdeG@FLI@{%+7spEU!B``w&Z{=X?eR1_n=8KbLh* G2~7YM=(B48 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/knockdowns/lang/en_us.json b/src/main/resources/assets/knockdowns/lang/en_us.json new file mode 100644 index 0000000..256bd9b --- /dev/null +++ b/src/main/resources/assets/knockdowns/lang/en_us.json @@ -0,0 +1,102 @@ +{ + "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", + "knockdown.attack.arrow.item": "%1$s was knocked down due to an arrow fired by %2$s using %3$s", + "knockdown.attack.badRespawnPoint.link": "Intentional Game Design", + "knockdown.attack.badRespawnPoint.message": "%1$s was knocked down by %2$s", + "knockdown.attack.cactus": "%1$s was knocked down by a cactus", + "knockdown.attack.cactus.player": "%1$s was knocked down into a cactus while trying to escape %2$s", + "knockdown.attack.cramming": "%1$s was knocked down by squishing", + "knockdown.attack.cramming.player": "%1$s was knocked down due to being squashed by %2$s", + "knockdown.attack.dragonBreath": "%1$s was knocked down by dragon's breath", + "knockdown.attack.dragonBreath.player": "%1$s was knocked down by dragon's breath by %2$s", + "knockdown.attack.drown": "%1$s was knocked down by drowning", + "knockdown.attack.drown.player": "%1$s was knocked down by drowning while trying to escape %2$s", + "knockdown.attack.dryout": "%1$s was knocked down by dehydration", + "knockdown.attack.dryout.player": "%1$s was knocked down by dehydration while trying to escape %2$s", + "knockdown.attack.even_more_magic": "%1$s was knocked down by even more magic", + "knockdown.attack.explosion": "%1$s was knocked down due to an explosion", + "knockdown.attack.explosion.player": "%1$s was knocked down due to an explosion caused by %2$s", + "knockdown.attack.explosion.player.item": "%1$s was knocked down due to an explosion caused by %2$s using %3$s", + "knockdown.attack.fall": "%1$s was knocked down by a fall", + "knockdown.attack.fall.player": "%1$s was knocked down by a fall while trying to escape %2$s", + "knockdown.attack.fallingBlock": "%1$s was knocked down by a falling block", + "knockdown.attack.fallingBlock.player": "%1$s was knocked down by a falling block while fighting %2$s", + "knockdown.attack.fallingStalactite": "%1$s was knocked down by a falling stalactite", + "knockdown.attack.fallingStalactite.player": "%1$s was knocked down by a falling stalactite while fighting %2$s", + "knockdown.attack.fireball": "%1$s was knocked down due to a fireball fired by %2$s", + "knockdown.attack.fireball.item": "%1$s was knocked down due to a fireball fired by %2$s using %3$s", + "knockdown.attack.fireworks": "%1$s was knocked down with a bang", + "knockdown.attack.fireworks.item": "%1$s was knocked down with a bang due to a firework fired from %3$s by %2$s", + "knockdown.attack.fireworks.player": "%1$s was knocked down with a bang while fighting %2$s", + "knockdown.attack.flyIntoWall": "%1$s was knocked down by kinetic energy", + "knockdown.attack.flyIntoWall.player": "%1$s was knocked down by kinetic energy while trying to escape %2$s", + "knockdown.attack.freeze": "%1$s was knocked down by freezing", + "knockdown.attack.freeze.player": "%1$s was knocked down by freezing by %2$s", + "knockdown.attack.generic": "%1$s was knocked down", + "knockdown.attack.generic.player": "%1$s was knocked down because of %2$s", + "knockdown.attack.genericKill": "%1$s was knocked down", + "knockdown.attack.genericKill.player": "%1$s was knocked down while fighting %2$s", + "knockdown.attack.hotFloor": "%1$s was knocked down by the lava floor", + "knockdown.attack.hotFloor.player": "%1$s was knocked down by the lava floor due to %2$s", + "knockdown.attack.indirectMagic": "%1$s was knocked down by %2$s using magic", + "knockdown.attack.indirectMagic.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.inFire": "%1$s was knocked down by fire", + "knockdown.attack.inFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.inWall": "%1$s was knocked down by suffocation", + "knockdown.attack.inWall.player": "%1$s was knocked down by suffocation while fighting %2$s", + "knockdown.attack.lava": "%1$s was knocked down by lava", + "knockdown.attack.lava.player": "%1$s was knocked down by lava while trying to escape %2$s", + "knockdown.attack.lightningBolt": "%1$s was knocked down by lightning", + "knockdown.attack.lightningBolt.player": "%1$s was knocked down by lightning while fighting %2$s", + "knockdown.attack.magic": "%1$s was knocked down by magic", + "knockdown.attack.magic.player": "%1$s was knocked down by magic while trying to escape %2$s", + "knockdown.attack.message_too_long": "Actually, the message was too long to deliver fully. Sorry! Here's a stripped version: %s", + "knockdown.attack.mob": "%1$s was knocked down by %2$s", + "knockdown.attack.mob.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.onFire": "%1$s was knocked down by fire", + "knockdown.attack.onFire.item": "%1$s was knocked down by fire while fighting %2$s wielding %3$s", + "knockdown.attack.onFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.outOfWorld": "%1$s was knocked down by the void", + "knockdown.attack.outOfWorld.player": "%1$s was knocked down by the void while fighting %2$s", + "knockdown.attack.outsideBorder": "%1$s was knocked down by the world border", + "knockdown.attack.outsideBorder.player": "%1$s was knocked down by the world border while fighting %2$s", + "knockdown.attack.player": "%1$s was knocked down by %2$s", + "knockdown.attack.player.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.sonic_boom": "%1$s was knocked down by a sonically-charged shriek", + "knockdown.attack.sonic_boom.item": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s wielding %3$s", + "knockdown.attack.sonic_boom.player": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s", + "knockdown.attack.stalagmite": "%1$s was knocked down by a stalagmite", + "knockdown.attack.stalagmite.player": "%1$s was knocked down by a stalagmite while fighting %2$s", + "knockdown.attack.starve": "%1$s was knocked down by starving", + "knockdown.attack.starve.player": "%1$s was knocked down by starving while fighting %2$s", + "knockdown.attack.sting": "%1$s was knocked down by stings", + "knockdown.attack.sting.item": "%1$s was knocked down by stings by %2$s using %3$s", + "knockdown.attack.sting.player": "%1$s was knocked down by stings by %2$s", + "knockdown.attack.sweetBerryBush": "%1$s was knocked down by a sweet berry bush", + "knockdown.attack.sweetBerryBush.player": "%1$s was knocked down by a sweet berry bush while trying to escape %2$s", + "knockdown.attack.thorns": "%1$s was knocked down while trying to hurt %2$s", + "knockdown.attack.thorns.item": "%1$s was knocked down by %3$s while trying to hurt %2$s", + "knockdown.attack.thrown": "%1$s was knocked down due to being thrown by %2$s", + "knockdown.attack.thrown.item": "%1$s was knocked down due to being thrown by %2$s using %3$s", + "knockdown.attack.trident": "%1$s was knocked down due to being impaled by %2$s", + "knockdown.attack.trident.item": "%1$s was knocked down due to being impaled by %2$s with %3$s", + "knockdown.attack.wither": "%1$s was knocked down by withering", + "knockdown.attack.wither.player": "%1$s was knocked down by withering while fighting %2$s", + "knockdown.attack.witherSkull": "%1$s was knocked down by a skull from %2$s", + "knockdown.attack.witherSkull.item": "%1$s was knocked down by a skull from %2$s using %3$s", + "knockdown.fell.accident.generic": "%1$s was knocked down by a fall", + "knockdown.fell.accident.ladder": "%1$s was knocked down by a fall off a ladder", + "knockdown.fell.accident.other_climbable": "%1$s was knocked down by a fall while climbing", + "knockdown.fell.accident.scaffolding": "%1$s was knocked down by a fall off scaffolding", + "knockdown.fell.accident.twisting_vines": "%1$s was knocked down by a fall off some twisting vines", + "knockdown.fell.accident.vines": "%1$s was knocked down by a fall off some vines", + "knockdown.fell.accident.weeping_vines": "%1$s was knocked down by a fall off some weeping vines", + "knockdown.fell.assist": "%1$s was doomed to get knocked down by %2$s", + "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", + "subtitles.knockdowns.knocked_down": "Player knocked down" +} \ No newline at end of file diff --git a/src/main/resources/assets/knockdowns/lang/ru_ru.json b/src/main/resources/assets/knockdowns/lang/ru_ru.json new file mode 100644 index 0000000..5cdd965 --- /dev/null +++ b/src/main/resources/assets/knockdowns/lang/ru_ru.json @@ -0,0 +1,102 @@ +{ + "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", + "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", + "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", + "knockdown.attack.arrow.item": "%1$s тяжело ранен стрелой %2$s из %3$s", + "knockdown.attack.badRespawnPoint.link": "жестокими правилами игры", + "knockdown.attack.badRespawnPoint.message": "%1$s был тяжело ранен %2$s", + "knockdown.attack.cactus": "%1$s был тяжело ранен кактусом", + "knockdown.attack.cactus.player": "%1$s был тяжело ранен кактусом, спасаясь от %2$s", + "knockdown.attack.cramming": "%1$s был тяжело ранен расплющиванием", + "knockdown.attack.cramming.player": "%1$s был тяжело ранен расплющиванием %2$s", + "knockdown.attack.dragonBreath": "%1$s был тяжело ранен в драконьем дыхании", + "knockdown.attack.dragonBreath.player": "%1$s был тяжело ранен в драконьем дыхании из-за %2$s", + "knockdown.attack.drown": "%1$s был тяжело ранен утоплением", + "knockdown.attack.drown.player": "%1$s был тяжело ранен утоплением, спасаясь от %2$s", + "knockdown.attack.dryout": "%1$s был тяжело ранен обезвоживанием", + "knockdown.attack.dryout.player": "%1$s был тяжело ранен обезвоживанием, спасаясь от %2$s", + "knockdown.attack.even_more_magic": "%1$s был тяжело ранен неизведанной магией", + "knockdown.attack.explosion": "%1$s был тяжело ранен взрывом", + "knockdown.attack.explosion.player": "%1$s был тяжело ранен взрывом %2$s", + "knockdown.attack.explosion.player.item": "%1$s был тяжело ранен взрывом %2$s с помощью %3$s", + "knockdown.attack.fall": "%1$s был тяжело ранен падением", + "knockdown.attack.fall.player": "%1$s был тяжело ранен падением, спасаясь от %2$s", + "knockdown.attack.fallingBlock": "%1$s был тяжело ранен упавшим блоком", + "knockdown.attack.fallingBlock.player": "%1$s был тяжело ранен упавшим блоком, пока боролся с %2$s", + "knockdown.attack.fallingStalactite": "%1$s был тяжело ранен обрушившимся сталактитом", + "knockdown.attack.fallingStalactite.player": "%1$s был тяжело ранен обрушившимся сталактитом, пока боролся с %2$s", + "knockdown.attack.fireball": "%1$s был тяжело ранен файерболом %2$s", + "knockdown.attack.fireball.item": "%1$s был тяжело ранен файерболом %2$s с помощью %3$s", + "knockdown.attack.fireworks": "%1$s был тяжело ранен взрывом фейерверка", + "knockdown.attack.fireworks.item": "%1$s был тяжело ранен взрывом фейерверка %2$s, выпущенного из %3$s", + "knockdown.attack.fireworks.player": "%1$s был тяжело ранен взрывом фейерверка, пока боролся с %2$s", + "knockdown.attack.flyIntoWall": "%1$s был тяжело ранен кинетической энергией", + "knockdown.attack.flyIntoWall.player": "%1$s был тяжело ранен кинетической энергией, спасаясь от %2$s", + "knockdown.attack.freeze": "%1$s был тяжело ранен замерзанием", + "knockdown.attack.freeze.player": "%1$s был тяжело ранен замерзанием благодаря %2$s", + "knockdown.attack.generic": "%1$s тяжело ранен", + "knockdown.attack.generic.player": "%1$s тяжело ранен из-за %2$s", + "knockdown.attack.genericKill": "%1$s тяжело ранен", + "knockdown.attack.genericKill.player": "%1$s был тяжело ранен, пока боролся с %2$s", + "knockdown.attack.hotFloor": "%1$s был тяжело ранен, обнаружив, что пол — это лава", + "knockdown.attack.hotFloor.player": "%1$s был тяжело ранен, так как зашёл в опасную зону из-за %2$s", + "knockdown.attack.inFire": "%1$s был тяжело ранен в огне", + "knockdown.attack.inFire.player": "%1$s был тяжело ранен в огне, пока боролся с %2$s", + "knockdown.attack.inWall": "%1$s был тяжело ранен погребением заживо", + "knockdown.attack.inWall.player": "%1$s был тяжело ранен погребением заживо, пока боролся с %2$s", + "knockdown.attack.indirectMagic": "%1$s был тяжело ранен %2$s с помощью магии", + "knockdown.attack.indirectMagic.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.lava": "%1$s был тяжело ранен лавой", + "knockdown.attack.lava.player": "%1$s был тяжело ранен лавой, убегая от %2$s", + "knockdown.attack.lightningBolt": "%1$s был тяжело ранен молнией", + "knockdown.attack.lightningBolt.player": "%1$s был тяжело ранен молнией, пока сражался с %2$s", + "knockdown.attack.magic": "%1$s был тяжело ранен магией", + "knockdown.attack.magic.player": "%1$s был тяжело ранен магией, убегая от %2$s", + "knockdown.attack.message_too_long": "Сообщение слишком длинное для доставки. Извините! Вот сокращённая версия: %s", + "knockdown.attack.mob": "%1$s был тяжело ранен %2$s", + "knockdown.attack.mob.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.onFire": "%1$s был тяжело ранен огнём", + "knockdown.attack.onFire.item": "%1$s был тяжело ранен огнём, пока боролся с %2$s, с помощью %3$s", + "knockdown.attack.onFire.player": "%1$s был тяжело ранен огнём, пока боролся с %2$s", + "knockdown.attack.outOfWorld": "%1$s был тяжело ранен пустотой", + "knockdown.attack.outOfWorld.player": "%1$s был тяжело ранен пустотой, благодаря %2$s", + "knockdown.attack.outsideBorder": "%1$s был тяжело ранен пределами этого мира", + "knockdown.attack.outsideBorder.player": "%1$s был тяжело ранен пределами этого мира, пока боролся с %2$s", + "knockdown.attack.player": "%1$s был тяжело ранен %2$s", + "knockdown.attack.player.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.sonic_boom": "%1$s был тяжело ранен звуковым зарядом", + "knockdown.attack.sonic_boom.item": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s c %3$s", + "knockdown.attack.sonic_boom.player": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s", + "knockdown.attack.stalagmite": "%1$s был тяжело ранен сталагмитом", + "knockdown.attack.stalagmite.player": "%1$s был тяжело ранен сталагмитом, пока боролся с %2$s", + "knockdown.attack.starve": "%1$s был тяжело ранен голодом", + "knockdown.attack.starve.player": "%1$s был тяжело ранен голодом, пока боролся с %2$s", + "knockdown.attack.sting": "%1$s был тяжело ранен изжалением", + "knockdown.attack.sting.item": "%1$s был тяжело ранен изжалением %2$s с помощью %3$s", + "knockdown.attack.sting.player": "%1$s был тяжело ранен изжалением %2$s", + "knockdown.attack.sweetBerryBush": "%1$s был тяжело ранен исколением в кустах сладких ягод", + "knockdown.attack.sweetBerryBush.player": "%1$s был тяжело ранен исколением в кустах сладких ягод, спасаясь от %2$s", + "knockdown.attack.thorns": "%1$s был тяжело ранен, пытаясь навредить %2$s", + "knockdown.attack.thorns.item": "%1$s был тяжело ранен %3$s, пытаясь навредить %2$s", + "knockdown.attack.thrown": "%1$s был тяжело ранен %2$s", + "knockdown.attack.thrown.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.trident": "%1$s был тяжело ранен пронзанием %2$s", + "knockdown.attack.trident.item": "%1$s был тяжело ранен пронзанием %2$s с помощью %3$s", + "knockdown.attack.wither": "%1$s был тяжело ранен иссушением", + "knockdown.attack.wither.player": "%1$s был тяжело ранен иссушением, пока боролся с %2$s", + "knockdown.attack.witherSkull": "%1$s был тяжело ранен поражением черепом из %2$s", + "knockdown.attack.witherSkull.item": "%1$s был тяжело ранен поражением черепом из %2$s с помощью %3$s", + "knockdown.fell.accident.generic": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.ladder": "%1$s был тяжело ранен падением с лестницы", + "knockdown.fell.accident.other_climbable": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.scaffolding": "%1$s был тяжело ранен падением с подмосток", + "knockdown.fell.accident.twisting_vines": "%1$s был тяжело ранен падением с вьющейся лозы", + "knockdown.fell.accident.vines": "%1$s был тяжело ранен падением с лианы", + "knockdown.fell.accident.weeping_vines": "%1$s был тяжело ранен падением с плакучей лозы", + "knockdown.fell.assist": "%1$s был тяжело ранен падением благодаря %2$s", + "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 был тяжело ранен падением", + "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен" +} \ No newline at end of file diff --git a/src/main/resources/assets/knockdowns/sounds.json b/src/main/resources/assets/knockdowns/sounds.json new file mode 100644 index 0000000..52d77b8 --- /dev/null +++ b/src/main/resources/assets/knockdowns/sounds.json @@ -0,0 +1,10 @@ +{ + "knocked_down": { + "subtitle": "subtitles.knockdowns.knocked_down", + "sounds": [ + { + "name": "knockdowns:knocked_down" + } + ] + } +} \ No newline at end of file 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 0000000000000000000000000000000000000000..a01bae84c53c5b42085083f32cfe8a2b4732b162 GIT binary patch literal 45653 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 ADgXcg literal 0 HcmV?d00001 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 From 38394d5c28859841b136cd3d345fa5a48c812e09 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 31 Oct 2023 00:11:40 +0500 Subject: [PATCH 02/13] feat: drop items when knocked down --- gradle.properties | 2 +- src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0ddbc52..a72b539 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.20.2+build.4 loader_version=0.14.24 # Mod Properties -mod_version=1.0.0 +mod_version=1.1.0 maven_group=ru.octol1ttle.knockdowns archives_base_name=knockdowns diff --git a/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java b/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java index 39c228f..a59282c 100644 --- a/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java +++ b/src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java @@ -25,6 +25,7 @@ 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; @@ -54,13 +55,18 @@ public class Knockdowns implements ModInitializer { 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.setFireTicks(0); + entity.extinguish(); entity.setFrozenTicks(0); + entity.setOnFire(false); entity.clearStatusEffects(); knockableDown.knockdowns$setKnockedDown(true); @@ -69,8 +75,6 @@ public class Knockdowns implements ModInitializer { buf.writeUuid(entity.getUuid()); buf.writeBoolean(true); - ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; - 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); From 22a7d5b03661ccf2ee9988cbb6fc0a985c7ca411 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 31 Oct 2023 10:29:03 +0500 Subject: [PATCH 03/13] fix: copy packet buffer in ClientOnEntityLoad to fix a weird crash --- src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java b/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java index 889224a..25ff0de 100644 --- a/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java +++ b/src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java @@ -81,7 +81,7 @@ public class KnockdownsClient implements ClientModInitializer { PacketByteBuf buf = PacketByteBufs.create(); buf.writeUuid(entity.getUuid()); - ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, buf); + ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, PacketByteBufs.copy(buf)); ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_REVIVING, buf); } }); From 430b79c1620700063ef2935bc76ca710217015ee Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 31 Oct 2023 10:29:36 +0500 Subject: [PATCH 04/13] fix: save KnockedDown state to player NBT --- .../knockdowns/mixin/PlayerEntityMixin.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java b/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java index 28a70a1..3852bee 100644 --- a/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java +++ b/src/main/java/ru/octol1ttle/knockdowns/mixin/PlayerEntityMixin.java @@ -4,9 +4,12 @@ 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) @@ -15,11 +18,9 @@ public abstract class PlayerEntityMixin implements IKnockableDown { private boolean knockedDown; @Unique private boolean beingRevived; - @Unique - private int reviveAt; @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) - private boolean isKnockedDown(boolean original) { + private boolean enterSwimmingIfKnockedDown(boolean original) { PlayerEntity player = (PlayerEntity)(Object)this; if (!(player instanceof IKnockableDown knockableDown)) { throw new IllegalStateException(); @@ -29,10 +30,20 @@ public abstract class PlayerEntityMixin implements IKnockableDown { } @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) - private boolean canFoodHeal(boolean original) { + 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; From ac3d92fc97a2020e95c07fa365f1213a85e38f63 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 31 Oct 2023 10:29:47 +0500 Subject: [PATCH 05/13] chore: bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a72b539..f5d3061 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.20.2+build.4 loader_version=0.14.24 # Mod Properties -mod_version=1.1.0 +mod_version=1.1.1 maven_group=ru.octol1ttle.knockdowns archives_base_name=knockdowns From 76d4b44153ea700e0b24799200b991a63baccdda Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Mon, 15 Jan 2024 17:47:32 +0500 Subject: [PATCH 06/13] Initial commit --- .gitignore | 19 ++ build.gradle | 52 ++++ common/build.gradle | 29 +++ .../knockdowns/common/KnockdownsCommon.java | 7 + .../main/resources/architectury.common.json | 3 + .../assets/knockdowns/lang/en_us.json | 3 + .../resources/knockdowns-common.mixins.json | 14 + .../main/resources/knockdowns.accesswidener | 1 + fabric/build.gradle | 77 ++++++ .../knockdowns/fabric/KnockdownsFabric.java | 11 + fabric/src/main/resources/fabric.mod.json | 31 +++ .../src/main/resources/knockdowns.mixins.json | 13 + forge/build.gradle | 84 ++++++ forge/gradle.properties | 1 + .../knockdowns/forge/KnockdownsForge.java | 15 ++ forge/src/main/resources/META-INF/mods.toml | 35 +++ .../src/main/resources/knockdowns.mixins.json | 13 + forge/src/main/resources/pack.mcmeta | 6 + gradle.properties | 17 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 244 ++++++++++++++++++ gradlew.bat | 92 +++++++ settings.gradle | 14 + 24 files changed, 787 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 common/build.gradle create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java create mode 100644 common/src/main/resources/architectury.common.json create mode 100644 common/src/main/resources/assets/knockdowns/lang/en_us.json create mode 100644 common/src/main/resources/knockdowns-common.mixins.json create mode 100644 common/src/main/resources/knockdowns.accesswidener create mode 100644 fabric/build.gradle create mode 100644 fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabric.java create mode 100644 fabric/src/main/resources/fabric.mod.json create mode 100644 fabric/src/main/resources/knockdowns.mixins.json create mode 100644 forge/build.gradle create mode 100644 forge/gradle.properties create mode 100644 forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java create mode 100644 forge/src/main/resources/META-INF/mods.toml create mode 100644 forge/src/main/resources/knockdowns.mixins.json create mode 100644 forge/src/main/resources/pack.mcmeta create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ccb0c56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +build/ +*.ipr +run/ +*.iws +out/ +*.iml +.gradle/ +output/ +bin/ +libs/ + +.classpath +.project +.idea/ +classes/ +.metadata +.vscode +.settings +*.launch \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..d5234af --- /dev/null +++ b/build.gradle @@ -0,0 +1,52 @@ +plugins { + id "architectury-plugin" version "3.4-SNAPSHOT" + id "dev.architectury.loom" version "1.3-SNAPSHOT" apply false +} + +architectury { + minecraft = rootProject.minecraft_version +} + +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" + } +} + +allprojects { + apply plugin: "java" + apply plugin: "architectury-plugin" + apply plugin: "maven-publish" + + base { + archivesName = rootProject.archives_base_name + } + + 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) { + options.encoding = "UTF-8" + options.release = 17 + } + + java { + withSourcesJar() + } +} diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..fe38fa0 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,29 @@ +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}" +} + +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/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java new file mode 100644 index 0000000..f41886f --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -0,0 +1,7 @@ +package ru.octol1ttle.knockdowns.common; + +public class KnockdownsCommon { + public static final String MOD_ID = "knockdowns"; + public static void init() { + } +} diff --git a/common/src/main/resources/architectury.common.json b/common/src/main/resources/architectury.common.json new file mode 100644 index 0000000..2fc0d72 --- /dev/null +++ b/common/src/main/resources/architectury.common.json @@ -0,0 +1,3 @@ +{ + "accessWidener": "knockdowns.accesswidener" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/lang/en_us.json b/common/src/main/resources/assets/knockdowns/lang/en_us.json new file mode 100644 index 0000000..2805821 --- /dev/null +++ b/common/src/main/resources/assets/knockdowns/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "item.knockdowns.example_item": "Example Item" +} \ No newline at end of file diff --git a/common/src/main/resources/knockdowns-common.mixins.json b/common/src/main/resources/knockdowns-common.mixins.json new file mode 100644 index 0000000..fd7619d --- /dev/null +++ b/common/src/main/resources/knockdowns-common.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "net.knockdowns.mixin", + "compatibilityLevel": "JAVA_17", + "minVersion": "0.8", + "client": [ + "ru.octol1ttle.knockdowns.mixin.MixinTitleScreen" + ], + "mixins": [ + ], + "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 new file mode 100644 index 0000000..13268c3 --- /dev/null +++ b/common/src/main/resources/knockdowns.accesswidener @@ -0,0 +1 @@ +accessWidener v2 named \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle new file mode 100644 index 0000000..5ca331b --- /dev/null +++ b/fabric/build.gradle @@ -0,0 +1,77 @@ +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 new file mode 100644 index 0000000..6a6fa40 --- /dev/null +++ b/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabric.java @@ -0,0 +1,11 @@ +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/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..b0ce26e --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,31 @@ +{ + "schemaVersion": 1, + "id": "knockdowns", + "version": "${version}", + "name": "Knockdowns", + "description": "DNBO 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" + ] + }, + "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 new file mode 100644 index 0000000..c33ed01 --- /dev/null +++ b/fabric/src/main/resources/knockdowns.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "package": "net.knockdowns.mixin.fabric", + "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 new file mode 100644 index 0000000..8f7a732 --- /dev/null +++ b/forge/build.gradle @@ -0,0 +1,84 @@ +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 } +} + +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 new file mode 100644 index 0000000..32f842a --- /dev/null +++ b/forge/gradle.properties @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..a8e8643 --- /dev/null +++ b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java @@ -0,0 +1,15 @@ +package ru.octol1ttle.knockdowns.forge; + +import dev.architectury.platform.forge.EventBuses; +import ru.octol1ttle.knockdowns.common.KnockdownsCommon; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +@Mod(KnockdownsCommon.MOD_ID) +public class KnockdownsForge { + public KnockdownsForge() { + // Submit our event bus to let architectury register our content on the right time + EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); + KnockdownsCommon.init(); + } +} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..0a8e11a --- /dev/null +++ b/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,35 @@ +modLoader = "javafml" +loaderVersion = "[47,)" +#issueTrackerURL = "" +license = "ARR" + +[[mods]] +modId = "knockdowns" +version = "${version}" +displayName = "Knockdowns" +authors = "Octol1ttle" +description = ''' +DNBO 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/knockdowns.mixins.json b/forge/src/main/resources/knockdowns.mixins.json new file mode 100644 index 0000000..66779c8 --- /dev/null +++ b/forge/src/main/resources/knockdowns.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "package": "net.knockdowns.mixin.forge", + "compatibilityLevel": "JAVA_17", + "minVersion": "0.8", + "client": [ + ], + "mixins": [ + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..edf178a --- /dev/null +++ b/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Knockdowns", + "pack_format": 15 + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a7ea28d --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +org.gradle.jvmargs=-Xmx2048M + +minecraft_version=1.20.1 +enabled_platforms=fabric,forge + +archives_base_name=knockdowns +mod_version=2.0.0 +maven_group=ru.octol1ttle.knockdowns + +architectury_version=9.1.12 +fabric_api_version=0.90.4+1.20.1 + +fabric_loader_version=0.15.4 +forge_version=1.20.1-47.2.0 + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..744c64d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..79a61d4 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +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. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# 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 +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..163e9f2 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,14 @@ +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" From ae6d9095e3a6e914c9f5a0be24be4c3ce3151007 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Tue, 16 Jan 2024 22:27:14 +0500 Subject: [PATCH 07/13] Port mod from Fabric --- build.gradle | 2 +- common/build.gradle | 2 + .../knockdowns/common/KnockdownsCommon.java | 9 ++ .../knockdowns/common/KnockdownsNetwork.java | 90 ++++++++++++ .../knockdowns/common/api/IKnockableDown.java | 15 ++ .../common/events/KnockdownsClientEvents.java | 105 +++++++++++++ .../common/events/KnockdownsEvents.java | 87 +++++++++++ .../common/mixin/PlayerEntityMixin.java | 71 +++++++++ .../mixin/client/ClientPlayerEntityMixin.java | 16 ++ .../common/packets/KnockdownsPacket.java | 19 +++ .../packets/KnockedDownStatusPacket.java | 69 +++++++++ .../PlayKnockedDownSoundS2CPacket.java | 40 +++++ .../common/packets/ReviveStatusPacket.java | 138 ++++++++++++++++++ .../registries/KnockdownsSoundEvents.java | 18 +++ .../registries/KnockedDownSoundInstance.java | 31 ++++ .../assets/knockdowns/lang/en_us.json | 101 ++++++++++++- .../assets/knockdowns/lang/ru_ru.json | 102 +++++++++++++ .../resources/assets/knockdowns/sounds.json | 10 ++ .../assets/knockdowns/sounds/knocked_down.ogg | Bin 0 -> 26287 bytes .../resources/knockdowns-common.mixins.json | 5 +- .../main/resources/knockdowns.accesswidener | 5 +- .../src/main/resources/knockdowns.mixins.json | 2 +- forge/build.gradle | 2 + .../knockdowns/forge/KnockdownsForge.java | 2 +- .../src/main/resources/knockdowns.mixins.json | 2 +- gradle.properties | 2 +- 26 files changed, 936 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java create mode 100644 common/src/main/resources/assets/knockdowns/lang/ru_ru.json create mode 100644 common/src/main/resources/assets/knockdowns/sounds.json create mode 100644 common/src/main/resources/assets/knockdowns/sounds/knocked_down.ogg diff --git a/build.gradle b/build.gradle index d5234af..b4c6a6d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.3-SNAPSHOT" apply false + id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false } architectury { diff --git a/common/build.gradle b/common/build.gradle index fe38fa0..a545d0d 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -12,6 +12,8 @@ dependencies { 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 { diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java index f41886f..d7fea13 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -1,7 +1,16 @@ package ru.octol1ttle.knockdowns.common; +import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; +import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; + public class KnockdownsCommon { public static final String MOD_ID = "knockdowns"; + public static void init() { + KnockdownsSoundEvents.register(); + KnockdownsNetwork.registerPackets(); + KnockdownsClientEvents.registerCallbacks(); + KnockdownsEvents.registerCallbacks(); } } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java new file mode 100644 index 0000000..3e552bc --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java @@ -0,0 +1,90 @@ +package ru.octol1ttle.knockdowns.common; + +import dev.architectury.networking.NetworkChannel; +import dev.architectury.networking.NetworkManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.Packet; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.EntityTrackingListener; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.util.Identifier; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; +import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; + +public class KnockdownsNetwork { + private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main")); + public static void registerPackets() { + CHANNEL.register(KnockedDownStatusPacket.SendS2C.class, KnockedDownStatusPacket.SendS2C::encode, KnockedDownStatusPacket.SendS2C::new, KnockedDownStatusPacket.SendS2C::apply); + CHANNEL.register(KnockedDownStatusPacket.RequestC2S.class, KnockedDownStatusPacket.RequestC2S::encode, KnockedDownStatusPacket.RequestC2S::new, KnockedDownStatusPacket.RequestC2S::apply); + + CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply); + + CHANNEL.register(ReviveStatusPacket.SendS2C.class, ReviveStatusPacket.SendS2C::encode, ReviveStatusPacket.SendS2C::new, ReviveStatusPacket.SendS2C::apply); + CHANNEL.register(ReviveStatusPacket.SendC2S.class, ReviveStatusPacket.SendC2S::encode, ReviveStatusPacket.SendC2S::new, ReviveStatusPacket.SendC2S::apply); + CHANNEL.register(ReviveStatusPacket.RequestC2S.class, ReviveStatusPacket.RequestC2S::encode, ReviveStatusPacket.RequestC2S::new, ReviveStatusPacket.RequestC2S::apply); + CHANNEL.register(ReviveStatusPacket.RevivedC2S.class, ReviveStatusPacket.RevivedC2S::encode, ReviveStatusPacket.RevivedC2S::new, ReviveStatusPacket.RevivedC2S::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); + } + } + + // TODO: PR to Architectury API + public static void sendToListeners(Entity entity, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + + sendToListeners(entity, packet, messageClass); + } + + private static void sendToListeners(Entity entity, Packet packet, Class messageClass) { + ServerChunkManager chunkManager = (ServerChunkManager) entity.getWorld().getChunkManager(); + ThreadedAnvilChunkStorage.EntityTracker entityTracker = chunkManager.threadedAnvilChunkStorage.entityTrackers.get(entity.getId()); + + for (EntityTrackingListener listener : entityTracker.listeners) { + sendToPlayer(listener.getPlayer(), packet, messageClass); + } + } + + public static void sendToListenersAndSelf(PlayerEntity player, T message) { + Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); + Class messageClass = message.getClass(); + sendToPlayer(player, packet, messageClass); + sendToListeners(player, packet, messageClass); + } + + 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/api/IKnockableDown.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java new file mode 100644 index 0000000..03f010f --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java @@ -0,0 +1,15 @@ +package ru.octol1ttle.knockdowns.common.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/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java new file mode 100644 index 0000000..459d763 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java @@ -0,0 +1,105 @@ +package ru.octol1ttle.knockdowns.common.events; + +import dev.architectury.event.EventResult; +import dev.architectury.event.events.client.ClientGuiEvent; +import dev.architectury.event.events.client.ClientPlayerEvent; +import dev.architectury.event.events.client.ClientTickEvent; +import dev.architectury.event.events.common.InteractionEvent; +import java.util.UUID; +import net.minecraft.SharedConstants; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.hit.HitResult; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; + +public class KnockdownsClientEvents { + private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; + private static IKnockableDown reviving = null; + private static int revivalTimer = -1; + + public static void registerCallbacks() { + registerOnEntityLoad(); + registerOnEntityUse(); + registerOnWorldTick(); + registerOnHudRender(); + } + + private static void registerOnEntityLoad() { + ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> { + UUID playerUuid = player.getUuid(); + KnockdownsNetwork.sendToServer(new KnockedDownStatusPacket.RequestC2S(playerUuid)); + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RequestC2S(playerUuid)); + }); + } + + private static void registerOnEntityUse() { + InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) -> { + if (!(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown() + || knockableEntity.knockdowns$isBeingRevived()) { + return EventResult.pass(); + } + + IKnockableDown self = (IKnockableDown) player; + if (self.knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + + knockableEntity.knockdowns$setBeingRevived(true); + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(entity.getUuid(), true)); + + reviving = knockableEntity; + revivalTimer = REVIVAL_WAIT_TIME; + + return EventResult.interruptTrue(); + }); + } + + private static void registerOnWorldTick() { + ClientTickEvent.ClientLevel.CLIENT_LEVEL_POST.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); + + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(reviving.knockdowns$getUuid(), false)); + if (revived) { + reviving.knockdowns$setKnockedDown(false); + + KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RevivedC2S(reviving.knockdowns$getUuid())); + } + + reviving = null; + revivalTimer = -1; + } + }); + } + + private static void registerOnHudRender() { + ClientGuiEvent.RENDER_HUD.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); + }); + } +} 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 new file mode 100644 index 0000000..a812469 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -0,0 +1,87 @@ +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 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.world.GameRules; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; +import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; + +public class KnockdownsEvents { + public static void registerCallbacks() { + registerOnLivingDeath(); + registerOnPlayerInteractions(); + } + + private static void registerOnLivingDeath() { + EntityEvent.LIVING_DEATH.register((entity, source) -> { + if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) { + return EventResult.pass(); + } + + 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); + + KnockdownsNetwork.sendToListenersAndSelf(serverPlayer, new KnockedDownStatusPacket.SendS2C(serverPlayer.getUuid(), true)); + KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); + + TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); + Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), content.getKey(), content.getArgs()); + MinecraftServer server = serverPlayer.getServer(); + if (server != null) { + server.getPlayerManager().broadcast(replaced, false); + } + + return EventResult.interruptFalse(); + }); + } + + private static void registerOnPlayerInteractions() { + InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack()); + } + return CompoundEventResult.pass(); + }); + InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> { + if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + return EventResult.interruptFalse(); + } + return EventResult.pass(); + }); + } +} 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 new file mode 100644 index 0000000..15ebee3 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java @@ -0,0 +1,71 @@ +package ru.octol1ttle.knockdowns.common.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.common.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/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java new file mode 100644 index 0000000..5756cdf --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java @@ -0,0 +1,16 @@ +package ru.octol1ttle.knockdowns.common.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; + +@Mixin(ClientPlayerEntity.class) +public abstract class ClientPlayerEntityMixin { + @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) + private boolean shouldSlowDown(boolean original) { + IKnockableDown self = (IKnockableDown) this; + return original || self.knockdowns$isKnockedDown(); + } +} \ No newline at end of file diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java new file mode 100644 index 0000000..0267f16 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java @@ -0,0 +1,19 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.function.Supplier; +import net.minecraft.network.PacketByteBuf; + +public abstract class KnockdownsPacket { + public KnockdownsPacket(PacketByteBuf buf) { + // Decode data into a message + } + + 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/packets/KnockedDownStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java new file mode 100644 index 0000000..fa0fc82 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java @@ -0,0 +1,69 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.UUID; +import java.util.function.Supplier; +import net.minecraft.network.PacketByteBuf; +import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +public class KnockedDownStatusPacket { + public static class SendS2C extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean knockedDown; + + public SendS2C(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendS2C(UUID playerUuid, boolean knockedDown) { + this.playerUuid = playerUuid; + this.knockedDown = knockedDown; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.knockedDown); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setKnockedDown(this.knockedDown); + } + }); + } + } + + public static class RequestC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RequestC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RequestC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + KnockdownsNetwork.sendToPlayer(context.getPlayer(), new SendS2C(this.playerUuid, knockableDown.knockdowns$isKnockedDown())); + } + }); + } + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java new file mode 100644 index 0000000..041d112 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java @@ -0,0 +1,40 @@ +package ru.octol1ttle.knockdowns.common.packets; + +import dev.architectury.networking.NetworkManager; +import java.util.function.Supplier; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.math.Vec3d; +import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; + +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(() -> MinecraftClient.getInstance().getSoundManager().play( + new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), new Vec3d(this.x, this.y, this.z)) + )); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java new file mode 100644 index 0000000..e84b7da --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java @@ -0,0 +1,138 @@ +package ru.octol1ttle.knockdowns.common.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.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; + +public class ReviveStatusPacket { + public static class SendS2C extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean beingRevived; + + public SendS2C(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendS2C(UUID playerUuid, boolean beingRevived) { + this.playerUuid = playerUuid; + this.beingRevived = beingRevived; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.beingRevived); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setBeingRevived(this.beingRevived); + } + }); + } + } + + public static class SendC2S extends KnockdownsPacket { + private final UUID playerUuid; + private final boolean beingRevived; + + public SendC2S(PacketByteBuf buf) { + this(buf.readUuid(), buf.readBoolean()); + } + + public SendC2S(UUID playerUuid, boolean beingRevived) { + this.playerUuid = playerUuid; + this.beingRevived = beingRevived; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + buf.writeBoolean(this.beingRevived); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + knockableDown.knockdowns$setBeingRevived(this.beingRevived); + KnockdownsNetwork.sendToListenersAndSelf(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, this.beingRevived)); + } + }); + } + } + + public static class RequestC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RequestC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RequestC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + if (knockableDown != null) { + KnockdownsNetwork.sendToPlayer(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, knockableDown.knockdowns$isBeingRevived())); + } + }); + } + } + + public static class RevivedC2S extends KnockdownsPacket { + private final UUID playerUuid; + + public RevivedC2S(PacketByteBuf buf) { + this(buf.readUuid()); + } + + public RevivedC2S(UUID playerUuid) { + this.playerUuid = playerUuid; + } + + @Override + public void encode(PacketByteBuf buf) { + buf.writeUuid(this.playerUuid); + } + + @Override + public void apply(Supplier contextSupplier) { + NetworkManager.PacketContext context = contextSupplier.get(); + context.queue(() -> { + PlayerEntity reviving = context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); + IKnockableDown knockableDown = (IKnockableDown) reviving; + if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) { + return; + } + + reviving.setInvulnerable(false); + reviving.setGlowing(false); + reviving.setHealth(6.0f); + + knockableDown.knockdowns$setKnockedDown(false); + KnockdownsNetwork.sendToListenersAndSelf(reviving, new KnockedDownStatusPacket.SendS2C(reviving.getUuid(), false)); + }); + } + } +} 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 new file mode 100644 index 0000000..a207925 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockdownsSoundEvents.java @@ -0,0 +1,18 @@ +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 new file mode 100644 index 0000000..7781165 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/registries/KnockedDownSoundInstance.java @@ -0,0 +1,31 @@ +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/assets/knockdowns/lang/en_us.json b/common/src/main/resources/assets/knockdowns/lang/en_us.json index 2805821..256bd9b 100644 --- a/common/src/main/resources/assets/knockdowns/lang/en_us.json +++ b/common/src/main/resources/assets/knockdowns/lang/en_us.json @@ -1,3 +1,102 @@ { - "item.knockdowns.example_item": "Example Item" + "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", + "knockdown.attack.arrow.item": "%1$s was knocked down due to an arrow fired by %2$s using %3$s", + "knockdown.attack.badRespawnPoint.link": "Intentional Game Design", + "knockdown.attack.badRespawnPoint.message": "%1$s was knocked down by %2$s", + "knockdown.attack.cactus": "%1$s was knocked down by a cactus", + "knockdown.attack.cactus.player": "%1$s was knocked down into a cactus while trying to escape %2$s", + "knockdown.attack.cramming": "%1$s was knocked down by squishing", + "knockdown.attack.cramming.player": "%1$s was knocked down due to being squashed by %2$s", + "knockdown.attack.dragonBreath": "%1$s was knocked down by dragon's breath", + "knockdown.attack.dragonBreath.player": "%1$s was knocked down by dragon's breath by %2$s", + "knockdown.attack.drown": "%1$s was knocked down by drowning", + "knockdown.attack.drown.player": "%1$s was knocked down by drowning while trying to escape %2$s", + "knockdown.attack.dryout": "%1$s was knocked down by dehydration", + "knockdown.attack.dryout.player": "%1$s was knocked down by dehydration while trying to escape %2$s", + "knockdown.attack.even_more_magic": "%1$s was knocked down by even more magic", + "knockdown.attack.explosion": "%1$s was knocked down due to an explosion", + "knockdown.attack.explosion.player": "%1$s was knocked down due to an explosion caused by %2$s", + "knockdown.attack.explosion.player.item": "%1$s was knocked down due to an explosion caused by %2$s using %3$s", + "knockdown.attack.fall": "%1$s was knocked down by a fall", + "knockdown.attack.fall.player": "%1$s was knocked down by a fall while trying to escape %2$s", + "knockdown.attack.fallingBlock": "%1$s was knocked down by a falling block", + "knockdown.attack.fallingBlock.player": "%1$s was knocked down by a falling block while fighting %2$s", + "knockdown.attack.fallingStalactite": "%1$s was knocked down by a falling stalactite", + "knockdown.attack.fallingStalactite.player": "%1$s was knocked down by a falling stalactite while fighting %2$s", + "knockdown.attack.fireball": "%1$s was knocked down due to a fireball fired by %2$s", + "knockdown.attack.fireball.item": "%1$s was knocked down due to a fireball fired by %2$s using %3$s", + "knockdown.attack.fireworks": "%1$s was knocked down with a bang", + "knockdown.attack.fireworks.item": "%1$s was knocked down with a bang due to a firework fired from %3$s by %2$s", + "knockdown.attack.fireworks.player": "%1$s was knocked down with a bang while fighting %2$s", + "knockdown.attack.flyIntoWall": "%1$s was knocked down by kinetic energy", + "knockdown.attack.flyIntoWall.player": "%1$s was knocked down by kinetic energy while trying to escape %2$s", + "knockdown.attack.freeze": "%1$s was knocked down by freezing", + "knockdown.attack.freeze.player": "%1$s was knocked down by freezing by %2$s", + "knockdown.attack.generic": "%1$s was knocked down", + "knockdown.attack.generic.player": "%1$s was knocked down because of %2$s", + "knockdown.attack.genericKill": "%1$s was knocked down", + "knockdown.attack.genericKill.player": "%1$s was knocked down while fighting %2$s", + "knockdown.attack.hotFloor": "%1$s was knocked down by the lava floor", + "knockdown.attack.hotFloor.player": "%1$s was knocked down by the lava floor due to %2$s", + "knockdown.attack.indirectMagic": "%1$s was knocked down by %2$s using magic", + "knockdown.attack.indirectMagic.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.inFire": "%1$s was knocked down by fire", + "knockdown.attack.inFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.inWall": "%1$s was knocked down by suffocation", + "knockdown.attack.inWall.player": "%1$s was knocked down by suffocation while fighting %2$s", + "knockdown.attack.lava": "%1$s was knocked down by lava", + "knockdown.attack.lava.player": "%1$s was knocked down by lava while trying to escape %2$s", + "knockdown.attack.lightningBolt": "%1$s was knocked down by lightning", + "knockdown.attack.lightningBolt.player": "%1$s was knocked down by lightning while fighting %2$s", + "knockdown.attack.magic": "%1$s was knocked down by magic", + "knockdown.attack.magic.player": "%1$s was knocked down by magic while trying to escape %2$s", + "knockdown.attack.message_too_long": "Actually, the message was too long to deliver fully. Sorry! Here's a stripped version: %s", + "knockdown.attack.mob": "%1$s was knocked down by %2$s", + "knockdown.attack.mob.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.onFire": "%1$s was knocked down by fire", + "knockdown.attack.onFire.item": "%1$s was knocked down by fire while fighting %2$s wielding %3$s", + "knockdown.attack.onFire.player": "%1$s was knocked down by fire while fighting %2$s", + "knockdown.attack.outOfWorld": "%1$s was knocked down by the void", + "knockdown.attack.outOfWorld.player": "%1$s was knocked down by the void while fighting %2$s", + "knockdown.attack.outsideBorder": "%1$s was knocked down by the world border", + "knockdown.attack.outsideBorder.player": "%1$s was knocked down by the world border while fighting %2$s", + "knockdown.attack.player": "%1$s was knocked down by %2$s", + "knockdown.attack.player.item": "%1$s was knocked down by %2$s using %3$s", + "knockdown.attack.sonic_boom": "%1$s was knocked down by a sonically-charged shriek", + "knockdown.attack.sonic_boom.item": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s wielding %3$s", + "knockdown.attack.sonic_boom.player": "%1$s was knocked down by a sonically-charged shriek while trying to escape %2$s", + "knockdown.attack.stalagmite": "%1$s was knocked down by a stalagmite", + "knockdown.attack.stalagmite.player": "%1$s was knocked down by a stalagmite while fighting %2$s", + "knockdown.attack.starve": "%1$s was knocked down by starving", + "knockdown.attack.starve.player": "%1$s was knocked down by starving while fighting %2$s", + "knockdown.attack.sting": "%1$s was knocked down by stings", + "knockdown.attack.sting.item": "%1$s was knocked down by stings by %2$s using %3$s", + "knockdown.attack.sting.player": "%1$s was knocked down by stings by %2$s", + "knockdown.attack.sweetBerryBush": "%1$s was knocked down by a sweet berry bush", + "knockdown.attack.sweetBerryBush.player": "%1$s was knocked down by a sweet berry bush while trying to escape %2$s", + "knockdown.attack.thorns": "%1$s was knocked down while trying to hurt %2$s", + "knockdown.attack.thorns.item": "%1$s was knocked down by %3$s while trying to hurt %2$s", + "knockdown.attack.thrown": "%1$s was knocked down due to being thrown by %2$s", + "knockdown.attack.thrown.item": "%1$s was knocked down due to being thrown by %2$s using %3$s", + "knockdown.attack.trident": "%1$s was knocked down due to being impaled by %2$s", + "knockdown.attack.trident.item": "%1$s was knocked down due to being impaled by %2$s with %3$s", + "knockdown.attack.wither": "%1$s was knocked down by withering", + "knockdown.attack.wither.player": "%1$s was knocked down by withering while fighting %2$s", + "knockdown.attack.witherSkull": "%1$s was knocked down by a skull from %2$s", + "knockdown.attack.witherSkull.item": "%1$s was knocked down by a skull from %2$s using %3$s", + "knockdown.fell.accident.generic": "%1$s was knocked down by a fall", + "knockdown.fell.accident.ladder": "%1$s was knocked down by a fall off a ladder", + "knockdown.fell.accident.other_climbable": "%1$s was knocked down by a fall while climbing", + "knockdown.fell.accident.scaffolding": "%1$s was knocked down by a fall off scaffolding", + "knockdown.fell.accident.twisting_vines": "%1$s was knocked down by a fall off some twisting vines", + "knockdown.fell.accident.vines": "%1$s was knocked down by a fall off some vines", + "knockdown.fell.accident.weeping_vines": "%1$s was knocked down by a fall off some weeping vines", + "knockdown.fell.assist": "%1$s was doomed to get knocked down by %2$s", + "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", + "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/common/src/main/resources/assets/knockdowns/lang/ru_ru.json new file mode 100644 index 0000000..5cdd965 --- /dev/null +++ b/common/src/main/resources/assets/knockdowns/lang/ru_ru.json @@ -0,0 +1,102 @@ +{ + "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", + "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", + "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", + "knockdown.attack.arrow.item": "%1$s тяжело ранен стрелой %2$s из %3$s", + "knockdown.attack.badRespawnPoint.link": "жестокими правилами игры", + "knockdown.attack.badRespawnPoint.message": "%1$s был тяжело ранен %2$s", + "knockdown.attack.cactus": "%1$s был тяжело ранен кактусом", + "knockdown.attack.cactus.player": "%1$s был тяжело ранен кактусом, спасаясь от %2$s", + "knockdown.attack.cramming": "%1$s был тяжело ранен расплющиванием", + "knockdown.attack.cramming.player": "%1$s был тяжело ранен расплющиванием %2$s", + "knockdown.attack.dragonBreath": "%1$s был тяжело ранен в драконьем дыхании", + "knockdown.attack.dragonBreath.player": "%1$s был тяжело ранен в драконьем дыхании из-за %2$s", + "knockdown.attack.drown": "%1$s был тяжело ранен утоплением", + "knockdown.attack.drown.player": "%1$s был тяжело ранен утоплением, спасаясь от %2$s", + "knockdown.attack.dryout": "%1$s был тяжело ранен обезвоживанием", + "knockdown.attack.dryout.player": "%1$s был тяжело ранен обезвоживанием, спасаясь от %2$s", + "knockdown.attack.even_more_magic": "%1$s был тяжело ранен неизведанной магией", + "knockdown.attack.explosion": "%1$s был тяжело ранен взрывом", + "knockdown.attack.explosion.player": "%1$s был тяжело ранен взрывом %2$s", + "knockdown.attack.explosion.player.item": "%1$s был тяжело ранен взрывом %2$s с помощью %3$s", + "knockdown.attack.fall": "%1$s был тяжело ранен падением", + "knockdown.attack.fall.player": "%1$s был тяжело ранен падением, спасаясь от %2$s", + "knockdown.attack.fallingBlock": "%1$s был тяжело ранен упавшим блоком", + "knockdown.attack.fallingBlock.player": "%1$s был тяжело ранен упавшим блоком, пока боролся с %2$s", + "knockdown.attack.fallingStalactite": "%1$s был тяжело ранен обрушившимся сталактитом", + "knockdown.attack.fallingStalactite.player": "%1$s был тяжело ранен обрушившимся сталактитом, пока боролся с %2$s", + "knockdown.attack.fireball": "%1$s был тяжело ранен файерболом %2$s", + "knockdown.attack.fireball.item": "%1$s был тяжело ранен файерболом %2$s с помощью %3$s", + "knockdown.attack.fireworks": "%1$s был тяжело ранен взрывом фейерверка", + "knockdown.attack.fireworks.item": "%1$s был тяжело ранен взрывом фейерверка %2$s, выпущенного из %3$s", + "knockdown.attack.fireworks.player": "%1$s был тяжело ранен взрывом фейерверка, пока боролся с %2$s", + "knockdown.attack.flyIntoWall": "%1$s был тяжело ранен кинетической энергией", + "knockdown.attack.flyIntoWall.player": "%1$s был тяжело ранен кинетической энергией, спасаясь от %2$s", + "knockdown.attack.freeze": "%1$s был тяжело ранен замерзанием", + "knockdown.attack.freeze.player": "%1$s был тяжело ранен замерзанием благодаря %2$s", + "knockdown.attack.generic": "%1$s тяжело ранен", + "knockdown.attack.generic.player": "%1$s тяжело ранен из-за %2$s", + "knockdown.attack.genericKill": "%1$s тяжело ранен", + "knockdown.attack.genericKill.player": "%1$s был тяжело ранен, пока боролся с %2$s", + "knockdown.attack.hotFloor": "%1$s был тяжело ранен, обнаружив, что пол — это лава", + "knockdown.attack.hotFloor.player": "%1$s был тяжело ранен, так как зашёл в опасную зону из-за %2$s", + "knockdown.attack.inFire": "%1$s был тяжело ранен в огне", + "knockdown.attack.inFire.player": "%1$s был тяжело ранен в огне, пока боролся с %2$s", + "knockdown.attack.inWall": "%1$s был тяжело ранен погребением заживо", + "knockdown.attack.inWall.player": "%1$s был тяжело ранен погребением заживо, пока боролся с %2$s", + "knockdown.attack.indirectMagic": "%1$s был тяжело ранен %2$s с помощью магии", + "knockdown.attack.indirectMagic.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.lava": "%1$s был тяжело ранен лавой", + "knockdown.attack.lava.player": "%1$s был тяжело ранен лавой, убегая от %2$s", + "knockdown.attack.lightningBolt": "%1$s был тяжело ранен молнией", + "knockdown.attack.lightningBolt.player": "%1$s был тяжело ранен молнией, пока сражался с %2$s", + "knockdown.attack.magic": "%1$s был тяжело ранен магией", + "knockdown.attack.magic.player": "%1$s был тяжело ранен магией, убегая от %2$s", + "knockdown.attack.message_too_long": "Сообщение слишком длинное для доставки. Извините! Вот сокращённая версия: %s", + "knockdown.attack.mob": "%1$s был тяжело ранен %2$s", + "knockdown.attack.mob.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.onFire": "%1$s был тяжело ранен огнём", + "knockdown.attack.onFire.item": "%1$s был тяжело ранен огнём, пока боролся с %2$s, с помощью %3$s", + "knockdown.attack.onFire.player": "%1$s был тяжело ранен огнём, пока боролся с %2$s", + "knockdown.attack.outOfWorld": "%1$s был тяжело ранен пустотой", + "knockdown.attack.outOfWorld.player": "%1$s был тяжело ранен пустотой, благодаря %2$s", + "knockdown.attack.outsideBorder": "%1$s был тяжело ранен пределами этого мира", + "knockdown.attack.outsideBorder.player": "%1$s был тяжело ранен пределами этого мира, пока боролся с %2$s", + "knockdown.attack.player": "%1$s был тяжело ранен %2$s", + "knockdown.attack.player.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.sonic_boom": "%1$s был тяжело ранен звуковым зарядом", + "knockdown.attack.sonic_boom.item": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s c %3$s", + "knockdown.attack.sonic_boom.player": "%1$s был тяжело ранен звуковым ударом, спасаясь от %2$s", + "knockdown.attack.stalagmite": "%1$s был тяжело ранен сталагмитом", + "knockdown.attack.stalagmite.player": "%1$s был тяжело ранен сталагмитом, пока боролся с %2$s", + "knockdown.attack.starve": "%1$s был тяжело ранен голодом", + "knockdown.attack.starve.player": "%1$s был тяжело ранен голодом, пока боролся с %2$s", + "knockdown.attack.sting": "%1$s был тяжело ранен изжалением", + "knockdown.attack.sting.item": "%1$s был тяжело ранен изжалением %2$s с помощью %3$s", + "knockdown.attack.sting.player": "%1$s был тяжело ранен изжалением %2$s", + "knockdown.attack.sweetBerryBush": "%1$s был тяжело ранен исколением в кустах сладких ягод", + "knockdown.attack.sweetBerryBush.player": "%1$s был тяжело ранен исколением в кустах сладких ягод, спасаясь от %2$s", + "knockdown.attack.thorns": "%1$s был тяжело ранен, пытаясь навредить %2$s", + "knockdown.attack.thorns.item": "%1$s был тяжело ранен %3$s, пытаясь навредить %2$s", + "knockdown.attack.thrown": "%1$s был тяжело ранен %2$s", + "knockdown.attack.thrown.item": "%1$s был тяжело ранен %2$s с помощью %3$s", + "knockdown.attack.trident": "%1$s был тяжело ранен пронзанием %2$s", + "knockdown.attack.trident.item": "%1$s был тяжело ранен пронзанием %2$s с помощью %3$s", + "knockdown.attack.wither": "%1$s был тяжело ранен иссушением", + "knockdown.attack.wither.player": "%1$s был тяжело ранен иссушением, пока боролся с %2$s", + "knockdown.attack.witherSkull": "%1$s был тяжело ранен поражением черепом из %2$s", + "knockdown.attack.witherSkull.item": "%1$s был тяжело ранен поражением черепом из %2$s с помощью %3$s", + "knockdown.fell.accident.generic": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.ladder": "%1$s был тяжело ранен падением с лестницы", + "knockdown.fell.accident.other_climbable": "%1$s был тяжело ранен падением", + "knockdown.fell.accident.scaffolding": "%1$s был тяжело ранен падением с подмосток", + "knockdown.fell.accident.twisting_vines": "%1$s был тяжело ранен падением с вьющейся лозы", + "knockdown.fell.accident.vines": "%1$s был тяжело ранен падением с лианы", + "knockdown.fell.accident.weeping_vines": "%1$s был тяжело ранен падением с плакучей лозы", + "knockdown.fell.assist": "%1$s был тяжело ранен падением благодаря %2$s", + "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 был тяжело ранен падением", + "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/sounds.json b/common/src/main/resources/assets/knockdowns/sounds.json new file mode 100644 index 0000000..52d77b8 --- /dev/null +++ b/common/src/main/resources/assets/knockdowns/sounds.json @@ -0,0 +1,10 @@ +{ + "knocked_down": { + "subtitle": "subtitles.knockdowns.knocked_down", + "sounds": [ + { + "name": "knockdowns:knocked_down" + } + ] + } +} \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..ccb17cc9f69e393851008a80c58f865057b229c6 GIT binary patch literal 26287 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 Date: Tue, 16 Jan 2024 23:43:41 +0500 Subject: [PATCH 08/13] Fabric is a terrible modding platform. --- .../knockdowns/common/KnockdownsClient.java | 19 +++++++++++++++++++ .../knockdowns/common/KnockdownsCommon.java | 3 +-- .../common/events/KnockdownsClientEvents.java | 12 ++++++------ .../common/events/KnockdownsEvents.java | 6 +++--- .../common/mixin/PlayerEntityMixin.java | 16 ++++++++-------- .../{ => network}/KnockdownsNetwork.java | 9 +++++---- .../packets/KnockdownsPacket.java | 6 +----- .../packets/KnockedDownStatusPacket.java | 4 ++-- .../PlayKnockedDownSoundS2CPacket.java | 10 +++------- .../packets/ReviveStatusPacket.java | 4 ++-- .../fabric/KnockdownsFabricClient.java | 11 +++++++++++ fabric/src/main/resources/fabric.mod.json | 3 +++ .../knockdowns/forge/KnockdownsForge.java | 9 +++++++++ 13 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java rename common/src/main/java/ru/octol1ttle/knockdowns/common/{ => network}/KnockdownsNetwork.java (92%) rename common/src/main/java/ru/octol1ttle/knockdowns/common/{ => network}/packets/KnockdownsPacket.java (73%) rename common/src/main/java/ru/octol1ttle/knockdowns/common/{ => network}/packets/KnockedDownStatusPacket.java (95%) rename common/src/main/java/ru/octol1ttle/knockdowns/common/{ => network}/packets/PlayKnockedDownSoundS2CPacket.java (67%) rename common/src/main/java/ru/octol1ttle/knockdowns/common/{ => network}/packets/ReviveStatusPacket.java (97%) create mode 100644 fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java new file mode 100644 index 0000000..1929320 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java @@ -0,0 +1,19 @@ +package ru.octol1ttle.knockdowns.common; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.Vec3d; +import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; +import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; + +public class KnockdownsClient { + public static void init() { + KnockdownsClientEvents.registerCallbacks(); + } + + public static void playKnockedDownSound(Vec3d pos) { + MinecraftClient.getInstance().getSoundManager().play( + new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos) + ); + } +} diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java index d7fea13..8514251 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -1,7 +1,7 @@ package ru.octol1ttle.knockdowns.common; -import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents; import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; public class KnockdownsCommon { @@ -10,7 +10,6 @@ public class KnockdownsCommon { public static void init() { KnockdownsSoundEvents.register(); KnockdownsNetwork.registerPackets(); - KnockdownsClientEvents.registerCallbacks(); KnockdownsEvents.registerCallbacks(); } } 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 index 459d763..08daac4 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java @@ -11,10 +11,10 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; -import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; -import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; +import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket; public class KnockdownsClientEvents { private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; @@ -22,13 +22,13 @@ public class KnockdownsClientEvents { private static int revivalTimer = -1; public static void registerCallbacks() { - registerOnEntityLoad(); + registerOnClientPlayerJoin(); registerOnEntityUse(); registerOnWorldTick(); registerOnHudRender(); } - private static void registerOnEntityLoad() { + private static void registerOnClientPlayerJoin() { ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> { UUID playerUuid = player.getUuid(); KnockdownsNetwork.sendToServer(new KnockedDownStatusPacket.RequestC2S(playerUuid)); @@ -38,7 +38,7 @@ public class KnockdownsClientEvents { private static void registerOnEntityUse() { InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) -> { - if (!(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown() + if (!player.getWorld().isClient() || !(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown() || knockableEntity.knockdowns$isBeingRevived()) { return EventResult.pass(); } 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 index a812469..429a3ce 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -11,10 +11,10 @@ import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; import net.minecraft.util.Hand; import net.minecraft.world.GameRules; -import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; -import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; +import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; public class KnockdownsEvents { public static void registerCallbacks() { 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 index 15ebee3..4d7ced9 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java @@ -15,9 +15,9 @@ import ru.octol1ttle.knockdowns.common.api.IKnockableDown; @Mixin(PlayerEntity.class) public abstract class PlayerEntityMixin implements IKnockableDown { @Unique - private boolean knockedDown; + private boolean knockdowns$knockedDown; @Unique - private boolean beingRevived; + private boolean knockdowns$beingRevived; @ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z")) private boolean enterSwimmingIfKnockedDown(boolean original) { @@ -36,32 +36,32 @@ public abstract class PlayerEntityMixin implements IKnockableDown { @Inject(method = "readCustomDataFromNbt", at = @At("TAIL")) public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { - this.knockedDown = nbt.getBoolean("KnockedDown"); + this.knockdowns$knockedDown = nbt.getBoolean("KnockedDown"); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { - nbt.putBoolean("KnockedDown", this.knockedDown); + nbt.putBoolean("KnockedDown", this.knockdowns$knockedDown); } @Override public boolean knockdowns$isKnockedDown() { - return knockedDown; + return knockdowns$knockedDown; } @Override public void knockdowns$setKnockedDown(boolean knockedDown) { - this.knockedDown = knockedDown; + this.knockdowns$knockedDown = knockedDown; } @Override public boolean knockdowns$isBeingRevived() { - return beingRevived; + return knockdowns$beingRevived; } @Override public void knockdowns$setBeingRevived(boolean beingRevived) { - this.beingRevived = beingRevived; + this.knockdowns$beingRevived = beingRevived; } @Override diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java similarity index 92% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java rename to common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java index 3e552bc..1d9af8e 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsNetwork.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java @@ -1,4 +1,4 @@ -package ru.octol1ttle.knockdowns.common; +package ru.octol1ttle.knockdowns.common.network; import dev.architectury.networking.NetworkChannel; import dev.architectury.networking.NetworkManager; @@ -11,9 +11,10 @@ import net.minecraft.server.world.ServerChunkManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ThreadedAnvilChunkStorage; import net.minecraft.util.Identifier; -import ru.octol1ttle.knockdowns.common.packets.KnockedDownStatusPacket; -import ru.octol1ttle.knockdowns.common.packets.PlayKnockedDownSoundS2CPacket; -import ru.octol1ttle.knockdowns.common.packets.ReviveStatusPacket; +import ru.octol1ttle.knockdowns.common.KnockdownsCommon; +import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; +import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket; public class KnockdownsNetwork { private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main")); diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java similarity index 73% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java rename to common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java index 0267f16..f9288d7 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockdownsPacket.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockdownsPacket.java @@ -1,14 +1,10 @@ -package ru.octol1ttle.knockdowns.common.packets; +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(PacketByteBuf buf) { - // Decode data into a message - } - public KnockdownsPacket(/* args here */) { // Message creation } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java similarity index 95% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java rename to common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java index fa0fc82..a86205c 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/KnockedDownStatusPacket.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java @@ -1,10 +1,10 @@ -package ru.octol1ttle.knockdowns.common.packets; +package ru.octol1ttle.knockdowns.common.network.packets; import dev.architectury.networking.NetworkManager; import java.util.UUID; import java.util.function.Supplier; import net.minecraft.network.PacketByteBuf; -import ru.octol1ttle.knockdowns.common.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; public class KnockedDownStatusPacket { diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java similarity index 67% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java rename to common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java index 041d112..fa9f4fb 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/PlayKnockedDownSoundS2CPacket.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/PlayKnockedDownSoundS2CPacket.java @@ -1,12 +1,10 @@ -package ru.octol1ttle.knockdowns.common.packets; +package ru.octol1ttle.knockdowns.common.network.packets; import dev.architectury.networking.NetworkManager; import java.util.function.Supplier; -import net.minecraft.client.MinecraftClient; import net.minecraft.network.PacketByteBuf; import net.minecraft.util.math.Vec3d; -import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; -import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance; +import ru.octol1ttle.knockdowns.common.KnockdownsClient; public class PlayKnockedDownSoundS2CPacket extends KnockdownsPacket { private final double x; @@ -33,8 +31,6 @@ public class PlayKnockedDownSoundS2CPacket extends KnockdownsPacket { @Override public void apply(Supplier contextSupplier) { NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> MinecraftClient.getInstance().getSoundManager().play( - new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), new Vec3d(this.x, this.y, this.z)) - )); + context.queue(() -> KnockdownsClient.playKnockedDownSound(new Vec3d(this.x, this.y, this.z))); } } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java similarity index 97% rename from common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java rename to common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java index e84b7da..e486e7d 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/packets/ReviveStatusPacket.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java @@ -1,11 +1,11 @@ -package ru.octol1ttle.knockdowns.common.packets; +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.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; public class ReviveStatusPacket { diff --git a/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java b/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java new file mode 100644 index 0000000..2a651c4 --- /dev/null +++ b/fabric/src/main/java/ru/octol1ttle/knockdowns/fabric/KnockdownsFabricClient.java @@ -0,0 +1,11 @@ +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 index b0ce26e..b1f1a7c 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -17,6 +17,9 @@ "entrypoints": { "main": [ "ru.octol1ttle.knockdowns.fabric.KnockdownsFabric" + ], + "client": [ + "ru.octol1ttle.knockdowns.fabric.KnockdownsFabricClient" ] }, "mixins": [ diff --git a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java index e71aa25..dd706c0 100644 --- a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java +++ b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java @@ -1,10 +1,14 @@ package ru.octol1ttle.knockdowns.forge; import dev.architectury.platform.forge.EventBuses; +import net.minecraftforge.eventbus.api.SubscribeEvent; 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() { @@ -12,4 +16,9 @@ public class KnockdownsForge { EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); KnockdownsCommon.init(); } + + @SubscribeEvent + public void onInitializeClient(FMLClientSetupEvent event) { + KnockdownsClient.init(); + } } From 3a93c3beaefc52926a24f93aafd7885a9d4f976a Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Wed, 17 Jan 2024 22:51:32 +0500 Subject: [PATCH 09/13] make everything work --- build.gradle | 2 +- .../knockdowns/common/KnockdownsClient.java | 44 ++++++ .../knockdowns/common/KnockdownsCommon.java | 8 + .../knockdowns/common/api/IKnockableDown.java | 18 ++- .../common/events/KnockdownsClientEvents.java | 107 +++----------- .../common/events/KnockdownsEvents.java | 79 +++++++--- .../common/mixin/LivingEntityMixin.java | 15 ++ .../common/mixin/MobEntityMixin.java | 19 +++ .../common/mixin/PlayerEntityMixin.java | 94 ++++++++---- .../mixin/client/ClientPlayerEntityMixin.java | 5 +- .../common/network/KnockdownsNetwork.java | 42 +----- .../packets/KnockedDownStatusPacket.java | 69 --------- .../RequestStartRevivingC2SPacket.java | 39 +++++ .../network/packets/ReviveStatusPacket.java | 138 ------------------ .../packets/StopRevivingC2SPacket.java | 41 ++++++ .../assets/knockdowns/lang/en_us.json | 5 +- .../assets/knockdowns/lang/ru_ru.json | 5 +- .../resources/knockdowns-common.mixins.json | 2 + .../main/resources/knockdowns.accesswidener | 5 +- 19 files changed, 346 insertions(+), 391 deletions(-) create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/LivingEntityMixin.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/MobEntityMixin.java delete mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/RequestStartRevivingC2SPacket.java delete mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/StopRevivingC2SPacket.java diff --git a/build.gradle b/build.gradle index b4c6a6d..df015f7 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ allprojects { // for more information about repositories. } - tasks.withType(JavaCompile) { + tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" options.release = 17 } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java index 1929320..79f04c3 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java @@ -1,12 +1,25 @@ 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(); } @@ -16,4 +29,35 @@ public class KnockdownsClient { new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos) ); } + + public static EventResult onEntityUse(PlayerEntity player, Entity entity) { + if (KnockdownsCommon.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().getUuid().equals(reviving.getUuid()); + + 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 index 8514251..286367c 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -1,15 +1,23 @@ package ru.octol1ttle.knockdowns.common; +import net.minecraft.SharedConstants; +import net.minecraft.entity.player.PlayerEntity; +import ru.octol1ttle.knockdowns.common.api.IKnockableDown; 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(); } + + public static boolean isKnockedOrReviving(PlayerEntity player) { + return player instanceof IKnockableDown knockable && (knockable.is_KnockedDown() || knockable.is_Reviving()); + } } 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 index 03f010f..74fad18 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java @@ -1,15 +1,19 @@ package ru.octol1ttle.knockdowns.common.api; -import java.util.UUID; - public interface IKnockableDown { - boolean knockdowns$isKnockedDown(); + boolean is_KnockedDown(); - void knockdowns$setKnockedDown(boolean knockedDown); + void set_KnockedDown(boolean knockedDown); - boolean knockdowns$isBeingRevived(); + int get_ReviverCount(); - void knockdowns$setBeingRevived(boolean beingRevived); + void set_ReviverCount(int reviverCount); - UUID knockdowns$getUuid(); + boolean is_Reviving(); + + void set_Reviving(boolean reviving); + + int get_ReviveTimer(); + + void set_ReviveTimer(int reviveTimer); } 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 index 08daac4..47ec31d 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsClientEvents.java @@ -1,105 +1,44 @@ package ru.octol1ttle.knockdowns.common.events; -import dev.architectury.event.EventResult; import dev.architectury.event.events.client.ClientGuiEvent; -import dev.architectury.event.events.client.ClientPlayerEvent; -import dev.architectury.event.events.client.ClientTickEvent; -import dev.architectury.event.events.common.InteractionEvent; -import java.util.UUID; import net.minecraft.SharedConstants; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; -import net.minecraft.util.hit.EntityHitResult; -import net.minecraft.util.hit.HitResult; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; +import net.minecraft.util.Formatting; +import ru.octol1ttle.knockdowns.common.KnockdownsClient; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; -import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket; public class KnockdownsClientEvents { - private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND; - private static IKnockableDown reviving = null; - private static int revivalTimer = -1; - public static void registerCallbacks() { - registerOnClientPlayerJoin(); - registerOnEntityUse(); - registerOnWorldTick(); registerOnHudRender(); } - private static void registerOnClientPlayerJoin() { - ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> { - UUID playerUuid = player.getUuid(); - KnockdownsNetwork.sendToServer(new KnockedDownStatusPacket.RequestC2S(playerUuid)); - KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RequestC2S(playerUuid)); - }); - } - - private static void registerOnEntityUse() { - InteractionEvent.INTERACT_ENTITY.register((player, entity, hand) -> { - if (!player.getWorld().isClient() || !(entity instanceof IKnockableDown knockableEntity) || !knockableEntity.knockdowns$isKnockedDown() - || knockableEntity.knockdowns$isBeingRevived()) { - return EventResult.pass(); - } - - IKnockableDown self = (IKnockableDown) player; - if (self.knockdowns$isKnockedDown()) { - return EventResult.interruptFalse(); - } - - knockableEntity.knockdowns$setBeingRevived(true); - KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(entity.getUuid(), true)); - - reviving = knockableEntity; - revivalTimer = REVIVAL_WAIT_TIME; - - return EventResult.interruptTrue(); - }); - } - - private static void registerOnWorldTick() { - ClientTickEvent.ClientLevel.CLIENT_LEVEL_POST.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); - - KnockdownsNetwork.sendToServer(new ReviveStatusPacket.SendC2S(reviving.knockdowns$getUuid(), false)); - if (revived) { - reviving.knockdowns$setKnockedDown(false); - - KnockdownsNetwork.sendToServer(new ReviveStatusPacket.RevivedC2S(reviving.knockdowns$getUuid())); - } - - reviving = null; - revivalTimer = -1; - } - }); - } - private static void registerOnHudRender() { ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> { - if (revivalTimer == -1) { - return; + 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 = MinecraftClient.getInstance().textRenderer; - String text = String.format("%.1f", revivalTimer / (float) SharedConstants.TICKS_PER_SECOND); - int x = (drawContext.getScaledWindowWidth() - renderer.getWidth(text)) / 2; + TextRenderer renderer = client.textRenderer; - drawContext.drawTextWithShadow(renderer, text, x, drawContext.getScaledWindowHeight() / 2 + 15, 0xFFFFFF); + 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 index 429a3ce..d09e8ec 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -5,46 +5,50 @@ 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 java.util.Objects; +import net.minecraft.entity.damage.DamageSource; 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.world.GameRules; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; +import ru.octol1ttle.knockdowns.common.KnockdownsClient; +import ru.octol1ttle.knockdowns.common.KnockdownsCommon; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; -import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; +import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; public class KnockdownsEvents { + private static final float KNOCKED_DOWN_TIMER = 50.0f; + public static void registerCallbacks() { registerOnLivingDeath(); + registerOnPlayerTick(); registerOnPlayerInteractions(); + registerOnEntityUse(); } private static void registerOnLivingDeath() { EntityEvent.LIVING_DEATH.register((entity, source) -> { - if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) { + if (entity.getWorld().isClient() || !(entity instanceof IKnockableDown knockable) || knockable.is_KnockedDown()) { return EventResult.pass(); } ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; - // TODO: timer - if (!serverPlayer.getWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) { - serverPlayer.getInventory().dropAll(); - } - entity.setHealth(1.0f); + + entity.clearStatusEffects(); entity.setInvulnerable(true); entity.setGlowing(true); - entity.setAir(entity.getMaxAir()); + entity.setHealth(entity.getMaxHealth()); entity.extinguish(); + entity.setAir(entity.getMaxAir()); entity.setFrozenTicks(0); - entity.setOnFire(false); - entity.clearStatusEffects(); + serverPlayer.stopFallFlying(); - knockableDown.knockdowns$setKnockedDown(true); + knockable.set_KnockedDown(true); + knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); - KnockdownsNetwork.sendToListenersAndSelf(serverPlayer, new KnockedDownStatusPacket.SendS2C(serverPlayer.getUuid(), true)); KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); @@ -58,30 +62,69 @@ public class KnockdownsEvents { }); } + private static void registerOnPlayerTick() { + TickEvent.PLAYER_POST.register(player -> { + if (player.getWorld().isClient()) { + KnockdownsClient.onPlayerTick(player); + return; + } + if (!(player instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) { + 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); + + player.setInvulnerable(false); + player.setGlowing(false); + player.setHealth(6.0f); + } + return; + } + knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 2)); + + if (player.age % 20 == 0) { + player.setInvulnerable(false); + DamageSource recent = player.getRecentDamageSource(); + player.damage(Objects.requireNonNullElse(recent, player.getDamageSources().generic()), player.getMaxHealth() / KNOCKED_DOWN_TIMER); + player.velocityModified = false; + } + }); + } + private static void registerOnPlayerInteractions() { InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> { - if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + if (KnockdownsCommon.isKnockedOrReviving(player)) { return EventResult.interruptFalse(); } return EventResult.pass(); }); PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { - if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + if (KnockdownsCommon.isKnockedOrReviving(player)) { return EventResult.interruptFalse(); } return EventResult.pass(); }); InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { - if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + if (KnockdownsCommon.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 (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) { + if (KnockdownsCommon.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 new file mode 100644 index 0000000..369a8b4 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/LivingEntityMixin.java @@ -0,0 +1,15 @@ +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 new file mode 100644 index 0000000..17540a0 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/MobEntityMixin.java @@ -0,0 +1,19 @@ +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 index 4d7ced9..876e0a1 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java @@ -2,70 +2,114 @@ package ru.octol1ttle.knockdowns.common.mixin; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import java.util.UUID; +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 implements IKnockableDown { +public abstract class PlayerEntityMixin extends Entity implements IKnockableDown { @Unique - private boolean knockdowns$knockedDown; + private static final TrackedData KNOCKED_DOWN = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN); @Unique - private boolean knockdowns$beingRevived; + 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); + + 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) { - PlayerEntity player = (PlayerEntity)(Object)this; - if (!(player instanceof IKnockableDown knockableDown)) { - throw new IllegalStateException(); - } - - return original || knockableDown.knockdowns$isKnockedDown(); + return original || this.is_KnockedDown(); } @ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN")) private boolean dontHealIfKnockedDown(boolean original) { - return original && !this.knockdowns$isKnockedDown(); + 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")) - public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { - this.knockdowns$knockedDown = nbt.getBoolean("KnockedDown"); + private void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) { + this.set_KnockedDown(nbt.getBoolean("KnockedDown")); + this.set_ReviveTimer(nbt.getInt("ReviveTimer")); } @Inject(method = "writeCustomDataToNbt", at = @At("TAIL")) - public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { - nbt.putBoolean("KnockedDown", this.knockdowns$knockedDown); + private void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) { + nbt.putBoolean("KnockedDown", this.is_KnockedDown()); + nbt.putInt("ReviveTimer", this.get_ReviveTimer()); } @Override - public boolean knockdowns$isKnockedDown() { - return knockdowns$knockedDown; + public boolean is_KnockedDown() { + return this.dataTracker.get(KNOCKED_DOWN); } @Override - public void knockdowns$setKnockedDown(boolean knockedDown) { - this.knockdowns$knockedDown = knockedDown; + public void set_KnockedDown(boolean knockedDown) { + this.dataTracker.set(KNOCKED_DOWN, knockedDown); } @Override - public boolean knockdowns$isBeingRevived() { - return knockdowns$beingRevived; + public boolean is_Reviving() { + return this.dataTracker.get(IS_REVIVING); } @Override - public void knockdowns$setBeingRevived(boolean beingRevived) { - this.knockdowns$beingRevived = beingRevived; + public void set_Reviving(boolean reviving) { + this.dataTracker.set(IS_REVIVING, reviving); } @Override - public UUID knockdowns$getUuid() { - return ((PlayerEntity)(Object)this).getUuid(); + 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); } } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java index 5756cdf..440c2ef 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/client/ClientPlayerEntityMixin.java @@ -7,10 +7,9 @@ import org.spongepowered.asm.mixin.injection.At; import ru.octol1ttle.knockdowns.common.api.IKnockableDown; @Mixin(ClientPlayerEntity.class) -public abstract class ClientPlayerEntityMixin { +public abstract class ClientPlayerEntityMixin implements IKnockableDown { @ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN")) private boolean shouldSlowDown(boolean original) { - IKnockableDown self = (IKnockableDown) this; - return original || self.knockdowns$isKnockedDown(); + return original || this.is_KnockedDown(); } } \ No newline at end of file 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 index 1d9af8e..8544efb 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/KnockdownsNetwork.java @@ -2,32 +2,22 @@ package ru.octol1ttle.knockdowns.common.network; import dev.architectury.networking.NetworkChannel; import dev.architectury.networking.NetworkManager; -import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.Packet; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.EntityTrackingListener; -import net.minecraft.server.world.ServerChunkManager; import net.minecraft.server.world.ServerWorld; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; import net.minecraft.util.Identifier; import ru.octol1ttle.knockdowns.common.KnockdownsCommon; -import ru.octol1ttle.knockdowns.common.network.packets.KnockedDownStatusPacket; import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket; -import ru.octol1ttle.knockdowns.common.network.packets.ReviveStatusPacket; +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(KnockedDownStatusPacket.SendS2C.class, KnockedDownStatusPacket.SendS2C::encode, KnockedDownStatusPacket.SendS2C::new, KnockedDownStatusPacket.SendS2C::apply); - CHANNEL.register(KnockedDownStatusPacket.RequestC2S.class, KnockedDownStatusPacket.RequestC2S::encode, KnockedDownStatusPacket.RequestC2S::new, KnockedDownStatusPacket.RequestC2S::apply); - CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply); - - CHANNEL.register(ReviveStatusPacket.SendS2C.class, ReviveStatusPacket.SendS2C::encode, ReviveStatusPacket.SendS2C::new, ReviveStatusPacket.SendS2C::apply); - CHANNEL.register(ReviveStatusPacket.SendC2S.class, ReviveStatusPacket.SendC2S::encode, ReviveStatusPacket.SendC2S::new, ReviveStatusPacket.SendC2S::apply); - CHANNEL.register(ReviveStatusPacket.RequestC2S.class, ReviveStatusPacket.RequestC2S::encode, ReviveStatusPacket.RequestC2S::new, ReviveStatusPacket.RequestC2S::apply); - CHANNEL.register(ReviveStatusPacket.RevivedC2S.class, ReviveStatusPacket.RevivedC2S::encode, ReviveStatusPacket.RevivedC2S::new, ReviveStatusPacket.RevivedC2S::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) { @@ -52,30 +42,6 @@ public class KnockdownsNetwork { } } - // TODO: PR to Architectury API - public static void sendToListeners(Entity entity, T message) { - Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); - Class messageClass = message.getClass(); - - sendToListeners(entity, packet, messageClass); - } - - private static void sendToListeners(Entity entity, Packet packet, Class messageClass) { - ServerChunkManager chunkManager = (ServerChunkManager) entity.getWorld().getChunkManager(); - ThreadedAnvilChunkStorage.EntityTracker entityTracker = chunkManager.threadedAnvilChunkStorage.entityTrackers.get(entity.getId()); - - for (EntityTrackingListener listener : entityTracker.listeners) { - sendToPlayer(listener.getPlayer(), packet, messageClass); - } - } - - public static void sendToListenersAndSelf(PlayerEntity player, T message) { - Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); - Class messageClass = message.getClass(); - sendToPlayer(player, packet, messageClass); - sendToListeners(player, packet, messageClass); - } - public static void sendToWorld(ServerWorld world, T message) { Packet packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message); Class messageClass = message.getClass(); diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java deleted file mode 100644 index a86205c..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/KnockedDownStatusPacket.java +++ /dev/null @@ -1,69 +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.network.PacketByteBuf; -import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class KnockedDownStatusPacket { - public static class SendS2C extends KnockdownsPacket { - private final UUID playerUuid; - private final boolean knockedDown; - - public SendS2C(PacketByteBuf buf) { - this(buf.readUuid(), buf.readBoolean()); - } - - public SendS2C(UUID playerUuid, boolean knockedDown) { - this.playerUuid = playerUuid; - this.knockedDown = knockedDown; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - buf.writeBoolean(this.knockedDown); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - if (knockableDown != null) { - knockableDown.knockdowns$setKnockedDown(this.knockedDown); - } - }); - } - } - - public static class RequestC2S extends KnockdownsPacket { - private final UUID playerUuid; - - public RequestC2S(PacketByteBuf buf) { - this(buf.readUuid()); - } - - public RequestC2S(UUID playerUuid) { - this.playerUuid = playerUuid; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - if (knockableDown != null) { - KnockdownsNetwork.sendToPlayer(context.getPlayer(), new SendS2C(this.playerUuid, knockableDown.knockdowns$isKnockedDown())); - } - }); - } - } -} 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 new file mode 100644 index 0000000..31a7de0 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/RequestStartRevivingC2SPacket.java @@ -0,0 +1,39 @@ +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/ReviveStatusPacket.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java deleted file mode 100644 index e486e7d..0000000 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/ReviveStatusPacket.java +++ /dev/null @@ -1,138 +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.network.KnockdownsNetwork; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; - -public class ReviveStatusPacket { - public static class SendS2C extends KnockdownsPacket { - private final UUID playerUuid; - private final boolean beingRevived; - - public SendS2C(PacketByteBuf buf) { - this(buf.readUuid(), buf.readBoolean()); - } - - public SendS2C(UUID playerUuid, boolean beingRevived) { - this.playerUuid = playerUuid; - this.beingRevived = beingRevived; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - buf.writeBoolean(this.beingRevived); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - if (knockableDown != null) { - knockableDown.knockdowns$setBeingRevived(this.beingRevived); - } - }); - } - } - - public static class SendC2S extends KnockdownsPacket { - private final UUID playerUuid; - private final boolean beingRevived; - - public SendC2S(PacketByteBuf buf) { - this(buf.readUuid(), buf.readBoolean()); - } - - public SendC2S(UUID playerUuid, boolean beingRevived) { - this.playerUuid = playerUuid; - this.beingRevived = beingRevived; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - buf.writeBoolean(this.beingRevived); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - if (knockableDown != null) { - knockableDown.knockdowns$setBeingRevived(this.beingRevived); - KnockdownsNetwork.sendToListenersAndSelf(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, this.beingRevived)); - } - }); - } - } - - public static class RequestC2S extends KnockdownsPacket { - private final UUID playerUuid; - - public RequestC2S(PacketByteBuf buf) { - this(buf.readUuid()); - } - - public RequestC2S(UUID playerUuid) { - this.playerUuid = playerUuid; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - IKnockableDown knockableDown = (IKnockableDown) context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - if (knockableDown != null) { - KnockdownsNetwork.sendToPlayer(context.getPlayer(), new ReviveStatusPacket.SendS2C(this.playerUuid, knockableDown.knockdowns$isBeingRevived())); - } - }); - } - } - - public static class RevivedC2S extends KnockdownsPacket { - private final UUID playerUuid; - - public RevivedC2S(PacketByteBuf buf) { - this(buf.readUuid()); - } - - public RevivedC2S(UUID playerUuid) { - this.playerUuid = playerUuid; - } - - @Override - public void encode(PacketByteBuf buf) { - buf.writeUuid(this.playerUuid); - } - - @Override - public void apply(Supplier contextSupplier) { - NetworkManager.PacketContext context = contextSupplier.get(); - context.queue(() -> { - PlayerEntity reviving = context.getPlayer().getWorld().getPlayerByUuid(this.playerUuid); - IKnockableDown knockableDown = (IKnockableDown) reviving; - if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) { - return; - } - - reviving.setInvulnerable(false); - reviving.setGlowing(false); - reviving.setHealth(6.0f); - - knockableDown.knockdowns$setKnockedDown(false); - KnockdownsNetwork.sendToListenersAndSelf(reviving, new KnockedDownStatusPacket.SendS2C(reviving.getUuid(), false)); - }); - } - } -} 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 new file mode 100644 index 0000000..260cc1e --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/network/packets/StopRevivingC2SPacket.java @@ -0,0 +1,41 @@ +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/resources/assets/knockdowns/lang/en_us.json b/common/src/main/resources/assets/knockdowns/lang/en_us.json index 256bd9b..57cacc4 100644 --- a/common/src/main/resources/assets/knockdowns/lang/en_us.json +++ b/common/src/main/resources/assets/knockdowns/lang/en_us.json @@ -1,4 +1,6 @@ { + "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", @@ -97,6 +99,5 @@ "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", - "subtitles.knockdowns.knocked_down": "Player knocked down" + "knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall" } \ No newline at end of file diff --git a/common/src/main/resources/assets/knockdowns/lang/ru_ru.json b/common/src/main/resources/assets/knockdowns/lang/ru_ru.json index 5cdd965..9fd0739 100644 --- a/common/src/main/resources/assets/knockdowns/lang/ru_ru.json +++ b/common/src/main/resources/assets/knockdowns/lang/ru_ru.json @@ -1,4 +1,6 @@ { + "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен", + "knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней", "knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s", "knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s", @@ -97,6 +99,5 @@ "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 был тяжело ранен падением", - "subtitles.knockdowns.knocked_down": "Игрок тяжело ранен" + "knockdown.fell.killer": "%1$s был тяжело ранен падением" } \ No newline at end of file diff --git a/common/src/main/resources/knockdowns-common.mixins.json b/common/src/main/resources/knockdowns-common.mixins.json index 8eef057..b157b56 100644 --- a/common/src/main/resources/knockdowns-common.mixins.json +++ b/common/src/main/resources/knockdowns-common.mixins.json @@ -7,6 +7,8 @@ "client.ClientPlayerEntityMixin" ], "mixins": [ + "LivingEntityMixin", + "MobEntityMixin", "PlayerEntityMixin" ], "injectors": { diff --git a/common/src/main/resources/knockdowns.accesswidener b/common/src/main/resources/knockdowns.accesswidener index 4dddd12..13268c3 100644 --- a/common/src/main/resources/knockdowns.accesswidener +++ b/common/src/main/resources/knockdowns.accesswidener @@ -1,4 +1 @@ -accessWidener v2 named - -accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage entityTrackers Lit/unimi/dsi/fastutil/ints/Int2ObjectMap; -accessible field net/minecraft/server/world/ThreadedAnvilChunkStorage$EntityTracker listeners Ljava/util/Set; \ No newline at end of file +accessWidener v2 named \ No newline at end of file From a13706f240e58f4719bacfb9b3364ad1f5530cae Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 18 Jan 2024 19:45:56 +0500 Subject: [PATCH 10/13] fucking forge --- fabric/src/main/resources/fabric.mod.json | 2 +- .../ru/octol1ttle/knockdowns/forge/KnockdownsForge.java | 8 +++++--- forge/src/main/resources/META-INF/mods.toml | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index b1f1a7c..5e30f2e 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -3,7 +3,7 @@ "id": "knockdowns", "version": "${version}", "name": "Knockdowns", - "description": "DNBO mechanic from Fortnite, ported to Minecraft", + "description": "DBNO mechanic from Fortnite, ported to Minecraft", "authors": [ "Octol1ttle" ], diff --git a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java index dd706c0..83a53a8 100644 --- a/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java +++ b/forge/src/main/java/ru/octol1ttle/knockdowns/forge/KnockdownsForge.java @@ -1,7 +1,7 @@ package ru.octol1ttle.knockdowns.forge; import dev.architectury.platform.forge.EventBuses; -import net.minecraftforge.eventbus.api.SubscribeEvent; +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; @@ -13,11 +13,13 @@ import ru.octol1ttle.knockdowns.common.KnockdownsCommon; public class KnockdownsForge { public KnockdownsForge() { // Submit our event bus to let architectury register our content on the right time - EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, modEventBus); + modEventBus.addListener(this::onInitializeClient); + KnockdownsCommon.init(); } - @SubscribeEvent 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 index 0a8e11a..9e1ece6 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -9,7 +9,7 @@ version = "${version}" displayName = "Knockdowns" authors = "Octol1ttle" description = ''' -DNBO mechanic from Fortnite, ported to Minecraft +DBNO mechanic from Fortnite, ported to Minecraft ''' #logoFile = "" From 81879b6ec4cba616ac88009efaa4d08f1e3e650a Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Fri, 19 Jan 2024 21:00:50 +0500 Subject: [PATCH 11/13] kill immediately if no players and reset data when knocked dies --- .../common/events/KnockdownsEvents.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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 index d09e8ec..30ee56c 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -31,11 +31,23 @@ public class KnockdownsEvents { private static void registerOnLivingDeath() { EntityEvent.LIVING_DEATH.register((entity, source) -> { - if (entity.getWorld().isClient() || !(entity instanceof IKnockableDown knockable) || knockable.is_KnockedDown()) { + if (entity.getWorld().isClient() || !(entity instanceof IKnockableDown knockable)) { return EventResult.pass(); } ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; + MinecraftServer server = serverPlayer.getServer(); + if (server == null || server.getCurrentPlayerCount() == 1) { + return EventResult.pass(); + } + + if (knockable.is_KnockedDown()) { + knockable.set_KnockedDown(false); + knockable.set_ReviverCount(0); + knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); + + return EventResult.pass(); + } entity.clearStatusEffects(); entity.setInvulnerable(true); @@ -53,10 +65,8 @@ public class KnockdownsEvents { TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), content.getKey(), content.getArgs()); - MinecraftServer server = serverPlayer.getServer(); - if (server != null) { - server.getPlayerManager().broadcast(replaced, false); - } + + server.getPlayerManager().broadcast(replaced, false); return EventResult.interruptFalse(); }); From cc20e19a4b616321dcd2e4887bb3a949d4aa9d46 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Sat, 20 Jan 2024 13:21:45 +0500 Subject: [PATCH 12/13] fix fallback death message --- .../knockdowns/common/events/KnockdownsEvents.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 index 30ee56c..be6b190 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -63,8 +63,9 @@ public class KnockdownsEvents { KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); - TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent(); - Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), content.getKey(), content.getArgs()); + 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); From 34ea071e5fc67cfa6030916a9927a1590661066b Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Fri, 16 Feb 2024 17:13:30 +0500 Subject: [PATCH 13/13] Increase invulnerability period and tenacity periods --- .../knockdowns/common/KnockdownsClient.java | 4 +- .../knockdowns/common/KnockdownsCommon.java | 6 --- .../knockdowns/common/KnockdownsUtils.java | 31 +++++++++++ .../knockdowns/common/api/IKnockableDown.java | 4 ++ .../common/events/KnockdownsEvents.java | 54 ++++++++++++------- .../common/mixin/PlayerEntityMixin.java | 14 +++++ gradle.properties | 2 +- 7 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java index 79f04c3..1b4272f 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsClient.java @@ -31,7 +31,7 @@ public class KnockdownsClient { } public static EventResult onEntityUse(PlayerEntity player, Entity entity) { - if (KnockdownsCommon.isKnockedOrReviving(player) || !(entity instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) { + if (KnockdownsUtils.isKnockedOrReviving(player) || !(entity instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) { return EventResult.pass(); } @@ -49,7 +49,7 @@ public class KnockdownsClient { boolean playerKnocked = ((IKnockableDown) player).is_KnockedDown(); boolean revivingTargeted = client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.ENTITY - && ((EntityHitResult) client.crosshairTarget).getEntity().getUuid().equals(reviving.getUuid()); + && ((EntityHitResult) client.crosshairTarget).getEntity().equals(reviving); if (!(reviving instanceof IKnockableDown knockable)) { return; diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java index 286367c..d36f5d7 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsCommon.java @@ -1,8 +1,6 @@ package ru.octol1ttle.knockdowns.common; import net.minecraft.SharedConstants; -import net.minecraft.entity.player.PlayerEntity; -import ru.octol1ttle.knockdowns.common.api.IKnockableDown; import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents; import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork; import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents; @@ -16,8 +14,4 @@ public class KnockdownsCommon { KnockdownsNetwork.registerPackets(); KnockdownsEvents.registerCallbacks(); } - - public static boolean isKnockedOrReviving(PlayerEntity player) { - return player instanceof IKnockableDown knockable && (knockable.is_KnockedDown() || knockable.is_Reviving()); - } } diff --git a/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java new file mode 100644 index 0000000..8715bd8 --- /dev/null +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/KnockdownsUtils.java @@ -0,0 +1,31 @@ +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 index 74fad18..17c4308 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/api/IKnockableDown.java @@ -16,4 +16,8 @@ public interface IKnockableDown { 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/KnockdownsEvents.java b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java index be6b190..8f46b96 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/events/KnockdownsEvents.java @@ -6,21 +6,24 @@ 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 java.util.Objects; -import net.minecraft.entity.damage.DamageSource; +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_DOWN_TIMER = 50.0f; + 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(); @@ -31,13 +34,14 @@ public class KnockdownsEvents { private static void registerOnLivingDeath() { EntityEvent.LIVING_DEATH.register((entity, source) -> { - if (entity.getWorld().isClient() || !(entity instanceof IKnockableDown knockable)) { + MinecraftServer server = entity.getServer(); + if (server == null || !(entity instanceof IKnockableDown knockable)) { return EventResult.pass(); } - ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity; - MinecraftServer server = serverPlayer.getServer(); - if (server == null || server.getCurrentPlayerCount() == 1) { + ServerPlayerEntity player = (ServerPlayerEntity) entity; + + if (KnockdownsUtils.allTeammatesKnocked(server, player)) { return EventResult.pass(); } @@ -45,6 +49,7 @@ public class KnockdownsEvents { knockable.set_KnockedDown(false); knockable.set_ReviverCount(0); knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); + knockable.set_KnockedAge(0); return EventResult.pass(); } @@ -56,12 +61,13 @@ public class KnockdownsEvents { entity.extinguish(); entity.setAir(entity.getMaxAir()); entity.setFrozenTicks(0); - serverPlayer.stopFallFlying(); + player.stopFallFlying(); knockable.set_KnockedDown(true); knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME); + knockable.set_KnockedAge(0); - KnockdownsNetwork.sendToWorld(serverPlayer.getServerWorld(), new PlayKnockedDownSoundS2CPacket(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ())); + KnockdownsNetwork.sendToWorld(player.getServerWorld(), new PlayKnockedDownSoundS2CPacket(player.getX(), player.getY(), player.getZ())); Text deathMessage = entity.getDamageTracker().getDeathMessage(); TranslatableTextContent content = (TranslatableTextContent) deathMessage.getContent(); @@ -75,13 +81,20 @@ public class KnockdownsEvents { private static void registerOnPlayerTick() { TickEvent.PLAYER_POST.register(player -> { - if (player.getWorld().isClient()) { + 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()); @@ -89,6 +102,7 @@ public class KnockdownsEvents { 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); @@ -96,38 +110,38 @@ public class KnockdownsEvents { } return; } - knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 2)); + knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 1)); - if (player.age % 20 == 0) { - player.setInvulnerable(false); - DamageSource recent = player.getRecentDamageSource(); - player.damage(Objects.requireNonNullElse(recent, player.getDamageSources().generic()), player.getMaxHealth() / KNOCKED_DOWN_TIMER); - player.velocityModified = false; + 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 (KnockdownsCommon.isKnockedOrReviving(player)) { + if (KnockdownsUtils.isKnockedOrReviving(player)) { return EventResult.interruptFalse(); } return EventResult.pass(); }); PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> { - if (KnockdownsCommon.isKnockedOrReviving(player)) { + if (KnockdownsUtils.isKnockedOrReviving(player)) { return EventResult.interruptFalse(); } return EventResult.pass(); }); InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> { - if (KnockdownsCommon.isKnockedOrReviving(player)) { + 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 (KnockdownsCommon.isKnockedOrReviving(player)) { + if (KnockdownsUtils.isKnockedOrReviving(player)) { return EventResult.interruptFalse(); } return EventResult.pass(); 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 index 876e0a1..ce8f9f1 100644 --- a/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java +++ b/common/src/main/java/ru/octol1ttle/knockdowns/common/mixin/PlayerEntityMixin.java @@ -30,6 +30,8 @@ public abstract class PlayerEntityMixin extends Entity implements IKnockableDown 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); @@ -65,12 +67,14 @@ public abstract class PlayerEntityMixin extends Entity implements IKnockableDown 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 @@ -112,4 +116,14 @@ public abstract class PlayerEntityMixin extends Entity implements IKnockableDown 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/gradle.properties b/gradle.properties index c3bc8bf..f07c82c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ minecraft_version=1.20.1 enabled_platforms=fabric,forge archives_base_name=knockdowns -mod_version=2.0.0 +mod_version=2.1.0 maven_group=ru.octol1ttle.knockdowns architectury_version=9.1.12