Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d31816ede | ||
|
|
ff35ca78dd | ||
|
|
aa76e82b16 | ||
|
|
9c6f94c10b | ||
|
|
fec77fba15 | ||
|
|
3a5625508b | ||
|
|
cb864f7eba | ||
|
|
9d50254dec | ||
|
|
2bfd3c48b6 | ||
|
|
c6df9f029a | ||
|
|
cc6e38c7f2 | ||
|
|
c22f4f0efe | ||
|
|
71cd756337 | ||
|
|
4a5543da2a | ||
|
|
5a8682dc26 | ||
|
|
0ed4e78afd | ||
|
|
6b049d98b0 | ||
|
|
791e0575a9 | ||
|
|
35faf08cfa | ||
|
|
622c23a729 | ||
|
|
2eba6ec2ab | ||
|
|
8783dd0683 | ||
|
|
e861daa8c1 | ||
|
|
c970c1af6f | ||
|
|
921c009a59 | ||
|
|
ea0c2736e1 | ||
|
|
25411ba54f | ||
|
|
715013d90f | ||
|
|
a59ff6e625 | ||
|
|
bf609a1964 | ||
|
|
af5446f33e | ||
|
|
da3ceeca55 | ||
|
|
f665d1e13e | ||
|
|
8e52dae5b2 | ||
|
|
01188bc79f | ||
|
|
664d3899d7 | ||
|
|
60f0b37adc | ||
|
|
511c634c41 | ||
|
|
a3b5999de3 | ||
|
|
1666d815b2 | ||
|
|
b447154d52 | ||
|
|
73e862a782 | ||
|
|
6d1ab1acbe | ||
|
|
151eb58fb6 | ||
|
|
1beedd84d0 | ||
|
|
bc3175faed | ||
|
|
58f61073c6 | ||
|
|
26c72b5490 | ||
|
|
991e70e676 | ||
|
|
c8519f4d74 | ||
|
|
c9fef78eac | ||
|
|
0ce7104f72 | ||
|
|
74e7c3930c | ||
|
|
31ab0aa7ff | ||
|
|
fc34b3402e | ||
|
|
db6440d87c | ||
|
|
0119309ffb | ||
|
|
f31fcc437f | ||
|
|
ab4d9d13c9 | ||
|
|
052454fee0 | ||
|
|
0f0b9f2cec | ||
|
|
2eb1fff556 | ||
|
|
b4f8786683 | ||
|
|
5c7f314071 |
No files matched your search
@@ -19,17 +19,17 @@ jobs:
|
||||
- name: Checkout main repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# 2. Install JDK 21 – NO Gradle cache here
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
cache: '' # ← disable Maven/Gradle cache
|
||||
|
||||
- name: Setup Gradle 8.10
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: "8.10" # Quotes required to prevent YAML converting to number
|
||||
# 3. Set up Gradle + dependency cache
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
|
||||
- name: Generate build number
|
||||
id: buildnumber
|
||||
@@ -53,39 +53,26 @@ jobs:
|
||||
# Ensure snapshot is set to false
|
||||
sed -i "s/val snapshot = .*/val snapshot = false/" build.gradle.kts
|
||||
|
||||
- name: Build with Gradle
|
||||
run: gradle clean build -x test
|
||||
- name: Build without PE shaded
|
||||
run: ./gradlew build -PshadePE=false -x test
|
||||
env:
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
|
||||
- name: Rename shaded jar to overwrite non-shaded jar
|
||||
run: mv bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}-all.jar bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar
|
||||
- name: Build with PE shaded Gradle
|
||||
run: ./gradlew build -PshadePE=true -x test
|
||||
env:
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
|
||||
# Download PaperSpigot (Choose version for reproducibility
|
||||
- name: Download PaperSpigot
|
||||
run: |
|
||||
mkdir -p paper-server/plugins
|
||||
curl -o paperclip.jar https://api.papermc.io/v2/projects/paper/versions/1.21.1/builds/120/downloads/paper-1.21.1-120.jar
|
||||
mv paperclip.jar paper-server/
|
||||
|
||||
# Copy Plugin into Plugins Folder and Start Server
|
||||
- name: Copy Plugin and Start Paper Server
|
||||
run: |
|
||||
cp bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar paper-server/plugins/
|
||||
cd paper-server
|
||||
# Start the server to remap the plugin
|
||||
timeout 60 java -Xms3G -Xmx3G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -jar -Dcom.mojang.eula.agree=true paperclip.jar --nogui || true
|
||||
|
||||
# Get the Hashes of the Original and Remapped JARs
|
||||
# Get the Hashes of the Original and Lite JARs
|
||||
- name: Calculate Hashes
|
||||
run: |
|
||||
# Calculate hash for the original JAR
|
||||
ORIGINAL_HASH=$(sha256sum bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar | awk '{print $1}')
|
||||
echo "ORIGINAL_HASH=$ORIGINAL_HASH" >> $GITHUB_ENV
|
||||
# Calculate hash for the remapped JAR
|
||||
REMAPPED_HASH=$(sha256sum paper-server/plugins/.paper-remapped/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar | awk '{print $1}')
|
||||
echo "REMAPPED_HASH=$REMAPPED_HASH" >> $GITHUB_ENV
|
||||
# Calculate hash for the remapped JAR
|
||||
BUKKIT_HASH=$(sha256sum bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar | awk '{print $1}')
|
||||
echo "BUKKIT_HASH=$BUKKIT_HASH" >> $GITHUB_ENV
|
||||
# Calculate hash for the lite JAR
|
||||
BUKKIT_LITE_HASH=$(sha256sum bukkit/build/libs/knockbacksync-bukkit-lite-${{ env.NEW_VERSION }}.jar | awk '{print $1}')
|
||||
echo "BUKKIT_LITE_HASH=$BUKKIT_LITE_HASH" >> $GITHUB_ENV
|
||||
# Calculate hash for the lite JAR
|
||||
FABRIC_HASH=$(sha256sum fabric/build/libs/knockbacksync-fabric-${{ env.NEW_VERSION }}.jar | awk '{print $1}')
|
||||
echo "FABRIC_HASH=$FABRIC_HASH" >> $GITHUB_ENV
|
||||
|
||||
@@ -111,8 +98,8 @@ jobs:
|
||||
}
|
||||
|
||||
# Add new hashes if they don't exist
|
||||
add_hash_if_new "${{ env.ORIGINAL_HASH }}"
|
||||
add_hash_if_new "${{ env.REMAPPED_HASH }}"
|
||||
add_hash_if_new "${{ env.BUKKIT_HASH }}"
|
||||
add_hash_if_new "${{ env.BUKKIT_LITE_HASH }}"
|
||||
add_hash_if_new "${{ env.FABRIC_HASH }}"
|
||||
|
||||
# Check if the file has changed
|
||||
@@ -143,23 +130,25 @@ jobs:
|
||||
# Clean up
|
||||
rm dev-builds.txt.original
|
||||
|
||||
- name: Create artifacts directory
|
||||
run: mkdir -p artifacts
|
||||
|
||||
- name: Copy bukkit artifact
|
||||
run: cp bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar artifacts/
|
||||
|
||||
- name: Copy fabric artifact
|
||||
run: cp fabric/build/libs/knockbacksync-fabric-${{ env.NEW_VERSION }}.jar artifacts/
|
||||
|
||||
- name: Zip the artifacts
|
||||
run: zip -r KnockbackSync-${{ env.NEW_VERSION }}.zip artifacts/*
|
||||
|
||||
- name: Upload artifacts zip
|
||||
- name: Upload Bukkit Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: KnockbackSync-${{ env.NEW_VERSION }}.zip
|
||||
path: KnockbackSync-${{ env.NEW_VERSION }}.zip
|
||||
name: knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar
|
||||
path: bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar
|
||||
retention-days: 90
|
||||
|
||||
- name: Upload Bukkit Lite Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: knockbacksync-bukkit-lite-${{ env.NEW_VERSION }}.jar
|
||||
path: bukkit/build/libs/knockbacksync-bukkit-lite-${{ env.NEW_VERSION }}.jar
|
||||
retention-days: 90
|
||||
|
||||
- name: Upload Fabric Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: knockbacksync-fabric-${{ env.NEW_VERSION }}.jar
|
||||
path: fabric/build/libs/knockbacksync-fabric-${{ env.NEW_VERSION }}.jar
|
||||
retention-days: 90
|
||||
|
||||
- uses: Kir-Antipov/mc-publish@v3.3
|
||||
@@ -171,6 +160,7 @@ jobs:
|
||||
|
||||
files: |
|
||||
bukkit/build/libs/knockbacksync-bukkit-${{ env.NEW_VERSION }}.jar
|
||||
bukkit/build/libs/knockbacksync-bukkit-lite-${{ env.NEW_VERSION }}.jar
|
||||
|
||||
name: KnockbackSync-${{ env.NEW_VERSION }}
|
||||
version: ${{ env.NEW_VERSION }}
|
||||
@@ -208,7 +198,10 @@ jobs:
|
||||
fabric
|
||||
|
||||
game-versions: |
|
||||
>=1.21
|
||||
1.21.11
|
||||
|
||||
dependencies: |
|
||||
fabric-api
|
||||
|
||||
retry-attempts: 2
|
||||
retry-delay: 10000
|
||||
|
||||
+39
-12
@@ -2,27 +2,54 @@ import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("java")
|
||||
id("com.gradleup.shadow") version "8.3.3" apply false
|
||||
id("fabric-loom") version "1.7.4" apply false
|
||||
id("com.gradleup.shadow") version "9.2.2" apply false
|
||||
id("fabric-loom") version "1.14.6" apply false
|
||||
}
|
||||
|
||||
fun getGitCommitHash(project: Project): String? {
|
||||
// Only try to get the hash if the .git directory exists
|
||||
if (!project.file(".git").isDirectory) {
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
val process = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
.start()
|
||||
|
||||
process.waitFor(5, TimeUnit.SECONDS)
|
||||
|
||||
if (process.exitValue() == 0) {
|
||||
process.inputStream.bufferedReader().readText().trim().takeIf { it.isNotEmpty() }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val fullVersion = "1.3.5"
|
||||
val snapshot = true
|
||||
val githubRepo = System.getenv("GITHUB_REPOSITORY") ?: project.findProperty("githubRepo").toString()
|
||||
|
||||
extra["shadePE"] = project.findProperty("shadePE")?.toString()?.toBoolean()
|
||||
?: System.getenv("SHADE_PE")?.toBoolean()
|
||||
?: true
|
||||
|
||||
extra["relocate"] = project.findProperty("relocate")?.toString()?.toBoolean()
|
||||
?: System.getenv("RELOCATE_JAR")?.toBoolean()
|
||||
?: true
|
||||
|
||||
allprojects {
|
||||
fun getVersionMeta(includeHash: Boolean): String {
|
||||
if (!snapshot) {
|
||||
return ""
|
||||
}
|
||||
var commitHash = ""
|
||||
if (includeHash && file(".git").isDirectory) {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "rev-parse", "--short", "HEAD")
|
||||
standardOutput = stdout
|
||||
}
|
||||
commitHash = "+${stdout.toString().trim()}"
|
||||
val commitHash = if (includeHash) {
|
||||
getGitCommitHash(project)?.let { "+$it" } ?: ""
|
||||
} else {
|
||||
""
|
||||
}
|
||||
return "$commitHash-SNAPSHOT"
|
||||
}
|
||||
@@ -33,7 +60,8 @@ allprojects {
|
||||
ext["githubRepo"] = githubRepo
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
// mavenLocal()
|
||||
maven("https://repo.grim.ac/snapshots")
|
||||
mavenCentral()
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
maven("https://repo.codemc.io/repository/maven-releases/")
|
||||
@@ -44,7 +72,6 @@ allprojects {
|
||||
}
|
||||
maven("https://libraries.minecraft.net/")
|
||||
maven("https://maven.neoforged.net/releases")
|
||||
maven("https://repo.codemc.io/repository/maven-snapshots/")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-5
@@ -4,8 +4,10 @@ plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
val shadePE: Boolean by rootProject.extra
|
||||
|
||||
base {
|
||||
archivesName.set("${rootProject.property("archives_base_name")}-bukkit")
|
||||
archivesName.set("${rootProject.property("archives_base_name")}-bukkit${if (shadePE) "" else "-lite"}")
|
||||
}
|
||||
|
||||
val shadeThisThing: Configuration by configurations.creating {
|
||||
@@ -31,14 +33,18 @@ dependencies {
|
||||
|
||||
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("io.netty:netty-all:4.1.72.Final")
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.34")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||
|
||||
shadeThisThing(implementation("org.kohsuke:github-api:1.326")!!)
|
||||
shadeThisThing(implementation("com.github.retrooper:packetevents-spigot:2.5.0")!!)
|
||||
// shadeThisThing(implementation("net.kyori:adventure-platform-bukkit:4.3.4")!!)
|
||||
shadeThisThing(implementation("org.incendo:cloud-paper:2.0.0-beta.10")!!)
|
||||
if (shadePE) {
|
||||
shadeThisThing(implementation("com.github.retrooper:packetevents-spigot:2.11.1+60a2c34-SNAPSHOT")!!)
|
||||
} else {
|
||||
compileOnly("com.github.retrooper:packetevents-spigot:2.11.1+60a2c34-SNAPSHOT")
|
||||
}
|
||||
shadeThisThing(implementation("org.incendo:cloud-paper:2.0.0-beta.14")!!)
|
||||
shadeThisThing(implementation("org.incendo:cloud-core:2.0.0")!!)
|
||||
|
||||
// Required for 1.14.4 support because gson is too old to have JosnParser.parseString()
|
||||
@@ -46,8 +52,15 @@ dependencies {
|
||||
}
|
||||
|
||||
tasks.withType<ShadowJar> {
|
||||
manifest {
|
||||
attributes["paperweight-mappings-namespace"] = "mojang"
|
||||
}
|
||||
|
||||
// Remove the -all suffix from the output JAR file name
|
||||
archiveClassifier.set("")
|
||||
|
||||
configurations = listOf(shadeThisThing)
|
||||
isEnableRelocation = false
|
||||
enableAutoRelocation = true
|
||||
relocationPrefix = "${project.property("maven_group")}.${project.property("archives_base_name")}.shaded"
|
||||
}
|
||||
|
||||
@@ -58,11 +71,14 @@ tasks.build {
|
||||
tasks.processResources {
|
||||
from(project(":common").sourceSets.main.get().resources)
|
||||
inputs.property("version", project.version)
|
||||
inputs.property("shadePE", shadePE) // Add shadePE as an input to trigger reprocessing if it changes
|
||||
filteringCharset = "UTF-8"
|
||||
|
||||
|
||||
filesMatching("plugin.yml") {
|
||||
expand(
|
||||
"version" to project.version,
|
||||
"depends" to if (shadePE) "[]" else listOf("packetevents")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.caseload.knockbacksync;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.Location;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
@@ -20,10 +21,13 @@ import me.caseload.knockbacksync.world.BukkitServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
@@ -36,6 +40,9 @@ import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -104,7 +111,7 @@ public class BukkitBase extends Base {
|
||||
public void enable() {
|
||||
super.enable();
|
||||
super.eventBus.registerListeners(this);
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5) && this.getPlatform() == Platform.BUKKIT) {
|
||||
scheduler.runTaskTimerAsynchronously(this::setUpdateIntervals, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -125,8 +132,7 @@ public class BukkitBase extends Base {
|
||||
protected void registerPlatformListeners() {
|
||||
registerPluginListeners(
|
||||
new BukkitPlayerDamageListener(),
|
||||
new BukkitPlayerKnockbackListener(),
|
||||
new BukkitPlayerJoinQuitListener()
|
||||
new BukkitPlayerKnockbackListener()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,6 +181,18 @@ public class BukkitBase extends Base {
|
||||
return 20.0f;
|
||||
}
|
||||
|
||||
public URL getJarURL() {
|
||||
try {
|
||||
String path = getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
File pluginJarFile = new File(path);
|
||||
|
||||
return Paths.get(getDataFolder().getParentFile().getAbsolutePath(), pluginJarFile.getName()).toUri().toURL();
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Couldn't find plugin file: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPluginListeners(Listener... listeners) {
|
||||
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
|
||||
for (Listener listener : listeners)
|
||||
@@ -225,4 +243,21 @@ public class BukkitBase extends Base {
|
||||
public void onConfigReload(ConfigReloadEvent event) {
|
||||
playerUpdateInterval = event.getConfigManager().getConfigWrapper().getInt("entity_tick_intervals.player", 2);
|
||||
}
|
||||
|
||||
public void restartServer() {
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
|
||||
}
|
||||
|
||||
public boolean hasRestartScript() {
|
||||
File spigotFile = new File("spigot.yml");
|
||||
if (!spigotFile.exists())
|
||||
return false;
|
||||
|
||||
FileConfiguration spigotConfig = YamlConfiguration.loadConfiguration(spigotFile);
|
||||
String scriptValue = spigotConfig.getString("settings.restart-script");
|
||||
if (scriptValue == null || scriptValue.isEmpty())
|
||||
return false;
|
||||
|
||||
return Paths.get(scriptValue).toAbsolutePath().normalize().toFile().exists();
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package me.caseload.knockbacksync.listener.bukkit;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import me.caseload.knockbacksync.listener.PlayerJoinQuitListener;
|
||||
import me.caseload.knockbacksync.player.BukkitPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitPlayerJoinQuitListener extends PlayerJoinQuitListener implements Listener {
|
||||
|
||||
public static List<Object> chunkMapList = new ArrayList<>();
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
onPlayerJoin(new PlayerData(new BukkitPlayer(event.getPlayer())));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
onPlayerQuit(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@ import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.caseload.knockbacksync.BukkitBase;
|
||||
import me.caseload.knockbacksync.Platform;
|
||||
import me.caseload.knockbacksync.world.FoliaWorld;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
import me.caseload.knockbacksync.world.SpigotWorld;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -22,18 +26,19 @@ import java.util.UUID;
|
||||
|
||||
public class BukkitPlayer implements PlatformPlayer {
|
||||
public final Player bukkitPlayer;
|
||||
public final User user;
|
||||
private String clientBrand = "vanilla";
|
||||
|
||||
// Reflection variables
|
||||
private static Class<?> craftPlayerClass;
|
||||
private static Method getHandleMethod;
|
||||
private static Method getAttackStrengthScaleMethod;
|
||||
private static ServerVersion currentVersion = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
private static final ServerVersion currentVersion = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
|
||||
// 1.12.2 support
|
||||
static {
|
||||
try {
|
||||
// Check the current server versio
|
||||
// Check the current server version
|
||||
|
||||
// If the version is greater than 1.14.4, use the Player method directly
|
||||
if (currentVersion.isOlderThan(ServerVersion.V_1_15)) {
|
||||
@@ -70,7 +75,9 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
}
|
||||
|
||||
public BukkitPlayer(Player player) {
|
||||
Preconditions.checkArgument(player != null);
|
||||
this.bukkitPlayer = player;
|
||||
this.user = PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +142,7 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public PlatformWorld getWorld() {
|
||||
return new SpigotWorld(bukkitPlayer.getWorld());
|
||||
return BukkitBase.INSTANCE.getPlatform() == Platform.FOLIA ? new FoliaWorld(bukkitPlayer.getWorld()): new SpigotWorld(bukkitPlayer.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -212,8 +219,8 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer);
|
||||
public @Nullable User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,4 +232,19 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
public String getClientBrand() {
|
||||
return this.clientBrand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlying() {
|
||||
return bukkitPlayer.isFlying();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
return bukkitPlayer.getWalkSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFlyingSpeed() {
|
||||
return bukkitPlayer.getFlySpeed();
|
||||
}
|
||||
}
|
||||
+8
-17
@@ -1,55 +1,46 @@
|
||||
package me.caseload.knockbacksync.scheduler;
|
||||
|
||||
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
|
||||
import io.github.retrooper.packetevents.util.folia.GlobalRegionScheduler;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FoliaSchedulerAdapter implements SchedulerAdapter {
|
||||
private final Plugin plugin;
|
||||
private GlobalRegionScheduler scheduler = null;
|
||||
|
||||
public FoliaSchedulerAdapter(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
try {
|
||||
// Attempt to find and call the `getGlobalRegionScheduler` method
|
||||
Method getSchedulerMethod = Bukkit.getServer().getClass().getMethod("getGlobalRegionScheduler");
|
||||
scheduler = (GlobalRegionScheduler) getSchedulerMethod.invoke(Bukkit.getServer());
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
plugin.getLogger().severe("Failed to access GlobalRegionScheduler: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTask(Runnable task) {
|
||||
return new FoliaTaskHandle(scheduler.run(plugin, scheduledTask -> task.run()));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getGlobalRegionScheduler().run(plugin, scheduledTask -> task.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskAsynchronously(Runnable task) {
|
||||
return new FoliaTaskHandle(scheduler.run(plugin, scheduledTask -> task.run()));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getAsyncScheduler().runNow(plugin, scheduledTask -> task.run()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskLater(Runnable task, long delayTicks) {
|
||||
return new FoliaTaskHandle(scheduler.runDelayed(plugin, scheduledTask -> task.run(), delayTicks));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> task.run(), delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskTimer(Runnable task, long delayTicks, long periodTicks) {
|
||||
return new FoliaTaskHandle(scheduler.runAtFixedRate(plugin, scheduledTask -> task.run(), delayTicks, periodTicks));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> task.run(), delayTicks, periodTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskLaterAsynchronously(Runnable task, long delay) {
|
||||
return new FoliaTaskHandle(scheduler.runDelayed(plugin, scheduledTask -> task.run(), delay));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getAsyncScheduler().runDelayed(plugin, scheduledTask -> task.run(), delay * 50, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskTimerAsynchronously(Runnable task, long delay, long period) {
|
||||
return new FoliaTaskHandle(scheduler.runAtFixedRate(plugin, scheduledTask -> task.run(), delay, period));
|
||||
return new FoliaTaskHandle(FoliaScheduler.getAsyncScheduler().runAtFixedRate(plugin, scheduledTask -> task.run(), delay, period));
|
||||
}
|
||||
|
||||
// Folia takes care of this
|
||||
|
||||
@@ -1,41 +1,19 @@
|
||||
package me.caseload.knockbacksync.scheduler;
|
||||
|
||||
import io.github.retrooper.packetevents.util.folia.TaskWrapper;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
public class FoliaTaskHandle implements AbstractTaskHandle {
|
||||
private static final MethodHandle GET_OWNING_PLUGIN;
|
||||
private static final MethodHandle CANCEL;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> scheduledTaskClass = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
GET_OWNING_PLUGIN = lookup.findVirtual(scheduledTaskClass, "getOwningPlugin", MethodType.methodType(Plugin.class));
|
||||
CANCEL = lookup.findVirtual(scheduledTaskClass, "cancel", MethodType.methodType(void.class));
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to initialize FoliaTaskHandle reflection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Object scheduledTask; // Store as Object instead of ScheduledTask
|
||||
private final TaskWrapper scheduledTask; // Store as Object instead of ScheduledTask
|
||||
private boolean cancelled;
|
||||
|
||||
public FoliaTaskHandle(@NotNull Object scheduledTask) {
|
||||
public FoliaTaskHandle(@NotNull TaskWrapper scheduledTask) {
|
||||
this.scheduledTask = scheduledTask;
|
||||
}
|
||||
|
||||
public Plugin getOwner() {
|
||||
try {
|
||||
return (Plugin) GET_OWNING_PLUGIN.invoke(scheduledTask);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to invoke getOwningPlugin", e);
|
||||
}
|
||||
return scheduledTask.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,11 +23,7 @@ public class FoliaTaskHandle implements AbstractTaskHandle {
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
try {
|
||||
CANCEL.invoke(scheduledTask);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to invoke cancel", e);
|
||||
}
|
||||
scheduledTask.cancel();
|
||||
this.cancelled = true;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.caseload.knockbacksync.world;
|
||||
import me.caseload.knockbacksync.player.BukkitPlayer;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
@@ -19,4 +20,9 @@ public class BukkitServer implements PlatformServer {
|
||||
public PlatformPlayer getPlayer(UUID uuid) {
|
||||
return new BukkitPlayer(Bukkit.getPlayer(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformPlayer getPlayer(Object nativePlatformPlayer) {
|
||||
return new BukkitPlayer((Player) nativePlatformPlayer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.caseload.knockbacksync.world;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
public class FoliaWorld extends SpigotWorld {
|
||||
public FoliaWorld(World world) {
|
||||
super(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedBlockState getBlockStateAt(int x, int y, int z) {
|
||||
BlockState block = this.world.getBlockState(x, y, z);
|
||||
return SpigotConversionUtil.fromBukkitBlockData(block.getBlockData());
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import org.bukkit.util.Vector;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SpigotWorld implements PlatformWorld {
|
||||
private final World world;
|
||||
protected final World world;
|
||||
|
||||
// Reflection variables
|
||||
private static Class<?> craftWorldClass;
|
||||
@@ -85,7 +85,7 @@ public class SpigotWorld implements PlatformWorld {
|
||||
|
||||
@Override
|
||||
public WrappedBlockState getBlockStateAt(Vector3d loc) {
|
||||
return getBlockStateAt((int) Math.floor(loc.x), (int) Math.floor(loc.x), (int) Math.floor(loc.x));
|
||||
return getBlockStateAt((int) Math.floor(loc.x), (int) Math.floor(loc.y), (int) Math.floor(loc.z));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ api-version: '1.13' # We actually support 1.12.2, api-version was added later th
|
||||
authors: [ Caseload, Axionize ]
|
||||
description: Synchronizes player knockback for smoother gameplay.
|
||||
folia-supported: true
|
||||
depends: ${depends}
|
||||
permissions:
|
||||
knockbacksync.update:
|
||||
default: op
|
||||
|
||||
+9
-18
@@ -1,25 +1,24 @@
|
||||
plugins {
|
||||
id("fabric-loom")
|
||||
id("com.github.gmazzo.buildconfig") version "3.1.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft("com.mojang:minecraft:1.16.5")
|
||||
mappings(loom.layered {
|
||||
officialMojangMappings()
|
||||
parchment("org.parchmentmc.data:parchment-1.16.5:2022.03.06")
|
||||
})
|
||||
val shadePE: Boolean by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
// True compileOnly deps
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("io.netty:netty-all:4.1.72.Final")
|
||||
compileOnly("org.projectlombok:lombok:1.18.34")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||
|
||||
// Shaded in or bundled by platform-specific code
|
||||
implementation("com.github.retrooper:packetevents-api:2.5.0")
|
||||
if (shadePE) {
|
||||
implementation("com.github.retrooper:packetevents-api:2.11.1+60a2c34-SNAPSHOT")
|
||||
} else {
|
||||
compileOnly("com.github.retrooper:packetevents-api:2.11.1+60a2c34-SNAPSHOT")
|
||||
}
|
||||
|
||||
implementation("org.yaml:snakeyaml:2.0")
|
||||
// implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
|
||||
// implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2")
|
||||
implementation("org.kohsuke:github-api:1.326") {
|
||||
exclude(group = "commons-io", module = "commons-io")
|
||||
exclude(group = "org.apache.commons", module = "commons-lang3")
|
||||
@@ -29,14 +28,6 @@ dependencies {
|
||||
implementation("org.incendo:cloud-minecraft-extras:2.0.0-beta.10")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://maven.neoforged.net/releases")
|
||||
}
|
||||
|
||||
tasks.named("remapJar").configure {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
buildConfig {
|
||||
buildConfigField("String", "GITHUB_REPO", "\"${project.rootProject.ext["githubRepo"]}\"")
|
||||
}
|
||||
@@ -13,9 +13,7 @@ import me.caseload.knockbacksync.command.subcommand.ToggleOffGroundSubcommand;
|
||||
import me.caseload.knockbacksync.event.Event;
|
||||
import me.caseload.knockbacksync.event.EventBus;
|
||||
import me.caseload.knockbacksync.event.OptimizedEventBus;
|
||||
import me.caseload.knockbacksync.listener.packetevents.AttributeChangeListener;
|
||||
import me.caseload.knockbacksync.listener.packetevents.ClientBrandListener;
|
||||
import me.caseload.knockbacksync.listener.packetevents.PingReceiveListener;
|
||||
import me.caseload.knockbacksync.listener.packetevents.*;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.scheduler.SchedulerAdapter;
|
||||
@@ -27,8 +25,14 @@ import me.caseload.knockbacksync.world.PlatformServer;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.kohsuke.github.GitHub;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -111,7 +115,9 @@ public abstract class Base {
|
||||
protected void registerCommonListeners() {
|
||||
PacketEvents.getAPI().getEventManager().registerListeners(
|
||||
new AttributeChangeListener(),
|
||||
new PingSendListener(),
|
||||
new PingReceiveListener(),
|
||||
new PacketPlayerJoinQuit(),
|
||||
new ClientBrandListener()
|
||||
);
|
||||
Event.setEventBus(eventBus);
|
||||
@@ -131,7 +137,6 @@ public abstract class Base {
|
||||
list.forEach(command -> command.register(commandManager));
|
||||
}
|
||||
|
||||
|
||||
protected abstract String getVersion();
|
||||
|
||||
protected void checkForUpdates() {
|
||||
@@ -152,15 +157,25 @@ public abstract class Base {
|
||||
LOGGER.warning("You are running an older version. A new update is available for download at: https://github.com/CASELOAD7000/knockback-sync/releases/latest");
|
||||
configManager.setUpdateAvailable(true);
|
||||
} else if (comparisonResult > 0) {
|
||||
if (currentVersion.contains("-dev")) {
|
||||
if (currentVersion.contains("-dev"))
|
||||
LOGGER.info("You are running a development build newer than the latest release.");
|
||||
} else {
|
||||
else {
|
||||
LOGGER.info("You are running a version newer than the latest release.");
|
||||
}
|
||||
configManager.setUpdateAvailable(false);
|
||||
} else {
|
||||
LOGGER.info("You are running the latest release.");
|
||||
configManager.setUpdateAvailable(false);
|
||||
}
|
||||
|
||||
if (configManager.isUpdateAvailable() && configManager.isAutoUpdate()) {
|
||||
LOGGER.info("Updating...");
|
||||
byte[] bytes = downloadLatestRelease(github);
|
||||
updatePlugin(bytes);
|
||||
|
||||
if (hasRestartScript())
|
||||
scheduler.runTask(this::restartServer);
|
||||
else {
|
||||
// Yeah
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Failed to check for updates: " + e.getMessage());
|
||||
@@ -168,6 +183,44 @@ public abstract class Base {
|
||||
});
|
||||
}
|
||||
|
||||
private void updatePlugin(byte[] pluginBytes) throws URISyntaxException {
|
||||
File file = new File(getJarURL().toURI());
|
||||
|
||||
try (FileOutputStream outputStream = new FileOutputStream(file)) {
|
||||
outputStream.write(pluginBytes);
|
||||
LOGGER.info("Successfully updated the plugin!");
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Failed to update: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] downloadLatestRelease(GitHub github) {
|
||||
try {
|
||||
return github.getRepository("CASELOAD7000/knockback-sync")
|
||||
.getLatestRelease()
|
||||
.getAssets().stream()
|
||||
.filter(asset -> asset.getName().endsWith(".jar")
|
||||
&& asset.getName().contains(platform.equals(Platform.FABRIC)
|
||||
? "fabric" : "bukkit"))
|
||||
.findFirst()
|
||||
.map(asset -> {
|
||||
try (InputStream inputStream = new URL(asset.getBrowserDownloadUrl()).openStream()) {
|
||||
inputStream.reset();
|
||||
byte[] bytes = new byte[inputStream.available()];
|
||||
DataInputStream dataInputStream = new DataInputStream(inputStream);
|
||||
dataInputStream.readFully(bytes);
|
||||
return bytes;
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Failed to download latest release: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}).orElse(null);
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Failed to locate latest release: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int compareVersions(String version1, String version2) {
|
||||
String[] v1Parts = version1.split("[-.]");
|
||||
String[] v2Parts = version2.split("[-.]");
|
||||
@@ -228,6 +281,12 @@ public abstract class Base {
|
||||
public abstract PermissionChecker getPermissionChecker();
|
||||
|
||||
public abstract float getTickRate();
|
||||
|
||||
public abstract URL getJarURL();
|
||||
|
||||
public abstract void restartServer();
|
||||
|
||||
public abstract boolean hasRestartScript();
|
||||
}
|
||||
|
||||
|
||||
+23
-18
@@ -1,11 +1,13 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.command.generic.BuilderCommand;
|
||||
import me.caseload.knockbacksync.command.generic.PlayerSelector;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
@@ -18,6 +20,8 @@ import java.util.function.Predicate;
|
||||
|
||||
public class PingCommand implements BuilderCommand {
|
||||
|
||||
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
|
||||
private String pingSelfAvailableMessage;
|
||||
private String pingSelfUnavailableMessage;
|
||||
private String pingOtherAvailableMessage;
|
||||
@@ -51,17 +55,17 @@ public class PingCommand implements BuilderCommand {
|
||||
if (context.sender().isConsole()) {
|
||||
context.sender().sendMessage(ChatUtil.translateAlternateColorCodes('&', mustSpecifyPlayerFromConsoleMessage));
|
||||
} else {
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), null));
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), context.sender().getName(), null, null));
|
||||
}
|
||||
} else {
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), targetSelector.getSinglePlayer().getUUID()));
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), context.sender().getName(), targetSelector.getSinglePlayer().getUUID(), targetSelector.getSinglePlayer().getName()));
|
||||
}
|
||||
})
|
||||
);
|
||||
Base.INSTANCE.getEventBus().registerListeners(this);
|
||||
}
|
||||
private void loadConfig() {
|
||||
ConfigWrapper configWrapper = Base.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
ConfigWrapper configWrapper = configManager.getConfigWrapper();
|
||||
pingSelfAvailableMessage = configWrapper.getString("messages.ping.self.available",
|
||||
"Your real ping is &b%ping%&rms. Jitter: &b%jitter%&rms. Spike: &b%spike%&r. Compensated ping: &b%compensated%&rms.");
|
||||
pingSelfUnavailableMessage = configWrapper.getString("messages.ping.self.unavailable",
|
||||
@@ -78,26 +82,27 @@ public class PingCommand implements BuilderCommand {
|
||||
"&cKnockback synchronization is currently disabled for the target player.");
|
||||
}
|
||||
|
||||
private String getPingMessage(UUID senderUUID, UUID targetUUID) {
|
||||
PlayerData playerData = null;
|
||||
private String getPingMessage(UUID senderUUID, String senderName, UUID targetUUID, String targetName) {
|
||||
boolean isSelf = senderUUID.equals(targetUUID) || targetUUID == null;
|
||||
|
||||
if (senderUUID == null) {
|
||||
senderUUID = targetUUID;
|
||||
}
|
||||
|
||||
String message;
|
||||
if (isSelf) {
|
||||
playerData = PlayerDataManager.getPlayerData(senderUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForYouMessage);
|
||||
}
|
||||
message = knockbacksyncDisabledForYouMessage;
|
||||
targetUUID = senderUUID;
|
||||
targetName = senderName;
|
||||
} else {
|
||||
playerData = PlayerDataManager.getPlayerData(targetUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForTargetMessage);
|
||||
}
|
||||
message = knockbacksyncDisabledForTargetMessage;
|
||||
}
|
||||
|
||||
User targetUser = Base.INSTANCE.getPlatformServer().getPlayer(targetUUID).getUser();
|
||||
if (targetUser == null)
|
||||
return ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getPlayerDisconnectedWhileExecutingCommand()
|
||||
).replace("%player%", targetName);
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(targetUser);
|
||||
if (playerData == null)
|
||||
return ChatUtil.translateAlternateColorCodes('&', message);
|
||||
|
||||
String rawReturnString;
|
||||
if (playerData.getPing() == null) {
|
||||
rawReturnString = isSelf ? pingSelfUnavailableMessage : pingOtherUnavailableMessage;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import java.util.function.Predicate;
|
||||
|
||||
public class ReloadCommand implements BuilderCommand {
|
||||
|
||||
private final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
private String rawReloadMessage = configManager.getConfigWrapper().getString("messages.reload.success", "&aSuccessfully reloaded KnockbackSync.");
|
||||
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
|
||||
+9
-4
@@ -14,7 +14,6 @@ import me.caseload.knockbacksync.util.ChatUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.permission.PredicatePermission;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class StatusCommand implements BuilderCommand {
|
||||
@@ -60,7 +59,7 @@ public class StatusCommand implements BuilderCommand {
|
||||
(globalStatus ? globalStatusEnabledMessage : globalStatusDisabledMessage))
|
||||
+ "\n"
|
||||
+ ChatUtil.translateAlternateColorCodes('&',
|
||||
globalStatus ? globalOffGroundStatusEnabledMessage : globalOffGroundStatusDisabledMessage)
|
||||
ToggleOffGroundSubcommand.offGroundSyncEnabled ? globalOffGroundStatusEnabledMessage : globalOffGroundStatusDisabledMessage)
|
||||
);
|
||||
|
||||
if (sender.hasPermission(STATUS_SELF_PERMISSION, true)) {
|
||||
@@ -85,9 +84,15 @@ public class StatusCommand implements BuilderCommand {
|
||||
}
|
||||
|
||||
private void showPlayerStatus(Sender sender, PlatformPlayer target) {
|
||||
if (target.getUser() == null) {
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getPlayerDisconnectedWhileExecutingCommand()
|
||||
).replace("%player%", target.getName());
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
boolean globalStatus = configManager.isToggled();
|
||||
UUID uuid = target.getUUID();
|
||||
boolean playerStatus = PlayerDataManager.containsPlayerData(uuid);
|
||||
boolean playerStatus = PlayerDataManager.containsPlayerData(target.getUser());
|
||||
|
||||
String statusMessage;
|
||||
if (!globalStatus) {
|
||||
|
||||
+24
-15
@@ -1,20 +1,20 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.command.generic.BuilderCommand;
|
||||
import me.caseload.knockbacksync.command.generic.PlayerSelector;
|
||||
import me.caseload.knockbacksync.event.events.ToggleOnOffEvent;
|
||||
import me.caseload.knockbacksync.manager.CombatManager;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.util.ChatUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.permission.PredicatePermission;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ToggleCommand implements BuilderCommand {
|
||||
@@ -67,7 +67,7 @@ public class ToggleCommand implements BuilderCommand {
|
||||
if (targetSelector == null) {
|
||||
// Global toggle
|
||||
if (permissionChecker.hasPermission(sender, TOGGLE_GLOBAL_PERMISSION, false)) {
|
||||
toggleGlobalKnockback(configManager, sender);
|
||||
toggleGlobalKnockback(sender);
|
||||
} else {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noGlobalPermissionMessage));
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class ToggleCommand implements BuilderCommand {
|
||||
if (!configManager.isToggled()) {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', serverDisabledMessage));
|
||||
} else {
|
||||
togglePlayerKnockback(target, configManager, sender);
|
||||
togglePlayerKnockback(target, sender);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -93,7 +93,7 @@ public class ToggleCommand implements BuilderCommand {
|
||||
}
|
||||
|
||||
|
||||
private static void toggleGlobalKnockback(ConfigManager configManager, Sender sender) {
|
||||
private static void toggleGlobalKnockback(Sender sender) {
|
||||
boolean toggledState = !configManager.isToggled();
|
||||
ToggleOnOffEvent toggleOnOffEvent = new ToggleOnOffEvent(toggledState);
|
||||
toggleOnOffEvent.post();
|
||||
@@ -103,8 +103,8 @@ public class ToggleCommand implements BuilderCommand {
|
||||
toggledState = toggleOnOffEvent.getStatus();
|
||||
configManager.setToggled(toggledState);
|
||||
|
||||
Base.INSTANCE.getConfigManager().getConfigWrapper().set("enabled", toggledState);
|
||||
Base.INSTANCE.getConfigManager().saveConfig();
|
||||
configManager.getConfigWrapper().set("enabled", toggledState);
|
||||
configManager.saveConfig();
|
||||
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
toggledState ? configManager.getEnableMessage() : configManager.getDisableMessage()
|
||||
@@ -112,10 +112,16 @@ public class ToggleCommand implements BuilderCommand {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', message));
|
||||
}
|
||||
|
||||
private static void togglePlayerKnockback(PlatformPlayer target, ConfigManager configManager, Sender sender) {
|
||||
UUID uuid = target.getUUID();
|
||||
private static void togglePlayerKnockback(PlatformPlayer target, Sender sender) {
|
||||
User user = target.getUser();
|
||||
if (user == null) {
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getPlayerDisconnectedWhileExecutingCommand()
|
||||
).replace("%player%", target.getName());
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
if (PlayerDataManager.shouldExempt(uuid)) {
|
||||
if (PlayerDataManager.shouldExempt(target.getUUID())) {
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getPlayerIneligibleMessage()
|
||||
).replace("%player%", target.getName());
|
||||
@@ -124,11 +130,14 @@ public class ToggleCommand implements BuilderCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasPlayerData = PlayerDataManager.containsPlayerData(uuid);
|
||||
if (hasPlayerData)
|
||||
PlayerDataManager.removePlayerData(uuid);
|
||||
else {
|
||||
PlayerDataManager.addPlayerData(uuid, new PlayerData(Base.INSTANCE.getPlatformServer().getPlayer(uuid)));
|
||||
boolean hasPlayerData = PlayerDataManager.containsPlayerData(user);
|
||||
if (hasPlayerData) {
|
||||
if (CombatManager.getPlayers().contains(user)) {
|
||||
CombatManager.removePlayer(user);
|
||||
}
|
||||
PlayerDataManager.removePlayerData(user);
|
||||
} else {
|
||||
PlayerDataManager.addPlayerData(user, target);
|
||||
}
|
||||
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
|
||||
+2
-2
@@ -52,10 +52,10 @@ public class ToggleOffGroundSubcommand implements BuilderCommand {
|
||||
|
||||
private void loadConfigSettings() {
|
||||
ConfigWrapper configWrapper = Base.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
offGroundSyncEnabled = configWrapper.getBoolean("settings.offground.enabled", true);
|
||||
offGroundSyncEnabled = configWrapper.getBoolean("enable_offground_synchronization", true);
|
||||
this.offGroundSyncEnableMessage = configWrapper.getString("messages.offground.enable",
|
||||
"&aSuccessfully enabled offground synchronization.");
|
||||
this.offGroundSyncDisableMessage = configWrapper.getString("messages.offground.disable",
|
||||
"&cSuccessfully disabled offground synchronization.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,12 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class OptimizedEventBus implements EventBus {
|
||||
private final Map<Class<? extends Event>, List<OptimizedListener>> listenerMap = new HashMap<>();
|
||||
// ordinarily this would just be a hashmap since we don't modify it on different threads, but Folia forces us to make it concurrent
|
||||
private final Map<Class<? extends Event>, List<OptimizedListener>> listenerMap = new ConcurrentHashMap<>();
|
||||
private final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
@Override
|
||||
@@ -37,7 +40,8 @@ public class OptimizedEventBus implements EventBus {
|
||||
|
||||
MethodHandle handle = lookup.unreflect(method);
|
||||
OptimizedListener optimizedListener = new OptimizedListener(instance, handle, annotation.priority(), method.getDeclaringClass());
|
||||
listenerMap.computeIfAbsent((Class<? extends Event>) eventType, k -> new ArrayList<>()).add(optimizedListener);
|
||||
// Yes the CopyOnWriteArrayList is slow, yes we need to for thread safety on Folia, we'll replace when performance problems actually crop up
|
||||
listenerMap.computeIfAbsent((Class<? extends Event>) eventType, k -> new CopyOnWriteArrayList<>()).add(optimizedListener);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
@@ -10,7 +11,10 @@ public abstract class PlayerDamageListener {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(victim.getUUID());
|
||||
User user = victim.getUser();
|
||||
if (user == null) return; // Prevent errors with players disconnecting while this is running (or with fake player?)
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData == null)
|
||||
return;
|
||||
|
||||
@@ -19,6 +23,6 @@ public abstract class PlayerDamageListener {
|
||||
playerData.updateCombat();
|
||||
|
||||
if (!Base.INSTANCE.getConfigManager().isRunnableEnabled())
|
||||
playerData.sendPing(false);
|
||||
playerData.sendPing(true);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.ChatUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class PlayerJoinQuitListener {
|
||||
public void onPlayerJoin(PlayerData player) {
|
||||
PlayerDataManager.addPlayerData(player.getUuid(), player);
|
||||
PlatformPlayer platformPlayer = player.getPlatformPlayer();
|
||||
|
||||
if (Base.INSTANCE.getConfigManager().isUpdateAvailable() && Base.INSTANCE.getConfigManager().isNotifyUpdate() && Base.INSTANCE.getPermissionChecker().hasPermission(platformPlayer, "knockbacksync.update"))
|
||||
platformPlayer.sendMessage(ChatUtil.translateAlternateColorCodes(
|
||||
'&',
|
||||
"&6An updated version of &eKnockbackSync &6is now available for download at: &bhttps://github.com/CASELOAD7000/knockback-sync/releases/latest"
|
||||
));
|
||||
}
|
||||
|
||||
public void onPlayerQuit(UUID uuid) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(uuid);
|
||||
if (playerData == null)
|
||||
return;
|
||||
|
||||
if (playerData.isInCombat())
|
||||
playerData.quitCombat();
|
||||
|
||||
PlayerDataManager.removePlayerData(uuid);
|
||||
}
|
||||
}
|
||||
+11
-8
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
@@ -14,14 +15,13 @@ public abstract class PlayerKnockbackListener {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
PlayerData victimPlayerData = PlayerDataManager.getPlayerData(victim.getUUID());
|
||||
User user = victim.getUser();
|
||||
if (user == null) return; // Prevent errors with players disconnecting while this is running (or with fake player?)
|
||||
|
||||
PlayerData victimPlayerData = PlayerDataManager.getPlayerData(user);
|
||||
if (victimPlayerData == null)
|
||||
return;
|
||||
|
||||
/* Integer damageTicks = playerData.getLastDamageTicks();
|
||||
if (damageTicks != null && damageTicks > 8)
|
||||
return;*/
|
||||
|
||||
if (victimPlayerData.getNotNullPing() < PlayerData.PING_OFFSET)
|
||||
return;
|
||||
|
||||
@@ -38,10 +38,13 @@ public abstract class PlayerKnockbackListener {
|
||||
return;
|
||||
|
||||
Vector3d adjustedVelocity;
|
||||
if (victimPlayerData.isOnGroundClientSide(velocity.getY(), distanceToGround))
|
||||
if (victimPlayerData.isOnGroundClientSide(velocity.getY(), distanceToGround)) {
|
||||
Integer damageTicks = victimPlayerData.getLastDamageTicks();
|
||||
if (damageTicks != null && damageTicks > 8)
|
||||
return;
|
||||
|
||||
adjustedVelocity = velocity.withY(victimPlayerData.getVerticalVelocity()); // Should be impossible to produce a NPE in this context
|
||||
// Todo FIX
|
||||
// This can be spoofed! Clients can just say they're onGround to take less KB!
|
||||
}
|
||||
else if (victimPlayerData.isOffGroundSyncEnabled())
|
||||
adjustedVelocity = velocity.withY(victimPlayerData.getCompensatedOffGroundVelocity());
|
||||
else
|
||||
|
||||
+9
-9
@@ -5,6 +5,7 @@ import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
@@ -36,17 +37,16 @@ public class AttributeChangeListener extends PacketListenerAbstract {
|
||||
|
||||
WrapperPlayServerUpdateAttributes packet = new WrapperPlayServerUpdateAttributes(event);
|
||||
|
||||
UUID uuid = event.getUser().getUUID();
|
||||
if (!PlayerDataManager.containsPlayerData(uuid))
|
||||
return;
|
||||
User user = event.getUser();
|
||||
if (!PlayerDataManager.containsPlayerData(user)) return;
|
||||
|
||||
// Get the attributes from the packet
|
||||
for (WrapperPlayServerUpdateAttributes.Property property : packet.getProperties()) {
|
||||
// You can now check for specific attributes
|
||||
if (property.getAttribute().equals(Attributes.GENERIC_GRAVITY)) {
|
||||
onPlayerGravityChange(uuid, calculateValueWithModifiers(property));
|
||||
onPlayerGravityChange(user, calculateValueWithModifiers(property));
|
||||
} else if (property.getAttribute().equals(Attributes.GENERIC_KNOCKBACK_RESISTANCE)) {
|
||||
onPlayerKnockBackChange(uuid, calculateValueWithModifiers(property));
|
||||
onPlayerKnockBackChange(user, calculateValueWithModifiers(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,8 @@ public class AttributeChangeListener extends PacketListenerAbstract {
|
||||
|
||||
// Yes this is not properly latency compensated, that would require including a proper simulation engine
|
||||
// Laggy players will just have to deal with being on the wrong gravity for a few hundred ms, too bad!
|
||||
public void onPlayerGravityChange(UUID uuid, double newGravity) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(uuid);
|
||||
public void onPlayerGravityChange(User user, double newGravity) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_20_5)) {
|
||||
playerData.setGravityAttribute(newGravity);
|
||||
} else {
|
||||
@@ -96,8 +96,8 @@ public class AttributeChangeListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
private void onPlayerKnockBackChange(UUID uuid, double newKnockbackResistance) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(uuid);
|
||||
private void onPlayerKnockBackChange(User user, double newKnockbackResistance) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
playerData.setKnockbackResistanceAttribute(newKnockbackResistance);
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -5,6 +5,8 @@ import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
|
||||
public class ClientBrandListener extends PacketListenerAbstract {
|
||||
String brand = "vanilla";
|
||||
@@ -16,7 +18,9 @@ public class ClientBrandListener extends PacketListenerAbstract {
|
||||
WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event);
|
||||
String channelName = packet.getChannelName();
|
||||
handle(channelName, packet.getData());
|
||||
Base.INSTANCE.getPlatformServer().getPlayer(event.getUser().getUUID()).setClientBrand(brand);
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser());
|
||||
if (playerData == null) return;
|
||||
playerData.getPlatformPlayer().setClientBrand(brand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package me.caseload.knockbacksync.listener.packetevents;
|
||||
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.UserDisconnectEvent;
|
||||
import com.github.retrooper.packetevents.event.UserLoginEvent;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.ChatUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PacketPlayerJoinQuit extends PacketListenerAbstract {
|
||||
@Override
|
||||
public void onUserLogin(UserLoginEvent event) {
|
||||
Object nativePlayerObject = event.getPlayer();
|
||||
Preconditions.checkArgument(nativePlayerObject != null);
|
||||
|
||||
@NotNull PlatformPlayer platformPlayer = Base.INSTANCE.getPlatformServer().getPlayer(nativePlayerObject);
|
||||
onPlayerJoin(event.getUser(), platformPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserDisconnect(UserDisconnectEvent event) {
|
||||
onPlayerQuit(event.getUser());
|
||||
}
|
||||
|
||||
public void onPlayerJoin(User user, PlatformPlayer player) {
|
||||
PlayerDataManager.addPlayerData(user, player);
|
||||
|
||||
if (Base.INSTANCE.getConfigManager().isUpdateAvailable() && Base.INSTANCE.getConfigManager().isNotifyUpdate() && Base.INSTANCE.getPermissionChecker().hasPermission(player, "knockbacksync.update"))
|
||||
player.sendMessage(ChatUtil.translateAlternateColorCodes(
|
||||
'&',
|
||||
"&6An updated version of &eKnockbackSync &6is now available for download at: &bhttps://github.com/CASELOAD7000/knockback-sync/releases/latest"
|
||||
));
|
||||
}
|
||||
|
||||
public void onPlayerQuit(@NotNull User user) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData == null)
|
||||
return;
|
||||
|
||||
playerData.quitCombat(true);
|
||||
PlayerDataManager.removePlayerData(user);
|
||||
}
|
||||
}
|
||||
+43
-63
@@ -1,55 +1,25 @@
|
||||
package me.caseload.knockbacksync.listener.packetevents;
|
||||
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.event.*;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientKeepAlive;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPong;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWindowConfirmation;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerKeepAlive;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPing;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowConfirmation;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PingStrategy;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.data.Pair;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PingReceiveListener extends PacketListenerAbstract {
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled()) return;
|
||||
|
||||
PacketTypeCommon packetType = event.getPacketType();
|
||||
if (packetType.equals(PacketType.Play.Server.KEEP_ALIVE)) {
|
||||
WrapperPlayServerKeepAlive keepAlive = new WrapperPlayServerKeepAlive(event);
|
||||
long id = keepAlive.getId();
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
playerData.keepaliveMap.add(new Pair<>(id, System.nanoTime()));
|
||||
} else if (packetType.equals(PacketType.Play.Server.PING)) {
|
||||
WrapperPlayServerPing ping = new WrapperPlayServerPing(event);
|
||||
int id = ping.getId();
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
playerData.transactionsSent.add(new Pair<>(id, System.nanoTime()));
|
||||
} else if (packetType.equals(PacketType.Play.Server.WINDOW_CONFIRMATION)) {
|
||||
WrapperPlayServerWindowConfirmation confirmation = new WrapperPlayServerWindowConfirmation(event);
|
||||
int id = confirmation.getActionId();
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
playerData.transactionsSent.add(new Pair<>(id, System.nanoTime()));
|
||||
}
|
||||
public PingReceiveListener() {
|
||||
super(PacketListenerPriority.LOWEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,55 +28,65 @@ public class PingReceiveListener extends PacketListenerAbstract {
|
||||
// And then receive a response we have to ensure that if the packet was sent by us we cancel it
|
||||
// otherwise the server will likely kick the player
|
||||
PacketTypeCommon packetType = event.getPacketType();
|
||||
if (packetType == PacketType.Play.Client.KEEP_ALIVE) {
|
||||
User user = event.getUser();
|
||||
if (user == null) return;
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData == null) return;
|
||||
|
||||
if (playerData.pingStrategy == PingStrategy.KEEPALIVE && packetType == PacketType.Play.Client.KEEP_ALIVE) {
|
||||
WrapperPlayClientKeepAlive keepAlive = new WrapperPlayClientKeepAlive(event);
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
long receivedId = keepAlive.getId();
|
||||
|
||||
handlePingCalculationPackets(event, playerData, receivedId, playerData.keepaliveMap);
|
||||
} else if (packetType == PacketType.Play.Client.PONG) {
|
||||
} else if (playerData.pingStrategy == PingStrategy.TRANSACTION && packetType == PacketType.Play.Client.PONG) {
|
||||
WrapperPlayClientPong pong = new WrapperPlayClientPong(event);
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
int id = pong.getId();
|
||||
|
||||
handlePingCalculationPackets(event, playerData, id, playerData.transactionsSent);
|
||||
} else if (packetType == PacketType.Play.Client.WINDOW_CONFIRMATION) {
|
||||
} else if (playerData.pingStrategy == PingStrategy.TRANSACTION && packetType == PacketType.Play.Client.WINDOW_CONFIRMATION) {
|
||||
WrapperPlayClientWindowConfirmation windowConfirmation = new WrapperPlayClientWindowConfirmation(event);
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser().getUUID());
|
||||
if (playerData == null) return;
|
||||
|
||||
int id = windowConfirmation.getActionId();
|
||||
|
||||
handlePingCalculationPackets(event, playerData, id, playerData.transactionsSent);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Number> void handlePingCalculationPackets(PacketReceiveEvent event, PlayerData playerData, long id, List<Pair<T, Long>> packetSentList) {
|
||||
// We can cancel for all 3 cases
|
||||
private <T extends Number> void handlePingCalculationPackets(PacketReceiveEvent event, PlayerData playerData, long id, Queue<Pair<T, Long>> packetSentList) {
|
||||
// System.out.println("Received response ID: " + id + " Queue size before: " + packetSentList.size());
|
||||
// System.out.println("Current queue contents: " + packetSentList.toString());
|
||||
|
||||
if (playerData.didWeSendThatPacket(id)) {
|
||||
// Pong not needed as vanilla ignores the packet, its needed for packet limiters
|
||||
// We want to cancel Window Confirmations that we send, and ...
|
||||
// Minecraft kicks players that send invalid keepAliveID packets
|
||||
// Since Minecraft doesn't know we just sent a keepalive, we gotta cancel it
|
||||
// To stop MC from processing it and kicking the player
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled()) return;
|
||||
|
||||
long pingNanos = (System.nanoTime() - packetSentList.remove(0).getSecond());
|
||||
double diffMillisDouble = pingNanos / 1_000_000.0;
|
||||
Pair<T, Long> data = null;
|
||||
int cleared = 0;
|
||||
// Keep polling until we find the matching ID
|
||||
do {
|
||||
data = packetSentList.poll();
|
||||
cleared++;
|
||||
|
||||
playerData.setPreviousPing(playerData.getPing());
|
||||
playerData.setPing(diffMillisDouble);
|
||||
if (data == null) {
|
||||
// System.out.println("No data found in queue!");
|
||||
break;
|
||||
}
|
||||
|
||||
playerData.getJitterCalculator().addPing(pingNanos);
|
||||
double jitter = playerData.getJitterCalculator().calculateJitter();
|
||||
playerData.setJitter(jitter);
|
||||
// System.out.println("Cleared entry " + cleared + ": ID=" + data.getFirst() + " Time=" + data.getSecond());
|
||||
|
||||
long pingNanos = (System.nanoTime() - data.getSecond());
|
||||
double diffMillisDouble = pingNanos / 1_000_000.0;
|
||||
|
||||
playerData.setPreviousPing(playerData.getPing());
|
||||
playerData.setPing(diffMillisDouble);
|
||||
|
||||
playerData.getJitterCalculator().addPing(pingNanos);
|
||||
double jitter = playerData.getJitterCalculator().calculateJitter();
|
||||
playerData.setJitter(jitter);
|
||||
|
||||
} while (data.getFirst().longValue() != id);
|
||||
|
||||
// System.out.println("Finished processing - Cleared " + cleared + " entries. Queue size after: " + packetSentList.size());
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package me.caseload.knockbacksync.listener.packetevents;
|
||||
|
||||
import com.github.retrooper.packetevents.event.*;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientKeepAlive;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPong;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWindowConfirmation;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerKeepAlive;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPing;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowConfirmation;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PingStrategy;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.data.Pair;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PingSendListener extends PacketListenerAbstract {
|
||||
|
||||
public PingSendListener() {
|
||||
// See all actually outgoing, not cancelled, ping packets
|
||||
super(PacketListenerPriority.MONITOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled()) return;
|
||||
if (event.isCancelled()) return;
|
||||
|
||||
PacketTypeCommon packetType = event.getPacketType();
|
||||
UUID playerUUID = event.getUser().getUUID();
|
||||
if (playerUUID == null) return;
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(event.getUser());
|
||||
if (playerData == null) return;
|
||||
|
||||
if (playerData.pingStrategy == PingStrategy.KEEPALIVE && packetType.equals(PacketType.Play.Server.KEEP_ALIVE)) {
|
||||
WrapperPlayServerKeepAlive keepAlive = new WrapperPlayServerKeepAlive(event);
|
||||
long id = keepAlive.getId();
|
||||
|
||||
// System.out.println("Adding ping to queue - ID: " + id + " Time: " + System.nanoTime() + " Queue size before: " + playerData.keepaliveMap.size());
|
||||
playerData.keepaliveMap.add(new Pair<>(id, System.nanoTime()));
|
||||
} else if (playerData.pingStrategy == PingStrategy.TRANSACTION && packetType.equals(PacketType.Play.Server.PING)) {
|
||||
WrapperPlayServerPing ping = new WrapperPlayServerPing(event);
|
||||
int id = ping.getId();
|
||||
|
||||
playerData.transactionsSent.add(new Pair<>(id, System.nanoTime()));
|
||||
} else if (playerData.pingStrategy == PingStrategy.TRANSACTION && packetType.equals(PacketType.Play.Server.WINDOW_CONFIRMATION)) {
|
||||
WrapperPlayServerWindowConfirmation confirmation = new WrapperPlayServerWindowConfirmation(event);
|
||||
int id = confirmation.getActionId();
|
||||
|
||||
playerData.transactionsSent.add(new Pair<>(id, System.nanoTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.manager;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -8,17 +9,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CombatManager {
|
||||
|
||||
private static final Set<UUID> combatPlayers = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<@NotNull User> combatPlayers = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static @NotNull Set<UUID> getPlayers() {
|
||||
public static @NotNull Set<@NotNull User> getPlayers() {
|
||||
return combatPlayers;
|
||||
}
|
||||
|
||||
public static void addPlayer(UUID uuid) {
|
||||
combatPlayers.add(uuid);
|
||||
public static void addPlayer(@NotNull User user) {
|
||||
combatPlayers.add(user);
|
||||
}
|
||||
|
||||
public static void removePlayer(UUID uuid) {
|
||||
combatPlayers.remove(uuid);
|
||||
public static void removePlayer(@NotNull User user) {
|
||||
combatPlayers.remove(user);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.Platform;
|
||||
import me.caseload.knockbacksync.config.YamlConfiguration;
|
||||
import me.caseload.knockbacksync.runnable.PingRunnable;
|
||||
import me.caseload.knockbacksync.runnable.TickRunnable;
|
||||
import me.caseload.knockbacksync.scheduler.AbstractTaskHandle;
|
||||
|
||||
import java.io.File;
|
||||
@@ -19,12 +20,13 @@ import java.util.Map;
|
||||
@Setter
|
||||
public class ConfigManager {
|
||||
|
||||
public static final long CONFIG_VERSION = 6;
|
||||
public static final long CONFIG_VERSION = 7;
|
||||
|
||||
private boolean toggled;
|
||||
private boolean runnableEnabled;
|
||||
private boolean updateAvailable;
|
||||
private boolean notifyUpdate;
|
||||
private boolean autoUpdate;
|
||||
|
||||
private long runnableInterval;
|
||||
private long combatTimer;
|
||||
@@ -35,8 +37,10 @@ public class ConfigManager {
|
||||
private String playerEnableMessage;
|
||||
private String playerDisableMessage;
|
||||
private String playerIneligibleMessage;
|
||||
private String playerDisconnectedWhileExecutingCommand;
|
||||
|
||||
private AbstractTaskHandle pingTask;
|
||||
private AbstractTaskHandle tickTask;
|
||||
|
||||
private Map<String, Object> config;
|
||||
private File configFile;
|
||||
@@ -112,7 +116,10 @@ public class ConfigManager {
|
||||
pingTask = Base.INSTANCE.getScheduler().runTaskTimerAsynchronously(new PingRunnable(), initialDelay, pingTaskRunnableInterval);
|
||||
}
|
||||
|
||||
tickTask = Base.INSTANCE.getScheduler().runTaskTimer(new TickRunnable(), 0L, 1L);
|
||||
|
||||
notifyUpdate = configWrapper.getBoolean("notify_updates", true);
|
||||
autoUpdate = configWrapper.getBoolean("auto_update", true);
|
||||
combatTimer = configWrapper.getLong("runnable.timer", 30L);
|
||||
spikeThreshold = configWrapper.getLong("spike_threshold", 20L);
|
||||
enableMessage = configWrapper.getString("messages.toggle.global.enable", "&aSuccessfully enabled KnockbackSync.");
|
||||
@@ -120,6 +127,7 @@ public class ConfigManager {
|
||||
playerEnableMessage = configWrapper.getString("messages.toggle.player.enable", "&aSuccessfully enabled KnockbackSync for %player%.");
|
||||
playerDisableMessage = configWrapper.getString("messages.toggle.player.disable", "&aSuccessfully &cdisabled &aKnockbackSync for %player%.");
|
||||
playerIneligibleMessage = configWrapper.getString("messages.toggle.player.ineligible", "&c%player% is ineligible for KnockbackSync. If you believe this is in error, please contact your server administrators.");
|
||||
playerDisconnectedWhileExecutingCommand = configWrapper.getString("messages.toggle.player.disconnected-while-executing-command", "&c%player% disconnected while executing command.");
|
||||
}
|
||||
|
||||
public void updateConfig() {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package me.caseload.knockbacksync.manager;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.FloodgateUtil;
|
||||
import me.caseload.knockbacksync.util.GeyserUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -12,27 +15,28 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PlayerDataManager {
|
||||
|
||||
private static final Map<UUID, PlayerData> playerDataMap = new ConcurrentHashMap<>();
|
||||
private static final Map<User, PlayerData> playerDataMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static PlayerData getPlayerData(@NotNull UUID uuid) {
|
||||
return playerDataMap.get(uuid);
|
||||
public static @Nullable PlayerData getPlayerData(@NotNull User user) {
|
||||
return playerDataMap.get(user);
|
||||
}
|
||||
|
||||
public static void addPlayerData(@NotNull UUID uuid, @NotNull PlayerData playerData) {
|
||||
if (!shouldExempt(uuid)) {
|
||||
playerDataMap.put(uuid, playerData);
|
||||
public static void addPlayerData(@NotNull User user, @NotNull PlatformPlayer platformPlayer) {
|
||||
if (!shouldExempt(platformPlayer.getUUID())) {
|
||||
PlayerData playerData = new PlayerData(user, platformPlayer);
|
||||
playerDataMap.put(user, playerData);
|
||||
Base.INSTANCE.getEventBus().registerListeners(playerData);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removePlayerData(@NotNull UUID uuid) {
|
||||
PlayerData playerData = playerDataMap.remove(uuid);
|
||||
public static void removePlayerData(@NotNull User user) {
|
||||
PlayerData playerData = playerDataMap.remove(user);
|
||||
if (playerData != null)
|
||||
Base.INSTANCE.getEventBus().unregisterListeners(playerData);
|
||||
}
|
||||
|
||||
public static boolean containsPlayerData(@NotNull UUID uuid) {
|
||||
return playerDataMap.containsKey(uuid);
|
||||
public static boolean containsPlayerData(@NotNull User user) {
|
||||
return playerDataMap.containsKey(user);
|
||||
}
|
||||
|
||||
public static boolean shouldExempt(@NotNull UUID uuid) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.caseload.knockbacksync.player;
|
||||
|
||||
public enum PingStrategy {
|
||||
KEEPALIVE,
|
||||
TRANSACTION
|
||||
}
|
||||
@@ -52,10 +52,21 @@ public interface PlatformPlayer {
|
||||
|
||||
BoundingBox getBoundingBox();
|
||||
|
||||
User getUser();
|
||||
/**
|
||||
* If a player disconnects while we are running the constructor of a PlatformPlayer
|
||||
* PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer) may return null
|
||||
* This will make the internal field null and thus this function may return null.
|
||||
*/
|
||||
@Nullable User getUser();
|
||||
|
||||
void setClientBrand(String brand);
|
||||
|
||||
String getClientBrand();
|
||||
// Add more methods as needed
|
||||
|
||||
boolean isFlying();
|
||||
|
||||
float getSpeed();
|
||||
|
||||
float getFlyingSpeed();
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import com.github.retrooper.packetevents.protocol.ConnectionState;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerKeepAlive;
|
||||
@@ -23,7 +21,9 @@ import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.events.ToggleOnOffEvent;
|
||||
import me.caseload.knockbacksync.manager.CombatManager;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.scheduler.AbstractTaskHandle;
|
||||
import me.caseload.knockbacksync.scheduler.NettyTaskHandle;
|
||||
import me.caseload.knockbacksync.util.MathUtil;
|
||||
import me.caseload.knockbacksync.util.data.Pair;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
@@ -33,7 +33,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter
|
||||
public class PlayerData {
|
||||
@@ -45,8 +48,8 @@ public class PlayerData {
|
||||
|
||||
private static Field playerField;
|
||||
|
||||
public final List<Pair<Integer, Long>> transactionsSent = new LinkedList<>();
|
||||
public final List<Pair<Long, Long>> keepaliveMap = new LinkedList<>();
|
||||
public final Queue<Pair<Integer, Long>> transactionsSent = new ConcurrentLinkedQueue<>();
|
||||
public final Queue<Pair<Long, Long>> keepaliveMap = new ConcurrentLinkedQueue<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -77,31 +80,22 @@ public class PlayerData {
|
||||
@Getter private final JitterCalculator jitterCalculator = new JitterCalculator();
|
||||
@Setter private double jitter;
|
||||
@Nullable private AbstractTaskHandle combatTask;
|
||||
@NotNull private final Object combatTaskLock = new Object(); // Lock object for synchronization
|
||||
@Nullable @Setter private Double ping, previousPing;
|
||||
@Nullable @Setter private Double verticalVelocity;
|
||||
@Nullable @Setter private Integer lastDamageTicks;
|
||||
@Setter private double gravityAttribute = 0.08;
|
||||
@Setter private double knockbackResistanceAttribute = 0.0;
|
||||
public PingStrategy pingStrategy; // this is currently shared between all instances, but can be made per-player later
|
||||
|
||||
public PlayerData(PlatformPlayer platformPlayer) {
|
||||
@Getter private final SimulatedPlayer simulatedPlayer;
|
||||
|
||||
public PlayerData(User user, PlatformPlayer platformPlayer) {
|
||||
this.uuid = platformPlayer.getUUID();
|
||||
this.user = user;
|
||||
this.platformPlayer = platformPlayer;
|
||||
|
||||
User tempUser = null;
|
||||
|
||||
PlayerManager playerManager = PacketEvents.getAPI().getPlayerManager();
|
||||
Object player = null;
|
||||
try {
|
||||
player = playerField.get(platformPlayer);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
tempUser = playerManager.getUser(player);
|
||||
}
|
||||
|
||||
this.user = tempUser;
|
||||
this.pingStrategy = loadPingStrategy(Base.INSTANCE.getConfigManager());
|
||||
this.simulatedPlayer = new SimulatedPlayer(this);
|
||||
}
|
||||
|
||||
public double getNotNullPing() {
|
||||
@@ -142,9 +136,8 @@ public class PlayerData {
|
||||
public void sendPing(boolean async) {
|
||||
if (user == null || user.getEncoderState() != ConnectionState.PLAY) return;
|
||||
|
||||
String pingStrategy = Base.INSTANCE.getConfigManager().getConfigWrapper().getString("ping_strategy", "KEEPALIVE");
|
||||
switch (pingStrategy) {
|
||||
case "KEEPALIVE":
|
||||
case KEEPALIVE:
|
||||
long keepAliveID = async ? NETTY_THREAD_TRANSACTION_ID : MAIN_THREAD_TRANSACTION_ID;
|
||||
if (async) {
|
||||
ChannelHelper.runInEventLoop(user.getChannel(), () -> {
|
||||
@@ -157,8 +150,7 @@ public class PlayerData {
|
||||
user.sendPacket(new WrapperPlayServerKeepAlive(keepAliveID));
|
||||
}
|
||||
break;
|
||||
case "PING":
|
||||
case "TRANSACTION":
|
||||
case TRANSACTION:
|
||||
PacketWrapper<?> packet;
|
||||
short pingTransactionID = async ? NETTY_THREAD_TRANSACTION_ID : MAIN_THREAD_TRANSACTION_ID;
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
|
||||
@@ -292,28 +284,41 @@ public class PlayerData {
|
||||
return yAxis;
|
||||
}
|
||||
|
||||
public boolean isInCombat() {
|
||||
return combatTask != null;
|
||||
}
|
||||
|
||||
public void updateCombat() {
|
||||
if (isInCombat())
|
||||
combatTask.cancel();
|
||||
|
||||
combatTask = newCombatTask();
|
||||
CombatManager.addPlayer(uuid);
|
||||
Channel channel = (Channel) user.getChannel();
|
||||
channel.eventLoop().execute(() -> {
|
||||
if (combatTask != null) {
|
||||
combatTask.cancel();
|
||||
}
|
||||
combatTask = newCombatTask(channel);
|
||||
CombatManager.addPlayer(user);
|
||||
});
|
||||
}
|
||||
|
||||
public void quitCombat() {
|
||||
combatTask.cancel(); // should do nothing
|
||||
combatTask = null;
|
||||
CombatManager.removePlayer(uuid);
|
||||
/**
|
||||
* Cancels the combatTask quit task. This prevents quitCombat() from being called later against a new combat task
|
||||
* Or against a player that has disconnected already
|
||||
* @param async Whether we are already in the players netty thread
|
||||
*/
|
||||
public void quitCombat(boolean async) {
|
||||
Channel channel = (Channel) user.getChannel();
|
||||
Runnable runnable = () -> {
|
||||
if (combatTask != null) {
|
||||
combatTask.cancel();
|
||||
combatTask = null;
|
||||
}
|
||||
CombatManager.removePlayer(user);
|
||||
};
|
||||
if (async) {
|
||||
channel.eventLoop().execute(runnable);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private AbstractTaskHandle newCombatTask() {
|
||||
return Base.INSTANCE.getScheduler().runTaskLaterAsynchronously(
|
||||
this::quitCombat, Base.INSTANCE.getConfigManager().getCombatTimer());
|
||||
private AbstractTaskHandle newCombatTask(Channel channel) {
|
||||
return new NettyTaskHandle(channel.eventLoop().schedule(() -> quitCombat(false), convertTicksToMilliseconds(Base.INSTANCE.getConfigManager().getCombatTimer()), TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
public ClientVersion getClientVersion() {
|
||||
@@ -337,9 +342,31 @@ public class PlayerData {
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onToggledEvent(ToggleOnOffEvent event) {
|
||||
if (event.getStatus() == false) {
|
||||
if (!event.getStatus()) {
|
||||
transactionsSent.clear();
|
||||
keepaliveMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReloadEvent(ConfigReloadEvent event) {
|
||||
this.pingStrategy = loadPingStrategy(event.getConfigManager());
|
||||
}
|
||||
|
||||
private PingStrategy loadPingStrategy(ConfigManager configManager) {
|
||||
String pingStrategy = configManager.getConfigWrapper().getString("ping_strategy", "KEEPALIVE");
|
||||
switch (pingStrategy) {
|
||||
case "KEEPALIVE":
|
||||
return PingStrategy.KEEPALIVE;
|
||||
case "PING":
|
||||
case "TRANSACTION":
|
||||
return PingStrategy.TRANSACTION;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown ping_strategy: " + pingStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
private long convertTicksToMilliseconds(long ticks) {
|
||||
return ticks * 50;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package me.caseload.knockbacksync.player;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.potion.PotionEffect;
|
||||
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import lombok.Getter;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.util.data.Vec2d;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class SimulatedPlayer {
|
||||
|
||||
private final PlayerData playerData;
|
||||
private final PlatformPlayer platformPlayer;
|
||||
private Vec2d position;
|
||||
@Getter private double yCoordinate;
|
||||
@Getter private double verticalVelocity;
|
||||
@Getter private BoundingBox boundingBox;
|
||||
@Getter private boolean isOnGround;
|
||||
|
||||
public SimulatedPlayer(PlayerData playerData) {
|
||||
this.playerData = playerData;
|
||||
this.platformPlayer = playerData.getPlatformPlayer();
|
||||
this.position = new Vec2d(platformPlayer.getX(), platformPlayer.getZ());
|
||||
this.yCoordinate = platformPlayer.getY();
|
||||
this.verticalVelocity = platformPlayer.getVelocity().y;
|
||||
this.boundingBox = platformPlayer.getBoundingBox();
|
||||
this.isOnGround = platformPlayer.isOnGround(); // not safe but its only for a single tick
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
Vector3d location = playerData.getPlatformPlayer().getLocation();
|
||||
Vec2d newPosition = new Vec2d(location.x, location.z);
|
||||
|
||||
if (!position.equals(newPosition)) {
|
||||
double dx = newPosition.x - position.x;
|
||||
double dz = newPosition.z - position.z;
|
||||
boundingBox.shift(dx, 0, dz);
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
if (platformPlayer.isFlying() || platformPlayer.isGliding()) {
|
||||
double newY = platformPlayer.getY();
|
||||
double shiftY = newY - yCoordinate;
|
||||
boundingBox.shift(0, shiftY, 0);
|
||||
yCoordinate = newY;
|
||||
verticalVelocity = platformPlayer.getVelocity().getY();
|
||||
return;
|
||||
}
|
||||
|
||||
verticalVelocity += playerData.getGravityAttribute();
|
||||
verticalVelocity = Math.min(verticalVelocity, 3.92);
|
||||
verticalVelocity *= 0.98;
|
||||
|
||||
double newY = yCoordinate -= verticalVelocity;
|
||||
double shiftY = newY - yCoordinate;
|
||||
boundingBox.shift(0, shiftY, 0);
|
||||
yCoordinate = newY;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.runnable;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.CombatManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
@@ -14,9 +15,10 @@ public class PingRunnable implements Runnable {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
for (UUID uuid : CombatManager.getPlayers()) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(uuid);
|
||||
playerData.sendPing(true);
|
||||
for (User user : CombatManager.getPlayers()) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData != null)
|
||||
playerData.sendPing(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.caseload.knockbacksync.runnable;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.player.SimulatedPlayer;
|
||||
|
||||
public class TickRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
for (PlatformPlayer player : Base.INSTANCE.getPlatformServer().getOnlinePlayers()) {
|
||||
User user = player.getUser();
|
||||
if (user == null)
|
||||
continue;
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(user);
|
||||
if (playerData == null)
|
||||
continue;
|
||||
|
||||
SimulatedPlayer simulatedPlayer = playerData.getSimulatedPlayer();
|
||||
if (simulatedPlayer == null)
|
||||
continue;
|
||||
|
||||
simulatedPlayer.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.caseload.knockbacksync.scheduler;
|
||||
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
|
||||
public class NettyTaskHandle implements AbstractTaskHandle {
|
||||
|
||||
private final ScheduledFuture scheduledFuture;
|
||||
|
||||
public NettyTaskHandle(ScheduledFuture<?> scheduledFuture) {
|
||||
this.scheduledFuture = scheduledFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCancelled() {
|
||||
return scheduledFuture.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
scheduledFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,9 @@ public class GeyserUtil {
|
||||
public static boolean isGeyserPlayer(UUID uuid) {
|
||||
if (!CHECKED_FOR_GEYSER) {
|
||||
try {
|
||||
switch (Base.INSTANCE.getPlatform()) {
|
||||
case BUKKIT:
|
||||
case FOLIA:
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
GEYSER_CLASS = classLoader.loadClass("org.geysermc.api.Geyser");
|
||||
break;
|
||||
case FABRIC:
|
||||
GEYSER_CLASS = Class.forName("org.geysermc.api.Geyser");
|
||||
GEYSER_PRESENT = true;
|
||||
break;
|
||||
}
|
||||
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
GEYSER_CLASS = classLoader.loadClass("org.geysermc.api.Geyser");
|
||||
GEYSER_PRESENT = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
GEYSER_PRESENT = false;
|
||||
}
|
||||
@@ -40,17 +31,8 @@ public class GeyserUtil {
|
||||
if (GEYSER_PRESENT) {
|
||||
if (GEYSER_API_CLASS == null) {
|
||||
try {
|
||||
switch (Base.INSTANCE.getPlatform()) {
|
||||
case BUKKIT:
|
||||
case FOLIA:
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
GEYSER_API_CLASS = classLoader.loadClass("org.geysermc.api.GeyserApiBase");
|
||||
break;
|
||||
case FABRIC:
|
||||
GEYSER_CLASS = Class.forName("org.geysermc.api.GeyserApiBase");
|
||||
GEYSER_PRESENT = true;
|
||||
break;
|
||||
}
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
GEYSER_API_CLASS = classLoader.loadClass("org.geysermc.api.GeyserApiBase");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.caseload.knockbacksync.util.data;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Vec2d {
|
||||
|
||||
public final double x;
|
||||
public final double z;
|
||||
|
||||
public Vec2d(double x, double z) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object)
|
||||
return true;
|
||||
|
||||
if (!(object instanceof Vec2d))
|
||||
return false;
|
||||
|
||||
Vec2d v = (Vec2d) object;
|
||||
return Double.compare(v.x, x) == 0 && Double.compare(v.z, z) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Vec2d(" + x + ", " + z + ")";
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,5 @@ public interface PlatformServer {
|
||||
|
||||
PlatformPlayer getPlayer(UUID uuid);
|
||||
|
||||
// switch (KnockbackSyncBase.INSTANCE.platform) {
|
||||
// case BUKKIT:
|
||||
// case FOLIA:
|
||||
// return Bukkit.getOnlinePlayers().stream()
|
||||
// .map(BukkitPlayer::new)
|
||||
// .collect(Collectors.toList());
|
||||
// case FABRIC:
|
||||
// MinecraftServer server = (MinecraftServer) FabricLoader.getInstance().getGameInstance();
|
||||
// return server.getPlayerList().getPlayers().stream()
|
||||
// .map(FabricPlayer::new)
|
||||
// .collect(Collectors.toList());
|
||||
// default:
|
||||
// throw new IllegalStateException("Unexpected platform: " + KnockbackSyncBase.INSTANCE.platform);
|
||||
// }
|
||||
PlatformPlayer getPlayer(Object nativePlatformPlayer);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#########################################
|
||||
|
||||
# Do not modify!
|
||||
config_version: 6
|
||||
config_version: 7
|
||||
|
||||
# Plugin enabled state
|
||||
# Toggleable using /knockbacksync toggle
|
||||
@@ -14,6 +14,10 @@ enabled: true
|
||||
# Required permission: knockbacksync.update
|
||||
notify_updates: true
|
||||
|
||||
# Automatically updates the plugin
|
||||
# Only servers with restart compatibility can use this feature
|
||||
auto_update: false
|
||||
|
||||
# Grabs the ping of combat-tagged players every x ticks
|
||||
# Disabling this may lead to inaccuracies in calculations
|
||||
runnable:
|
||||
@@ -190,6 +194,7 @@ messages:
|
||||
enable: "&aSuccessfully enabled KnockbackSync for %player%."
|
||||
disable: "&aSuccessfully &cdisabled &aKnockbackSync for %player%."
|
||||
ineligible: "&c%player% is ineligible for KnockbackSync. If you believe this is in error, please contact your server administrators."
|
||||
disconnected-while-executing-command: "&c%player% disconnected while executing command."
|
||||
permission:
|
||||
no_global: "&cYou don't have permission to toggle the global setting."
|
||||
no_self: "&cYou do not have permission to toggle your knockback."
|
||||
|
||||
+14
-29
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id("fabric-loom")
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
loom {
|
||||
@@ -11,11 +10,6 @@ base {
|
||||
archivesName.set("${rootProject.property("archives_base_name")}-fabric")
|
||||
}
|
||||
|
||||
val shadeThisThing: Configuration by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isTransitive = true
|
||||
}
|
||||
|
||||
// TODO migrate to only including sourceset for compile, test and javadoc tasks
|
||||
// Currently must build with gradle build -x test to skip test
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
@@ -30,21 +24,24 @@ tasks.named<JavaCompile>("compileTestJava") {
|
||||
exclude("**/*")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // Conditional Mixin
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":common"))
|
||||
|
||||
minecraft("com.mojang:minecraft:${rootProject.property("minecraft_version")}")
|
||||
mappings(loom.layered {
|
||||
officialMojangMappings()
|
||||
parchment("org.parchmentmc.data:parchment-1.21:${rootProject.property("parchment_mappings")}")
|
||||
})
|
||||
mappings("net.fabricmc:yarn:${rootProject.property("yarn_mappings")}")
|
||||
modImplementation("net.fabricmc:fabric-loader:${rootProject.property("loader_version")}")
|
||||
modImplementation("net.fabricmc.fabric-api:fabric-api:${rootProject.property("fabric_version")}")
|
||||
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", "${rootProject.property("fabric_version")}"))
|
||||
modImplementation(fabricApi.module("fabric-events-interaction-v0", "${rootProject.property("fabric_version")}"))
|
||||
|
||||
include(modImplementation("me.lucko:fabric-permissions-api:0.3.1")!!)
|
||||
include(modImplementation("com.github.retrooper:packetevents-fabric:2.6.0-SNAPSHOT")!!)
|
||||
include(modImplementation("org.incendo:cloud-fabric:2.0.0-beta.9")!!)
|
||||
include(modImplementation("me.lucko:fabric-permissions-api:0.6.1")!!)
|
||||
include(modImplementation("com.github.retrooper:packetevents-fabric:2.11.1+60a2c34-SNAPSHOT")!!)
|
||||
include(modImplementation("org.incendo:cloud-fabric:2.0.0-beta.15")!!)
|
||||
|
||||
include(implementation("org.incendo:cloud-minecraft-extras:2.0.0-beta.10")!!)
|
||||
include(implementation("org.yaml:snakeyaml:2.0")!!)
|
||||
include(implementation("org.kohsuke:github-api:1.326")!!)
|
||||
// Required for org.kohsuke.github
|
||||
@@ -57,22 +54,10 @@ dependencies {
|
||||
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("org.projectlombok:lombok:1.18.34")
|
||||
compileOnly("io.netty:netty-all:4.1.72.Final")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
archiveClassifier.set("dev")
|
||||
configurations = listOf(shadeThisThing)
|
||||
isEnableRelocation = true
|
||||
relocationPrefix = "${project.property("maven_group")}.${project.property("archives_base_name")}.shaded"
|
||||
finalizedBy(tasks.remapJar)
|
||||
}
|
||||
tasks.remapJar {
|
||||
archiveClassifier.set(null as String?)
|
||||
dependsOn(tasks.shadowJar)
|
||||
inputFile = tasks.shadowJar.get().archiveFile
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
from(project(":common").sourceSets.main.get().resources)
|
||||
|
||||
@@ -84,8 +69,8 @@ tasks.processResources {
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand(
|
||||
"version" to project.version,
|
||||
"minecraft_version" to rootProject.property("minecraft_version"),
|
||||
"loader_version" to rootProject.property("loader_version")
|
||||
"minecraft_version" to rootProject.property("minecraft_version")!!,
|
||||
"loader_version" to rootProject.property("loader_version")!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI;
|
||||
import lombok.Getter;
|
||||
import me.caseload.knockbacksync.entity.EntityTickManager;
|
||||
import me.caseload.knockbacksync.listener.fabric.FabricPlayerDamageListener;
|
||||
import me.caseload.knockbacksync.listener.fabric.FabricPlayerJoinQuitListener;
|
||||
import me.caseload.knockbacksync.listener.fabric.FabricPlayerKnockbackListener;
|
||||
import me.caseload.knockbacksync.listener.fabric.FabricTickRateChangeListener;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
@@ -69,8 +68,8 @@ public class FabricBase extends Base {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
PacketEvents.setAPI(new FabricPacketEventsAPI("knockbacksync", EnvType.SERVER));
|
||||
PacketEvents.getAPI().load();
|
||||
// Since we JiJ PacketEvents and depend on it on Fabric, we can always just get the API instance since it loads firsts
|
||||
// No need to make an API instance and load it
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +84,6 @@ public class FabricBase extends Base {
|
||||
|
||||
@Override
|
||||
protected void registerPlatformListeners() {
|
||||
new FabricPlayerJoinQuitListener().register();
|
||||
new FabricPlayerDamageListener().register();
|
||||
new FabricPlayerKnockbackListener().register();
|
||||
new FabricTickRateChangeListener().register();
|
||||
@@ -127,7 +125,7 @@ public class FabricBase extends Base {
|
||||
this.tickRate = tickRate;
|
||||
}
|
||||
|
||||
private URL getJarURL() {
|
||||
public URL getJarURL() {
|
||||
URL jarUrl = null;
|
||||
Optional<ModContainer> modContainer = FabricLoader.getInstance().getModContainer("knockbacksync");
|
||||
if (modContainer.isPresent()) {
|
||||
@@ -141,4 +139,12 @@ public class FabricBase extends Base {
|
||||
}
|
||||
return jarUrl;
|
||||
}
|
||||
|
||||
public void restartServer() {
|
||||
|
||||
}
|
||||
|
||||
public boolean hasRestartScript() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
@@ -16,15 +17,24 @@ public class FabricLoaderMod implements PreLaunchEntrypoint, ModInitializer {
|
||||
|
||||
@Override
|
||||
public void onPreLaunch() {
|
||||
ensureServer();
|
||||
core.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ensureServer();
|
||||
core.enable();
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
|
||||
core.scheduler.shutdown();
|
||||
core.statsManager.getMetrics().shutdown();
|
||||
// bstats removal
|
||||
// core.statsManager.getMetrics().shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
private void ensureServer() {
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
|
||||
throw new IllegalStateException("This mod can only be run on servers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,23 @@ package me.caseload.knockbacksync.callback;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public interface PlayerVelocityEvent {
|
||||
Event<PlayerVelocityEvent> EVENT = EventFactory.createArrayBacked(PlayerVelocityEvent.class,
|
||||
(listeners) -> (player, velocity) -> {
|
||||
for (PlayerVelocityEvent listener : listeners) {
|
||||
InteractionResult result = listener.onVelocityChange(player, velocity);
|
||||
ActionResult result = listener.onVelocityChange(player, velocity);
|
||||
|
||||
if (result != InteractionResult.PASS) {
|
||||
if (result != ActionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
return ActionResult.PASS;
|
||||
});
|
||||
|
||||
InteractionResult onVelocityChange(ServerPlayer player, Vec3 velocity);
|
||||
ActionResult onVelocityChange(ServerPlayerEntity player, Vec3d velocity);
|
||||
}
|
||||
@@ -3,9 +3,8 @@ package me.caseload.knockbacksync.entity;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -32,9 +31,9 @@ public class EntityTickManager {
|
||||
customTickIntervals.clear();
|
||||
for (String entityKey : configWrapper.getKeys("entity_tick_intervals")) {
|
||||
try {
|
||||
Optional<EntityType<?>> entityType = EntityType.byString(entityKey.toLowerCase());
|
||||
Optional<EntityType<?>> entityType = EntityType.get(entityKey.toLowerCase());
|
||||
if (entityType.isPresent()) {
|
||||
int interval = configWrapper.getInt("entity_tick_intervals." + entityKey, entityType.get().updateInterval());
|
||||
int interval = configWrapper.getInt("entity_tick_intervals." + entityKey, entityType.get().getTrackTickInterval());
|
||||
customTickIntervals.put(entityType.get(), interval);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
@@ -44,6 +43,6 @@ public class EntityTickManager {
|
||||
}
|
||||
|
||||
public static int getCustomUpdateInterval(EntityType<?> entityType) {
|
||||
return customTickIntervals.getOrDefault(entityType, entityType.updateInterval());
|
||||
return customTickIntervals.getOrDefault(entityType, entityType.getTrackTickInterval());
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -5,24 +5,24 @@ import me.caseload.knockbacksync.listener.PlayerDamageListener;
|
||||
import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
||||
public class FabricPlayerDamageListener extends PlayerDamageListener {
|
||||
|
||||
public void register() {
|
||||
AttackEntityCallback.EVENT.register((player, world, hand, entity, source) -> {
|
||||
onPlayerDamage((ServerPlayer) player, entity);
|
||||
return InteractionResult.PASS;
|
||||
onPlayerDamage((ServerPlayerEntity) player, entity);
|
||||
return ActionResult.PASS;
|
||||
});
|
||||
}
|
||||
|
||||
private void onPlayerDamage(ServerPlayer attacker, Entity victimEntity) {
|
||||
if (victimEntity instanceof Player victim) {
|
||||
private void onPlayerDamage(ServerPlayerEntity attacker, Entity victimEntity) {
|
||||
if (victimEntity instanceof PlayerEntity victim) {
|
||||
PlatformPlayer platformAttacker = new FabricPlayer(attacker);
|
||||
PlatformPlayer platformVictim = new FabricPlayer((ServerPlayer) victim);
|
||||
PlatformPlayer platformVictim = new FabricPlayer((ServerPlayerEntity) victim);
|
||||
super.onPlayerDamage(platformVictim, platformAttacker); // Call the shared method
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package me.caseload.knockbacksync.listener.fabric;
|
||||
|
||||
import me.caseload.knockbacksync.listener.PlayerJoinQuitListener;
|
||||
import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
|
||||
public class FabricPlayerJoinQuitListener extends PlayerJoinQuitListener {
|
||||
public void register() {
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
onPlayerJoin(new PlayerData(new FabricPlayer(handler.player)));
|
||||
});
|
||||
ServerPlayConnectionEvents.DISCONNECT.register(((handler, server) -> {
|
||||
onPlayerQuit(handler.player.getUUID());
|
||||
}));
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -4,27 +4,27 @@ import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import me.caseload.knockbacksync.callback.PlayerVelocityEvent;
|
||||
import me.caseload.knockbacksync.listener.PlayerKnockbackListener;
|
||||
import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.damage.DamageTypes;
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
||||
public class FabricPlayerKnockbackListener extends PlayerKnockbackListener {
|
||||
|
||||
public void register() {
|
||||
PlayerVelocityEvent.EVENT.register((player, velocity) -> {
|
||||
DamageSource lastDamageSource = player.getLastDamageSource();
|
||||
DamageSource lastDamageSource = player.getRecentDamageSource();
|
||||
if (lastDamageSource == null)
|
||||
return InteractionResult.PASS;
|
||||
return ActionResult.PASS;
|
||||
|
||||
// Check if the damage is from a player attack
|
||||
if (!lastDamageSource.is(DamageTypes.PLAYER_ATTACK))
|
||||
return InteractionResult.PASS;
|
||||
if (!lastDamageSource.isOf(DamageTypes.PLAYER_ATTACK))
|
||||
return ActionResult.PASS;
|
||||
|
||||
onPlayerVelocity(new FabricPlayer(player), new Vector3d(velocity.x, velocity.y, velocity.z));
|
||||
// This SHOULD mean we return original velocity for Fabric (no modification here)
|
||||
// But since we set it ourselves due to following bukkit's design patterns and doing victim
|
||||
// We return a pass so velocity isn't set twice
|
||||
return InteractionResult.PASS;
|
||||
return ActionResult.PASS;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.entity.EntityTickManager;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.server.world.ServerChunkLoadingManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
@Mixin(ServerChunkLoadingManager.class)
|
||||
public abstract class ChunkMapMixin {
|
||||
|
||||
@Redirect(method = "addEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;updateInterval()I"))
|
||||
@Redirect(method = "loadEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/EntityType;getTrackTickInterval()I"))
|
||||
private int getCustomUpdateInterval(EntityType<?> entityType) {
|
||||
return EntityTickManager.getCustomUpdateInterval(entityType);
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
//package me.caseload.knockbacksync.mixin;
|
||||
//
|
||||
//import me.caseload.knockbacksync.ConfigWrapper;
|
||||
//import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
//import me.caseload.knockbacksync.event.ConfigReloadEvent;
|
||||
//import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
//import me.caseload.knockbacksync.util.CustomUpdateIntervals;
|
||||
//import net.minecraft.Util;
|
||||
//import net.minecraft.server.level.ChunkMap;
|
||||
//import net.minecraft.server.level.ServerLevel;
|
||||
//import net.minecraft.server.level.ServerPlayer;
|
||||
//import net.minecraft.world.entity.Entity;
|
||||
//import net.minecraft.world.entity.EntityType;
|
||||
//import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
//import org.spongepowered.asm.mixin.Final;
|
||||
//import org.spongepowered.asm.mixin.Mixin;
|
||||
//import org.spongepowered.asm.mixin.Shadow;
|
||||
//import org.spongepowered.asm.mixin.injection.At;
|
||||
//import org.spongepowered.asm.mixin.injection.Inject;
|
||||
//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
//
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//
|
||||
//@Mixin(ChunkMap.class)
|
||||
//public class ChunkMapMixin {
|
||||
//
|
||||
// @Shadow @Final
|
||||
// private Map<Integer, ChunkMap.TrackedEntity> entityMap;
|
||||
// @Shadow @Final private ServerLevel level;
|
||||
//
|
||||
// @Inject(method = "addEntity", at = @At("HEAD"), cancellable = true)
|
||||
// private void onAddEntity(Entity entity, CallbackInfo ci) {
|
||||
// if (!(entity instanceof EnderDragonPart)) {
|
||||
// EntityType<?> entityType = entity.getType();
|
||||
// int trackingRange = entityType.clientTrackingRange() * 16;
|
||||
//
|
||||
// if (trackingRange != 0) {
|
||||
// String entityTypeName = entityType.builtInRegistryHolder().getRegisteredName();
|
||||
// ConfigWrapper config = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
//
|
||||
// int defaultInterval = entityType.updateInterval();
|
||||
// int customInterval = config.getInt("entity_update_intervals." + entityTypeName, defaultInterval);
|
||||
//
|
||||
// if (this.entityMap.containsKey(entity.getId())) {
|
||||
// throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Entity is already tracked!"));
|
||||
// } else {
|
||||
// ChunkMap.TrackedEntity trackedEntity = new ChunkMap.TrackedEntity(entity, trackingRange, customInterval, entityType.trackDeltas());
|
||||
// this.entityMap.put(entity.getId(), trackedEntity);
|
||||
// trackedEntity.updatePlayers(this.level.players());
|
||||
//
|
||||
// if (entity instanceof ServerPlayer serverPlayer) {
|
||||
// this.updatePlayerStatus(serverPlayer, true);
|
||||
//
|
||||
// for (ChunkMap.TrackedEntity trackedEntity2 : this.entityMap.values()) {
|
||||
// if (trackedEntity2.entity != serverPlayer) {
|
||||
// trackedEntity2.updatePlayer(serverPlayer);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ci.cancel();
|
||||
// }
|
||||
//
|
||||
// @Shadow
|
||||
// private void updatePlayerStatus(ServerPlayer player, boolean track) {
|
||||
// // This is a shadow method, implementation is provided by Minecraft
|
||||
// }
|
||||
//
|
||||
// @KBSyncEventHandler
|
||||
// public void onConfigReload(ConfigReloadEvent event) {
|
||||
// Map<String, Integer> newIntervals = CustomUpdateIntervals.loadEntityIntervalsFromConfig();
|
||||
//
|
||||
// for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) {
|
||||
// Entity entity = trackedEntity.entity;
|
||||
// String entityTypeName = entity.getType().builtInRegistryHolder().getRegisteredName();
|
||||
// int newInterval = newIntervals.getOrDefault(entityTypeName, entity.getType().updateInterval());
|
||||
//
|
||||
// // Assuming TrackedEntity has a method to update its update interval
|
||||
// trackedEntity.setUpdateInterval(newInterval);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,14 +0,0 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import net.minecraft.commands.CommandSource;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(CommandSourceStack.class)
|
||||
public interface CommandStackSourceAccessor {
|
||||
|
||||
@Accessor("source")
|
||||
CommandSource getSource();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.callback.PlayerVelocityEvent;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.EntityTrackerEntry;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(EntityTrackerEntry.class)
|
||||
public class EntityTrackerEntryMixin {
|
||||
|
||||
@Shadow @Final private Entity entity;
|
||||
|
||||
// We inject exactly where 'this.entity.knockedBack = false' is about to happen.
|
||||
@Inject(method = "tick",
|
||||
at = @At(value = "FIELD",
|
||||
target = "Lnet/minecraft/entity/Entity;knockedBack:Z",
|
||||
opcode = Opcodes.PUTFIELD),
|
||||
cancellable = true)
|
||||
private void onKnockbackSync(CallbackInfo ci) {
|
||||
// This check ensures we are in the "if (entity.knockedBack)" block
|
||||
// and operating on a Player.
|
||||
if (this.entity instanceof ServerPlayerEntity serverPlayer) {
|
||||
|
||||
// 1. Capture the velocity CURRENTLY on the player
|
||||
Vec3d currentVelocity = serverPlayer.getVelocity();
|
||||
|
||||
// 2. Fire the event
|
||||
ActionResult result = PlayerVelocityEvent.EVENT.invoker().onVelocityChange(serverPlayer, currentVelocity);
|
||||
|
||||
if (result == ActionResult.FAIL) {
|
||||
// CASE: CANCELLED
|
||||
// We want to STOP the packet from sending.
|
||||
// We also want to keep 'knockedBack' as true (technically),
|
||||
// effectively acting like the block never happened.
|
||||
ci.cancel();
|
||||
}
|
||||
else if (result == ActionResult.SUCCESS) {
|
||||
// CASE: MODIFIED
|
||||
// The event handler has already updated the player's velocity:
|
||||
// player.setVelocity(newVel);
|
||||
|
||||
// We do NOT cancel. We let Vanilla proceed.
|
||||
// Vanilla will:
|
||||
// 1. Set knockedBack = false;
|
||||
// 2. Create a NEW packet reading the current velocity (which is now your custom velocity).
|
||||
// 3. Send it.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,63 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.callback.PlayerVelocityEvent;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Player.class)
|
||||
@Mixin(PlayerEntity.class)
|
||||
public class PlayerMixin {
|
||||
|
||||
@Inject(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundSetEntityMotionPacket;<init>(Lnet/minecraft/world/entity/Entity;)V"), cancellable = true)
|
||||
private void onAttack(Entity target, CallbackInfo ci) {
|
||||
if (target instanceof ServerPlayer serverPlayer && target.hurtMarked) {
|
||||
Vec3 velocity = target.getDeltaMovement();
|
||||
@Inject(method = "knockbackTarget",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/network/ServerPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V"),
|
||||
cancellable = true)
|
||||
private void onKnockback(Entity target, float strength, Vec3d playerTargetVelocity, CallbackInfo ci) {
|
||||
if (target instanceof ServerPlayerEntity serverPlayer && target.knockedBack) {
|
||||
|
||||
InteractionResult result = PlayerVelocityEvent.EVENT.invoker().onVelocityChange(serverPlayer, velocity);
|
||||
// 1. Get the "Proposed" Velocity (The knockback calculated lines above)
|
||||
Vec3d proposedVelocity = target.getVelocity();
|
||||
|
||||
if (result == InteractionResult.FAIL) {
|
||||
target.hurtMarked = false;
|
||||
ci.cancel(); // Prevent sending the velocity packet
|
||||
} else if (result == InteractionResult.SUCCESS) {
|
||||
// Currently unnecessary since we do this in the handler, will move later
|
||||
target.setDeltaMovement(velocity);
|
||||
serverPlayer.connection.send(new ClientboundSetEntityMotionPacket(target));
|
||||
target.hurtMarked = false;
|
||||
ci.cancel(); // Prevent sending the original velocity packet
|
||||
// 2. Fire the Event
|
||||
ActionResult result = PlayerVelocityEvent.EVENT.invoker().onVelocityChange(serverPlayer, proposedVelocity);
|
||||
|
||||
if (result == ActionResult.FAIL) {
|
||||
// --- REPLICATING PAPER: CANCELLED ---
|
||||
// In Paper: if (event.isCancelled()) { cancelled = true; }
|
||||
// In Paper: if (!cancelled) { sendPacket; cleanup; }
|
||||
|
||||
// So, if cancelled, we stop EVERYTHING. We do not send packet, we do not reset velocity.
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == ActionResult.SUCCESS) {
|
||||
// --- REPLICATING PAPER: MODIFIED ---
|
||||
// In Paper: if (!velocity.equals(event.getVelocity())) { player.setVelocity(...) }
|
||||
// In Paper: if (!cancelled) { sendPacket; setDeltaMovement(currentMovement); }
|
||||
|
||||
// 1. The event listener should have already set the new velocity on the entity.
|
||||
// 2. We must send a NEW packet (because the one on the stack is old).
|
||||
serverPlayer.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(target));
|
||||
|
||||
// 3. Replicate the Cleanup: Paper resets velocity to 'currentMovement' (playerTargetVelocity)
|
||||
// This prevents the server from thinking the player is flying, maintaining Client Authority.
|
||||
target.knockedBack = false;
|
||||
target.setVelocity(playerTargetVelocity);
|
||||
|
||||
// 4. Cancel the original logic so Vanilla doesn't send the old packet or do double cleanup.
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
// CASE: PASS
|
||||
// Vanilla behaves normally (Sends packet, does cleanup).
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.callback.PlayerVelocityEvent;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerEntity.class)
|
||||
public class ServerEntityMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Entity entity;
|
||||
|
||||
// Velocity event
|
||||
@Inject(method = "sendChanges", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;hurtMarked:Z", ordinal = 1), cancellable = true)
|
||||
private void onSendChanges(CallbackInfo ci) {
|
||||
if (this.entity.hurtMarked && this.entity instanceof ServerPlayer player) {
|
||||
Vec3 velocity = player.getDeltaMovement();
|
||||
|
||||
InteractionResult result = PlayerVelocityEvent.EVENT.invoker().onVelocityChange(player, velocity);
|
||||
|
||||
if (result == InteractionResult.FAIL) {
|
||||
this.entity.hurtMarked = false;
|
||||
ci.cancel();
|
||||
} else if (result == InteractionResult.SUCCESS) {
|
||||
// Currently unnecessary since we do this in the handler, will move later
|
||||
player.setDeltaMovement(velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom tick intervals
|
||||
// @Shadow
|
||||
// private int updateInterval;
|
||||
|
||||
// @Accessor("entity")
|
||||
// Entity getEntity() {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// @Invoker("sendChanges")
|
||||
// void invokeSendChanges() {
|
||||
//
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public void setUpdateInterval(int interval) {
|
||||
// this.updateInterval = interval;
|
||||
// }
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.callback.TickRateChangeEvent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.TickRateManager;
|
||||
import net.minecraft.world.tick.TickManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(TickRateManager.class)
|
||||
@Mixin(TickManager.class)
|
||||
public class TickRateManagerMixin {
|
||||
@Shadow
|
||||
protected float tickrate;
|
||||
protected float tickRate;
|
||||
|
||||
@Inject(method = "setTickRate", at = @At("HEAD"))
|
||||
private void onSetTickRate(float tickRate, CallbackInfo ci) {
|
||||
float oldTickRate = this.tickrate;
|
||||
float oldTickRate = this.tickRate;
|
||||
TickRateChangeEvent.EVENT.invoker().onTickRateChange(oldTickRate, tickRate);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
//package me.caseload.knockbacksync.mixin;
|
||||
//
|
||||
//import me.caseload.knockbacksync.ConfigWrapper;
|
||||
//import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
//import net.minecraft.core.SectionPos;
|
||||
//import net.minecraft.server.level.ChunkMap;
|
||||
//import net.minecraft.server.level.ServerEntity;
|
||||
//import net.minecraft.world.entity.Entity;
|
||||
//import org.spongepowered.asm.mixin.Mixin;
|
||||
//import org.spongepowered.asm.mixin.Shadow;
|
||||
//import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
//import org.spongepowered.asm.mixin.injection.At;
|
||||
//import org.spongepowered.asm.mixin.injection.Inject;
|
||||
//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
//
|
||||
//@Mixin(ChunkMap.TrackedEntity.class)
|
||||
//public class TrackedEntityMixin {
|
||||
// @Shadow
|
||||
// ServerEntity serverEntity;
|
||||
// Entity entity;
|
||||
// int range;
|
||||
// SectionPos lastSectionPos;
|
||||
//
|
||||
// @Inject(method = "<init>", at = @At("HEAD"))
|
||||
// private void onInit(ChunkMap outer, Entity entity, int range, int updateInterval, boolean trackDelta, CallbackInfo ci) {
|
||||
//
|
||||
// String entityTypeName = entity.getType().builtInRegistryHolder().getRegisteredName();
|
||||
// ConfigWrapper config = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
// int customInterval = config.getInt("entity_update_intervals." + entityTypeName, updateInterval);
|
||||
//
|
||||
// this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, customInterval, trackDelta, this::broadcast);
|
||||
// this.entity = entity;
|
||||
// this.range = range;
|
||||
// this.lastSectionPos = SectionPos.of(entity);
|
||||
// }
|
||||
//
|
||||
//// @Accessor("updateInterval")
|
||||
//// public void setUpdateInterval(int interval) {
|
||||
// // This method will be implemented by the mixin system
|
||||
//// }
|
||||
//}
|
||||
+4
-4
@@ -4,21 +4,21 @@ import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
public class FabricPermissionChecker implements PermissionChecker {
|
||||
|
||||
public boolean hasPermission(CommandSourceStack source, String permission) {
|
||||
public boolean hasPermission(ServerCommandSource source, String permission) {
|
||||
return Permissions.check(source, permission, false);
|
||||
}
|
||||
|
||||
public boolean hasPermission(CommandSourceStack source, String permission, boolean defaultIfUnset) {
|
||||
public boolean hasPermission(ServerCommandSource source, String permission, boolean defaultIfUnset) {
|
||||
return Permissions.check(source, permission, defaultIfUnset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Object nativeType, String s, boolean defaultIfUnset) {
|
||||
if (nativeType instanceof CommandSourceStack commandSourceStack) {
|
||||
if (nativeType instanceof ServerCommandSource commandSourceStack) {
|
||||
return Permissions.check(commandSourceStack, s, defaultIfUnset);
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
@@ -3,22 +3,24 @@ package me.caseload.knockbacksync.player;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.Location;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.caseload.knockbacksync.world.FabricWorld;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.registries.VanillaRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.item.enchantment.Enchantment;
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||
import net.minecraft.entity.effect.StatusEffects;
|
||||
import net.minecraft.registry.BuiltinRegistries;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -26,16 +28,20 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FabricPlayer implements PlatformPlayer {
|
||||
public final ServerPlayer fabricPlayer;
|
||||
private String clientBrand = "fabric";
|
||||
public final ServerPlayerEntity fabricPlayer;
|
||||
public final User user;
|
||||
|
||||
public FabricPlayer(ServerPlayer player) {
|
||||
private String clientBrand = "vanilla";
|
||||
|
||||
public FabricPlayer(ServerPlayerEntity player) {
|
||||
Preconditions.checkArgument(player != null);
|
||||
this.fabricPlayer = player;
|
||||
this.user = PacketEvents.getAPI().getPlayerManager().getUser(fabricPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return fabricPlayer.getUUID();
|
||||
return fabricPlayer.getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,32 +66,32 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public float getPitch() {
|
||||
return fabricPlayer.getXRot();
|
||||
return fabricPlayer.getPitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYaw() {
|
||||
return fabricPlayer.getYRot();
|
||||
return fabricPlayer.getYaw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnGround() {
|
||||
return fabricPlayer.onGround();
|
||||
return fabricPlayer.isOnGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
return fabricPlayer.connection.latency();
|
||||
return fabricPlayer.networkHandler.getLatency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding() {
|
||||
return fabricPlayer.isFallFlying();
|
||||
return fabricPlayer.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformWorld getWorld() {
|
||||
return new FabricWorld(fabricPlayer.level());
|
||||
return new FabricWorld(fabricPlayer.getEntityWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,13 +101,13 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull String s) {
|
||||
fabricPlayer.sendSystemMessage(Component.literal(s));
|
||||
fabricPlayer.sendMessage(Text.literal(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAttackCooldown() {
|
||||
// this is what paper does I have no idea how this works
|
||||
return fabricPlayer.getAttackStrengthScale(0.5f);
|
||||
return fabricPlayer.getAttackCooldownProgress(0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,37 +117,33 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public int getMainHandKnockbackLevel() {
|
||||
Optional<Holder.Reference<Enchantment>>
|
||||
entry = VanillaRegistries.createLookup().asGetterLookup().get(
|
||||
Registries.ENCHANTMENT, Enchantments.KNOCKBACK
|
||||
);
|
||||
Holder<Enchantment> registryEntry1 = entry.orElseThrow(); // Reference implements RegistryEntry, this is fine
|
||||
return EnchantmentHelper.getItemEnchantmentLevel(registryEntry1, fabricPlayer.getMainHandItem());
|
||||
RegistryEntry<Enchantment> knockbackEntry = fabricPlayer.getEntityWorld().getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT).getOrThrow(Enchantments.KNOCKBACK);
|
||||
return EnchantmentHelper.getLevel(knockbackEntry, fabricPlayer.getMainHandStack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer getNoDamageTicks() {
|
||||
return fabricPlayer.invulnerableTime;
|
||||
return fabricPlayer.timeUntilRegen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVelocity(Vector3d adjustedVelocity) {
|
||||
fabricPlayer.setDeltaMovement(adjustedVelocity.x, adjustedVelocity.y, adjustedVelocity.z);
|
||||
fabricPlayer.setVelocity(adjustedVelocity.x, adjustedVelocity.y, adjustedVelocity.z);
|
||||
// TODO
|
||||
// fix paper-ism? for some reason setVelocity() in paper marks the entity as hurt marked every time its called?
|
||||
fabricPlayer.hurtMarked = true;
|
||||
fabricPlayer.velocityDirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d getVelocity() {
|
||||
final Vec3 fabricVelocity = fabricPlayer.getDeltaMovement();
|
||||
final Vec3d fabricVelocity = fabricPlayer.getVelocity();
|
||||
return new Vector3d(fabricVelocity.x, fabricVelocity.y, fabricVelocity.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getJumpPower() {
|
||||
double jumpVelocity = 0.42;
|
||||
MobEffectInstance jumpEffect = fabricPlayer.getEffect(MobEffects.JUMP);
|
||||
StatusEffectInstance jumpEffect = fabricPlayer.getStatusEffect(StatusEffects.JUMP_BOOST);
|
||||
if (jumpEffect != null) {
|
||||
int amplifier = jumpEffect.getAmplifier();
|
||||
jumpVelocity += (amplifier + 1) * 0.1F;
|
||||
@@ -151,13 +153,13 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
}
|
||||
@Override
|
||||
public BoundingBox getBoundingBox() {
|
||||
AABB boundingBox = fabricPlayer.getBoundingBox();
|
||||
Box boundingBox = fabricPlayer.getBoundingBox();
|
||||
return new BoundingBox(boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.minY, boundingBox.maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return PacketEvents.getAPI().getPlayerManager().getUser(fabricPlayer);
|
||||
public @Nullable User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,4 +171,19 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
public String getClientBrand() {
|
||||
return this.clientBrand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlying() {
|
||||
return this.fabricPlayer.getAbilities().flying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
return this.fabricPlayer.getAbilities().getWalkSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFlyingSpeed() {
|
||||
return this.fabricPlayer.getAbilities().getFlySpeed();
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -19,10 +19,10 @@ public class FabricSchedulerAdapter implements SchedulerAdapter {
|
||||
Iterator<ScheduledTask> iterator = taskMap.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ScheduledTask task = iterator.next();
|
||||
if (server.getTickCount() >= task.nextRunTick) {
|
||||
if (server.getTicks() >= task.nextRunTick) {
|
||||
task.task.run();
|
||||
if (task.isPeriodic) {
|
||||
task.nextRunTick = server.getTickCount() + task.period;
|
||||
task.nextRunTick = server.getTicks() + task.period;
|
||||
} else {
|
||||
iterator.remove();
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class FabricSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTask(Runnable task) {
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount(), 0, false);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTicks(), 0, false);
|
||||
Runnable cancellationTask = () -> taskMap.remove(scheduledTask);
|
||||
taskMap.put(scheduledTask, cancellationTask);
|
||||
return new FabricTaskHandle(cancellationTask);
|
||||
@@ -52,7 +52,7 @@ public class FabricSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskLater(Runnable task, long delayTicks) {
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount() + delayTicks, 0, false);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTicks() + delayTicks, 0, false);
|
||||
Runnable cancellationTask = () -> taskMap.remove(scheduledTask);
|
||||
taskMap.put(scheduledTask, cancellationTask);
|
||||
return new FabricTaskHandle(cancellationTask);
|
||||
@@ -60,7 +60,7 @@ public class FabricSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTaskTimer(Runnable task, long delayTicks, long periodTicks) {
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount() + delayTicks, periodTicks, true);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTicks() + delayTicks, periodTicks, true);
|
||||
Runnable cancellationTask = () -> taskMap.remove(scheduledTask);
|
||||
taskMap.put(scheduledTask, cancellationTask);
|
||||
return new FabricTaskHandle(cancellationTask);
|
||||
|
||||
@@ -2,18 +2,17 @@ package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.FabricBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.mixin.CommandStackSourceAccessor;
|
||||
import me.caseload.knockbacksync.permission.FabricPermissionChecker;
|
||||
import net.minecraft.commands.CommandSource;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.rcon.RconConsoleSource;
|
||||
import net.minecraft.server.command.CommandOutput;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.rcon.RconCommandOutput;
|
||||
import net.minecraft.text.Text;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FabricSenderFactory extends SenderFactory<FabricBase, CommandSourceStack> implements SenderMapper<CommandSourceStack, Sender> {
|
||||
public class FabricSenderFactory extends SenderFactory<FabricBase, ServerCommandSource> implements SenderMapper<ServerCommandSource, Sender> {
|
||||
private final FabricBase plugin;
|
||||
|
||||
public FabricSenderFactory(FabricBase kbSyncFabricBase) {
|
||||
@@ -27,16 +26,16 @@ public class FabricSenderFactory extends SenderFactory<FabricBase, CommandSource
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getUniqueId(CommandSourceStack commandSource) {
|
||||
protected UUID getUniqueId(ServerCommandSource commandSource) {
|
||||
if (commandSource.getEntity() != null) {
|
||||
return commandSource.getEntity().getUUID();
|
||||
return commandSource.getEntity().getUuid();
|
||||
}
|
||||
return Sender.CONSOLE_UUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(CommandSourceStack commandSource) {
|
||||
String name = commandSource.getTextName();
|
||||
protected String getName(ServerCommandSource commandSource) {
|
||||
String name = commandSource.getName();
|
||||
if (commandSource.getEntity() != null && name.equals("Server")) {
|
||||
return Sender.CONSOLE_NAME;
|
||||
}
|
||||
@@ -44,7 +43,7 @@ public class FabricSenderFactory extends SenderFactory<FabricBase, CommandSource
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(CommandSourceStack sender, String message) {
|
||||
protected void sendMessage(ServerCommandSource sender, String message) {
|
||||
// final Locale locale;
|
||||
// if (sender.getEntity() instanceof ServerPlayer) {
|
||||
// String language = ((ServerPlayer) sender.getEntity()).clientInformation().language();
|
||||
@@ -53,39 +52,39 @@ public class FabricSenderFactory extends SenderFactory<FabricBase, CommandSource
|
||||
// locale = null;
|
||||
// }
|
||||
// sender.sendFeedback(() -> toNativeText(TranslationManager.render(message, locale)), false);
|
||||
sender.sendSuccess(() -> Component.literal(message), false);
|
||||
sender.sendFeedback(() -> Text.literal(message), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSourceStack commandSource, String node) {
|
||||
protected boolean hasPermission(ServerCommandSource commandSource, String node) {
|
||||
return ((FabricPermissionChecker) Base.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSourceStack commandSource, String node, boolean defaultIfUnset) {
|
||||
protected boolean hasPermission(ServerCommandSource commandSource, String node, boolean defaultIfUnset) {
|
||||
return ((FabricPermissionChecker) Base.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node, defaultIfUnset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommand(CommandSourceStack sender, String command) {
|
||||
protected void performCommand(ServerCommandSource sender, String command) {
|
||||
// sender.getServer().getCommandManager().executeWithPrefix(sender, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConsole(CommandSourceStack sender) {
|
||||
CommandSource output = ((CommandStackSourceAccessor) sender).getSource();
|
||||
protected boolean isConsole(ServerCommandSource sender) {
|
||||
CommandOutput output = sender.output;
|
||||
return output == sender.getServer() || // Console
|
||||
output.getClass() == RconConsoleSource.class || // Rcon
|
||||
(output == CommandSource.NULL && sender.getTextName().equals("")); // Functions
|
||||
output.getClass() == RconCommandOutput.class || // Rcon
|
||||
(output == CommandOutput.DUMMY && sender.getName().equals("")); // Functions
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Sender map(@NonNull CommandSourceStack base) {
|
||||
public @NonNull Sender map(@NonNull ServerCommandSource base) {
|
||||
return this.wrap(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CommandSourceStack reverse(@NonNull Sender mapped) {
|
||||
public @NonNull ServerCommandSource reverse(@NonNull Sender mapped) {
|
||||
return this.unwrap(mapped);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BStatsConfig {
|
||||
private static final String HEADER = """
|
||||
# bStats (https://bStats.org) collects some basic information for plugin authors, like how
|
||||
# many people use their plugin and their total player count. It's recommended to keep bStats
|
||||
# enabled, but if you're not comfortable with this, you can turn this setting off. There is no
|
||||
# performance penalty associated with having metrics enabled, and data sent to bStats is fully
|
||||
# anonymous.
|
||||
""";
|
||||
|
||||
public static Config loadConfig() {
|
||||
File bStatsFolder = new File(FabricLoader.getInstance().getConfigDir().toString(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
options.setPrettyFlow(true);
|
||||
Yaml yaml = new Yaml(options);
|
||||
|
||||
Map<String, Object> data;
|
||||
Config config = new Config();
|
||||
|
||||
try {
|
||||
if (!configFile.exists()) {
|
||||
bStatsFolder.mkdirs();
|
||||
|
||||
// Create default config
|
||||
data = new LinkedHashMap<>();
|
||||
data.put("enabled", true);
|
||||
data.put("serverUuid", UUID.randomUUID().toString());
|
||||
data.put("logFailedRequests", false);
|
||||
data.put("logSentData", false);
|
||||
data.put("logResponseStatusText", false);
|
||||
|
||||
// Write config with header
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8)) {
|
||||
writer.write(HEADER);
|
||||
yaml.dump(data, writer);
|
||||
}
|
||||
} else {
|
||||
// Load existing config
|
||||
data = yaml.load(new FileInputStream(configFile));
|
||||
if (data == null) {
|
||||
data = new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
// Map the data to Config object
|
||||
config.enabled = getBoolean(data, "enabled", true);
|
||||
config.serverUuid = getString(data, "serverUuid", UUID.randomUUID().toString());
|
||||
config.logFailedRequests = getBoolean(data, "logFailedRequests", false);
|
||||
config.logSentData = getBoolean(data, "logSentData", false);
|
||||
config.logResponseStatusText = getBoolean(data, "logResponseStatusText", false);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// Fallback to default values
|
||||
config.enabled = true;
|
||||
config.serverUuid = UUID.randomUUID().toString();
|
||||
config.logFailedRequests = false;
|
||||
config.logSentData = false;
|
||||
config.logResponseStatusText = false;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static boolean getBoolean(Map<String, Object> map, String key, boolean defaultValue) {
|
||||
Object value = map.get(key);
|
||||
return value instanceof Boolean ? (Boolean) value : defaultValue;
|
||||
}
|
||||
|
||||
private static String getString(Map<String, Object> map, String key, String defaultValue) {
|
||||
Object value = map.get(key);
|
||||
return value instanceof String ? (String) value : defaultValue;
|
||||
}
|
||||
|
||||
// Your existing Config class
|
||||
public static class Config {
|
||||
public boolean enabled = true;
|
||||
public String serverUuid;
|
||||
public boolean logFailedRequests = false;
|
||||
public boolean logSentData = false;
|
||||
public boolean logResponseStatusText = false;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,11 @@ import me.caseload.knockbacksync.Base;
|
||||
|
||||
public class FabricStatsManager extends StatsManager {
|
||||
public FabricStatsManager() {
|
||||
super(new MetricsFabric(23568));
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// no op remove bstats
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
*
|
||||
* IMPORTANT: You are not allowed to modify this class, except changing the package.
|
||||
*
|
||||
* Disallowed modifications include but are not limited to:
|
||||
* - Remove the option for users to opt-out
|
||||
* - Change the frequency for data submission
|
||||
* - Obfuscate the code (every obfuscator should allow you to make an exception for specific files)
|
||||
* - Reformat the code (if you use a linter, add an exception)
|
||||
*
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
|
||||
import me.caseload.knockbacksync.FabricLoaderMod;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.stats.CustomChart;
|
||||
import me.caseload.knockbacksync.stats.JsonObjectBuilder;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MetricsFabric implements Metrics {
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public MetricsFabric(int serviceId) {
|
||||
// Get the config file
|
||||
BStatsConfig.Config config = BStatsConfig.loadConfig();
|
||||
|
||||
// Load the data
|
||||
boolean enabled = config.enabled;
|
||||
String serverUUID = config.serverUuid;
|
||||
boolean logErrors = config.logFailedRequests;
|
||||
boolean logSentData = config.logSentData;
|
||||
boolean logResponseStatusText = config.logResponseStatusText;
|
||||
|
||||
metricsBase =
|
||||
new // See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
// See https://github.com/Bastian/bstats-metrics/pull/126
|
||||
MetricsBase(
|
||||
"fabric",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> Base.INSTANCE.getScheduler().runTask(submitDataTask),
|
||||
() -> true,
|
||||
(message, error) -> Base.INSTANCE.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> Base.INSTANCE.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the underlying scheduler service.
|
||||
*/
|
||||
public void shutdown() {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", FabricLoaderMod.getServer().usesAuthentication() ? 0 : 1);
|
||||
builder.appendField("bukkitVersion", "Fabric " + FabricLoader.getInstance().getModContainer("fabricloader").get().getMetadata().getVersion().getFriendlyString() + " (MC: " + FabricLoaderMod.getServer().getServerVersion() + ")");
|
||||
builder.appendField("bukkitName", "Fabric");
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", FabricLoader.getInstance().getModContainer("knockbacksync").get().getMetadata().getVersion().getFriendlyString());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
if (FabricLoaderMod.getServer().isRunning()) {
|
||||
return FabricLoaderMod.getServer().getPlayerCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,45 @@
|
||||
package me.caseload.knockbacksync.util;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class CommandUtil {
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, String message) {
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, String message) {
|
||||
sendSuccessMessage(context, () -> getComponent(message), false);
|
||||
}
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, String message, boolean allowLogging) {
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, String message, boolean allowLogging) {
|
||||
sendSuccessMessage(context, () -> getComponent(message), allowLogging);
|
||||
}
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, Component message) {
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, Text message) {
|
||||
sendSuccessMessage(context, () -> message, false);
|
||||
}
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, Component message, boolean allowLogging) {
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, Text message, boolean allowLogging) {
|
||||
sendSuccessMessage(context, () -> message, allowLogging);
|
||||
}
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, Supplier<Component> messageSupplier) {
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, Supplier<Text> messageSupplier) {
|
||||
sendSuccessMessage(context, messageSupplier, false);
|
||||
}
|
||||
|
||||
public static void sendSuccessMessage(CommandContext<CommandSourceStack> context, Supplier<Component> messageSupplier, boolean allowLogging) {
|
||||
context.getSource().sendSuccess(messageSupplier, allowLogging);
|
||||
public static void sendSuccessMessage(CommandContext<ServerCommandSource> context, Supplier<Text> messageSupplier, boolean allowLogging) {
|
||||
context.getSource().sendFeedback(messageSupplier, allowLogging);
|
||||
}
|
||||
|
||||
public static void sendFailureMessage(CommandContext<CommandSourceStack> context, String message) {
|
||||
public static void sendFailureMessage(CommandContext<ServerCommandSource> context, String message) {
|
||||
sendFailureMessage(context, getComponent(message));
|
||||
}
|
||||
|
||||
public static void sendFailureMessage(CommandContext<CommandSourceStack> context, Component message) {
|
||||
context.getSource().sendFailure(message);
|
||||
public static void sendFailureMessage(CommandContext<ServerCommandSource> context, Text message) {
|
||||
context.getSource().sendError(message);
|
||||
}
|
||||
|
||||
public static Component getComponent(String message) {
|
||||
return Component.literal(message);
|
||||
public static Text getComponent(String message) {
|
||||
return Text.literal(message);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,10 @@ package me.caseload.knockbacksync.util;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.mixin.UpdateIntervalAccessor;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.world.ServerChunkLoadingManager;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -27,11 +25,11 @@ public class CustomUpdateIntervals {
|
||||
entityIntervals.clear();
|
||||
}
|
||||
|
||||
public static void updateIntervals(ServerLevel level, Map<String, Integer> entityIntervals) {
|
||||
for (Entity entity : level.getAllEntities()) {
|
||||
String entityType = entity.getType().builtInRegistryHolder().getRegisteredName();
|
||||
public static void updateIntervals(ServerWorld level, Map<String, Integer> entityIntervals) {
|
||||
for (Entity entity : level.iterateEntities()) {
|
||||
String entityType = entity.getType().getRegistryEntry().getIdAsString();
|
||||
if (entityIntervals.containsKey(entityType)) {
|
||||
ChunkMap.TrackedEntity serverEntity = ((ServerChunkCache) entity.level().getChunkSource()).chunkMap.entityMap.get(entity.getId());
|
||||
ServerChunkLoadingManager.EntityTracker serverEntity = ((ServerChunkManager) entity.getEntityWorld().getChunkManager()).chunkLoadingManager.entityTrackers.get(entity.getId());
|
||||
if (serverEntity != null) {
|
||||
((UpdateIntervalAccessor) serverEntity).setUpdateInterval(entityIntervals.get(entityType));
|
||||
}
|
||||
|
||||
@@ -3,21 +3,25 @@ package me.caseload.knockbacksync.world;
|
||||
import me.caseload.knockbacksync.FabricLoaderMod;
|
||||
import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FabricServer implements PlatformServer {
|
||||
public Collection<PlatformPlayer> getOnlinePlayers() {
|
||||
return FabricLoaderMod.getServer().getPlayerList().getPlayers().stream()
|
||||
return FabricLoaderMod.getServer().getPlayerManager().getPlayerList().stream()
|
||||
.map(FabricPlayer::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformPlayer getPlayer(UUID uuid) {
|
||||
return new FabricPlayer(FabricLoaderMod.getServer().getPlayerList().getPlayer(uuid));
|
||||
return new FabricPlayer(FabricLoaderMod.getServer().getPlayerManager().getPlayer(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformPlayer getPlayer(Object nativePlatformPlayer) {
|
||||
return new FabricPlayer((ServerPlayerEntity) nativePlatformPlayer);
|
||||
}
|
||||
}
|
||||
@@ -6,28 +6,27 @@ import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import me.caseload.knockbacksync.world.raytrace.FluidHandling;
|
||||
import me.caseload.knockbacksync.world.raytrace.RayTraceResult;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.RaycastContext;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FabricWorld implements PlatformWorld {
|
||||
private final Level world;
|
||||
private final World world;
|
||||
|
||||
public FabricWorld(Level world) {
|
||||
public FabricWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedBlockState getBlockStateAt(int x, int y, int z) {
|
||||
BlockState blockState = this.world.getBlockState(new BlockPos(x, y, z));
|
||||
return WrappedBlockState.getByGlobalId(Block.BLOCK_STATE_REGISTRY.getId(blockState));
|
||||
return WrappedBlockState.getByGlobalId(Block.STATE_IDS.getRawId(blockState));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,26 +36,26 @@ public class FabricWorld implements PlatformWorld {
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(Vector3d start, Vector3d direction, double maxDistance, FluidHandling fluidHandling, boolean ignorePassableBlocks) {
|
||||
Vec3 startVec = new Vec3(start.getX(), start.getY(), start.getZ());
|
||||
Vec3 endVec = startVec.add(direction.getX() * maxDistance, direction.getY() * maxDistance, direction.getZ() * maxDistance);
|
||||
Vec3d startVec = new Vec3d(start.getX(), start.getY(), start.getZ());
|
||||
Vec3d endVec = startVec.add(direction.getX() * maxDistance, direction.getY() * maxDistance, direction.getZ() * maxDistance);
|
||||
|
||||
BlockHitResult result = world.clip(new ClipContext(
|
||||
BlockHitResult result = world.raycast(new RaycastContext(
|
||||
startVec,
|
||||
endVec,
|
||||
ClipContext.Block.OUTLINE,
|
||||
fluidHandling == FluidHandling.NONE ? ClipContext.Fluid.NONE :
|
||||
fluidHandling == FluidHandling.SOURCE_ONLY ? ClipContext.Fluid.SOURCE_ONLY :
|
||||
ClipContext.Fluid.ANY,
|
||||
CollisionContext.empty()
|
||||
RaycastContext.ShapeType.OUTLINE,
|
||||
fluidHandling == FluidHandling.NONE ? RaycastContext.FluidHandling.NONE :
|
||||
fluidHandling == FluidHandling.SOURCE_ONLY ? RaycastContext.FluidHandling.SOURCE_ONLY :
|
||||
RaycastContext.FluidHandling.ANY,
|
||||
ShapeContext.absent()
|
||||
));
|
||||
|
||||
if (result.getType() == HitResult.Type.MISS) return null;
|
||||
|
||||
Vec3 hitLocation = result.getLocation();
|
||||
Vec3d hitLocation = result.getPos();
|
||||
BlockPos blockPos = result.getBlockPos();
|
||||
return new RayTraceResult(
|
||||
new Vector3d(hitLocation.x, hitLocation.y, hitLocation.z),
|
||||
BlockFace.getBlockFaceByValue(result.getDirection().ordinal()),
|
||||
BlockFace.getBlockFaceByValue(result.getSide().ordinal()),
|
||||
new Vector3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
||||
getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ())
|
||||
);
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"icon": "assets/knockbacksync/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"preLaunch": [
|
||||
"me.caseload.knockbacksync.FabricLoaderMod"
|
||||
],
|
||||
"main": [
|
||||
"me.caseload.knockbacksync.FabricLoaderMod"
|
||||
]
|
||||
@@ -18,8 +21,9 @@
|
||||
"knockbacksync.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"fabric": "*",
|
||||
"fabricloader": ">=0.15.11",
|
||||
"fabric-lifecycle-events-v1": "*",
|
||||
"fabric-events-interaction-v0": "*",
|
||||
"minecraft": "~${minecraft_version}",
|
||||
"packetevents": "*"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
accessWidener v2 named
|
||||
|
||||
# We don't actually need this right now, but it's useful to keep an access widener around for when we do need one
|
||||
accessible field net/minecraft/server/level/ChunkMap entityMap Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
|
||||
accessible class net/minecraft/server/level/ChunkMap$TrackedEntity
|
||||
accessible method net/minecraft/server/level/ChunkMap$TrackedEntity updatePlayers (Ljava/util/List;)V
|
||||
accessible field net/minecraft/server/command/ServerCommandSource output Lnet/minecraft/server/command/CommandOutput;
|
||||
accessible field net/minecraft/server/world/ServerChunkLoadingManager entityTrackers Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
|
||||
accessible class net/minecraft/server/world/ServerChunkLoadingManager$EntityTracker
|
||||
#accessible method net/minecraft/server/level/ChunkMap$TrackedEntity updatePlayers (Ljava/util/List;)V
|
||||
@@ -5,9 +5,8 @@
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"ChunkMapMixin",
|
||||
"CommandStackSourceAccessor",
|
||||
"EntityTrackerEntryMixin",
|
||||
"PlayerMixin",
|
||||
"ServerEntityMixin",
|
||||
"TickRateManagerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
|
||||
+4
-6
@@ -4,11 +4,9 @@ org.gradle.parallel=true
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.21
|
||||
parchment_mappings=2024.07.28
|
||||
parchment_minecraft_version=1.21
|
||||
yarn_mappings=1.21+build.9
|
||||
loader_version=0.15.11
|
||||
minecraft_version=1.21.11
|
||||
yarn_mappings=1.21.11+build.3:v2
|
||||
loader_version=0.16.13
|
||||
|
||||
# Mod Properties
|
||||
maven_group=me.caseload
|
||||
@@ -16,6 +14,6 @@ archives_base_name=knockbacksync
|
||||
|
||||
# Dependencies
|
||||
# check this on https://modmuss50.me/fabric.html
|
||||
fabric_version=0.102.0+1.21
|
||||
fabric_version=0.140.0+1.21.11
|
||||
|
||||
githubRepo=CASELOAD7000/knockback-sync
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in new issue
Block a user