Compare commits
No commits in common. "34ea071e5fc67cfa6030916a9927a1590661066b" and "ac3d92fc97a2020e95c07fa365f1213a85e38f63" have entirely different histories.
34ea071e5f
...
ac3d92fc97
54 changed files with 855 additions and 1191 deletions
40
.github/workflows/build.yml
vendored
Normal file
40
.github/workflows/build.yml
vendored
Normal file
|
@ -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/
|
53
.gitignore
vendored
53
.gitignore
vendored
|
@ -1,19 +1,40 @@
|
||||||
build/
|
# gradle
|
||||||
*.ipr
|
|
||||||
run/
|
|
||||||
*.iws
|
|
||||||
out/
|
|
||||||
*.iml
|
|
||||||
.gradle/
|
|
||||||
output/
|
|
||||||
bin/
|
|
||||||
libs/
|
|
||||||
|
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
classes/
|
||||||
|
|
||||||
|
# eclipse
|
||||||
|
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# idea
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# vscode
|
||||||
|
|
||||||
|
.settings/
|
||||||
|
.vscode/
|
||||||
|
bin/
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.idea/
|
|
||||||
classes/
|
# macos
|
||||||
.metadata
|
|
||||||
.vscode
|
*.DS_Store
|
||||||
.settings
|
|
||||||
*.launch
|
# fabric
|
||||||
|
|
||||||
|
run/
|
||||||
|
|
||||||
|
# java
|
||||||
|
|
||||||
|
hs_err_*.log
|
||||||
|
replay_*.log
|
||||||
|
*.hprof
|
||||||
|
*.jfr
|
||||||
|
|
121
LICENSE
Normal file
121
LICENSE
Normal file
|
@ -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.
|
96
build.gradle
96
build.gradle
|
@ -1,38 +1,15 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
id 'fabric-loom' version '1.4-SNAPSHOT'
|
||||||
id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
architectury {
|
version = project.mod_version
|
||||||
minecraft = rootProject.minecraft_version
|
group = project.maven_group
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
base {
|
||||||
archivesName = rootProject.archives_base_name
|
archivesName = project.archives_base_name
|
||||||
}
|
}
|
||||||
|
|
||||||
version = rootProject.mod_version
|
|
||||||
group = rootProject.maven_group
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Add repositories to retrieve artifacts from in here.
|
// Add repositories to retrieve artifacts from in here.
|
||||||
// You should only use this when depending on other mods because
|
// You should only use this when depending on other mods because
|
||||||
|
@ -41,12 +18,71 @@ allprojects {
|
||||||
// for more information about repositories.
|
// 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 {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
options.encoding = "UTF-8"
|
it.options.release = 17
|
||||||
options.release = 17
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
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()
|
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.
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
architectury {
|
|
||||||
common(rootProject.enabled_platforms.split(","))
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath = file("src/main/resources/knockdowns.accesswidener")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
|
||||||
// Do NOT use other classes from fabric loader
|
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
|
||||||
// Remove the next line if you don't want to depend on the API
|
|
||||||
modApi "dev.architectury:architectury:${rootProject.architectury_version}"
|
|
||||||
|
|
||||||
compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.3.3"))
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenCommon(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common;
|
|
||||||
|
|
||||||
import dev.architectury.event.EventResult;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.util.hit.EntityHitResult;
|
|
||||||
import net.minecraft.util.hit.HitResult;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
import ru.octol1ttle.knockdowns.common.events.KnockdownsClientEvents;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket;
|
|
||||||
import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents;
|
|
||||||
import ru.octol1ttle.knockdowns.common.registries.KnockedDownSoundInstance;
|
|
||||||
|
|
||||||
public class KnockdownsClient {
|
|
||||||
@Nullable
|
|
||||||
public static Entity reviving;
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
KnockdownsClientEvents.registerCallbacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void playKnockedDownSound(Vec3d pos) {
|
|
||||||
MinecraftClient.getInstance().getSoundManager().play(
|
|
||||||
new KnockedDownSoundInstance(KnockdownsSoundEvents.KNOCKED_DOWN.get(), pos)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EventResult onEntityUse(PlayerEntity player, Entity entity) {
|
|
||||||
if (KnockdownsUtils.isKnockedOrReviving(player) || !(entity instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) {
|
|
||||||
return EventResult.pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
KnockdownsNetwork.sendToServer(new RequestStartRevivingC2SPacket(entity.getUuid()));
|
|
||||||
reviving = entity;
|
|
||||||
|
|
||||||
return EventResult.interruptTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onPlayerTick(PlayerEntity player) {
|
|
||||||
MinecraftClient client = MinecraftClient.getInstance();
|
|
||||||
if (!player.equals(client.player) || reviving == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean playerKnocked = ((IKnockableDown) player).is_KnockedDown();
|
|
||||||
boolean revivingTargeted = client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.ENTITY
|
|
||||||
&& ((EntityHitResult) client.crosshairTarget).getEntity().equals(reviving);
|
|
||||||
|
|
||||||
if (!(reviving instanceof IKnockableDown knockable)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!knockable.is_KnockedDown() || playerKnocked || !revivingTargeted) {
|
|
||||||
KnockdownsNetwork.sendToServer(new StopRevivingC2SPacket(reviving.getUuid()));
|
|
||||||
reviving = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common;
|
|
||||||
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import ru.octol1ttle.knockdowns.common.events.KnockdownsEvents;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
|
|
||||||
import ru.octol1ttle.knockdowns.common.registries.KnockdownsSoundEvents;
|
|
||||||
|
|
||||||
public class KnockdownsCommon {
|
|
||||||
public static final String MOD_ID = "knockdowns";
|
|
||||||
public static final int REVIVE_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND;
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
KnockdownsSoundEvents.register();
|
|
||||||
KnockdownsNetwork.registerPackets();
|
|
||||||
KnockdownsEvents.registerCallbacks();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common;
|
|
||||||
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
public class KnockdownsUtils {
|
|
||||||
public static boolean isKnockedOrReviving(PlayerEntity player) {
|
|
||||||
return player instanceof IKnockableDown knockable && (knockable.is_KnockedDown() || knockable.is_Reviving());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean allTeammatesKnocked(MinecraftServer server, PlayerEntity player) {
|
|
||||||
for (PlayerEntity teammate : server.getPlayerManager().getPlayerList()) {
|
|
||||||
if (teammate.equals(player)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
IKnockableDown knockable = (IKnockableDown) teammate;
|
|
||||||
if (!knockable.is_KnockedDown() && !player.isDead()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hurtTenacity(PlayerEntity player, float damage) {
|
|
||||||
player.setInvulnerable(false);
|
|
||||||
//DamageSource recent = player.getRecentDamageSource();
|
|
||||||
player.damage(/*Objects.requireNonNullElse(recent, */player.getDamageSources().generic()/*)*/, damage);
|
|
||||||
player.velocityModified = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.api;
|
|
||||||
|
|
||||||
public interface IKnockableDown {
|
|
||||||
boolean is_KnockedDown();
|
|
||||||
|
|
||||||
void set_KnockedDown(boolean knockedDown);
|
|
||||||
|
|
||||||
int get_ReviverCount();
|
|
||||||
|
|
||||||
void set_ReviverCount(int reviverCount);
|
|
||||||
|
|
||||||
boolean is_Reviving();
|
|
||||||
|
|
||||||
void set_Reviving(boolean reviving);
|
|
||||||
|
|
||||||
int get_ReviveTimer();
|
|
||||||
|
|
||||||
void set_ReviveTimer(int reviveTimer);
|
|
||||||
|
|
||||||
int get_KnockedAge();
|
|
||||||
|
|
||||||
void set_KnockedAge(int knockedAge);
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.events;
|
|
||||||
|
|
||||||
import dev.architectury.event.events.client.ClientGuiEvent;
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.font.TextRenderer;
|
|
||||||
import net.minecraft.util.Formatting;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsClient;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
public class KnockdownsClientEvents {
|
|
||||||
public static void registerCallbacks() {
|
|
||||||
registerOnHudRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerOnHudRender() {
|
|
||||||
ClientGuiEvent.RENDER_HUD.register((drawContext, tickDelta) -> {
|
|
||||||
IKnockableDown reviving = (IKnockableDown) KnockdownsClient.reviving;
|
|
||||||
MinecraftClient client = MinecraftClient.getInstance();
|
|
||||||
if (reviving == null) {
|
|
||||||
reviving = (IKnockableDown) client.player;
|
|
||||||
if (reviving == null || reviving.get_ReviverCount() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextRenderer renderer = client.textRenderer;
|
|
||||||
|
|
||||||
String timerText = String.format("%.1f", reviving.get_ReviveTimer() / (float) SharedConstants.TICKS_PER_SECOND);
|
|
||||||
int timerX = (drawContext.getScaledWindowWidth() - renderer.getWidth(timerText)) / 2;
|
|
||||||
|
|
||||||
int reviverCount = reviving.get_ReviverCount();
|
|
||||||
Integer color = reviverCount > 1 ? Formatting.GREEN.getColorValue() : Formatting.WHITE.getColorValue();
|
|
||||||
|
|
||||||
String reviverCountText = "x" + reviverCount;
|
|
||||||
int reviveCountX = (drawContext.getScaledWindowWidth() - renderer.getWidth(reviverCountText)) / 2;
|
|
||||||
|
|
||||||
if (color != null) {
|
|
||||||
drawContext.drawTextWithShadow(renderer, timerText, timerX, drawContext.getScaledWindowHeight() / 2 + 5, color);
|
|
||||||
drawContext.drawTextWithShadow(renderer, reviverCountText, reviveCountX, drawContext.getScaledWindowHeight() / 2 + 14, color);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.events;
|
|
||||||
|
|
||||||
import dev.architectury.event.CompoundEventResult;
|
|
||||||
import dev.architectury.event.EventResult;
|
|
||||||
import dev.architectury.event.events.common.EntityEvent;
|
|
||||||
import dev.architectury.event.events.common.InteractionEvent;
|
|
||||||
import dev.architectury.event.events.common.PlayerEvent;
|
|
||||||
import dev.architectury.event.events.common.TickEvent;
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.text.TranslatableTextContent;
|
|
||||||
import net.minecraft.util.Hand;
|
|
||||||
import net.minecraft.util.math.MathHelper;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsClient;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsUtils;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.KnockdownsNetwork;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket;
|
|
||||||
|
|
||||||
public class KnockdownsEvents {
|
|
||||||
private static final float KNOCKED_INVULNERABILITY_TICKS = 3.0f * SharedConstants.TICKS_PER_SECOND;
|
|
||||||
private static final float KNOCKED_HURT_PERIOD = 1.2f;
|
|
||||||
private static final float KNOCKED_TENACITY = 60.0f;
|
|
||||||
|
|
||||||
public static void registerCallbacks() {
|
|
||||||
registerOnLivingDeath();
|
|
||||||
registerOnPlayerTick();
|
|
||||||
registerOnPlayerInteractions();
|
|
||||||
registerOnEntityUse();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerOnLivingDeath() {
|
|
||||||
EntityEvent.LIVING_DEATH.register((entity, source) -> {
|
|
||||||
MinecraftServer server = entity.getServer();
|
|
||||||
if (server == null || !(entity instanceof IKnockableDown knockable)) {
|
|
||||||
return EventResult.pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerPlayerEntity player = (ServerPlayerEntity) entity;
|
|
||||||
|
|
||||||
if (KnockdownsUtils.allTeammatesKnocked(server, player)) {
|
|
||||||
return EventResult.pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (knockable.is_KnockedDown()) {
|
|
||||||
knockable.set_KnockedDown(false);
|
|
||||||
knockable.set_ReviverCount(0);
|
|
||||||
knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME);
|
|
||||||
knockable.set_KnockedAge(0);
|
|
||||||
|
|
||||||
return EventResult.pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
entity.clearStatusEffects();
|
|
||||||
entity.setInvulnerable(true);
|
|
||||||
entity.setGlowing(true);
|
|
||||||
entity.setHealth(entity.getMaxHealth());
|
|
||||||
entity.extinguish();
|
|
||||||
entity.setAir(entity.getMaxAir());
|
|
||||||
entity.setFrozenTicks(0);
|
|
||||||
player.stopFallFlying();
|
|
||||||
|
|
||||||
knockable.set_KnockedDown(true);
|
|
||||||
knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME);
|
|
||||||
knockable.set_KnockedAge(0);
|
|
||||||
|
|
||||||
KnockdownsNetwork.sendToWorld(player.getServerWorld(), new PlayKnockedDownSoundS2CPacket(player.getX(), player.getY(), player.getZ()));
|
|
||||||
|
|
||||||
Text deathMessage = entity.getDamageTracker().getDeathMessage();
|
|
||||||
TranslatableTextContent content = (TranslatableTextContent) deathMessage.getContent();
|
|
||||||
Text replaced = Text.translatableWithFallback(content.getKey().replace("death.", "knockdown."), deathMessage.getString(), content.getArgs());
|
|
||||||
|
|
||||||
server.getPlayerManager().broadcast(replaced, false);
|
|
||||||
|
|
||||||
return EventResult.interruptFalse();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerOnPlayerTick() {
|
|
||||||
TickEvent.PLAYER_POST.register(player -> {
|
|
||||||
MinecraftServer server = player.getServer();
|
|
||||||
if (server == null) {
|
|
||||||
KnockdownsClient.onPlayerTick(player);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!(player instanceof IKnockableDown knockable) || !knockable.is_KnockedDown()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KnockdownsUtils.allTeammatesKnocked(server, player)) {
|
|
||||||
KnockdownsUtils.hurtTenacity(player, player.getMaxHealth());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (knockable.get_ReviverCount() > 0) {
|
|
||||||
knockable.set_ReviveTimer(knockable.get_ReviveTimer() - knockable.get_ReviverCount());
|
|
||||||
|
|
||||||
if (knockable.get_ReviveTimer() <= 0) {
|
|
||||||
knockable.set_KnockedDown(false);
|
|
||||||
knockable.set_ReviverCount(0);
|
|
||||||
knockable.set_ReviveTimer(KnockdownsCommon.REVIVE_WAIT_TIME);
|
|
||||||
knockable.set_KnockedAge(0);
|
|
||||||
|
|
||||||
player.setInvulnerable(false);
|
|
||||||
player.setGlowing(false);
|
|
||||||
player.setHealth(6.0f);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
knockable.set_ReviveTimer(Math.min(KnockdownsCommon.REVIVE_WAIT_TIME, knockable.get_ReviveTimer() + 1));
|
|
||||||
|
|
||||||
knockable.set_KnockedAge(knockable.get_KnockedAge() + 1);
|
|
||||||
|
|
||||||
int period = MathHelper.floor(KNOCKED_HURT_PERIOD * SharedConstants.TICKS_PER_SECOND);
|
|
||||||
if (knockable.get_KnockedAge() >= KNOCKED_INVULNERABILITY_TICKS && knockable.get_KnockedAge() % period == 0) {
|
|
||||||
KnockdownsUtils.hurtTenacity(player, player.getMaxHealth() / (KNOCKED_TENACITY / KNOCKED_HURT_PERIOD));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerOnPlayerInteractions() {
|
|
||||||
InteractionEvent.LEFT_CLICK_BLOCK.register((player, hand, pos, direction) -> {
|
|
||||||
if (KnockdownsUtils.isKnockedOrReviving(player)) {
|
|
||||||
return EventResult.interruptFalse();
|
|
||||||
}
|
|
||||||
return EventResult.pass();
|
|
||||||
});
|
|
||||||
PlayerEvent.ATTACK_ENTITY.register((player, world, hand, entity, hitResult) -> {
|
|
||||||
if (KnockdownsUtils.isKnockedOrReviving(player)) {
|
|
||||||
return EventResult.interruptFalse();
|
|
||||||
}
|
|
||||||
return EventResult.pass();
|
|
||||||
});
|
|
||||||
InteractionEvent.RIGHT_CLICK_ITEM.register((player, hand) -> {
|
|
||||||
if (KnockdownsUtils.isKnockedOrReviving(player)) {
|
|
||||||
return CompoundEventResult.interruptFalse(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack());
|
|
||||||
}
|
|
||||||
return CompoundEventResult.pass();
|
|
||||||
});
|
|
||||||
InteractionEvent.RIGHT_CLICK_BLOCK.register((player, hand, pos, direction) -> {
|
|
||||||
if (KnockdownsUtils.isKnockedOrReviving(player)) {
|
|
||||||
return EventResult.interruptFalse();
|
|
||||||
}
|
|
||||||
return EventResult.pass();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerOnEntityUse() {
|
|
||||||
InteractionEvent.INTERACT_ENTITY.register((player, entity, hand)
|
|
||||||
-> player.getWorld().isClient() ? KnockdownsClient.onEntityUse(player, entity) : EventResult.pass());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.mixin;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
|
||||||
import net.minecraft.entity.LivingEntity;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
@Mixin(LivingEntity.class)
|
|
||||||
public abstract class LivingEntityMixin {
|
|
||||||
@ModifyReturnValue(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("RETURN"))
|
|
||||||
private boolean dontTargetKnockedPlayers(boolean original, LivingEntity target) {
|
|
||||||
return original && !(target instanceof IKnockableDown knockable && knockable.is_KnockedDown());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.mixin;
|
|
||||||
|
|
||||||
import net.minecraft.entity.LivingEntity;
|
|
||||||
import net.minecraft.entity.mob.MobEntity;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
@Mixin(MobEntity.class)
|
|
||||||
public abstract class MobEntityMixin {
|
|
||||||
@Inject(method = "setTarget", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void setTarget(LivingEntity target, CallbackInfo ci) {
|
|
||||||
if (target instanceof IKnockableDown knockable && knockable.is_KnockedDown()) {
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.mixin;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.entity.EntityType;
|
|
||||||
import net.minecraft.entity.data.DataTracker;
|
|
||||||
import net.minecraft.entity.data.TrackedData;
|
|
||||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
@SuppressWarnings("WrongEntityDataParameterClass")
|
|
||||||
@Mixin(PlayerEntity.class)
|
|
||||||
public abstract class PlayerEntityMixin extends Entity implements IKnockableDown {
|
|
||||||
@Unique
|
|
||||||
private static final TrackedData<Boolean> KNOCKED_DOWN = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
|
||||||
@Unique
|
|
||||||
private static final TrackedData<Boolean> IS_REVIVING = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
|
||||||
@Unique
|
|
||||||
private static final TrackedData<Integer> REVIVER_COUNT = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
|
||||||
@Unique
|
|
||||||
private static final TrackedData<Integer> REVIVE_TIMER = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
|
||||||
@Unique
|
|
||||||
public int knocked_age;
|
|
||||||
|
|
||||||
private PlayerEntityMixin(EntityType<?> type, World world) {
|
|
||||||
super(type, world);
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z"))
|
|
||||||
private boolean enterSwimmingIfKnockedDown(boolean original) {
|
|
||||||
return original || this.is_KnockedDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN"))
|
|
||||||
private boolean dontHealIfKnockedDown(boolean original) {
|
|
||||||
return original && !this.is_KnockedDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "checkFallFlying", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void dontOpenElytraIfKnockedDown(CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
if (this.is_KnockedDown()) {
|
|
||||||
cir.setReturnValue(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "initDataTracker", at = @At("TAIL"))
|
|
||||||
private void initCustomDataTracker(CallbackInfo ci) {
|
|
||||||
this.dataTracker.startTracking(KNOCKED_DOWN, false);
|
|
||||||
this.dataTracker.startTracking(IS_REVIVING, false);
|
|
||||||
this.dataTracker.startTracking(REVIVER_COUNT, 0);
|
|
||||||
this.dataTracker.startTracking(REVIVE_TIMER, KnockdownsCommon.REVIVE_WAIT_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
|
|
||||||
private void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) {
|
|
||||||
this.set_KnockedDown(nbt.getBoolean("KnockedDown"));
|
|
||||||
this.set_ReviveTimer(nbt.getInt("ReviveTimer"));
|
|
||||||
this.set_KnockedAge(nbt.getInt("KnockedAge"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
|
|
||||||
private void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) {
|
|
||||||
nbt.putBoolean("KnockedDown", this.is_KnockedDown());
|
|
||||||
nbt.putInt("ReviveTimer", this.get_ReviveTimer());
|
|
||||||
nbt.putInt("KnockedAge", this.get_KnockedAge());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean is_KnockedDown() {
|
|
||||||
return this.dataTracker.get(KNOCKED_DOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set_KnockedDown(boolean knockedDown) {
|
|
||||||
this.dataTracker.set(KNOCKED_DOWN, knockedDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean is_Reviving() {
|
|
||||||
return this.dataTracker.get(IS_REVIVING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set_Reviving(boolean reviving) {
|
|
||||||
this.dataTracker.set(IS_REVIVING, reviving);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_ReviverCount() {
|
|
||||||
return this.dataTracker.get(REVIVER_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set_ReviverCount(int reviverCount) {
|
|
||||||
this.dataTracker.set(REVIVER_COUNT, reviverCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_ReviveTimer() {
|
|
||||||
return this.dataTracker.get(REVIVE_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set_ReviveTimer(int reviveTimer) {
|
|
||||||
this.dataTracker.set(REVIVE_TIMER, reviveTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get_KnockedAge() {
|
|
||||||
return knocked_age;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set_KnockedAge(int knockedAge) {
|
|
||||||
this.knocked_age = knockedAge;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.network;
|
|
||||||
|
|
||||||
import dev.architectury.networking.NetworkChannel;
|
|
||||||
import dev.architectury.networking.NetworkManager;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.network.packet.Packet;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.PlayKnockedDownSoundS2CPacket;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.RequestStartRevivingC2SPacket;
|
|
||||||
import ru.octol1ttle.knockdowns.common.network.packets.StopRevivingC2SPacket;
|
|
||||||
|
|
||||||
public class KnockdownsNetwork {
|
|
||||||
private static final NetworkChannel CHANNEL = NetworkChannel.create(new Identifier(KnockdownsCommon.MOD_ID, "main"));
|
|
||||||
public static void registerPackets() {
|
|
||||||
CHANNEL.register(PlayKnockedDownSoundS2CPacket.class, PlayKnockedDownSoundS2CPacket::encode, PlayKnockedDownSoundS2CPacket::new, PlayKnockedDownSoundS2CPacket::apply);
|
|
||||||
CHANNEL.register(RequestStartRevivingC2SPacket.class, RequestStartRevivingC2SPacket::encode, RequestStartRevivingC2SPacket::new, RequestStartRevivingC2SPacket::apply);
|
|
||||||
CHANNEL.register(StopRevivingC2SPacket.class, StopRevivingC2SPacket::encode, StopRevivingC2SPacket::new, StopRevivingC2SPacket::apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> void sendToServer(T message) {
|
|
||||||
if (CHANNEL.canServerReceive(message.getClass())) {
|
|
||||||
CHANNEL.sendToServer(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> void sendToPlayer(PlayerEntity player, T message) {
|
|
||||||
Packet<?> packet = CHANNEL.toPacket(NetworkManager.Side.S2C, message);
|
|
||||||
Class<?> messageClass = message.getClass();
|
|
||||||
|
|
||||||
sendToPlayer(player, packet, messageClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendToPlayer(PlayerEntity player, Packet<?> packet, Class<?> messageClass) {
|
|
||||||
if (!(player instanceof ServerPlayerEntity serverPlayer)) {
|
|
||||||
throw new IllegalArgumentException("Cannot send to client players");
|
|
||||||
}
|
|
||||||
if (CHANNEL.canPlayerReceive(serverPlayer, messageClass)) {
|
|
||||||
serverPlayer.networkHandler.sendPacket(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.network.packets;
|
|
||||||
|
|
||||||
import dev.architectury.networking.NetworkManager;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
|
|
||||||
public abstract class KnockdownsPacket {
|
|
||||||
public KnockdownsPacket(/* args here */) {
|
|
||||||
// Message creation
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void encode(PacketByteBuf buf);
|
|
||||||
|
|
||||||
public abstract void apply(Supplier<NetworkManager.PacketContext> contextSupplier);
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.network.packets;
|
|
||||||
|
|
||||||
import dev.architectury.networking.NetworkManager;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsClient;
|
|
||||||
|
|
||||||
public class PlayKnockedDownSoundS2CPacket extends KnockdownsPacket {
|
|
||||||
private final double x;
|
|
||||||
private final double y;
|
|
||||||
private final double z;
|
|
||||||
|
|
||||||
public PlayKnockedDownSoundS2CPacket(PacketByteBuf buf) {
|
|
||||||
this(buf.readDouble(), buf.readDouble(), buf.readDouble());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayKnockedDownSoundS2CPacket(double x, double y, double z) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(PacketByteBuf buf) {
|
|
||||||
buf.writeDouble(this.x);
|
|
||||||
buf.writeDouble(this.y);
|
|
||||||
buf.writeDouble(this.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(Supplier<NetworkManager.PacketContext> contextSupplier) {
|
|
||||||
NetworkManager.PacketContext context = contextSupplier.get();
|
|
||||||
context.queue(() -> KnockdownsClient.playKnockedDownSound(new Vec3d(this.x, this.y, this.z)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.network.packets;
|
|
||||||
|
|
||||||
import dev.architectury.networking.NetworkManager;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
public class RequestStartRevivingC2SPacket extends KnockdownsPacket {
|
|
||||||
private final UUID targetUuid;
|
|
||||||
|
|
||||||
public RequestStartRevivingC2SPacket(PacketByteBuf buf) {
|
|
||||||
this(buf.readUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RequestStartRevivingC2SPacket(UUID targetUuid) {
|
|
||||||
this.targetUuid = targetUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(PacketByteBuf buf) {
|
|
||||||
buf.writeUuid(this.targetUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(Supplier<NetworkManager.PacketContext> 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.network.packets;
|
|
||||||
|
|
||||||
import dev.architectury.networking.NetworkManager;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
|
||||||
|
|
||||||
public class StopRevivingC2SPacket extends KnockdownsPacket {
|
|
||||||
private final UUID targetUuid;
|
|
||||||
|
|
||||||
public StopRevivingC2SPacket(PacketByteBuf buf) {
|
|
||||||
this(buf.readUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
public StopRevivingC2SPacket(UUID targetUuid) {
|
|
||||||
this.targetUuid = targetUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(PacketByteBuf buf) {
|
|
||||||
buf.writeUuid(this.targetUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(Supplier<NetworkManager.PacketContext> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.registries;
|
|
||||||
|
|
||||||
import dev.architectury.registry.registries.DeferredRegister;
|
|
||||||
import dev.architectury.registry.registries.RegistrySupplier;
|
|
||||||
import net.minecraft.registry.RegistryKeys;
|
|
||||||
import net.minecraft.sound.SoundEvent;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
|
|
||||||
public class KnockdownsSoundEvents {
|
|
||||||
private static final DeferredRegister<SoundEvent> SOUND_EVENTS = DeferredRegister.create(KnockdownsCommon.MOD_ID, RegistryKeys.SOUND_EVENT);
|
|
||||||
public static final RegistrySupplier<SoundEvent> KNOCKED_DOWN = SOUND_EVENTS.register(KnockdownsCommon.MOD_ID,
|
|
||||||
() -> SoundEvent.of(new Identifier(KnockdownsCommon.MOD_ID, "knocked_down")));
|
|
||||||
|
|
||||||
public static void register() {
|
|
||||||
SOUND_EVENTS.register();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.common.registries;
|
|
||||||
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
|
||||||
import net.minecraft.client.sound.MovingSoundInstance;
|
|
||||||
import net.minecraft.sound.SoundCategory;
|
|
||||||
import net.minecraft.sound.SoundEvent;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import net.minecraft.util.math.random.Random;
|
|
||||||
|
|
||||||
public class KnockedDownSoundInstance extends MovingSoundInstance {
|
|
||||||
private final Vec3d pos;
|
|
||||||
|
|
||||||
public KnockedDownSoundInstance(SoundEvent sound, Vec3d pos) {
|
|
||||||
super(sound, SoundCategory.MASTER, Random.create(0L));
|
|
||||||
this.pos = pos;
|
|
||||||
this.relative = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick() {
|
|
||||||
ClientPlayerEntity player = MinecraftClient.getInstance().player;
|
|
||||||
if (player == null) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
Vec3d vec = pos.subtract(player.getPos()).normalize();
|
|
||||||
this.x = vec.x;
|
|
||||||
this.y = vec.y;
|
|
||||||
this.z = vec.z;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"accessWidener": "knockdowns.accesswidener"
|
|
||||||
}
|
|
Binary file not shown.
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "ru.octol1ttle.knockdowns.common.mixin",
|
|
||||||
"compatibilityLevel": "JAVA_17",
|
|
||||||
"minVersion": "0.8",
|
|
||||||
"client": [
|
|
||||||
"client.ClientPlayerEntityMixin"
|
|
||||||
],
|
|
||||||
"mixins": [
|
|
||||||
"LivingEntityMixin",
|
|
||||||
"MobEntityMixin",
|
|
||||||
"PlayerEntityMixin"
|
|
||||||
],
|
|
||||||
"injectors": {
|
|
||||||
"defaultRequire": 1
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
accessWidener v2 named
|
|
|
@ -1,77 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
|
||||||
}
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
platformSetupLoomIde()
|
|
||||||
fabric()
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath = project(":common").loom.accessWidenerPath
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
common
|
|
||||||
shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files.
|
|
||||||
compileClasspath.extendsFrom common
|
|
||||||
runtimeClasspath.extendsFrom common
|
|
||||||
developmentFabric.extendsFrom common
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
|
||||||
modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
|
|
||||||
// Remove the next line if you don't want to depend on the API
|
|
||||||
modApi "dev.architectury:architectury-fabric:${rootProject.architectury_version}"
|
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
|
||||||
shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
inputs.property "version", project.version
|
|
||||||
|
|
||||||
filesMatching("fabric.mod.json") {
|
|
||||||
expand "version": project.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
exclude "architectury.common.json"
|
|
||||||
|
|
||||||
configurations = [project.configurations.shadowCommon]
|
|
||||||
archiveClassifier = "dev-shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
injectAccessWidener = true
|
|
||||||
input.set shadowJar.archiveFile
|
|
||||||
dependsOn shadowJar
|
|
||||||
}
|
|
||||||
|
|
||||||
sourcesJar {
|
|
||||||
def commonSources = project(":common").sourcesJar
|
|
||||||
dependsOn commonSources
|
|
||||||
from commonSources.archiveFile.map { zipTree(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
components.java {
|
|
||||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
|
||||||
skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenFabric(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name + "-" + project.name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.fabric;
|
|
||||||
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
|
||||||
|
|
||||||
public class KnockdownsFabric implements ModInitializer {
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
KnockdownsCommon.init();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.fabric;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsClient;
|
|
||||||
|
|
||||||
public class KnockdownsFabricClient implements ClientModInitializer {
|
|
||||||
@Override
|
|
||||||
public void onInitializeClient() {
|
|
||||||
KnockdownsClient.init();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"id": "knockdowns",
|
|
||||||
"version": "${version}",
|
|
||||||
"name": "Knockdowns",
|
|
||||||
"description": "DBNO mechanic from Fortnite, ported to Minecraft",
|
|
||||||
"authors": [
|
|
||||||
"Octol1ttle"
|
|
||||||
],
|
|
||||||
"contact": {
|
|
||||||
"homepage": "https://fabricmc.net/",
|
|
||||||
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
|
||||||
},
|
|
||||||
"license": "ARR",
|
|
||||||
"icon": "assets/knockdowns/icon.png",
|
|
||||||
"environment": "*",
|
|
||||||
"entrypoints": {
|
|
||||||
"main": [
|
|
||||||
"ru.octol1ttle.knockdowns.fabric.KnockdownsFabric"
|
|
||||||
],
|
|
||||||
"client": [
|
|
||||||
"ru.octol1ttle.knockdowns.fabric.KnockdownsFabricClient"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mixins": [
|
|
||||||
"knockdowns.mixins.json",
|
|
||||||
"knockdowns-common.mixins.json"
|
|
||||||
],
|
|
||||||
"depends": {
|
|
||||||
"fabric": "*",
|
|
||||||
"minecraft": ">=1.20.1",
|
|
||||||
"architectury": ">=9.1.12"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "ru.octol1ttle.knockdowns.fabric.mixin",
|
|
||||||
"compatibilityLevel": "JAVA_17",
|
|
||||||
"minVersion": "0.8",
|
|
||||||
"client": [
|
|
||||||
],
|
|
||||||
"mixins": [
|
|
||||||
],
|
|
||||||
"injectors": {
|
|
||||||
"defaultRequire": 1
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
|
||||||
}
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
platformSetupLoomIde()
|
|
||||||
forge()
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath = project(":common").loom.accessWidenerPath
|
|
||||||
|
|
||||||
forge {
|
|
||||||
convertAccessWideners = true
|
|
||||||
extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
|
|
||||||
|
|
||||||
mixinConfig "knockdowns-common.mixins.json"
|
|
||||||
mixinConfig "knockdowns.mixins.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
common
|
|
||||||
shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files.
|
|
||||||
compileClasspath.extendsFrom common
|
|
||||||
runtimeClasspath.extendsFrom common
|
|
||||||
developmentForge.extendsFrom common
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
forge "net.minecraftforge:forge:${rootProject.forge_version}"
|
|
||||||
// Remove the next line if you don't want to depend on the API
|
|
||||||
modApi "dev.architectury:architectury-forge:${rootProject.architectury_version}"
|
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
|
||||||
shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
|
|
||||||
|
|
||||||
implementation(include("io.github.llamalad7:mixinextras-forge:0.3.3"))
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
inputs.property "version", project.version
|
|
||||||
|
|
||||||
filesMatching("META-INF/mods.toml") {
|
|
||||||
expand "version": project.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
exclude "fabric.mod.json"
|
|
||||||
exclude "architectury.common.json"
|
|
||||||
|
|
||||||
configurations = [project.configurations.shadowCommon]
|
|
||||||
archiveClassifier = "dev-shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
input.set shadowJar.archiveFile
|
|
||||||
dependsOn shadowJar
|
|
||||||
}
|
|
||||||
|
|
||||||
sourcesJar {
|
|
||||||
def commonSources = project(":common").sourcesJar
|
|
||||||
dependsOn commonSources
|
|
||||||
from commonSources.archiveFile.map { zipTree(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
components.java {
|
|
||||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
|
||||||
skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenForge(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name + "-" + project.name
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
|
||||||
repositories {
|
|
||||||
// Add repositories to publish to here.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
loom.platform=forge
|
|
|
@ -1,26 +0,0 @@
|
||||||
package ru.octol1ttle.knockdowns.forge;
|
|
||||||
|
|
||||||
import dev.architectury.platform.forge.EventBuses;
|
|
||||||
import net.minecraftforge.eventbus.api.IEventBus;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
|
||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsClient;
|
|
||||||
import ru.octol1ttle.knockdowns.common.KnockdownsCommon;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Mod(KnockdownsCommon.MOD_ID)
|
|
||||||
public class KnockdownsForge {
|
|
||||||
public KnockdownsForge() {
|
|
||||||
// Submit our event bus to let architectury register our content on the right time
|
|
||||||
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
|
||||||
EventBuses.registerModEventBus(KnockdownsCommon.MOD_ID, modEventBus);
|
|
||||||
modEventBus.addListener(this::onInitializeClient);
|
|
||||||
|
|
||||||
KnockdownsCommon.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInitializeClient(FMLClientSetupEvent event) {
|
|
||||||
KnockdownsClient.init();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
modLoader = "javafml"
|
|
||||||
loaderVersion = "[47,)"
|
|
||||||
#issueTrackerURL = ""
|
|
||||||
license = "ARR"
|
|
||||||
|
|
||||||
[[mods]]
|
|
||||||
modId = "knockdowns"
|
|
||||||
version = "${version}"
|
|
||||||
displayName = "Knockdowns"
|
|
||||||
authors = "Octol1ttle"
|
|
||||||
description = '''
|
|
||||||
DBNO mechanic from Fortnite, ported to Minecraft
|
|
||||||
'''
|
|
||||||
#logoFile = ""
|
|
||||||
|
|
||||||
[[dependencies.knockdowns]]
|
|
||||||
modId = "forge"
|
|
||||||
mandatory = true
|
|
||||||
versionRange = "[47,)"
|
|
||||||
ordering = "NONE"
|
|
||||||
side = "BOTH"
|
|
||||||
|
|
||||||
[[dependencies.knockdowns]]
|
|
||||||
modId = "minecraft"
|
|
||||||
mandatory = true
|
|
||||||
versionRange = "[1.20.1,)"
|
|
||||||
ordering = "NONE"
|
|
||||||
side = "BOTH"
|
|
||||||
|
|
||||||
[[dependencies.knockdowns]]
|
|
||||||
modId = "architectury"
|
|
||||||
mandatory = true
|
|
||||||
versionRange = "[9.1.12,)"
|
|
||||||
ordering = "AFTER"
|
|
||||||
side = "BOTH"
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"pack": {
|
|
||||||
"description": "Knockdowns",
|
|
||||||
"pack_format": 15
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,17 @@
|
||||||
org.gradle.jvmargs=-Xmx2048M
|
# Done to increase the memory available to gradle.
|
||||||
|
org.gradle.jvmargs=-Xmx1G
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
minecraft_version=1.20.1
|
# Fabric Properties
|
||||||
enabled_platforms=fabric,forge
|
# check these on https://fabricmc.net/develop
|
||||||
|
minecraft_version=1.20.2
|
||||||
|
yarn_mappings=1.20.2+build.4
|
||||||
|
loader_version=0.14.24
|
||||||
|
|
||||||
archives_base_name=knockdowns
|
# Mod Properties
|
||||||
mod_version=2.1.0
|
mod_version=1.1.1
|
||||||
maven_group=ru.octol1ttle.knockdowns
|
maven_group=ru.octol1ttle.knockdowns
|
||||||
|
archives_base_name=knockdowns
|
||||||
|
|
||||||
architectury_version=9.1.12
|
# Dependencies
|
||||||
fabric_api_version=0.90.4+1.20.1
|
fabric_version=0.90.4+1.20.2
|
||||||
|
|
||||||
fabric_loader_version=0.15.5
|
|
||||||
forge_version=1.20.1-47.2.0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,7 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
15
gradlew
vendored
15
gradlew
vendored
|
@ -83,10 +83,8 @@ done
|
||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# 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
|
||||||
# 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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
@ -133,11 +131,14 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then
|
||||||
done
|
done
|
||||||
fi
|
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;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://maven.fabricmc.net/" }
|
maven {
|
||||||
maven { url "https://maven.architectury.dev/" }
|
name = 'Fabric'
|
||||||
maven { url "https://maven.minecraftforge.net/" }
|
url = 'https://maven.fabricmc.net/'
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include("common")
|
|
||||||
include("fabric")
|
|
||||||
include("forge")
|
|
||||||
|
|
||||||
rootProject.name = "knockdowns-modern"
|
|
||||||
|
|
166
src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java
Normal file
166
src/client/java/ru/octol1ttle/knockdowns/KnockdownsClient.java
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
package ru.octol1ttle.knockdowns;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.font.TextRenderer;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
import net.minecraft.util.ActionResult;
|
||||||
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
|
import net.minecraft.util.hit.HitResult;
|
||||||
|
import ru.octol1ttle.knockdowns.api.IKnockableDown;
|
||||||
|
import ru.octol1ttle.knockdowns.network.KnockdownsNetworkingConstants;
|
||||||
|
|
||||||
|
public class KnockdownsClient implements ClientModInitializer {
|
||||||
|
private static final int REVIVAL_WAIT_TIME = 10 * SharedConstants.TICKS_PER_SECOND;
|
||||||
|
private static IKnockableDown reviving = null;
|
||||||
|
private static int revivalTimer = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitializeClient() {
|
||||||
|
registerOnReceiveKnockedDown();
|
||||||
|
registerOnReceiveReviving();
|
||||||
|
registerOnEntityLoad();
|
||||||
|
registerOnEntityUse();
|
||||||
|
registerOnStartWorldTick();
|
||||||
|
registerOnHudRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnReceiveKnockedDown() {
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, (client, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
boolean knockedDown = buf.readBoolean();
|
||||||
|
|
||||||
|
client.execute(() -> {
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) handler.getWorld().getPlayerByUuid(uuid);
|
||||||
|
if (knockableDown != null) {
|
||||||
|
knockableDown.knockdowns$setKnockedDown(knockedDown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnReceiveReviving() {
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, (client, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
boolean beingRevived = buf.readBoolean();
|
||||||
|
|
||||||
|
client.execute(() -> {
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) handler.getWorld().getPlayerByUuid(uuid);
|
||||||
|
if (knockableDown != null) {
|
||||||
|
knockableDown.knockdowns$setBeingRevived(beingRevived);
|
||||||
|
|
||||||
|
if (client.player != null && uuid.equals(client.player.getUuid())) {
|
||||||
|
if (knockableDown.knockdowns$isBeingRevived()) {
|
||||||
|
revivalTimer = REVIVAL_WAIT_TIME;
|
||||||
|
} else {
|
||||||
|
revivalTimer = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reviving != null && knockableDown.knockdowns$getUuid().equals(reviving.knockdowns$getUuid())
|
||||||
|
&& !knockableDown.knockdowns$isBeingRevived()) {
|
||||||
|
reviving = null;
|
||||||
|
revivalTimer = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnEntityLoad() {
|
||||||
|
ClientEntityEvents.ENTITY_LOAD.register((entity, world) -> {
|
||||||
|
if (entity instanceof IKnockableDown) {
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeUuid(entity.getUuid());
|
||||||
|
|
||||||
|
ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, PacketByteBufs.copy(buf));
|
||||||
|
ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_REVIVING, buf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnEntityUse() {
|
||||||
|
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
|
||||||
|
if (!(entity instanceof IKnockableDown knockableDown) || !knockableDown.knockdowns$isKnockedDown()
|
||||||
|
|| knockableDown.knockdowns$isBeingRevived()) {
|
||||||
|
return ActionResult.PASS;
|
||||||
|
}
|
||||||
|
if (((IKnockableDown)player).knockdowns$isKnockedDown()) {
|
||||||
|
return ActionResult.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
knockableDown.knockdowns$setBeingRevived(true);
|
||||||
|
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeUuid(entity.getUuid());
|
||||||
|
buf.writeBoolean(true);
|
||||||
|
|
||||||
|
ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, buf);
|
||||||
|
|
||||||
|
reviving = knockableDown;
|
||||||
|
revivalTimer = REVIVAL_WAIT_TIME;
|
||||||
|
|
||||||
|
return ActionResult.SUCCESS;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnStartWorldTick() {
|
||||||
|
ClientTickEvents.START_WORLD_TICK.register(world -> {
|
||||||
|
boolean revived = false;
|
||||||
|
revivalTimer--;
|
||||||
|
if (revivalTimer <= 0) {
|
||||||
|
revivalTimer = -1;
|
||||||
|
revived = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reviving == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HitResult crosshairTarget = MinecraftClient.getInstance().crosshairTarget;
|
||||||
|
if (revived || crosshairTarget == null || crosshairTarget.getType() != HitResult.Type.ENTITY
|
||||||
|
|| !((EntityHitResult) crosshairTarget).getEntity().getUuid().equals(reviving.knockdowns$getUuid())) {
|
||||||
|
reviving.knockdowns$setBeingRevived(false);
|
||||||
|
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeUuid(reviving.knockdowns$getUuid());
|
||||||
|
buf.writeBoolean(false);
|
||||||
|
|
||||||
|
ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, buf);
|
||||||
|
if (revived) {
|
||||||
|
reviving.knockdowns$setKnockedDown(false);
|
||||||
|
|
||||||
|
PacketByteBuf revivedBuf = PacketByteBufs.create();
|
||||||
|
revivedBuf.writeUuid(reviving.knockdowns$getUuid());
|
||||||
|
|
||||||
|
ClientPlayNetworking.send(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVED, revivedBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
reviving = null;
|
||||||
|
revivalTimer = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnHudRender() {
|
||||||
|
HudRenderCallback.EVENT.register((drawContext, tickDelta) -> {
|
||||||
|
if (revivalTimer == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextRenderer renderer = MinecraftClient.getInstance().textRenderer;
|
||||||
|
String text = String.format("%.1f", revivalTimer / (float) SharedConstants.TICKS_PER_SECOND);
|
||||||
|
int x = (drawContext.getScaledWindowWidth() - renderer.getWidth(text)) / 2;
|
||||||
|
|
||||||
|
drawContext.drawTextWithShadow(renderer, text, x, drawContext.getScaledWindowHeight() / 2 + 15, 0xFFFFFF);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
package ru.octol1ttle.knockdowns.common.mixin.client;
|
package ru.octol1ttle.knockdowns.mixin.client;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import ru.octol1ttle.knockdowns.common.api.IKnockableDown;
|
import ru.octol1ttle.knockdowns.api.IKnockableDown;
|
||||||
|
|
||||||
@Mixin(ClientPlayerEntity.class)
|
@Mixin(ClientPlayerEntity.class)
|
||||||
public abstract class ClientPlayerEntityMixin implements IKnockableDown {
|
public abstract class ClientPlayerEntityMixin {
|
||||||
@ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN"))
|
@ModifyReturnValue(method = "shouldSlowDown", at = @At("RETURN"))
|
||||||
private boolean shouldSlowDown(boolean original) {
|
private boolean shouldSlowDown(boolean original) {
|
||||||
return original || this.is_KnockedDown();
|
return original || ((IKnockableDown) this).knockdowns$isKnockedDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,9 @@
|
||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"package": "ru.octol1ttle.knockdowns.forge.mixin",
|
"package": "ru.octol1ttle.knockdowns.mixin.client",
|
||||||
"compatibilityLevel": "JAVA_17",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"minVersion": "0.8",
|
|
||||||
"client": [
|
"client": [
|
||||||
],
|
"ClientPlayerEntityMixin"
|
||||||
"mixins": [
|
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
219
src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java
Normal file
219
src/main/java/ru/octol1ttle/knockdowns/Knockdowns.java
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
package ru.octol1ttle.knockdowns;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
|
||||||
|
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
|
import net.fabricmc.fabric.api.event.player.UseItemCallback;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.registry.Registry;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.text.TranslatableTextContent;
|
||||||
|
import net.minecraft.util.ActionResult;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.TypedActionResult;
|
||||||
|
import net.minecraft.world.GameRules;
|
||||||
|
import ru.octol1ttle.knockdowns.api.IKnockableDown;
|
||||||
|
import ru.octol1ttle.knockdowns.network.KnockdownsNetworkingConstants;
|
||||||
|
|
||||||
|
public class Knockdowns implements ModInitializer {
|
||||||
|
public static final String MOD_ID = "knockdowns";
|
||||||
|
private static final Identifier KNOCKED_DOWN_ID = new Identifier("knockdowns:knocked_down");
|
||||||
|
private static final SoundEvent KNOCKED_DOWN = SoundEvent.of(KNOCKED_DOWN_ID);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
registerSoundEvents();
|
||||||
|
registerOnDeath();
|
||||||
|
registerOnRequestKnockedDown();
|
||||||
|
registerOnRequestReviving();
|
||||||
|
registerOnReceiveReviving();
|
||||||
|
registerOnReceiveRevived();
|
||||||
|
registerOnPlayerInteractions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerSoundEvents() {
|
||||||
|
Registry.register(Registries.SOUND_EVENT, KNOCKED_DOWN_ID, KNOCKED_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnDeath() {
|
||||||
|
ServerLivingEntityEvents.ALLOW_DEATH.register((entity, damageSource, damageAmount) -> {
|
||||||
|
if (!(entity instanceof IKnockableDown knockableDown) || knockableDown.knockdowns$isKnockedDown()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) entity;
|
||||||
|
// TODO: timer
|
||||||
|
if (!serverPlayer.getWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) {
|
||||||
|
serverPlayer.getInventory().dropAll();
|
||||||
|
}
|
||||||
|
entity.setHealth(1.0f);
|
||||||
|
entity.setInvulnerable(true);
|
||||||
|
entity.setGlowing(true);
|
||||||
|
entity.setAir(entity.getMaxAir());
|
||||||
|
entity.extinguish();
|
||||||
|
entity.setFrozenTicks(0);
|
||||||
|
entity.setOnFire(false);
|
||||||
|
entity.clearStatusEffects();
|
||||||
|
|
||||||
|
knockableDown.knockdowns$setKnockedDown(true);
|
||||||
|
|
||||||
|
PacketByteBuf buf = PacketByteBufs.create();
|
||||||
|
buf.writeUuid(entity.getUuid());
|
||||||
|
buf.writeBoolean(true);
|
||||||
|
|
||||||
|
ServerPlayNetworking.send(serverPlayer, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, buf);
|
||||||
|
for (ServerPlayerEntity player : PlayerLookup.tracking(entity)) {
|
||||||
|
ServerPlayNetworking.send(player, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ServerPlayerEntity player : serverPlayer.getServerWorld().getPlayers()) {
|
||||||
|
player.networkHandler.sendPacket(new PlaySoundS2CPacket(Registries.SOUND_EVENT.getEntry(KNOCKED_DOWN), SoundCategory.PLAYERS,
|
||||||
|
player.getX(), player.getY(), player.getZ(), 1.0f, 1.0f, 0L));
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslatableTextContent content = (TranslatableTextContent) entity.getDamageTracker().getDeathMessage().getContent();
|
||||||
|
Text replaced = Text.translatable(content.getKey().replace("death.", "knockdown."), content.getArgs());
|
||||||
|
MinecraftServer server = serverPlayer.getServer();
|
||||||
|
if (server != null) {
|
||||||
|
server.getPlayerManager().broadcast(replaced, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnRequestKnockedDown() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_KNOCKED_DOWN, (server, player, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
|
||||||
|
server.execute(() -> {
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) player.getWorld().getPlayerByUuid(uuid);
|
||||||
|
if (knockableDown == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketByteBuf responseBuf = PacketByteBufs.create();
|
||||||
|
responseBuf.writeUuid(uuid);
|
||||||
|
responseBuf.writeBoolean(knockableDown.knockdowns$isKnockedDown());
|
||||||
|
|
||||||
|
responseSender.sendPacket(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, responseBuf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnRequestReviving() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_REQUEST_PLAYER_REVIVING, (server, player, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
|
||||||
|
server.execute(() -> {
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) player.getWorld().getPlayerByUuid(uuid);
|
||||||
|
if (knockableDown == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketByteBuf responseBuf = PacketByteBufs.create();
|
||||||
|
responseBuf.writeUuid(uuid);
|
||||||
|
responseBuf.writeBoolean(knockableDown.knockdowns$isBeingRevived());
|
||||||
|
|
||||||
|
responseSender.sendPacket(KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, responseBuf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnReceiveReviving() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVING, (server, player, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
boolean beingRevived = buf.readBoolean();
|
||||||
|
|
||||||
|
// TODO: revival by multiple players
|
||||||
|
server.execute(() -> {
|
||||||
|
PlayerEntity reviving = player.getWorld().getPlayerByUuid(uuid);
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) reviving;
|
||||||
|
if (knockableDown == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
knockableDown.knockdowns$setBeingRevived(beingRevived);
|
||||||
|
|
||||||
|
PacketByteBuf broadcastBuf = PacketByteBufs.create();
|
||||||
|
broadcastBuf.writeUuid(uuid);
|
||||||
|
broadcastBuf.writeBoolean(beingRevived);
|
||||||
|
|
||||||
|
ServerPlayNetworking.send((ServerPlayerEntity) reviving, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, broadcastBuf);
|
||||||
|
for (ServerPlayerEntity entity : PlayerLookup.tracking(reviving)) {
|
||||||
|
ServerPlayNetworking.send(entity, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_REVIVING, broadcastBuf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnReceiveRevived() {
|
||||||
|
ServerPlayNetworking.registerGlobalReceiver(KnockdownsNetworkingConstants.C2S_SEND_PLAYER_REVIVED, (server, player, handler, buf, responseSender) -> {
|
||||||
|
UUID uuid = buf.readUuid();
|
||||||
|
|
||||||
|
server.execute(() -> {
|
||||||
|
PlayerEntity reviving = player.getWorld().getPlayerByUuid(uuid);
|
||||||
|
IKnockableDown knockableDown = (IKnockableDown) reviving;
|
||||||
|
if (knockableDown == null || !knockableDown.knockdowns$isKnockedDown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reviving.setInvulnerable(false);
|
||||||
|
reviving.setGlowing(false);
|
||||||
|
reviving.setHealth(reviving.getHealth() + 5.0f);
|
||||||
|
|
||||||
|
knockableDown.knockdowns$setKnockedDown(false);
|
||||||
|
|
||||||
|
PacketByteBuf sendBuf = PacketByteBufs.create();
|
||||||
|
sendBuf.writeUuid(reviving.getUuid());
|
||||||
|
sendBuf.writeBoolean(false);
|
||||||
|
|
||||||
|
ServerPlayNetworking.send((ServerPlayerEntity) reviving, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, sendBuf);
|
||||||
|
for (ServerPlayerEntity entity : PlayerLookup.tracking(reviving)) {
|
||||||
|
ServerPlayNetworking.send(entity, KnockdownsNetworkingConstants.S2C_SEND_PLAYER_KNOCKED_DOWN, sendBuf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerOnPlayerInteractions() {
|
||||||
|
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
|
||||||
|
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) {
|
||||||
|
return ActionResult.FAIL;
|
||||||
|
}
|
||||||
|
return ActionResult.PASS;
|
||||||
|
});
|
||||||
|
AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
|
||||||
|
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) {
|
||||||
|
return ActionResult.FAIL;
|
||||||
|
}
|
||||||
|
return ActionResult.PASS;
|
||||||
|
});
|
||||||
|
UseItemCallback.EVENT.register((player, world, hand) -> {
|
||||||
|
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) {
|
||||||
|
return TypedActionResult.fail(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack());
|
||||||
|
}
|
||||||
|
return TypedActionResult.pass(hand == Hand.MAIN_HAND ? player.getMainHandStack() : player.getOffHandStack());
|
||||||
|
});
|
||||||
|
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
|
||||||
|
if (player instanceof IKnockableDown && ((IKnockableDown) player).knockdowns$isKnockedDown()) {
|
||||||
|
return ActionResult.FAIL;
|
||||||
|
}
|
||||||
|
return ActionResult.PASS;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ru.octol1ttle.knockdowns.mixin;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import ru.octol1ttle.knockdowns.api.IKnockableDown;
|
||||||
|
|
||||||
|
@Mixin(PlayerEntity.class)
|
||||||
|
public abstract class PlayerEntityMixin implements IKnockableDown {
|
||||||
|
@Unique
|
||||||
|
private boolean knockedDown;
|
||||||
|
@Unique
|
||||||
|
private boolean beingRevived;
|
||||||
|
|
||||||
|
@ModifyExpressionValue(method = "updatePose", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;isSwimming()Z"))
|
||||||
|
private boolean enterSwimmingIfKnockedDown(boolean original) {
|
||||||
|
PlayerEntity player = (PlayerEntity)(Object)this;
|
||||||
|
if (!(player instanceof IKnockableDown knockableDown)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return original || knockableDown.knockdowns$isKnockedDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModifyReturnValue(method = "canFoodHeal", at = @At("RETURN"))
|
||||||
|
private boolean dontHealIfKnockedDown(boolean original) {
|
||||||
|
return original && !this.knockdowns$isKnockedDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
|
||||||
|
public void readKnockedDownFromNbt(NbtCompound nbt, CallbackInfo ci) {
|
||||||
|
this.knockedDown = nbt.getBoolean("KnockedDown");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
|
||||||
|
public void writeKnockedDownToNbt(NbtCompound nbt, CallbackInfo ci) {
|
||||||
|
nbt.putBoolean("KnockedDown", this.knockedDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean knockdowns$isKnockedDown() {
|
||||||
|
return knockedDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void knockdowns$setKnockedDown(boolean knockedDown) {
|
||||||
|
this.knockedDown = knockedDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean knockdowns$isBeingRevived() {
|
||||||
|
return beingRevived;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void knockdowns$setBeingRevived(boolean beingRevived) {
|
||||||
|
this.beingRevived = beingRevived;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID knockdowns$getUuid() {
|
||||||
|
return ((PlayerEntity)(Object)this).getUuid();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
BIN
src/main/resources/assets/knockdowns/icon.png
Normal file
BIN
src/main/resources/assets/knockdowns/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 453 B |
|
@ -1,6 +1,4 @@
|
||||||
{
|
{
|
||||||
"subtitles.knockdowns.knocked_down": "Player knocked down",
|
|
||||||
|
|
||||||
"knockdown.attack.anvil": "%1$s was knocked down by a falling anvil",
|
"knockdown.attack.anvil": "%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.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": "%1$s was knocked down due to an arrow fired by %2$s",
|
||||||
|
@ -99,5 +97,6 @@
|
||||||
"knockdown.fell.assist.item": "%1$s was doomed to get knocked down by %2$s using %3$s",
|
"knockdown.fell.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": "%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.finish.item": "%1$s fell too far and was knocked down by %2$s using %3$s",
|
||||||
"knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall"
|
"knockdown.fell.killer": "%1$s was doomed to get knocked down by a fall",
|
||||||
|
"subtitles.knockdowns.knocked_down": "Player knocked down"
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
{
|
{
|
||||||
"subtitles.knockdowns.knocked_down": "Игрок тяжело ранен",
|
|
||||||
|
|
||||||
"knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней",
|
"knockdown.attack.anvil": "%1$s был тяжело ранен упавшей наковальней",
|
||||||
"knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s",
|
"knockdown.attack.anvil.player": "%1$s был тяжело ранен упавшей наковальней, пока сражался с %2$s",
|
||||||
"knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s",
|
"knockdown.attack.arrow": "%1$s тяжело ранен стрелой %2$s",
|
||||||
|
@ -99,5 +97,6 @@
|
||||||
"knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s",
|
"knockdown.fell.assist.item": "%1$s был тяжело ранен падением благодаря %2$s с помощью %3$s",
|
||||||
"knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s",
|
"knockdown.fell.finish": "%1$s упал с высоты и был тяжело ранен %2$s",
|
||||||
"knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s",
|
"knockdown.fell.finish.item": "%1$s упал с высоты и был тяжело ранен %2$s с помощью %3$s",
|
||||||
"knockdown.fell.killer": "%1$s был тяжело ранен падением"
|
"knockdown.fell.killer": "%1$s был тяжело ранен падением",
|
||||||
|
"subtitles.knockdowns.knocked_down": "Игрок тяжело ранен"
|
||||||
}
|
}
|
BIN
src/main/resources/assets/knockdowns/sounds/knocked_down.ogg
Normal file
BIN
src/main/resources/assets/knockdowns/sounds/knocked_down.ogg
Normal file
Binary file not shown.
38
src/main/resources/fabric.mod.json
Normal file
38
src/main/resources/fabric.mod.json
Normal file
|
@ -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": "*"
|
||||||
|
}
|
||||||
|
}
|
11
src/main/resources/knockdowns.mixins.json
Normal file
11
src/main/resources/knockdowns.mixins.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"package": "ru.octol1ttle.knockdowns.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [
|
||||||
|
"PlayerEntityMixin"
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue