Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e94b5ab84 | ||
|
|
2f34adeeb2 | ||
|
|
73ab2388dc | ||
|
|
7c129185c5 | ||
|
|
ac5bd598f0 | ||
|
|
98f23966db | ||
|
|
0803d2800f | ||
|
|
83dab3cb8c | ||
|
|
768bc20e14 | ||
|
|
f3bd42ff2f | ||
|
|
a56fd79d29 | ||
|
|
e1b2487396 | ||
|
|
f19499205f | ||
|
|
39e8cbddcb | ||
|
|
42c3129060 | ||
|
|
a6a2577e07 | ||
|
|
47ff08d62a | ||
|
|
619e50dd6a | ||
|
|
98cbf4adfc | ||
|
|
185bb72f10 | ||
|
|
7b5949dba6 | ||
|
|
0be787c1e2 | ||
|
|
74ee5d8c7e | ||
|
|
2d98d4cbff | ||
|
|
c2bacd2f5c | ||
|
|
164d073861 | ||
|
|
77c4dde70f | ||
|
|
3745648821 | ||
|
|
b339a53dc8 | ||
|
|
3895b35941 | ||
|
|
ac5175b63c | ||
|
|
8e61e44012 | ||
|
|
233a1e04d3 | ||
|
|
170caff375 | ||
|
|
a2cc42a0a7 | ||
|
|
ae76933a5d | ||
|
|
992ab4002a | ||
|
|
bb5b39810c | ||
|
|
dc7edf4361 | ||
|
|
702618482d | ||
|
|
a35b208df2 | ||
|
|
a51e9ac32e | ||
|
|
f4df09802b | ||
|
|
0a08b76d35 | ||
|
|
f7e572ef02 | ||
|
|
40d60b62a2 | ||
|
|
92f0cf7729 | ||
|
|
b34417d1c8 | ||
|
|
b9cc9f1f7f | ||
|
|
0c7b6effde | ||
|
|
f2807153b5 | ||
|
|
4a460f28cd | ||
|
|
fb62b8d99d | ||
|
|
e056cec07c | ||
|
|
e18a58e4f2 | ||
|
|
36a2bb5165 | ||
|
|
7a898acfd9 | ||
|
|
ac5d3307ed | ||
|
|
a652147e2c | ||
|
|
f7c4e179b7 | ||
|
|
3991e8f09a | ||
|
|
44239355b9 | ||
|
|
0b6af2aacd | ||
|
|
85c6a2cf14 | ||
|
|
ca937a86d0 | ||
|
|
8a2f85fadc | ||
|
|
24b32f2eca | ||
|
|
7bfe5c584f | ||
|
|
e904f44a5f | ||
|
|
9797780d81 | ||
|
|
9da919e910 | ||
|
|
fe395f2cbd | ||
|
|
581bef78fe | ||
|
|
35e19c56b1 | ||
|
|
e2514fd229 | ||
|
|
1fceee5319 | ||
|
|
07b34d4744 | ||
|
|
b8ac667e5f | ||
|
|
a467a06df4 | ||
|
|
50bde1375a | ||
|
|
6ce4469db9 | ||
|
|
d63e4a0c29 | ||
|
|
e5b70523bc | ||
|
|
cf0e5decd7 | ||
|
|
cbe1c3c46e | ||
|
|
1db1e02c8b | ||
|
|
7a9fee6e2d |
No files matched your search
@@ -24,34 +24,12 @@ jobs:
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Setup Gradle 8.10
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: 8.8
|
||||
|
||||
# Temporary workaround to packetevents bugs, use custom fork build
|
||||
- name: Checkout packetevents repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Axionize/packetevents
|
||||
ref: 2.0-fabric-serverside-fix
|
||||
path: packetevents
|
||||
|
||||
- name: Update version in packetevents
|
||||
run: |
|
||||
cd packetevents
|
||||
sed -i 's/val fullVersion = ".*"/val fullVersion = "2.5.8"/' build.gradle.kts
|
||||
|
||||
- name: Build packetevents with Gradle
|
||||
run: |
|
||||
cd packetevents
|
||||
gradle build -x test
|
||||
|
||||
- name: Publish packetevents to Maven Local
|
||||
run: |
|
||||
cd packetevents
|
||||
gradle publishToMavenLocal
|
||||
gradle-version: "8.10" # Quotes required to prevent YAML converting to number
|
||||
|
||||
- name: Generate build number
|
||||
id: buildnumber
|
||||
@@ -77,6 +55,8 @@ jobs:
|
||||
|
||||
- name: Build with Gradle
|
||||
run: gradle clean build -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
|
||||
@@ -115,7 +95,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Download the current dev-builds.txt
|
||||
curl -L -o dev-builds.txt https://github.com/CASELOAD7000/knockback-sync/releases/latest/download/dev-builds.txt
|
||||
curl -L -o dev-builds.txt https://github.com/${{ github.repository }}/releases/latest/download/dev-builds.txt
|
||||
|
||||
# Create a copy of the original file
|
||||
cp dev-builds.txt dev-builds.txt.original
|
||||
@@ -142,22 +122,22 @@ jobs:
|
||||
echo "Changes detected in dev-builds.txt, uploading new version."
|
||||
|
||||
# Get the latest release ID
|
||||
RELEASE_ID=$(curl -s "https://api.github.com/repos/CASELOAD7000/knockback-sync/releases/latest" | jq -r .id)
|
||||
RELEASE_ID=$(curl -s "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r .id)
|
||||
|
||||
# Delete the old dev-builds.txt asset
|
||||
ASSET_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/CASELOAD7000/knockback-sync/releases/$RELEASE_ID/assets" | \
|
||||
"https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets" | \
|
||||
jq -r '.[] | select(.name == "dev-builds.txt") | .id')
|
||||
if [ ! -z "$ASSET_ID" ]; then
|
||||
curl -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/CASELOAD7000/knockback-sync/releases/assets/$ASSET_ID"
|
||||
"https://api.github.com/repos/${{ github.repository }}/releases/assets/$ASSET_ID"
|
||||
fi
|
||||
|
||||
# Upload the new dev-builds.txt
|
||||
curl -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @dev-builds.txt \
|
||||
"https://uploads.github.com/repos/CASELOAD7000/knockback-sync/releases/$RELEASE_ID/assets?name=dev-builds.txt"
|
||||
"https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=dev-builds.txt"
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
@@ -184,7 +164,7 @@ jobs:
|
||||
|
||||
- uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: wGTbjSTq
|
||||
modrinth-id: ${{ vars.MODRINTH_ID }}
|
||||
modrinth-featured: true
|
||||
modrinth-unfeature-mode: subset
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
@@ -204,7 +184,7 @@ jobs:
|
||||
purpur
|
||||
|
||||
game-versions: |
|
||||
>=1.14.4
|
||||
>=1.12.2
|
||||
|
||||
retry-attempts: 2
|
||||
retry-delay: 10000
|
||||
@@ -212,7 +192,7 @@ jobs:
|
||||
|
||||
- uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
modrinth-id: wGTbjSTq
|
||||
modrinth-id: ${{ vars.MODRINTH_ID }}
|
||||
modrinth-featured: true
|
||||
modrinth-unfeature-mode: subset
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
@@ -222,7 +202,7 @@ jobs:
|
||||
|
||||
name: KnockbackSync-${{ env.NEW_VERSION }}
|
||||
version: ${{ env.NEW_VERSION }}
|
||||
version-type: beta
|
||||
version-type: alpha
|
||||
|
||||
loaders: |
|
||||
fabric
|
||||
|
||||
@@ -9,7 +9,6 @@ target/
|
||||
|
||||
|
||||
.gradle/
|
||||
gradle/
|
||||
build/
|
||||
|
||||
run/
|
||||
+5
-9
@@ -1,4 +1,4 @@
|
||||
Project Structure
|
||||
## Project Structure
|
||||
|
||||
- `common` submodule contains shared code for all platforms.
|
||||
- It contains the interfaces and abstractions for
|
||||
@@ -6,11 +6,11 @@ Project Structure
|
||||
- Scheduling
|
||||
- Players
|
||||
- Blocks
|
||||
- World
|
||||
- Worlds/Levels
|
||||
- The Server
|
||||
- In addition to the interfaces, the code also contains the implementations for
|
||||
- Commands
|
||||
- Commands use Brigadier with Reflection to handle version differences. Simply call our command code in common by registering KnockBackSyncCommand.build() and implementing CommandOperations
|
||||
- Commands use incendio's `cloud` command system. All you have to do to add platform support is to create a CommandManager and call register().
|
||||
- Handling Events
|
||||
- Gathering Statistics
|
||||
- `fabric` contains code for latest fabric version (currently 1.21)
|
||||
@@ -21,6 +21,7 @@ If you want to add support for a new platform, simply make a new submodule and i
|
||||
- Checking for Permissions
|
||||
- Listening for events and calling the right handler
|
||||
- Players + Blocks + World + Server
|
||||
- Metrics
|
||||
|
||||
You will need to handle events when Players:
|
||||
- join/leave
|
||||
@@ -31,9 +32,4 @@ If you find your platform is lacking events, take a look at how the PlayerVeloci
|
||||
|
||||
|
||||
## List of version and platform-specific caveats
|
||||
- Runnable and manual ping does not work on fabric right now due b/c packetevents. Ping measurement will be less accurate
|
||||
|
||||
- Command System
|
||||
- context.getSource().sendSuccess() takes a Supplier<Component> in > 1.19.4
|
||||
- context.getSource().sendSuccess() takes a Component in <= 1.19.4
|
||||
- Component.literal() does not exist in <= 1.18.2
|
||||
- Runnable and manual ping does not work on fabric right now due b/c packetevents. Ping measurement will be less accurate
|
||||
@@ -18,24 +18,12 @@ Official Discord: https://discord.gg/nnpqpAtyVW
|
||||
### How does this change benefit high ping players?
|
||||
**Knockback control.** For example, it will be easier to escape crit chains and punish crit.
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
### How do I change the ping offset?
|
||||
Open up `config.yml` and edit the line that says `ping_offset: 25`. Replace 25 with your desired ping offset.
|
||||
;
|
||||
Then just type
|
||||
```
|
||||
/knockbacksync reload
|
||||
```
|
||||
or restart your server.
|
||||
=======
|
||||
### Why was the configurability of ping offset removed?
|
||||
**It promotes consistency across all servers.** Extensive testing with top players has shown that an offset of 25 provides a balanced experience for everyone.
|
||||
|
||||
### How do I change the ping offset?
|
||||
**You must run a modified build of KnockbackSync.** The variable can be changed inside of the [PlayerData](common/src/main/java/me/caseload/knockbacksync/player/PlayerData.java) class.
|
||||
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
## What servers are using this plugin?
|
||||
| IP | Location | Region | Ping Offset | spike_threshold |
|
||||
|------------------|-----------------------------------|--------|-------------|-----------------|
|
||||
|
||||
@@ -8,6 +8,7 @@ plugins {
|
||||
|
||||
val fullVersion = "1.3.5"
|
||||
val snapshot = true
|
||||
val githubRepo = System.getenv("GITHUB_REPOSITORY") ?: project.findProperty("githubRepo").toString()
|
||||
|
||||
allprojects {
|
||||
fun getVersionMeta(includeHash: Boolean): String {
|
||||
@@ -29,6 +30,7 @@ allprojects {
|
||||
group = "me.caseload.knockbacksync"
|
||||
version = "$fullVersion${getVersionMeta(true)}"
|
||||
ext["versionNoHash"] = "$fullVersion${getVersionMeta(false)}"
|
||||
ext["githubRepo"] = githubRepo
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -42,6 +44,7 @@ allprojects {
|
||||
}
|
||||
maven("https://libraries.minecraft.net/")
|
||||
maven("https://maven.neoforged.net/releases")
|
||||
maven("https://repo.codemc.io/repository/maven-snapshots/")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,15 +29,13 @@ tasks.named<JavaCompile>("compileTestJava") {
|
||||
dependencies {
|
||||
implementation(project(":common"))
|
||||
|
||||
compileOnly("org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT")
|
||||
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
|
||||
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.fasterxml.jackson.core:jackson-databind:2.17.2")!!)
|
||||
shadeThisThing(implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2")!!)
|
||||
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")!!)
|
||||
@@ -49,7 +47,7 @@ dependencies {
|
||||
|
||||
tasks.withType<ShadowJar> {
|
||||
configurations = listOf(shadeThisThing)
|
||||
isEnableRelocation = true
|
||||
isEnableRelocation = false
|
||||
relocationPrefix = "${project.property("maven_group")}.${project.property("archives_base_name")}.shaded"
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
package me.caseload.knockbacksync;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.listener.bukkit.*;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.permission.PluginPermissionChecker;
|
||||
import me.caseload.knockbacksync.scheduler.BukkitSchedulerAdapter;
|
||||
import me.caseload.knockbacksync.scheduler.FoliaSchedulerAdapter;
|
||||
import me.caseload.knockbacksync.sender.BukkitPlayerSelectorParser;
|
||||
import me.caseload.knockbacksync.sender.BukkitSenderFactory;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.stats.custom.BukkitStatsManager;
|
||||
import me.caseload.knockbacksync.stats.custom.PluginJarHashProvider;
|
||||
import me.caseload.knockbacksync.world.BukkitServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BukkitBase extends Base {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final BukkitSenderFactory bukkitSenderFactory = new BukkitSenderFactory(this);
|
||||
private final PluginPermissionChecker permissionChecker = new PluginPermissionChecker();
|
||||
|
||||
private int playerUpdateInterval;
|
||||
|
||||
private final MethodHandle tickRateMethodHandle;
|
||||
|
||||
public BukkitBase(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
super.configManager = new ConfigManager();
|
||||
super.playerSelectorParser = new BukkitPlayerSelectorParser<>();
|
||||
super.statsManager = new BukkitStatsManager(plugin);
|
||||
super.platformServer = new BukkitServer();
|
||||
super.pluginJarHashProvider = new PluginJarHashProvider(this.getClass().getProtectionDomain().getCodeSource().getLocation());
|
||||
this.playerUpdateInterval = this.getConfigManager().getConfigWrapper().getInt("entity_tick_intervals.player", 2);
|
||||
|
||||
MethodHandle handle = null;
|
||||
try {
|
||||
Server craftServer = Bukkit.getServer();
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
// Get handle for getServerTickManager
|
||||
MethodHandle getServerTickManager = lookup.findVirtual(craftServer.getClass(),
|
||||
"getServerTickManager", MethodType.methodType(Object.class));
|
||||
|
||||
Object serverTickManager = getServerTickManager.invoke(craftServer);
|
||||
|
||||
// Get handle for getTickRate
|
||||
handle = lookup.findVirtual(serverTickManager.getClass(),
|
||||
"getTickRate", MethodType.methodType(float.class));
|
||||
|
||||
} catch (Throwable t) {
|
||||
// If anything fails, handle will remain null
|
||||
}
|
||||
this.tickRateMethodHandle = handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return this.plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return this.plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResource(String filename) {
|
||||
return this.plugin.getResource(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this.plugin));
|
||||
PacketEvents.getAPI().load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
super.enable();
|
||||
super.eventBus.registerListeners(this);
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
|
||||
scheduler.runTaskTimerAsynchronously(this::setUpdateIntervals, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeScheduler() {
|
||||
switch (getPlatform()) {
|
||||
case BUKKIT:
|
||||
super.scheduler = new BukkitSchedulerAdapter(this.plugin);
|
||||
break;
|
||||
case FOLIA:
|
||||
super.scheduler = new FoliaSchedulerAdapter(this.plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerPlatformListeners() {
|
||||
registerPluginListeners(
|
||||
new BukkitPlayerDamageListener(),
|
||||
new BukkitPlayerKnockbackListener(),
|
||||
new BukkitPlayerJoinQuitListener()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerCommands() {
|
||||
super.commandManager = new LegacyPaperCommandManager<>(
|
||||
this.plugin,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
bukkitSenderFactory
|
||||
);
|
||||
if (commandManager instanceof LegacyPaperCommandManager) {
|
||||
LegacyPaperCommandManager<Sender> legacyPaperCommandManager = (LegacyPaperCommandManager<Sender>) commandManager;
|
||||
if (legacyPaperCommandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
legacyPaperCommandManager.registerBrigadier();
|
||||
} else if (commandManager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
|
||||
legacyPaperCommandManager.registerAsynchronousCompletions();
|
||||
}
|
||||
}
|
||||
super.registerCommands();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVersion() {
|
||||
return this.plugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDefaultConfig() {
|
||||
this.plugin.saveDefaultConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionChecker getPermissionChecker() {
|
||||
return permissionChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTickRate() {
|
||||
if (tickRateMethodHandle != null) {
|
||||
try {
|
||||
return (float) tickRateMethodHandle.invoke();
|
||||
} catch (Throwable t) {
|
||||
return 20.0f;
|
||||
}
|
||||
}
|
||||
return 20.0f;
|
||||
}
|
||||
|
||||
private void registerPluginListeners(Listener... listeners) {
|
||||
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
|
||||
for (Listener listener : listeners)
|
||||
pluginManager.registerEvents(listener, this.plugin);
|
||||
}
|
||||
|
||||
public BukkitSenderFactory getSenderFactory() {
|
||||
return this.bukkitSenderFactory;
|
||||
}
|
||||
|
||||
public void setUpdateIntervals() {
|
||||
try {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
Method getWorldHandleMethod = world.getClass().getMethod("getHandle");
|
||||
Object serverLevel = getWorldHandleMethod.invoke(world);
|
||||
|
||||
// Get ChunkMap
|
||||
Method getChunkSource = serverLevel.getClass().getMethod("getChunkSource");
|
||||
Object chunkSource = getChunkSource.invoke(serverLevel);
|
||||
Field chunkMapField = chunkSource.getClass().getDeclaredField("chunkMap");
|
||||
chunkMapField.setAccessible(true);
|
||||
Object chunkMap = chunkMapField.get(chunkSource);
|
||||
|
||||
// Get entityMap from ChunkMap
|
||||
Field entityMapField = chunkMap.getClass().getDeclaredField("entityMap");
|
||||
entityMapField.setAccessible(true);
|
||||
Map<Integer, ?> entityMap = (Map<Integer, ?>) entityMapField.get(chunkMap);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Object trackedEntity = entityMap.get(player.getEntityId());
|
||||
if (trackedEntity == null)
|
||||
continue;
|
||||
|
||||
Field serverEntityField = trackedEntity.getClass().getDeclaredField("serverEntity");
|
||||
serverEntityField.setAccessible(true);
|
||||
Object serverEntity = serverEntityField.get(trackedEntity);
|
||||
|
||||
Field updateIntervalField = serverEntity.getClass().getDeclaredField("updateInterval");
|
||||
updateIntervalField.setAccessible(true);
|
||||
updateIntervalField.set(serverEntity, playerUpdateInterval);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new IllegalStateException("Unable to use reflection to modify updateIntervals" + e);
|
||||
}
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReload(ConfigReloadEvent event) {
|
||||
playerUpdateInterval = event.getConfigManager().getConfigWrapper().getInt("entity_tick_intervals.player", 2);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,9 +3,9 @@ package me.caseload.knockbacksync;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class KBSyncBukkitLoaderPlugin extends JavaPlugin {
|
||||
public final class BukkitLoaderPlugin extends JavaPlugin {
|
||||
|
||||
private final KnockbackSyncBase core = new KBSyncBukkitBase(this);
|
||||
private final Base core = new BukkitBase(this);
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
@@ -1,138 +0,0 @@
|
||||
package me.caseload.knockbacksync;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import me.caseload.knockbacksync.listener.bukkit.BukkitPlayerDamageListener;
|
||||
import me.caseload.knockbacksync.listener.bukkit.BukkitPlayerJoinQuitListener;
|
||||
import me.caseload.knockbacksync.listener.bukkit.BukkitPlayerKnockbackListener;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.permission.PluginPermissionChecker;
|
||||
import me.caseload.knockbacksync.scheduler.BukkitSchedulerAdapter;
|
||||
import me.caseload.knockbacksync.scheduler.FoliaSchedulerAdapter;
|
||||
import me.caseload.knockbacksync.sender.BukkitPlayerSelectorParser;
|
||||
import me.caseload.knockbacksync.sender.BukkitSenderFactory;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.stats.custom.BukkitStatsManager;
|
||||
import me.caseload.knockbacksync.stats.custom.PluginJarHashProvider;
|
||||
import me.caseload.knockbacksync.world.BukkitServer;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class KBSyncBukkitBase extends KnockbackSyncBase {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final BukkitSenderFactory bukkitSenderFactory = new BukkitSenderFactory(this);
|
||||
private final PluginPermissionChecker permissionChecker = new PluginPermissionChecker();
|
||||
|
||||
public KBSyncBukkitBase(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
super.configManager = new ConfigManager();
|
||||
super.playerSelectorParser = new BukkitPlayerSelectorParser<>();
|
||||
super.statsManager = new BukkitStatsManager();
|
||||
super.platformServer = new BukkitServer();
|
||||
super.pluginJarHashProvider = new PluginJarHashProvider(this.getClass().getProtectionDomain().getCodeSource().getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return this.plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return this.plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResource(String filename) {
|
||||
return this.plugin.getResource(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this.plugin));
|
||||
PacketEvents.getAPI().load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
super.enable();
|
||||
initializeScheduler();
|
||||
configManager.loadConfig(false);
|
||||
statsManager.init();
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeScheduler() {
|
||||
switch (platform) {
|
||||
case BUKKIT:
|
||||
super.scheduler = new BukkitSchedulerAdapter(this.plugin);
|
||||
break;
|
||||
case FOLIA:
|
||||
super.scheduler = new FoliaSchedulerAdapter(this.plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerPlatformListeners() {
|
||||
registerPluginListeners(
|
||||
new BukkitPlayerDamageListener(),
|
||||
new BukkitPlayerKnockbackListener(),
|
||||
new BukkitPlayerJoinQuitListener()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerCommands() {
|
||||
super.commandManager = new LegacyPaperCommandManager<>(
|
||||
this.plugin,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
bukkitSenderFactory
|
||||
);
|
||||
if (commandManager instanceof LegacyPaperCommandManager) {
|
||||
LegacyPaperCommandManager<Sender> legacyPaperCommandManager = (LegacyPaperCommandManager<Sender>) commandManager;
|
||||
if (legacyPaperCommandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
|
||||
legacyPaperCommandManager.registerBrigadier();
|
||||
} else if (commandManager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
|
||||
legacyPaperCommandManager.registerAsynchronousCompletions();
|
||||
}
|
||||
}
|
||||
super.registerCommands();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVersion() {
|
||||
return this.plugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDefaultConfig() {
|
||||
this.plugin.saveDefaultConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionChecker getPermissionChecker() {
|
||||
return permissionChecker;
|
||||
}
|
||||
|
||||
private void registerPluginListeners(Listener... listeners) {
|
||||
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
|
||||
for (Listener listener : listeners)
|
||||
pluginManager.registerEvents(listener, this.plugin);
|
||||
}
|
||||
|
||||
public BukkitSenderFactory getSenderFactory() {
|
||||
return this.bukkitSenderFactory;
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -15,9 +15,12 @@ public class BukkitPlayerDamageListener extends PlayerDamageListener implements
|
||||
public void onPlayerDamage(EntityDamageByEntityEvent event) {
|
||||
Entity victim = event.getEntity();
|
||||
Entity attacker = event.getDamager();
|
||||
if ((victim instanceof Player) && (attacker instanceof Player))
|
||||
if (!MultiLibUtil.isExternalPlayer((Player) victim)) {
|
||||
onPlayerDamage(new BukkitPlayer((Player) victim), new BukkitPlayer((Player) attacker));
|
||||
}
|
||||
if (!(victim instanceof Player) || !(attacker instanceof Player))
|
||||
return;
|
||||
|
||||
if (MultiLibUtil.isExternalPlayer((Player) victim))
|
||||
return;
|
||||
|
||||
onPlayerDamage(new BukkitPlayer((Player) victim), new BukkitPlayer((Player) attacker));
|
||||
}
|
||||
}
|
||||
+14
@@ -1,15 +1,29 @@
|
||||
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())));
|
||||
|
||||
+1
-2
@@ -34,8 +34,7 @@ public class BukkitPlayerKnockbackListener extends PlayerKnockbackListener imple
|
||||
if (MultiLibUtil.isExternalPlayer(victim))
|
||||
return;
|
||||
|
||||
Vector vector = event.getVelocity();
|
||||
Vector vector = victim.getVelocity();
|
||||
onPlayerVelocity(new BukkitPlayer(victim), new Vector3d(vector.getX(), vector.getY(), vector.getZ()));
|
||||
// callback.setVelocity(victim.getVelocity()); // Update the callback's velocity in Spigot
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,71 @@
|
||||
package me.caseload.knockbacksync.player;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
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 me.caseload.knockbacksync.world.PlatformWorld;
|
||||
import me.caseload.knockbacksync.world.SpigotWorld;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitPlayer implements PlatformPlayer {
|
||||
public final Player bukkitPlayer;
|
||||
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();
|
||||
|
||||
// 1.12.2 support
|
||||
static {
|
||||
try {
|
||||
String bukkitversion = Bukkit.getServer().getClass().getPackage()
|
||||
.getName();
|
||||
// Step 1: Load the CraftPlayer class
|
||||
craftPlayerClass = Class.forName(bukkitversion + ".entity.CraftPlayer");
|
||||
// Step 2: Get the getHandle method
|
||||
getHandleMethod = craftPlayerClass.getMethod("getHandle");
|
||||
// Step 3: Get the getAttackStrengthScale method from the EntityPlayer class
|
||||
Class<?> entityPlayerClass = Class.forName("net.minecraft.server." + bukkitversion + ".EntityPlayer");
|
||||
getAttackStrengthScaleMethod = entityPlayerClass.getDeclaredMethod("getAttackStrengthScale", float.class);
|
||||
getAttackStrengthScaleMethod.setAccessible(true);
|
||||
// Check the current server versio
|
||||
|
||||
// If the version is greater than 1.14.4, use the Player method directly
|
||||
if (currentVersion.isOlderThan(ServerVersion.V_1_15)) {
|
||||
Object server = Bukkit.getServer().getClass().getDeclaredMethod("getServer").invoke(Bukkit.getServer());
|
||||
String nmsPackage = server.getClass().getPackage().getName();
|
||||
String bukkitPackage = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
// Step 1: Load the CraftPlayer class
|
||||
craftPlayerClass = Class.forName(bukkitPackage + ".entity.CraftPlayer");
|
||||
// Step 2: Get the getHandle method
|
||||
getHandleMethod = craftPlayerClass.getMethod("getHandle");
|
||||
// Step 3: Get the getAttackStrengthScale method from the EntityPlayer class
|
||||
Class<?> entityPlayerClass = Class.forName(nmsPackage + ".EntityPlayer");
|
||||
String getAttackStrengthScaleMethodName = "";
|
||||
|
||||
// Determine the method name based on the version
|
||||
if (currentVersion.isOlderThan(ServerVersion.V_1_13)) {
|
||||
getAttackStrengthScaleMethodName = "n";
|
||||
} else if (currentVersion.isOlderThan(ServerVersion.V_1_14)) {
|
||||
getAttackStrengthScaleMethodName = "r";
|
||||
} else if (currentVersion.isOlderThan(ServerVersion.V_1_15)) {
|
||||
getAttackStrengthScaleMethodName = "s";
|
||||
}
|
||||
|
||||
// Get the attack strength scale method for EntityPlayer
|
||||
getAttackStrengthScaleMethod = entityPlayerClass.getMethod(getAttackStrengthScaleMethodName, float.class);
|
||||
getAttackStrengthScaleMethod.setAccessible(true);
|
||||
}
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalStateException("Method of Class required to support this version not found via reflection" + e);
|
||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot access required methods via reflection to support this version" + e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +121,11 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
return PacketEvents.getAPI().getPlayerManager().getPing(bukkitPlayer);
|
||||
if (currentVersion.isNewerThanOrEquals(ServerVersion.V_1_16_5)) {
|
||||
return bukkitPlayer.getPing();
|
||||
} else {
|
||||
return PacketEvents.getAPI().getPlayerManager().getPing(bukkitPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +140,7 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public Vector3d getLocation() {
|
||||
Location location = bukkitPlayer.getLocation();
|
||||
org.bukkit.Location location = bukkitPlayer.getLocation();
|
||||
return new Vector3d(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
|
||||
@@ -119,16 +151,18 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public double getAttackCooldown() {
|
||||
try {
|
||||
// Step 1: Get the CraftPlayer instance
|
||||
Object craftPlayer = craftPlayerClass.cast(bukkitPlayer);
|
||||
// Step 2: Get the handle (NMS EntityPlayer)
|
||||
Object entityPlayer = getHandleMethod.invoke(craftPlayer);
|
||||
// Step 3: Invoke the getAttackStrengthScale method
|
||||
return (float) getAttackStrengthScaleMethod.invoke(entityPlayer, 0.5f);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return -1; // Return -1 if an error occurs
|
||||
if (currentVersion.isNewerThan(ServerVersion.V_1_14_4)) {
|
||||
return bukkitPlayer.getAttackCooldown();
|
||||
} else {
|
||||
try {
|
||||
// Step 1: Get the CraftPlayer instance
|
||||
// Step 2: Get the handle (NMS EntityPlayer)
|
||||
Object entityPlayer = getHandleMethod.invoke(bukkitPlayer);
|
||||
// Step 3: Invoke the getAttackStrengthScale method
|
||||
return (float) getAttackStrengthScaleMethod.invoke(entityPlayer, 0.5f);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("This plugin will not work. NMS mapping for getAttackCooldown() failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,4 +185,44 @@ public class BukkitPlayer implements PlatformPlayer {
|
||||
public void setVelocity(Vector3d adjustedVelocity) {
|
||||
bukkitPlayer.setVelocity(new Vector(adjustedVelocity.x, adjustedVelocity.y, adjustedVelocity.z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3d getVelocity() {
|
||||
final Vector bukkitVelocity = bukkitPlayer.getVelocity();
|
||||
return new Vector3d(bukkitVelocity.getX(), bukkitVelocity.getY(), bukkitVelocity.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getJumpPower() {
|
||||
double jumpVelocity = 0.42;
|
||||
|
||||
PotionEffect jumpEffect = bukkitPlayer.getPotionEffect(PotionEffectType.JUMP);
|
||||
if (jumpEffect != null) {
|
||||
int amplifier = jumpEffect.getAmplifier();
|
||||
jumpVelocity += (amplifier + 1) * 0.1F;
|
||||
}
|
||||
|
||||
return jumpVelocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox getBoundingBox() {
|
||||
org.bukkit.util.BoundingBox boundingBox = bukkitPlayer.getBoundingBox();
|
||||
return new BoundingBox(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientBrand(String brand) {
|
||||
this.clientBrand = brand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientBrand() {
|
||||
return this.clientBrand;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.KBSyncBukkitBase;
|
||||
import me.caseload.knockbacksync.BukkitBase;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.RemoteConsoleCommandSender;
|
||||
@@ -12,10 +12,10 @@ import org.incendo.cloud.SenderMapper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitSenderFactory extends SenderFactory<KBSyncBukkitBase, CommandSender> implements SenderMapper<CommandSender, Sender> {
|
||||
public class BukkitSenderFactory extends SenderFactory<BukkitBase, CommandSender> implements SenderMapper<CommandSender, Sender> {
|
||||
// private final BukkitAudiences audiences;
|
||||
|
||||
public BukkitSenderFactory(KBSyncBukkitBase kbSyncBukkitBase) {
|
||||
public BukkitSenderFactory(BukkitBase kbSyncBukkitBase) {
|
||||
super(kbSyncBukkitBase);
|
||||
// this.audiences = BukkitAudiences.create(kbSyncBukkitBase.getLoader());
|
||||
}
|
||||
|
||||
+4
-10
@@ -1,17 +1,11 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class BukkitStatsManager extends StatsManager {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
KnockbackSyncBase.INSTANCE.getScheduler().runTaskAsynchronously(() -> {
|
||||
BuildTypePie.determineBuildType(); // Function to calculate hash
|
||||
MetricsBukkit metrics = new MetricsBukkit(23568);
|
||||
metrics.addCustomChart(new PlayerVersionsPie());
|
||||
metrics.addCustomChart(new BuildTypePie());
|
||||
super.metrics = metrics;
|
||||
});
|
||||
public BukkitStatsManager(Plugin plugin) {
|
||||
super(new MetricsBukkit(plugin, 23568));
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ package me.caseload.knockbacksync.stats.custom;
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
|
||||
import me.caseload.knockbacksync.KBSyncBukkitLoaderPlugin;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.BukkitLoaderPlugin;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.stats.CustomChart;
|
||||
import me.caseload.knockbacksync.stats.JsonObjectBuilder;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -34,12 +34,11 @@ import java.util.logging.Level;
|
||||
public class MetricsBukkit implements Metrics {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
|
||||
public MetricsBukkit(int serviceId) {
|
||||
this.plugin = KBSyncBukkitLoaderPlugin.getPlugin(KBSyncBukkitLoaderPlugin.class);
|
||||
public MetricsBukkit(Plugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
@@ -91,7 +90,7 @@ public class MetricsBukkit implements Metrics {
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> KnockbackSyncBase.INSTANCE.getScheduler().runTask(submitDataTask),
|
||||
submitDataTask -> Base.INSTANCE.getScheduler().runTask(submitDataTask),
|
||||
// isFolia
|
||||
// ? null
|
||||
// : submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
|
||||
@@ -1,19 +1,59 @@
|
||||
package me.caseload.knockbacksync.world;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import me.caseload.knockbacksync.world.raytrace.FluidHandling;
|
||||
import me.caseload.knockbacksync.world.raytrace.RayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SpigotWorld implements PlatformWorld {
|
||||
private final World world;
|
||||
|
||||
// Reflection variables
|
||||
private static Class<?> craftWorldClass;
|
||||
private static Class<?> vec3DClass;
|
||||
private static Class<?> movingObjectPositionClass;
|
||||
private static Class<?> enumDirectionClass;
|
||||
private static Method craftWorldGetHandleMethod;
|
||||
private static Method rayTraceMethod;
|
||||
private static Method vec3DAddMethod;
|
||||
|
||||
static {
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13)) {
|
||||
try {
|
||||
Object server = Bukkit.getServer().getClass().getDeclaredMethod("getServer").invoke(Bukkit.getServer());
|
||||
String nmsPackage = server.getClass().getPackage().getName();
|
||||
String bukkitPackage = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
// Load necessary classes dynamically for Minecraft 1.12.2
|
||||
craftWorldClass = Class.forName(bukkitPackage + ".CraftWorld");
|
||||
vec3DClass = Class.forName(nmsPackage + ".Vec3D");
|
||||
movingObjectPositionClass = Class.forName(nmsPackage + ".MovingObjectPosition");
|
||||
enumDirectionClass = Class.forName(nmsPackage + ".EnumDirection");
|
||||
|
||||
// CraftWorld
|
||||
|
||||
// Get method
|
||||
craftWorldGetHandleMethod = craftWorldClass.getMethod("getHandle");
|
||||
rayTraceMethod = Class.forName(nmsPackage + ".World").getMethod("rayTrace", vec3DClass, vec3DClass, boolean.class, boolean.class, boolean.class);
|
||||
vec3DAddMethod = vec3DClass.getMethod("add", double.class, double.class, double.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SpigotWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
@@ -40,7 +80,7 @@ public class SpigotWorld implements PlatformWorld {
|
||||
@Override
|
||||
public WrappedBlockState getBlockStateAt(int x, int y, int z) {
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
return WrappedBlockState.getByString(block.getType().name());
|
||||
return SpigotConversionUtil.fromBukkitBlockData(block.getBlockData());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,22 +90,80 @@ public class SpigotWorld implements PlatformWorld {
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(Vector3d start, Vector3d direction, double maxDistance, FluidHandling fluidHandling, boolean ignorePassableBlocks) {
|
||||
Vector startVec = new Vector(start.getX(), start.getY(), start.getZ());
|
||||
Vector directionVec = new Vector(direction.getX(), direction.getY(), direction.getZ());
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_12_2)) {
|
||||
Vector startVec = new Vector(start.getX(), start.getY(), start.getZ());
|
||||
Vector directionVec = new Vector(direction.getX(), direction.getY(), direction.getZ());
|
||||
|
||||
FluidCollisionMode fluidMode = (fluidHandling == FluidHandling.NONE) ? FluidCollisionMode.NEVER :
|
||||
(fluidHandling == FluidHandling.SOURCE_ONLY) ? FluidCollisionMode.SOURCE_ONLY :
|
||||
FluidCollisionMode.ALWAYS;
|
||||
FluidCollisionMode fluidMode = (fluidHandling == FluidHandling.NONE) ? FluidCollisionMode.NEVER :
|
||||
(fluidHandling == FluidHandling.SOURCE_ONLY) ? FluidCollisionMode.SOURCE_ONLY :
|
||||
FluidCollisionMode.ALWAYS;
|
||||
|
||||
org.bukkit.util.RayTraceResult result = world.rayTraceBlocks(startVec.toLocation(world), directionVec, maxDistance, fluidMode, ignorePassableBlocks);
|
||||
org.bukkit.util.RayTraceResult result = world.rayTraceBlocks(startVec.toLocation(world), directionVec, maxDistance, fluidMode, ignorePassableBlocks);
|
||||
|
||||
if (result == null) return null;
|
||||
if (result == null) return null;
|
||||
|
||||
return new RayTraceResult(
|
||||
new Vector3d(result.getHitPosition().getX(), result.getHitPosition().getY(), result.getHitPosition().getZ()),
|
||||
getBlockFaceFrom(result.getHitBlockFace()),
|
||||
new Vector3i(result.getHitBlock().getX(), result.getHitBlock().getY(), result.getHitBlock().getZ()),
|
||||
result.getHitBlock() != null ? WrappedBlockState.getByString(result.getHitBlock().getType().name()) : null
|
||||
);
|
||||
return new RayTraceResult(
|
||||
new Vector3d(result.getHitPosition().getX(), result.getHitPosition().getY(), result.getHitPosition().getZ()),
|
||||
getBlockFaceFrom(result.getHitBlockFace()),
|
||||
new Vector3i(result.getHitBlock().getX(), result.getHitBlock().getY(), result.getHitBlock().getZ()),
|
||||
result.getHitBlock() != null ? SpigotConversionUtil.fromBukkitBlockData(result.getHitBlock().getBlockData()) : null
|
||||
);
|
||||
// Only tested on 1.12.2
|
||||
} else {
|
||||
try {
|
||||
// Create start and end positions
|
||||
Object startPos = vec3DClass.getConstructor(double.class, double.class, double.class)
|
||||
.newInstance(start.getX(), start.getY(), start.getZ());
|
||||
Object endPos = vec3DAddMethod.invoke(startPos, direction.getX(), direction.getY(), direction.getZ());
|
||||
|
||||
// Call rayTrace method
|
||||
Object hitResult = rayTraceMethod.invoke(craftWorldGetHandleMethod.invoke(world), startPos, endPos, false, true, false);
|
||||
if (hitResult == null) return null;
|
||||
|
||||
// Extract hit position and direction
|
||||
Vector3d hitPosition = new Vector3d(
|
||||
(double) hitResult.getClass().getField("pos").get(hitResult),
|
||||
(double) hitResult.getClass().getField("pos").get(hitResult).getClass().getField("y").get(hitResult.getClass().getField("pos").get(hitResult)),
|
||||
(double) hitResult.getClass().getField("pos").get(hitResult).getClass().getField("z").get(hitResult.getClass().getField("pos").get(hitResult))
|
||||
);
|
||||
|
||||
Object hitDirection = hitResult.getClass().getField("direction").get(hitResult);
|
||||
Object hitBlock = hitResult.getClass().getField("e").get(hitResult); // e = BlockHitResult
|
||||
|
||||
return new RayTraceResult(
|
||||
hitPosition,
|
||||
getHitBlockFace(hitDirection),
|
||||
new Vector3i((int) hitBlock.getClass().getField("x").get(hitBlock),
|
||||
(int) hitBlock.getClass().getField("y").get(hitBlock),
|
||||
(int) hitBlock.getClass().getField("z").get(hitBlock)),
|
||||
hitBlock != null ? WrappedBlockState.getByString(hitBlock.getClass().getField("type").get(hitBlock).toString()) : null
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null; // Return null if there was an error during ray tracing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockFace getHitBlockFace(Object enumDirection) {
|
||||
try {
|
||||
// Match the EnumDirection to the corresponding BlockFace
|
||||
if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("NORTH").get(null))) {
|
||||
return BlockFace.NORTH;
|
||||
} else if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("SOUTH").get(null))) {
|
||||
return BlockFace.SOUTH;
|
||||
} else if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("EAST").get(null))) {
|
||||
return BlockFace.EAST;
|
||||
} else if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("WEST").get(null))) {
|
||||
return BlockFace.WEST;
|
||||
} else if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("UP").get(null))) {
|
||||
return BlockFace.UP;
|
||||
} else if (enumDirection.equals(SpigotWorld.enumDirectionClass.getField("DOWN").get(null))) {
|
||||
return BlockFace.DOWN;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new IllegalStateException("Unexpected value: " + enumDirection);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
name: KnockbackSync
|
||||
version: ${version}
|
||||
main: me.caseload.knockbacksync.KBSyncBukkitLoaderPlugin
|
||||
api-version: '1.14'
|
||||
authors: [ Caseload ]
|
||||
main: me.caseload.knockbacksync.BukkitLoaderPlugin
|
||||
api-version: '1.13' # We actually support 1.12.2, api-version was added later though
|
||||
authors: [ Caseload, Axionize ]
|
||||
description: Synchronizes player knockback for smoother gameplay.
|
||||
folia-supported: true
|
||||
permissions:
|
||||
@@ -15,4 +15,8 @@ permissions:
|
||||
knockbacksync.toggle.self:
|
||||
default: op
|
||||
knockbacksync.ping:
|
||||
default: op
|
||||
knockbacksync.reload:
|
||||
default: op
|
||||
knockbacksync.toggleoffground:
|
||||
default: op
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("fabric-loom")
|
||||
// id("net.neoforged.moddev") version "1.0.11"
|
||||
id("com.github.gmazzo.buildconfig") version "3.1.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -17,8 +17,9 @@ dependencies {
|
||||
|
||||
// Shaded in or bundled by platform-specific code
|
||||
implementation("com.github.retrooper:packetevents-api:2.5.0")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
|
||||
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2")
|
||||
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")
|
||||
@@ -34,4 +35,8 @@ repositories {
|
||||
|
||||
tasks.named("remapJar").configure {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
buildConfig {
|
||||
buildConfigField("String", "GITHUB_REPO", "\"${project.rootProject.ext["githubRepo"]}\"")
|
||||
}
|
||||
+42
-27
@@ -9,12 +9,18 @@ import me.caseload.knockbacksync.command.subcommand.PingCommand;
|
||||
import me.caseload.knockbacksync.command.subcommand.ReloadCommand;
|
||||
import me.caseload.knockbacksync.command.subcommand.StatusCommand;
|
||||
import me.caseload.knockbacksync.command.subcommand.ToggleCommand;
|
||||
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.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.scheduler.SchedulerAdapter;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.stats.custom.ClientBrandsPie;
|
||||
import me.caseload.knockbacksync.stats.custom.PluginJarHashProvider;
|
||||
import me.caseload.knockbacksync.stats.custom.StatsManager;
|
||||
import me.caseload.knockbacksync.world.PlatformServer;
|
||||
@@ -23,51 +29,52 @@ import org.kohsuke.github.GitHub;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
// Base class
|
||||
public abstract class KnockbackSyncBase {
|
||||
public abstract class Base {
|
||||
public static Logger LOGGER;
|
||||
public static KnockbackSyncBase INSTANCE;
|
||||
public static Base INSTANCE;
|
||||
|
||||
public final Platform platform;
|
||||
public StatsManager statsManager;
|
||||
public PlatformServer platformServer;
|
||||
@Getter private final Platform platform;
|
||||
@Getter protected StatsManager statsManager;
|
||||
@Getter protected PlatformServer platformServer;
|
||||
@Getter protected PluginJarHashProvider pluginJarHashProvider;
|
||||
@Getter protected SchedulerAdapter scheduler;
|
||||
@Getter protected ConfigManager configManager;
|
||||
protected CommandManager<Sender> commandManager;
|
||||
@Getter protected CommandManager<Sender> commandManager;
|
||||
@Getter protected final EventBus eventBus = new OptimizedEventBus();
|
||||
|
||||
@Getter
|
||||
protected AbstractPlayerSelectorParser<Sender> playerSelectorParser;
|
||||
|
||||
protected KnockbackSyncBase() {
|
||||
this.platform = getPlatform();
|
||||
protected Base() {
|
||||
this.platform = detectPlatform();
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
private Platform getPlatform() {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
return Platform.FOLIA; // Paper (Folia) detected
|
||||
} catch (ClassNotFoundException ignored1) {
|
||||
}
|
||||
private Platform detectPlatform() {
|
||||
final Map<String, Platform> platforms = Collections.unmodifiableMap(new HashMap<String, Platform>() {{
|
||||
put("io.papermc.paper.threadedregions.RegionizedServer", Platform.FOLIA);
|
||||
put("org.bukkit.Bukkit", Platform.BUKKIT);
|
||||
put("net.fabricmc.loader.api.FabricLoader", Platform.FABRIC);
|
||||
}});
|
||||
|
||||
try {
|
||||
Class.forName("org.bukkit.Bukkit");
|
||||
return Platform.BUKKIT; // Bukkit (Spigot/Paper without Folia) detected
|
||||
} catch (ClassNotFoundException ignored2) {
|
||||
}
|
||||
return platforms.entrySet().stream()
|
||||
.filter(entry -> isClassPresent(entry.getKey()))
|
||||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Unknown platform!"));
|
||||
}
|
||||
|
||||
private boolean isClassPresent(String className) {
|
||||
try {
|
||||
Class.forName("net.fabricmc.loader.api.FabricLoader");
|
||||
return Platform.FABRIC; // Fabric detected
|
||||
} catch (ClassNotFoundException ignored3) {
|
||||
Class.forName(className);
|
||||
return true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unknown platform!");
|
||||
}
|
||||
|
||||
public abstract Logger getLogger();
|
||||
@@ -85,6 +92,10 @@ public abstract class KnockbackSyncBase {
|
||||
registerCommonListeners();
|
||||
registerPlatformListeners();
|
||||
registerCommands();
|
||||
initializeScheduler();
|
||||
configManager.loadConfig(false);
|
||||
statsManager.init();
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
public abstract void initializeScheduler();
|
||||
@@ -100,8 +111,10 @@ public abstract class KnockbackSyncBase {
|
||||
protected void registerCommonListeners() {
|
||||
PacketEvents.getAPI().getEventManager().registerListeners(
|
||||
new AttributeChangeListener(),
|
||||
new PingReceiveListener()
|
||||
new PingReceiveListener(),
|
||||
new ClientBrandListener()
|
||||
);
|
||||
Event.setEventBus(eventBus);
|
||||
}
|
||||
|
||||
protected abstract void registerPlatformListeners();
|
||||
@@ -112,6 +125,7 @@ public abstract class KnockbackSyncBase {
|
||||
new ReloadCommand(),
|
||||
new PingCommand(),
|
||||
new StatusCommand(),
|
||||
new ToggleOffGroundSubcommand(),
|
||||
new ToggleCommand()
|
||||
);
|
||||
list.forEach(command -> command.register(commandManager));
|
||||
@@ -213,6 +227,7 @@ public abstract class KnockbackSyncBase {
|
||||
|
||||
public abstract PermissionChecker getPermissionChecker();
|
||||
|
||||
public abstract float getTickRate();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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.PlayerDataManager;
|
||||
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 PingCommand implements BuilderCommand {
|
||||
|
||||
private String pingSelfAvailableMessage;
|
||||
private String pingSelfUnavailableMessage;
|
||||
private String pingOtherAvailableMessage;
|
||||
private String pingOtherUnavailableMessage;
|
||||
|
||||
private String mustSpecifyPlayerFromConsoleMessage;
|
||||
private String knockbacksyncDisabledForYouMessage;
|
||||
private String knockbacksyncDisabledForTargetMessage;
|
||||
|
||||
public PingCommand() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
manager.command(
|
||||
manager.commandBuilder("knockbacksync", "kbsync", "kbs")
|
||||
@@ -23,20 +43,82 @@ public class PingCommand implements BuilderCommand {
|
||||
return PredicatePermission.of(senderPredicate).testPermission(sender);
|
||||
}))
|
||||
.literal("ping")
|
||||
.optional("target", KnockbackSyncBase.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.optional("target", Base.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.handler(context -> {
|
||||
PlayerSelector targetSelector = context.getOrDefault("target", null);
|
||||
|
||||
if (targetSelector == null) {
|
||||
if (context.sender().isConsole()) {
|
||||
context.sender().sendMessage("You must specify a player to use this command from the console.");
|
||||
context.sender().sendMessage(ChatUtil.translateAlternateColorCodes('&', mustSpecifyPlayerFromConsoleMessage));
|
||||
} else {
|
||||
context.sender().sendMessage(ChatUtil.getPingMessage(context.sender().getUniqueId(), null));
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), null));
|
||||
}
|
||||
} else {
|
||||
context.sender().sendMessage(ChatUtil.getPingMessage(context.sender().getUniqueId(), targetSelector.getSinglePlayer().getUUID()));
|
||||
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), targetSelector.getSinglePlayer().getUUID()));
|
||||
}
|
||||
})
|
||||
);
|
||||
Base.INSTANCE.getEventBus().registerListeners(this);
|
||||
}
|
||||
private void loadConfig() {
|
||||
ConfigWrapper configWrapper = Base.INSTANCE.getConfigManager().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",
|
||||
"Accurate ping unavailable. Your estimated ping is &b%ping%&rms.");
|
||||
pingOtherAvailableMessage = configWrapper.getString("messages.ping.other.available",
|
||||
"%player%'s real ping is &b%ping%&rms. Jitter: &b%jitter%&rms. Spike: &b%spike%&r. Compensated ping: &b%compensated%&rms.");
|
||||
pingOtherUnavailableMessage = configWrapper.getString("messages.ping.other.unavailable",
|
||||
"Accurate ping unavailable. %player%'s estimated ping is &b%ping%&rms.");
|
||||
mustSpecifyPlayerFromConsoleMessage = configWrapper.getString("messages.console.must_specify_player",
|
||||
"&cYou must specify a player to use the knockbacksync ping command from the console.");
|
||||
knockbacksyncDisabledForYouMessage = configWrapper.getString("messages.disabled.self",
|
||||
"&cKnockback synchronization is currently disabled for you!");
|
||||
knockbacksyncDisabledForTargetMessage = configWrapper.getString("messages.disabled.target",
|
||||
"&cKnockback synchronization is currently disabled for the target player.");
|
||||
}
|
||||
|
||||
private String getPingMessage(UUID senderUUID, UUID targetUUID) {
|
||||
PlayerData playerData = null;
|
||||
boolean isSelf = senderUUID.equals(targetUUID) || targetUUID == null;
|
||||
|
||||
if (senderUUID == null) {
|
||||
senderUUID = targetUUID;
|
||||
}
|
||||
|
||||
if (isSelf) {
|
||||
playerData = PlayerDataManager.getPlayerData(senderUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForYouMessage);
|
||||
}
|
||||
} else {
|
||||
playerData = PlayerDataManager.getPlayerData(targetUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForTargetMessage);
|
||||
}
|
||||
}
|
||||
|
||||
String rawReturnString;
|
||||
if (playerData.getPing() == null) {
|
||||
rawReturnString = isSelf ? pingSelfUnavailableMessage : pingOtherUnavailableMessage;
|
||||
} else {
|
||||
rawReturnString = isSelf ? pingSelfAvailableMessage : pingOtherAvailableMessage;
|
||||
}
|
||||
|
||||
rawReturnString = rawReturnString
|
||||
.replace("%player%", playerData.getPlatformPlayer().getName())
|
||||
.replace("%ping%", playerData.getPing() == null ?
|
||||
String.valueOf(playerData.getPlatformPlayer().getPing()) :
|
||||
String.format("%.3f", playerData.getPing()))
|
||||
.replace("%jitter%", String.format("%.3f", playerData.getJitter()))
|
||||
.replace("%spike%", String.valueOf(playerData.isSpike()))
|
||||
.replace("%compensated%", String.format("%.3f", playerData.getCompensatedPing()));
|
||||
|
||||
return ChatUtil.translateAlternateColorCodes('&', rawReturnString);
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReload(ConfigReloadEvent event) {
|
||||
loadConfig();
|
||||
}
|
||||
}
|
||||
+30
-17
@@ -1,7 +1,9 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.command.generic.BuilderCommand;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.util.ChatUtil;
|
||||
@@ -11,27 +13,38 @@ import org.incendo.cloud.permission.PredicatePermission;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ReloadCommand implements BuilderCommand {
|
||||
|
||||
private final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
private String rawReloadMessage = configManager.getConfigWrapper().getString("messages.reload.success", "&aSuccessfully reloaded KnockbackSync.");
|
||||
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
manager.command(
|
||||
manager.commandBuilder("knockbacksync", "kbsync", "kbs")
|
||||
.literal("reload")
|
||||
.permission((sender -> {
|
||||
final String permission = "knockbacksync.reload";
|
||||
Predicate<Sender> senderPredicate = (s) -> {
|
||||
return s.hasPermission(permission, false);
|
||||
};
|
||||
.literal("reload")
|
||||
.permission((sender -> {
|
||||
final String permission = "knockbacksync.reload";
|
||||
Predicate<Sender> senderPredicate = (s) -> {
|
||||
return s.hasPermission(permission, false);
|
||||
};
|
||||
|
||||
return PredicatePermission.of(senderPredicate).testPermission(sender);
|
||||
}))
|
||||
.handler(context -> {
|
||||
ConfigManager configManager = KnockbackSyncBase.INSTANCE.getConfigManager();
|
||||
configManager.loadConfig(true);
|
||||
return PredicatePermission.of(senderPredicate).testPermission(sender);
|
||||
}))
|
||||
.handler(context -> {
|
||||
configManager.loadConfig(true);
|
||||
|
||||
String rawReloadMessage = configManager.getReloadMessage();
|
||||
String reloadMessage = ChatUtil.translateAlternateColorCodes('&', rawReloadMessage);
|
||||
// Fire the ConfigReloadEvent
|
||||
new ConfigReloadEvent(configManager).post();
|
||||
|
||||
context.sender().sendMessage(reloadMessage);
|
||||
})
|
||||
String reloadMessage = ChatUtil.translateAlternateColorCodes('&', rawReloadMessage);
|
||||
|
||||
context.sender().sendMessage(reloadMessage);
|
||||
})
|
||||
);
|
||||
Base.INSTANCE.getEventBus().registerListeners(this);
|
||||
}
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReload(ConfigReloadEvent event) {
|
||||
rawReloadMessage = event.getConfigManager().getConfigWrapper().getString("messages.reload.success", "&aSuccessfully reloaded KnockbackSync.");
|
||||
}
|
||||
}
|
||||
+68
-28
@@ -1,8 +1,11 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
@@ -16,15 +19,29 @@ import java.util.function.Predicate;
|
||||
|
||||
public class StatusCommand implements BuilderCommand {
|
||||
|
||||
private static final ConfigManager configManager = KnockbackSyncBase.INSTANCE.getConfigManager();
|
||||
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
private static final String STATUS_SELF_PERMISSION = "knockbacksync.status.self";
|
||||
private static final String STATUS_OTHER_PERMISSION = "knockbacksync.status.other";
|
||||
|
||||
private String globalStatusEnabledMessage;
|
||||
private String globalStatusDisabledMessage;
|
||||
private String globalOffGroundStatusEnabledMessage;
|
||||
private String globalOffGroundStatusDisabledMessage;
|
||||
private String playerStatusEnabledMessage;
|
||||
private String playerStatusDisabledMessage;
|
||||
private String playerStatusGlobalDisabledMessage;
|
||||
private String noSelfPermissionMessage;
|
||||
private String noOtherPermissionMessage;
|
||||
|
||||
public StatusCommand() {
|
||||
updateConfigValues();
|
||||
}
|
||||
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
manager.command(
|
||||
manager.commandBuilder("knockbacksync", "kbsync", "kbs")
|
||||
.literal("status")
|
||||
.optional("target", KnockbackSyncBase.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.optional("target", Base.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.permission((sender -> {
|
||||
Predicate<Sender> senderPredicate = (s) -> {
|
||||
return s.hasPermission(STATUS_SELF_PERMISSION, true) || sender.hasPermission(STATUS_OTHER_PERMISSION, false);
|
||||
@@ -35,53 +52,76 @@ public class StatusCommand implements BuilderCommand {
|
||||
.handler(context -> {
|
||||
Sender sender = context.sender();
|
||||
PlayerSelector targetSelector = context.getOrDefault("target", null);
|
||||
if (targetSelector == null) {
|
||||
// Show global status
|
||||
boolean globalStatus = configManager.isToggled();
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getConfigWrapper().getString("global_status_message", "&eGlobal KnockbackSync status: ")) +
|
||||
(globalStatus
|
||||
? ChatUtil.translateAlternateColorCodes('&', "&aEnabled")
|
||||
: ChatUtil.translateAlternateColorCodes('&', "&cDisabled")));
|
||||
if (targetSelector == null) {
|
||||
// Show global status
|
||||
boolean globalStatus = configManager.isToggled();
|
||||
sender.sendMessage(
|
||||
ChatUtil.translateAlternateColorCodes('&',
|
||||
(globalStatus ? globalStatusEnabledMessage : globalStatusDisabledMessage))
|
||||
+ "\n"
|
||||
+ ChatUtil.translateAlternateColorCodes('&',
|
||||
globalStatus ? globalOffGroundStatusEnabledMessage : globalOffGroundStatusDisabledMessage)
|
||||
);
|
||||
|
||||
if (sender.hasPermission(STATUS_SELF_PERMISSION, true)) {
|
||||
// Show player status for the sender (no target specified)
|
||||
if (!sender.isConsole()) {
|
||||
showPlayerStatus(sender, KnockbackSyncBase.INSTANCE.platformServer.getPlayer(sender.getUniqueId()), configManager);
|
||||
showPlayerStatus(sender, Base.INSTANCE.getPlatformServer().getPlayer(sender.getUniqueId()));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', "&cYou do not have permisssion to check your knockbacksync status."));
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noSelfPermissionMessage));
|
||||
}
|
||||
} else {
|
||||
if (sender.hasPermission(STATUS_OTHER_PERMISSION)) {
|
||||
if (sender.hasPermission(STATUS_OTHER_PERMISSION, true)) {
|
||||
PlatformPlayer target = targetSelector.getSinglePlayer();
|
||||
showPlayerStatus(sender, target, configManager);
|
||||
showPlayerStatus(sender, target);
|
||||
} else {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', "&cYou do not have permisssion to check the status of other players!"));
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noOtherPermissionMessage));
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
Base.INSTANCE.getEventBus().registerListeners(this);
|
||||
}
|
||||
|
||||
private static void showPlayerStatus(Sender sender, PlatformPlayer target, ConfigManager configManager) {
|
||||
private void showPlayerStatus(Sender sender, PlatformPlayer target) {
|
||||
boolean globalStatus = configManager.isToggled();
|
||||
UUID uuid = target.getUUID();
|
||||
boolean playerStatus = PlayerDataManager.containsPlayerData(uuid);
|
||||
|
||||
String statusMessage;
|
||||
if (!globalStatus) {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getConfigWrapper().getString("player_status_message", "&e%player%'s KnockbackSync status: ")
|
||||
.replace("%player%", target.getName()) +
|
||||
ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getConfigWrapper().getString("player_disabled_global_message", "&cDisabled (Global toggle is off)"))));
|
||||
statusMessage = playerStatusGlobalDisabledMessage.replace("%player%", target.getName());
|
||||
} else {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&',
|
||||
configManager.getConfigWrapper().getString("player_status_message", "&e%player%'s KnockbackSync status: ")
|
||||
.replace("%player%", target.getName()) +
|
||||
(playerStatus
|
||||
? ChatUtil.translateAlternateColorCodes('&', "&aEnabled")
|
||||
: ChatUtil.translateAlternateColorCodes('&', "&cDisabled"))));
|
||||
statusMessage = (playerStatus ? this.playerStatusEnabledMessage : this.playerStatusDisabledMessage).replace("%player%", target.getName());
|
||||
}
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', statusMessage));
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReload(ConfigReloadEvent event) {
|
||||
updateConfigValues();
|
||||
}
|
||||
|
||||
private void updateConfigValues() {
|
||||
ConfigWrapper config = configManager.getConfigWrapper();
|
||||
this.globalStatusEnabledMessage = config.getString("messages.status.global.enabled",
|
||||
"&eKnockbackSync global status: &aEnabled");
|
||||
this.globalStatusDisabledMessage = config.getString("messages.status.global.disabled",
|
||||
"&eKnockbackSync global status: &cDisabled");
|
||||
this.globalOffGroundStatusEnabledMessage = config.getString("messages.status.offground.enabled",
|
||||
"&eKnockbackSync off-ground status: &aEnabled");
|
||||
this.globalOffGroundStatusDisabledMessage = config.getString("messages.status.offground.disabled",
|
||||
"&eKnockbackSync off-ground status: &cDisabled");
|
||||
this.playerStatusEnabledMessage = config.getString("messages.status.player.enabled",
|
||||
"&e%player%'s KnockbackSync status: &aEnabled");
|
||||
this.playerStatusDisabledMessage = config.getString("messages.status.player.disabled",
|
||||
"&e%player%'s KnockbackSync status: &cDisabled");
|
||||
this.playerStatusGlobalDisabledMessage = config.getString("messages.status.player.global_disabled",
|
||||
"&e%player%'s KnockbackSync status: &cDisabled (Global toggle is off)");
|
||||
this.noSelfPermissionMessage = config.getString("messages.status.permission.no_self",
|
||||
"&cYou do not have permission to check your knockbacksync status.");
|
||||
this.noOtherPermissionMessage = config.getString("messages.status.permission.no_other",
|
||||
"&cYou do not have permission to check the status of other players!");
|
||||
}
|
||||
}
|
||||
+40
-13
@@ -1,7 +1,9 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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.ConfigManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
@@ -10,7 +12,6 @@ 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.Permission;
|
||||
import org.incendo.cloud.permission.PredicatePermission;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -18,23 +19,43 @@ import java.util.function.Predicate;
|
||||
|
||||
public class ToggleCommand implements BuilderCommand {
|
||||
|
||||
private static final ConfigManager configManager = KnockbackSyncBase.INSTANCE.getConfigManager();
|
||||
private static final PermissionChecker permissionChecker = KnockbackSyncBase.INSTANCE.getPermissionChecker();
|
||||
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
|
||||
private static final PermissionChecker permissionChecker = Base.INSTANCE.getPermissionChecker();
|
||||
private static final String TOGGLE_GLOBAL_PERMISSION = "knockbacksync.toggle.global";
|
||||
private static final String TOGGLE_SELF_PERMISSION = "knockbacksync.toggle.self";
|
||||
private static final String TOGGLE_OTHER_PERMISSION = "knockbacksync.toggle.other";
|
||||
|
||||
private String noGlobalPermissionMessage;
|
||||
private String noSelfPermissionMessage;
|
||||
private String noOtherPermissionMessage;
|
||||
private String serverDisabledMessage;
|
||||
|
||||
public ToggleCommand() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
ConfigWrapper configWrapper = configManager.getConfigWrapper();
|
||||
noGlobalPermissionMessage = configWrapper.getString("messages.toggle.permission.no_global",
|
||||
"&cYou don't have permission to toggle the global setting.");
|
||||
noSelfPermissionMessage = configWrapper.getString("messages.toggle.permission.no_self",
|
||||
"&cYou do not have permission to toggle your knockback.");
|
||||
noOtherPermissionMessage = configWrapper.getString("messages.toggle.permission.no_other",
|
||||
"&cYou do not have permission to toggle the knockback of other player's.");
|
||||
serverDisabledMessage = configWrapper.getString("messages.toggle.server_disabled",
|
||||
"&cKnockbacksync is currently disabled on this server. Contact your server administrator for more information.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
manager.command(
|
||||
manager.commandBuilder("knockbacksync", "kbsync", "kbs")
|
||||
.literal("toggle")
|
||||
.optional("target", KnockbackSyncBase.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.optional("target", Base.INSTANCE.getPlayerSelectorParser().descriptor())
|
||||
.permission((sender -> {
|
||||
Predicate<Sender> senderPredicate = (s) -> {
|
||||
return s.hasPermission(TOGGLE_GLOBAL_PERMISSION, false)
|
||||
|| sender.hasPermission(TOGGLE_SELF_PERMISSION, true)
|
||||
|| sender.hasPermission(TOGGLE_SELF_PERMISSION, false)
|
||||
|| sender.hasPermission(TOGGLE_OTHER_PERMISSION, false);
|
||||
};
|
||||
|
||||
@@ -48,21 +69,21 @@ public class ToggleCommand implements BuilderCommand {
|
||||
if (permissionChecker.hasPermission(sender, TOGGLE_GLOBAL_PERMISSION, false)) {
|
||||
toggleGlobalKnockback(configManager, sender);
|
||||
} else {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', "&cYou don't have permission to toggle the global setting."));
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noGlobalPermissionMessage));
|
||||
}
|
||||
} else {
|
||||
PlatformPlayer target = targetSelector.getSinglePlayer();
|
||||
boolean senderIsTarget = sender.getUniqueId() == target.getUUID();
|
||||
if (!senderIsTarget && !permissionChecker.hasPermission(sender, TOGGLE_OTHER_PERMISSION, false)) {
|
||||
sender.sendMessage("You do not have permission to toggle the knockback of other player's.");
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noOtherPermissionMessage));
|
||||
return;
|
||||
} else if (senderIsTarget && !permissionChecker.hasPermission(sender, TOGGLE_SELF_PERMISSION, true)) {
|
||||
sender.sendMessage("You do not have permission to toggle your knockback.");
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noSelfPermissionMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configManager.isToggled()) {
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', "&cKnockbacksync is currently disabled on this server. Contact your server administrator for more information."));
|
||||
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', serverDisabledMessage));
|
||||
} else {
|
||||
togglePlayerKnockback(target, configManager, sender);
|
||||
}
|
||||
@@ -74,10 +95,16 @@ public class ToggleCommand implements BuilderCommand {
|
||||
|
||||
private static void toggleGlobalKnockback(ConfigManager configManager, Sender sender) {
|
||||
boolean toggledState = !configManager.isToggled();
|
||||
ToggleOnOffEvent toggleOnOffEvent = new ToggleOnOffEvent(toggledState);
|
||||
toggleOnOffEvent.post();
|
||||
if (toggleOnOffEvent.isCancelled())
|
||||
return;
|
||||
|
||||
toggledState = toggleOnOffEvent.getStatus();
|
||||
configManager.setToggled(toggledState);
|
||||
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().set("enabled", toggledState);
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().saveConfig();
|
||||
Base.INSTANCE.getConfigManager().getConfigWrapper().set("enabled", toggledState);
|
||||
Base.INSTANCE.getConfigManager().saveConfig();
|
||||
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
toggledState ? configManager.getEnableMessage() : configManager.getDisableMessage()
|
||||
@@ -101,7 +128,7 @@ public class ToggleCommand implements BuilderCommand {
|
||||
if (hasPlayerData)
|
||||
PlayerDataManager.removePlayerData(uuid);
|
||||
else {
|
||||
PlayerDataManager.addPlayerData(uuid, new PlayerData(KnockbackSyncBase.INSTANCE.platformServer.getPlayer(uuid)));
|
||||
PlayerDataManager.addPlayerData(uuid, new PlayerData(Base.INSTANCE.getPlatformServer().getPlayer(uuid)));
|
||||
}
|
||||
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
|
||||
+31
-9
@@ -1,8 +1,11 @@
|
||||
package me.caseload.knockbacksync.command.subcommand;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.command.generic.BuilderCommand;
|
||||
import me.caseload.knockbacksync.permission.PermissionChecker;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
import me.caseload.knockbacksync.sender.Sender;
|
||||
import me.caseload.knockbacksync.util.ChatUtil;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -12,7 +15,14 @@ import java.util.function.Predicate;
|
||||
|
||||
public class ToggleOffGroundSubcommand implements BuilderCommand {
|
||||
|
||||
@Override
|
||||
public static boolean offGroundSyncEnabled = true;
|
||||
private String offGroundSyncEnableMessage;
|
||||
private String offGroundSyncDisableMessage;
|
||||
|
||||
public ToggleOffGroundSubcommand() {
|
||||
loadConfigSettings();
|
||||
}
|
||||
|
||||
public void register(CommandManager<Sender> manager) {
|
||||
manager.command(
|
||||
manager.commandBuilder("knockbacksync", "kbsync", "kbs")
|
||||
@@ -26,14 +36,26 @@ public class ToggleOffGroundSubcommand implements BuilderCommand {
|
||||
return PredicatePermission.of(senderPredicate).testPermission(sender);
|
||||
}))
|
||||
.handler(commandContext -> {
|
||||
boolean toggledState = !KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getBoolean("enable_experimental_offground", false);
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().set("enable_experimental_offground", toggledState);
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().saveConfig();
|
||||
offGroundSyncEnabled = !offGroundSyncEnabled;
|
||||
Base.INSTANCE.getConfigManager().getConfigWrapper().set("enable_offground_synchronization", offGroundSyncEnabled);
|
||||
Base.INSTANCE.getConfigManager().saveConfig();
|
||||
String message = ChatUtil.translateAlternateColorCodes('&',
|
||||
toggledState ?
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getString("enable_experimental_offground_message", "&aSuccessfully enabled offground experiment.") :
|
||||
KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getString("disable_experimental_offground_message", "&cSuccessfully disabled offground experiment."));
|
||||
offGroundSyncEnabled ? offGroundSyncEnableMessage : offGroundSyncDisableMessage);
|
||||
commandContext.sender().sendMessage(message);
|
||||
}));
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onConfigReloadEvent(ConfigReloadEvent event) {
|
||||
loadConfigSettings();
|
||||
}
|
||||
|
||||
private void loadConfigSettings() {
|
||||
ConfigWrapper configWrapper = Base.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
offGroundSyncEnabled = configWrapper.getBoolean("settings.offground.enabled", true);
|
||||
this.offGroundSyncEnableMessage = configWrapper.getString("messages.offground.enable",
|
||||
"&aSuccessfully enabled offground synchronization.");
|
||||
this.offGroundSyncDisableMessage = configWrapper.getString("messages.offground.disable",
|
||||
"&cSuccessfully disabled offground synchronization.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package me.caseload.knockbacksync.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
public class YamlConfiguration {
|
||||
private final File file;
|
||||
private final Yaml yaml;
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<String, Object> data;
|
||||
private String fileContents;
|
||||
|
||||
public YamlConfiguration(File file) {
|
||||
this.file = file;
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
options.setPrettyFlow(true);
|
||||
options.setWidth(Integer.MAX_VALUE);
|
||||
this.yaml = new Yaml(options);
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
if (!file.exists()) {
|
||||
data = new LinkedHashMap<>();
|
||||
return;
|
||||
}
|
||||
|
||||
fileContents = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
|
||||
data = yaml.load(fileContents);
|
||||
if (data == null) {
|
||||
data = new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
String dump = yaml.dump(data);
|
||||
|
||||
if (fileContents != null) {
|
||||
Map<String, YamlElement> oldElements = parseYaml(fileContents);
|
||||
Map<String, YamlElement> newElements = parseYaml(dump);
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
buildOutput(oldElements, newElements, result);
|
||||
dump = result.toString();
|
||||
}
|
||||
|
||||
try (Writer writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) {
|
||||
writer.write(dump);
|
||||
}
|
||||
}
|
||||
|
||||
private static class YamlElement {
|
||||
String key;
|
||||
String value;
|
||||
int indent;
|
||||
boolean isComment;
|
||||
List<YamlElement> children = new ArrayList<>();
|
||||
|
||||
YamlElement(String line, int indent) {
|
||||
this.indent = indent;
|
||||
if (line.trim().startsWith("#")) {
|
||||
this.isComment = true;
|
||||
this.value = line;
|
||||
} else {
|
||||
this.isComment = false;
|
||||
String[] parts = line.split(":", 2);
|
||||
this.key = parts[0].trim();
|
||||
this.value = parts.length > 1 ? parts[1].trim() : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, YamlElement> parseYaml(String content) {
|
||||
Map<String, YamlElement> elements = new LinkedHashMap<>();
|
||||
Stack<YamlElement> stack = new Stack<>();
|
||||
String[] lines = content.split("\n");
|
||||
|
||||
for (String line : lines) {
|
||||
if (line.trim().isEmpty()) continue;
|
||||
|
||||
int indent = getIndentation(line);
|
||||
YamlElement element = new YamlElement(line, indent);
|
||||
|
||||
while (!stack.isEmpty() && stack.peek().indent >= indent) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
if (!element.isComment) {
|
||||
elements.put(element.key, element);
|
||||
} else {
|
||||
elements.put("comment" + elements.size(), element);
|
||||
}
|
||||
} else {
|
||||
stack.peek().children.add(element);
|
||||
}
|
||||
|
||||
if (!element.isComment && element.value.isEmpty()) {
|
||||
stack.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
private void buildOutput(Map<String, YamlElement> oldElements, Map<String, YamlElement> newElements, StringBuilder result) {
|
||||
for (YamlElement element : oldElements.values()) {
|
||||
if (element.isComment) {
|
||||
result.append(element.value).append("\n");
|
||||
} else {
|
||||
YamlElement newElement = newElements.get(element.key);
|
||||
if (newElement != null) {
|
||||
String indent = createIndent(element.indent);
|
||||
result.append(indent).append(element.key).append(":");
|
||||
|
||||
if (!newElement.value.isEmpty()) {
|
||||
result.append(" ").append(newElement.value);
|
||||
}
|
||||
result.append("\n");
|
||||
|
||||
if (!element.children.isEmpty()) {
|
||||
Map<String, YamlElement> oldChildren = new LinkedHashMap<>();
|
||||
Map<String, YamlElement> newChildren = new LinkedHashMap<>();
|
||||
|
||||
for (YamlElement child : element.children) {
|
||||
if (!child.isComment) {
|
||||
oldChildren.put(child.key, child);
|
||||
} else {
|
||||
oldChildren.put("comment" + oldChildren.size(), child);
|
||||
}
|
||||
}
|
||||
|
||||
for (YamlElement child : newElement.children) {
|
||||
if (!child.isComment) {
|
||||
newChildren.put(child.key, child);
|
||||
}
|
||||
}
|
||||
|
||||
buildOutput(oldChildren, newChildren, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getIndentation(String line) {
|
||||
int indent = 0;
|
||||
while (indent < line.length() && line.charAt(indent) == ' ') {
|
||||
indent++;
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
|
||||
private String createIndent(int count) {
|
||||
StringBuilder sb = new StringBuilder(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.caseload.knockbacksync.event;
|
||||
|
||||
import lombok.Setter;
|
||||
import me.caseload.knockbacksync.event.OptimizedEventBus;
|
||||
|
||||
public abstract class Event {
|
||||
@Setter
|
||||
private static EventBus eventBus;
|
||||
@Setter
|
||||
private boolean cancelled = false;
|
||||
|
||||
public void post() {
|
||||
if (eventBus != null) {
|
||||
eventBus.post(this);
|
||||
} else {
|
||||
throw new IllegalStateException("EventBus has not been set");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.caseload.knockbacksync.event;
|
||||
|
||||
public interface EventBus {
|
||||
void registerListeners(Object listener);
|
||||
|
||||
void registerStaticListeners(Class<?> clazz);
|
||||
|
||||
void unregisterListeners(Object listener);
|
||||
|
||||
void unregisterStaticListeners(Class<?> clazz);
|
||||
|
||||
void post(Event event);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.caseload.knockbacksync.event;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface KBSyncEventHandler {
|
||||
int priority() default 0;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.caseload.knockbacksync.event;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class OptimizedEventBus implements EventBus {
|
||||
private final Map<Class<? extends Event>, List<OptimizedListener>> listenerMap = new HashMap<>();
|
||||
private final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
@Override
|
||||
public void registerListeners(Object listener) {
|
||||
registerMethods(listener, listener.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStaticListeners(Class<?> clazz) {
|
||||
registerMethods(null, clazz);
|
||||
}
|
||||
|
||||
private void registerMethods(Object instance, Class<?> clazz) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
KBSyncEventHandler annotation = method.getAnnotation(KBSyncEventHandler.class);
|
||||
if (annotation != null && method.getParameterCount() == 1) {
|
||||
Class<?> eventType = method.getParameterTypes()[0];
|
||||
if (Event.class.isAssignableFrom(eventType)) {
|
||||
try {
|
||||
// Skip instance methods when registering static listeners
|
||||
if (instance == null && !Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort listeners by priority
|
||||
listenerMap.values().forEach(list -> list.sort((a, b) -> Integer.compare(b.priority, a.priority)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListeners(Object listener) {
|
||||
listenerMap.values().forEach(list -> list.removeIf(l -> l.instance == listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterStaticListeners(Class<?> clazz) {
|
||||
listenerMap.values().forEach(list -> list.removeIf(l -> l.instance == null && l.declaringClass == clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void post(Event event) {
|
||||
List<OptimizedListener> listeners = listenerMap.get(event.getClass());
|
||||
if (listeners != null) {
|
||||
for (OptimizedListener listener : listeners) {
|
||||
try {
|
||||
if (listener.instance != null) {
|
||||
// Instance method
|
||||
listener.handle.invoke(listener.instance, event);
|
||||
} else {
|
||||
// Static method
|
||||
listener.handle.invoke(event);
|
||||
}
|
||||
if (event.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class OptimizedListener {
|
||||
final Object instance; // null for static methods
|
||||
final MethodHandle handle;
|
||||
final int priority;
|
||||
final Class<?> declaringClass;
|
||||
|
||||
OptimizedListener(Object instance, MethodHandle handle, int priority, Class<?> declaringClass) {
|
||||
this.instance = instance;
|
||||
this.handle = handle;
|
||||
this.priority = priority;
|
||||
this.declaringClass = declaringClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.caseload.knockbacksync.event.events;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.caseload.knockbacksync.event.Event;
|
||||
import me.caseload.knockbacksync.manager.ConfigManager;
|
||||
|
||||
@Getter
|
||||
public class ConfigReloadEvent extends Event {
|
||||
private final ConfigManager configManager;
|
||||
|
||||
public ConfigReloadEvent(ConfigManager configManager) {
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.caseload.knockbacksync.event.events;
|
||||
|
||||
import me.caseload.knockbacksync.event.Event;
|
||||
|
||||
public class ToggleOnOffEvent extends Event {
|
||||
|
||||
private boolean newEnabledState;
|
||||
|
||||
public ToggleOnOffEvent(boolean newEnabledState) {
|
||||
this.newEnabledState = newEnabledState;
|
||||
}
|
||||
|
||||
public boolean getStatus() {
|
||||
return newEnabledState;
|
||||
}
|
||||
|
||||
public void setStatus(boolean newEnabledState) {
|
||||
this.newEnabledState = newEnabledState;
|
||||
}
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
super.setCancelled(cancelled);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
|
||||
public abstract class PlayerDamageListener {
|
||||
public void onPlayerDamage(PlatformPlayer victim, PlatformPlayer attacker) {
|
||||
if (!KnockbackSyncBase.INSTANCE.getConfigManager().isToggled())
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(victim.getUUID());
|
||||
@@ -18,7 +18,7 @@ public abstract class PlayerDamageListener {
|
||||
playerData.setLastDamageTicks(victim.getNoDamageTicks());
|
||||
playerData.updateCombat();
|
||||
|
||||
if (!KnockbackSyncBase.INSTANCE.getConfigManager().isRunnableEnabled())
|
||||
if (!Base.INSTANCE.getConfigManager().isRunnableEnabled())
|
||||
playerData.sendPing(false);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
@@ -13,7 +13,7 @@ public abstract class PlayerJoinQuitListener {
|
||||
PlayerDataManager.addPlayerData(player.getUuid(), player);
|
||||
PlatformPlayer platformPlayer = player.getPlatformPlayer();
|
||||
|
||||
if (KnockbackSyncBase.INSTANCE.getConfigManager().isUpdateAvailable() && KnockbackSyncBase.INSTANCE.getConfigManager().isNotifyUpdate() && KnockbackSyncBase.INSTANCE.getPermissionChecker().hasPermission(platformPlayer, "knockbacksync.update"))
|
||||
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"
|
||||
@@ -26,7 +26,7 @@ public abstract class PlayerJoinQuitListener {
|
||||
return;
|
||||
|
||||
if (playerData.isInCombat())
|
||||
playerData.quitCombat(true);
|
||||
playerData.quitCombat();
|
||||
|
||||
PlayerDataManager.removePlayerData(uuid);
|
||||
}
|
||||
|
||||
+31
-11
@@ -1,7 +1,9 @@
|
||||
package me.caseload.knockbacksync.listener;
|
||||
|
||||
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 me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
@@ -9,24 +11,42 @@ import me.caseload.knockbacksync.player.PlayerData;
|
||||
public abstract class PlayerKnockbackListener {
|
||||
|
||||
public void onPlayerVelocity(PlatformPlayer victim, Vector3d velocity) {
|
||||
if (!KnockbackSyncBase.INSTANCE.getConfigManager().isToggled())
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(victim.getUUID());
|
||||
if (playerData == null)
|
||||
PlayerData victimPlayerData = PlayerDataManager.getPlayerData(victim.getUUID());
|
||||
if (victimPlayerData == null)
|
||||
return;
|
||||
|
||||
Integer damageTicks = playerData.getLastDamageTicks();
|
||||
/* Integer damageTicks = playerData.getLastDamageTicks();
|
||||
if (damageTicks != null && damageTicks > 8)
|
||||
return;*/
|
||||
|
||||
if (victimPlayerData.getNotNullPing() < PlayerData.PING_OFFSET)
|
||||
return;
|
||||
|
||||
Double verticalVelocity = playerData.getVerticalVelocity();
|
||||
if (verticalVelocity == null || !playerData.isOnGround(velocity.getY()))
|
||||
double distanceToGround = victimPlayerData.getDistanceToGround();
|
||||
if (distanceToGround <= 0)
|
||||
return; // minecraft already does the work for us
|
||||
|
||||
WrappedBlockState blockState = victim.getWorld().getBlockStateAt(victim.getLocation());
|
||||
if (victim.isGliding() ||
|
||||
blockState.getType() == StateTypes.WATER ||
|
||||
blockState.getType() == StateTypes.LAVA ||
|
||||
blockState.getType() == StateTypes.COBWEB ||
|
||||
blockState.getType() == StateTypes.SCAFFOLDING)
|
||||
return;
|
||||
|
||||
// Since we're already changing types do we need to use withY to get a new object
|
||||
// Or can we just go velcoity.y = verticalVelocity ?
|
||||
Vector3d adjustedVelocity = velocity.withY(verticalVelocity);
|
||||
victim.setVelocity(adjustedVelocity); // Use PlatformPlayer's setVelocity
|
||||
Vector3d adjustedVelocity;
|
||||
if (victimPlayerData.isOnGroundClientSide(velocity.getY(), distanceToGround))
|
||||
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
|
||||
return;
|
||||
|
||||
victim.setVelocity(adjustedVelocity);
|
||||
}
|
||||
}
|
||||
+3
@@ -6,6 +6,7 @@ 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.wrapper.play.server.WrapperPlayServerUpdateAttributes;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.MathUtil;
|
||||
@@ -29,6 +30,8 @@ public class AttributeChangeListener extends PacketListenerAbstract {
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled()) return;
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.UPDATE_ATTRIBUTES) {
|
||||
|
||||
WrapperPlayServerUpdateAttributes packet = new WrapperPlayServerUpdateAttributes(event);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
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.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPluginMessage;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
public class ClientBrandListener extends PacketListenerAbstract {
|
||||
String brand = "vanilla";
|
||||
boolean hasBrand = false;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(final PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLUGIN_MESSAGE) {
|
||||
WrapperPlayClientPluginMessage packet = new WrapperPlayClientPluginMessage(event);
|
||||
String channelName = packet.getChannelName();
|
||||
handle(channelName, packet.getData());
|
||||
Base.INSTANCE.getPlatformServer().getPlayer(event.getUser().getUUID()).setClientBrand(brand);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle(String channel, byte[] data) {
|
||||
if (channel.equalsIgnoreCase("minecraft:brand") || // 1.13+
|
||||
channel.equals("MC|Brand")) { // 1.12
|
||||
if (data.length > 64 || data.length == 0) {
|
||||
brand = "sent " + data.length + " bytes as brand";
|
||||
} else if (!hasBrand) {
|
||||
byte[] minusLength = new byte[data.length - 1];
|
||||
System.arraycopy(data, 1, minusLength, 0, minusLength.length);
|
||||
brand = new String(minusLength).replace(" (Velocity)", ""); //removes velocity's brand suffix
|
||||
}
|
||||
hasBrand = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-14
@@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientWi
|
||||
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.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.data.Pair;
|
||||
@@ -19,9 +19,11 @@ import me.caseload.knockbacksync.util.data.Pair;
|
||||
import java.util.List;
|
||||
|
||||
public class PingReceiveListener extends PacketListenerAbstract {
|
||||
// TODO supplment our own ping calculations with over keepalive sent by the server with
|
||||
|
||||
@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);
|
||||
@@ -52,8 +54,9 @@ public class PingReceiveListener extends PacketListenerAbstract {
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (!KnockbackSyncBase.INSTANCE.getConfigManager().isToggled()) return;
|
||||
|
||||
// Do not immediately return if KB sync is disabled. This is because if we send a packet, disable the plugin
|
||||
// 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) {
|
||||
|
||||
@@ -84,16 +87,6 @@ public class PingReceiveListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
private <T extends Number> void handlePingCalculationPackets(PacketReceiveEvent event, PlayerData playerData, long id, List<Pair<T, Long>> packetSentList) {
|
||||
long pingNanos = (System.nanoTime() - packetSentList.remove(0).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);
|
||||
|
||||
// We can cancel for all 3 cases
|
||||
if (playerData.didWeSendThatPacket(id)) {
|
||||
// Pong not needed as vanilla ignores the packet, its needed for packet limiters
|
||||
@@ -103,5 +96,17 @@ public class PingReceiveListener extends PacketListenerAbstract {
|
||||
// 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;
|
||||
|
||||
playerData.setPreviousPing(playerData.getPing());
|
||||
playerData.setPing(diffMillisDouble);
|
||||
|
||||
playerData.getJitterCalculator().addPing(pingNanos);
|
||||
double jitter = playerData.getJitterCalculator().calculateJitter();
|
||||
playerData.setJitter(jitter);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package me.caseload.knockbacksync.manager;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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.scheduler.AbstractTaskHandle;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.Map;
|
||||
@Setter
|
||||
public class ConfigManager {
|
||||
|
||||
public static final long CONFIG_VERSION = 2;
|
||||
public static final long CONFIG_VERSION = 6;
|
||||
|
||||
private boolean toggled;
|
||||
private boolean runnableEnabled;
|
||||
@@ -35,19 +35,18 @@ public class ConfigManager {
|
||||
private String playerEnableMessage;
|
||||
private String playerDisableMessage;
|
||||
private String playerIneligibleMessage;
|
||||
private String reloadMessage;
|
||||
|
||||
private AbstractTaskHandle pingTask;
|
||||
|
||||
private Map<String, Object> config;
|
||||
private File configFile;
|
||||
private ObjectMapper mapper;
|
||||
private ConfigWrapper configWrapper; // Cache the ConfigWrapper instance
|
||||
private YamlConfiguration yamlConfig;
|
||||
|
||||
public ConfigManager() {
|
||||
mapper = new ObjectMapper(new YAMLFactory());
|
||||
KnockbackSyncBase instance = KnockbackSyncBase.INSTANCE;
|
||||
Base instance = Base.INSTANCE;
|
||||
configFile = new File(instance.getDataFolder(), "config.yml");
|
||||
yamlConfig = new YamlConfiguration(configFile);
|
||||
}
|
||||
|
||||
public ConfigWrapper getConfigWrapper() {
|
||||
@@ -60,9 +59,10 @@ public class ConfigManager {
|
||||
public void reloadConfig() {
|
||||
try {
|
||||
if (!configFile.exists()) {
|
||||
KnockbackSyncBase.INSTANCE.saveDefaultConfig();
|
||||
Base.INSTANCE.saveDefaultConfig();
|
||||
}
|
||||
config = mapper.readValue(configFile, Map.class);
|
||||
yamlConfig.load();
|
||||
config = yamlConfig.getData();
|
||||
configWrapper = new ConfigWrapper(config);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -73,7 +73,8 @@ public class ConfigManager {
|
||||
|
||||
public void saveConfig() {
|
||||
try {
|
||||
mapper.writeValue(configFile, config);
|
||||
yamlConfig.setData(config);
|
||||
yamlConfig.save();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -84,40 +85,41 @@ public class ConfigManager {
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
ConfigWrapper config = getConfigWrapper(); // Use cached ConfigWrapper
|
||||
ConfigWrapper configWrapper = getConfigWrapper(); // Use cached ConfigWrapper
|
||||
|
||||
toggled = config.getBoolean("enabled", true);
|
||||
updateConfig();
|
||||
|
||||
toggled = configWrapper.getBoolean("enabled", true);
|
||||
|
||||
// Checks to see if the runnable was enabled...
|
||||
// and if we now want to disable it
|
||||
boolean newRunnableEnabled = config.getBoolean("runnable.enabled", true);
|
||||
boolean newRunnableEnabled = configWrapper.getBoolean("runnable.enabled", true);
|
||||
if (runnableEnabled && newRunnableEnabled && pingTask != null) { // null check for first startup
|
||||
pingTask.cancel();
|
||||
}
|
||||
|
||||
runnableEnabled = newRunnableEnabled;
|
||||
runnableInterval = config.getLong("runnable.interval", 5L);
|
||||
runnableInterval = configWrapper.getLong("runnable.interval", 5L);
|
||||
|
||||
if (runnableEnabled) {
|
||||
long initialDelay = 0L;
|
||||
long pingTaskRunnableInterval = runnableInterval;
|
||||
// Folia does not allow 0 ticks of wait time
|
||||
if (KnockbackSyncBase.INSTANCE.platform == Platform.FOLIA) {
|
||||
if (Base.INSTANCE.getPlatform() == Platform.FOLIA) {
|
||||
initialDelay = 1L;
|
||||
pingTaskRunnableInterval = Math.max(pingTaskRunnableInterval, 1L);
|
||||
}
|
||||
pingTask = KnockbackSyncBase.INSTANCE.getScheduler().runTaskTimerAsynchronously(new PingRunnable(), initialDelay, pingTaskRunnableInterval);
|
||||
pingTask = Base.INSTANCE.getScheduler().runTaskTimerAsynchronously(new PingRunnable(), initialDelay, pingTaskRunnableInterval);
|
||||
}
|
||||
|
||||
notifyUpdate = config.getBoolean("notify_updates", true);
|
||||
combatTimer = config.getLong("runnable.timer", 30L);
|
||||
spikeThreshold = config.getLong("spike_threshold", 20L);
|
||||
enableMessage = config.getString("enable_message", "&aSuccessfully enabled KnockbackSync.");
|
||||
disableMessage = config.getString("disable_message", "&cSuccessfully disabled KnockbackSync.");
|
||||
playerEnableMessage = config.getString("player_enable_message", "&aSuccessfully enabled KnockbackSync for %player%.");
|
||||
playerDisableMessage = config.getString("player_disable_message", "&cSuccessfully disabled KnockbackSync for %player%.");
|
||||
playerIneligibleMessage = config.getString("player_ineligible_message", "&c%player% is ineligible for KnockbackSync. If you believe this is an error, please open an issue on the github page.");
|
||||
reloadMessage = config.getString("reload_message", "&aSuccessfully reloaded KnockbackSync.");
|
||||
notifyUpdate = configWrapper.getBoolean("notify_updates", true);
|
||||
combatTimer = configWrapper.getLong("runnable.timer", 30L);
|
||||
spikeThreshold = configWrapper.getLong("spike_threshold", 20L);
|
||||
enableMessage = configWrapper.getString("messages.toggle.global.enable", "&aSuccessfully enabled KnockbackSync.");
|
||||
disableMessage = configWrapper.getString("messages.toggle.global.disable", "&cSuccessfully disabled KnockbackSync.");
|
||||
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.");
|
||||
}
|
||||
|
||||
public void updateConfig() {
|
||||
@@ -125,16 +127,20 @@ public class ConfigManager {
|
||||
long oldConfigVersion = oldConfig.getLong("config_version", 0);
|
||||
|
||||
if (oldConfigVersion < CONFIG_VERSION) {
|
||||
// Backup old config
|
||||
// Backup old config with comments
|
||||
File backupFile = new File(configFile.getParentFile(), "config-version-" + oldConfigVersion + ".yml");
|
||||
if (configFile.renameTo(backupFile)) {
|
||||
KnockbackSyncBase.INSTANCE.getLogger().info("Backed up old config to " + backupFile.getName());
|
||||
} else {
|
||||
KnockbackSyncBase.INSTANCE.getLogger().warning("Failed to backup old config");
|
||||
try {
|
||||
Files.move(configFile.toPath(), backupFile.toPath());
|
||||
Base.INSTANCE.getLogger().info("Backed up old config to " + backupFile.getName());
|
||||
} catch (IOException e) {
|
||||
Base.INSTANCE.getLogger().warning("Failed to backup old config: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Store old values
|
||||
Map<String, Object> oldValues = new HashMap<>(config);
|
||||
|
||||
// Create new config with default values
|
||||
KnockbackSyncBase.INSTANCE.saveDefaultConfig();
|
||||
Base.INSTANCE.saveDefaultConfig();
|
||||
reloadConfig();
|
||||
|
||||
// Transfer existing settings
|
||||
@@ -147,17 +153,17 @@ public class ConfigManager {
|
||||
// Save updated config
|
||||
saveConfig();
|
||||
|
||||
KnockbackSyncBase.INSTANCE.getLogger().info("Config updated to version " + CONFIG_VERSION);
|
||||
Base.INSTANCE.getLogger().info("Config updated to version " + CONFIG_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
private void transferAllSettings(ConfigWrapper oldConfig, ConfigWrapper newConfig) {
|
||||
transferSettingsRecursive(oldConfig, newConfig, "");
|
||||
transferSettingsRecursive(oldConfig, newConfig, ".");
|
||||
}
|
||||
|
||||
private void transferSettingsRecursive(ConfigWrapper oldConfig, ConfigWrapper newConfig, String currentPath) {
|
||||
for (String key : oldConfig.getKeys(currentPath)) {
|
||||
String fullPath = currentPath.isEmpty() ? key : currentPath + "." + key;
|
||||
String fullPath = currentPath.isEmpty() || currentPath.equals(".") ? key : currentPath + "." + key;
|
||||
if (newConfig.contains(fullPath)) {
|
||||
Object value = oldConfig.get(fullPath);
|
||||
if (value instanceof Map) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.caseload.knockbacksync.manager;
|
||||
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import me.caseload.knockbacksync.util.FloodgateUtil;
|
||||
import me.caseload.knockbacksync.util.GeyserUtil;
|
||||
@@ -18,12 +19,16 @@ public class PlayerDataManager {
|
||||
}
|
||||
|
||||
public static void addPlayerData(@NotNull UUID uuid, @NotNull PlayerData playerData) {
|
||||
if (!shouldExempt(uuid))
|
||||
if (!shouldExempt(uuid)) {
|
||||
playerDataMap.put(uuid, playerData);
|
||||
Base.INSTANCE.getEventBus().registerListeners(playerData);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removePlayerData(@NotNull UUID uuid) {
|
||||
playerDataMap.remove(uuid);
|
||||
PlayerData playerData = playerDataMap.remove(uuid);
|
||||
if (playerData != null)
|
||||
Base.INSTANCE.getEventBus().unregisterListeners(playerData);
|
||||
}
|
||||
|
||||
public static boolean containsPlayerData(@NotNull UUID uuid) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package me.caseload.knockbacksync.player;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
|
||||
public interface PlatformBlock {
|
||||
WrappedBlockState getMaterial();
|
||||
// Add more methods as needed
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.caseload.knockbacksync.player;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -43,5 +45,17 @@ public interface PlatformPlayer {
|
||||
@Nullable Integer getNoDamageTicks();
|
||||
|
||||
void setVelocity(Vector3d adjustedVelocity);
|
||||
|
||||
Vector3d getVelocity();
|
||||
|
||||
double getJumpPower();
|
||||
|
||||
BoundingBox getBoundingBox();
|
||||
|
||||
User getUser();
|
||||
|
||||
void setClientBrand(String brand);
|
||||
|
||||
String getClientBrand();
|
||||
// Add more methods as needed
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
|
||||
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;
|
||||
@@ -16,7 +17,11 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPi
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowConfirmation;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.command.subcommand.ToggleOffGroundSubcommand;
|
||||
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.scheduler.AbstractTaskHandle;
|
||||
import me.caseload.knockbacksync.util.MathUtil;
|
||||
@@ -24,31 +29,33 @@ import me.caseload.knockbacksync.util.data.Pair;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
import me.caseload.knockbacksync.world.raytrace.FluidHandling;
|
||||
import me.caseload.knockbacksync.world.raytrace.RayTraceResult;
|
||||
import org.incendo.cloud.CloudCapability;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@Getter
|
||||
public class PlayerData {
|
||||
|
||||
// Please read the GitHub FAQ before adjusting.
|
||||
private static final short MAIN_THREAD_TRANSACTION_ID = 31407;
|
||||
private static final short NETTY_THREAD_TRANSACTION_ID = 31408;
|
||||
public static final long PING_OFFSET = 25;
|
||||
public static float TICK_RATE = 20.0F;
|
||||
|
||||
private static Field playerField;
|
||||
|
||||
public final List<Pair<Integer, Long>> transactionsSent = new LinkedList<>();
|
||||
public final List<Pair<Long, Long>> keepaliveMap = new LinkedList<>();
|
||||
|
||||
private static final short MAIN_THREAD_TRANSACTION_ID = -1;
|
||||
private static final short NETTY_THREAD_TRANSACTION_ID = -2;
|
||||
private static Object grimPlayerDataManager = null;
|
||||
private static Method getGrimPlayerFromUserMethod = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
switch (KnockbackSyncBase.INSTANCE.platform) {
|
||||
switch (Base.INSTANCE.getPlatform()) {
|
||||
case BUKKIT:
|
||||
case FOLIA:
|
||||
Class<?> bukkitPlayerClass = Class.forName("me.caseload.knockbacksync.player.BukkitPlayer");
|
||||
@@ -59,12 +66,26 @@ public class PlayerData {
|
||||
playerField = fabricPlayerClass.getDeclaredField("fabricPlayer");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected platform: " + KnockbackSyncBase.INSTANCE.platform);
|
||||
throw new IllegalStateException("Unexpected platform: " + Base.INSTANCE.getPlatform());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
playerField.setAccessible(true);
|
||||
|
||||
// Step 1: Get the GrimAPI enum class dynamically
|
||||
Class<?> grimAPIClass = Class.forName("ac.grim.grimac.GrimAPI");
|
||||
|
||||
// Step 2: Access the INSTANCE of GrimAPI (it's a singleton enum)
|
||||
Object grimAPIInstance = Enum.valueOf((Class<? extends Enum>) grimAPIClass, "INSTANCE");
|
||||
|
||||
// Step 3: Get the PlayerDataManager instance
|
||||
Method getPlayerDataManagerMethod = grimAPIClass.getDeclaredMethod("getPlayerDataManager");
|
||||
grimPlayerDataManager = getPlayerDataManagerMethod.invoke(grimAPIInstance);
|
||||
|
||||
// Step 4: Get the getPlayer method from PlayerDataManager
|
||||
Class<?> playerDataManagerClass = grimPlayerDataManager.getClass();
|
||||
getGrimPlayerFromUserMethod = playerDataManagerClass.getDeclaredMethod("getPlayer", Class.forName("org.bukkit.entity.Player"));
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
playerField.setAccessible(true); // May not be needed since it's already public
|
||||
}
|
||||
|
||||
public final User user;
|
||||
@@ -102,6 +123,14 @@ public class PlayerData {
|
||||
this.user = tempUser;
|
||||
}
|
||||
|
||||
public double getNotNullPing() {
|
||||
return ping != null ? ping : platformPlayer.getPing();
|
||||
}
|
||||
|
||||
public double getNotNullPreviousPing() {
|
||||
return previousPing != null ? previousPing : platformPlayer.getPing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the player's ping with compensation for lag spikes.
|
||||
* A hardcoded offset is applied for several reasons,
|
||||
@@ -109,22 +138,30 @@ public class PlayerData {
|
||||
*
|
||||
* @return The compensated ping, with a minimum of 1.
|
||||
*/
|
||||
public double getEstimatedPing() {
|
||||
double currentPing = (ping != null) ? ping : platformPlayer.getPing();
|
||||
double lastPing = (previousPing != null) ? previousPing : platformPlayer.getPing();
|
||||
double ping = (currentPing - lastPing > KnockbackSyncBase.INSTANCE.getConfigManager().getSpikeThreshold()) ? lastPing : currentPing;
|
||||
public double getCompensatedPing() {
|
||||
double ping = getNotNullPing();
|
||||
double previousPing = getNotNullPreviousPing();
|
||||
double spikeCompensatedPing = (ping - previousPing > Base.INSTANCE.getConfigManager().getSpikeThreshold()) ? previousPing : ping;
|
||||
|
||||
return Math.max(1, ping - PING_OFFSET);
|
||||
return Math.max(1, spikeCompensatedPing - PING_OFFSET);
|
||||
}
|
||||
|
||||
// public boolean isKeepAliveIDOurs(long id) {
|
||||
// return id == lastKeepAliveID;
|
||||
// }
|
||||
public int getCompensatedTicks() {
|
||||
return (int) Math.ceil(getCompensatedPing() * Base.INSTANCE.getTickRate() / 1000); // Multiply ping by seconds per tick
|
||||
}
|
||||
|
||||
public int getTicks() {
|
||||
return (int) Math.ceil(getNotNullPing() * Base.INSTANCE.getTickRate() / 1000); // Multiply ping by seconds per tick
|
||||
}
|
||||
|
||||
public boolean isSpike() {
|
||||
return (getNotNullPing() - getNotNullPreviousPing()) > Base.INSTANCE.getConfigManager().getSpikeThreshold();
|
||||
}
|
||||
|
||||
public void sendPing(boolean async) {
|
||||
if (user == null || user.getEncoderState() != ConnectionState.PLAY) return;
|
||||
|
||||
String pingStrategy = KnockbackSyncBase.INSTANCE.getConfigManager().getConfigWrapper().getString("ping_strategy", "KEEPALIVE");
|
||||
String pingStrategy = Base.INSTANCE.getConfigManager().getConfigWrapper().getString("ping_strategy", "KEEPALIVE");
|
||||
switch (pingStrategy) {
|
||||
case "KEEPALIVE":
|
||||
long keepAliveID = async ? NETTY_THREAD_TRANSACTION_ID : MAIN_THREAD_TRANSACTION_ID;
|
||||
@@ -176,32 +213,55 @@ public class PlayerData {
|
||||
* @param verticalVelocity The Player's current vertical velocity.
|
||||
* @return <code>true</code> if the Player is on the ground; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isOnGround(double verticalVelocity) {
|
||||
WrappedBlockState blockState = platformPlayer.getWorld().getBlockStateAt(platformPlayer.getLocation());
|
||||
public boolean isOnGroundClientSide(double verticalVelocity, double distanceToGround) {
|
||||
try {
|
||||
if (getGrimPlayerFromUserMethod != null) {
|
||||
// Step 5: Invoke getPlayer to get PlayerData
|
||||
Object playerData = getGrimPlayerFromUserMethod.invoke(grimPlayerDataManager, playerField.get(platformPlayer));
|
||||
|
||||
if (platformPlayer.isGliding() ||
|
||||
blockState.getType() == StateTypes.WATER ||
|
||||
blockState.getType() == StateTypes.LAVA ||
|
||||
blockState.getType() == StateTypes.COBWEB ||
|
||||
blockState.getType() == StateTypes.SCAFFOLDING) {
|
||||
return false;
|
||||
// Step 6: Access the 'onGround' field in PlayerData
|
||||
Field onGroundField = playerData.getClass().getDeclaredField("onGround");
|
||||
onGroundField.setAccessible(true); // Bypass private access
|
||||
|
||||
System.out.println(this.platformPlayer.getName() + " is located at " + platformPlayer.getX() + ", " + platformPlayer.getY() + ", " + platformPlayer.getZ() + " serverside");
|
||||
System.out.println("Grim onGround for player " + this.platformPlayer.getName() + " is " + onGroundField.get(playerData));
|
||||
|
||||
// Step 7: Get the value of the 'onGround' field
|
||||
return (boolean) onGroundField.get(playerData);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (ping == null || ping < PING_OFFSET)
|
||||
return false;
|
||||
|
||||
double gDist = getDistanceToGround();
|
||||
if (gDist <= 0)
|
||||
return false; // prevent player from taking adjusted knockback when on ground serverside
|
||||
|
||||
int tMax = verticalVelocity > 0 ? MathUtil.calculateTimeToMaxVelocity(verticalVelocity, gravityAttribute) : 0;
|
||||
double mH = verticalVelocity > 0 ? MathUtil.calculateDistanceTraveled(verticalVelocity, tMax, gravityAttribute) : 0;
|
||||
int tFall = MathUtil.calculateFallTime(verticalVelocity, mH + gDist, gravityAttribute);
|
||||
if (tMax == -1)
|
||||
return false; // reached the max tick limit, not safe to predict
|
||||
|
||||
if (tFall == -1)
|
||||
return false; // reached the max tick limit, not safe to predict
|
||||
double mH = verticalVelocity > 0 ? MathUtil.calculateDistanceTraveled(verticalVelocity, tMax, gravityAttribute) : 0;
|
||||
int tFall = MathUtil.calculateFallTime(verticalVelocity, mH + distanceToGround, gravityAttribute);
|
||||
|
||||
return getEstimatedPing() >= tMax + tFall / TICK_RATE * 1000 && gDist <= 1.3;
|
||||
if (tFall == -1)
|
||||
return false; // reached the max tick limit, not safe to predict
|
||||
|
||||
return (tMax + tFall) - getCompensatedTicks() <= 0 && distanceToGround <= 1.3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not offGroundSynchronization is enabled for the player.
|
||||
* @return <code>true</code> if enabled; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isOffGroundSyncEnabled() {
|
||||
// TODO, per-player offground sync toggles?
|
||||
return ToggleOffGroundSubcommand.offGroundSyncEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compensated off-ground velocity.
|
||||
* This is used to make knockback feel more accurate off-ground.
|
||||
*
|
||||
* @return Compensated Y axis velocity
|
||||
*/
|
||||
public double getCompensatedOffGroundVelocity() {
|
||||
return MathUtil.getCompensatedVerticalVelocity(platformPlayer.getVelocity().getY(), getGravityAttribute(), getTicks());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,10 +278,10 @@ public class PlayerData {
|
||||
for (Vector3d corner : getBBCorners()) {
|
||||
RayTraceResult result = world.rayTraceBlocks(corner, new Vector3d(0, -1, 0), 5, FluidHandling.NONE, true);
|
||||
|
||||
if (result == null || result.getHitPosition() == null)
|
||||
if (result == null || result.getHitBlock() == null)
|
||||
continue;
|
||||
|
||||
collisionDist = Math.min(collisionDist, corner.getY() - result.getHitPosition().getY());
|
||||
collisionDist = Math.min(collisionDist, corner.getY() - result.getHitBlockPosition().getY());
|
||||
}
|
||||
|
||||
return collisionDist - 1;
|
||||
@@ -233,16 +293,16 @@ public class PlayerData {
|
||||
* @return An array of locations representing the corners of the bounding box.
|
||||
*/
|
||||
private Vector3d[] getBBCorners() {
|
||||
Vector3d playerPos = platformPlayer.getLocation();
|
||||
double width = 0.6; // typical player width
|
||||
// double height = 1.8; // typical player height
|
||||
double adjustment = 0.01;
|
||||
BoundingBox boundingBox = platformPlayer.getBoundingBox();
|
||||
Vector3d location = platformPlayer.getLocation();
|
||||
|
||||
return new Vector3d[]{
|
||||
new Vector3d(playerPos.getX() - width / 2 + adjustment, playerPos.getY(), playerPos.getZ() - width / 2 + adjustment),
|
||||
new Vector3d(playerPos.getX() - width / 2 + adjustment, playerPos.getY(), playerPos.getZ() + width / 2 - adjustment),
|
||||
new Vector3d(playerPos.getX() + width / 2 - adjustment, playerPos.getY(), playerPos.getZ() - width / 2 + adjustment),
|
||||
new Vector3d(playerPos.getX() + width / 2 - adjustment, playerPos.getY(), playerPos.getZ() + width / 2 - adjustment)
|
||||
double adjustment = 0.01; // To ensure the bounding box isn't clipping inside a wall
|
||||
|
||||
return new Vector3d[] {
|
||||
new Vector3d(boundingBox.getMinX() + adjustment, location.getY(), boundingBox.getMinZ() + adjustment),
|
||||
new Vector3d(boundingBox.getMinX() + adjustment, location.getY(), boundingBox.getMaxZ() - adjustment),
|
||||
new Vector3d(boundingBox.getMaxX() - adjustment, location.getY(), boundingBox.getMinZ() + adjustment),
|
||||
new Vector3d(boundingBox.getMaxX() - adjustment, location.getY(), boundingBox.getMaxZ() - adjustment)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -258,7 +318,6 @@ public class PlayerData {
|
||||
|
||||
if (!attacker.isSprinting()) {
|
||||
yAxis = 0.36080000519752503;
|
||||
// double knockbackResistance = knockbackResistanceAttribute;
|
||||
double resistanceFactor = 0.04000000119 * knockbackResistanceAttribute * 10;
|
||||
yAxis -= resistanceFactor;
|
||||
}
|
||||
@@ -270,19 +329,6 @@ public class PlayerData {
|
||||
return yAxis;
|
||||
}
|
||||
|
||||
// might need soon
|
||||
// public double calculateJumpVelocity() {
|
||||
// double jumpVelocity = 0.42;
|
||||
//
|
||||
// PotionEffect jumpEffect = player.getPotionEffect(PotionEffectType.JUMP);
|
||||
// if (jumpEffect != null) {
|
||||
// int amplifier = jumpEffect.getAmplifier();
|
||||
// jumpVelocity += (amplifier + 1) * 0.1F;
|
||||
// }
|
||||
//
|
||||
// return jumpVelocity;
|
||||
// }
|
||||
|
||||
public boolean isInCombat() {
|
||||
return combatTask != null;
|
||||
}
|
||||
@@ -295,18 +341,16 @@ public class PlayerData {
|
||||
CombatManager.addPlayer(uuid);
|
||||
}
|
||||
|
||||
public void quitCombat(boolean cancelTask) {
|
||||
if (cancelTask)
|
||||
combatTask.cancel();
|
||||
|
||||
public void quitCombat() {
|
||||
combatTask.cancel(); // should do nothing
|
||||
combatTask = null;
|
||||
CombatManager.removePlayer(uuid);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private AbstractTaskHandle newCombatTask() {
|
||||
return KnockbackSyncBase.INSTANCE.getScheduler().runTaskLaterAsynchronously(
|
||||
() -> quitCombat(false), KnockbackSyncBase.INSTANCE.getConfigManager().getCombatTimer());
|
||||
return Base.INSTANCE.getScheduler().runTaskLaterAsynchronously(
|
||||
this::quitCombat, Base.INSTANCE.getConfigManager().getCombatTimer());
|
||||
}
|
||||
|
||||
public ClientVersion getClientVersion() {
|
||||
@@ -327,4 +371,12 @@ public class PlayerData {
|
||||
public boolean didWeSendThatPacket(long receivedId) {
|
||||
return receivedId == NETTY_THREAD_TRANSACTION_ID || receivedId == MAIN_THREAD_TRANSACTION_ID;
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public void onToggledEvent(ToggleOnOffEvent event) {
|
||||
if (event.getStatus() == false) {
|
||||
transactionsSent.clear();
|
||||
keepaliveMap.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.runnable;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.manager.CombatManager;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
@@ -11,7 +11,7 @@ public class PingRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!KnockbackSyncBase.INSTANCE.getConfigManager().isToggled())
|
||||
if (!Base.INSTANCE.getConfigManager().isToggled())
|
||||
return;
|
||||
|
||||
for (UUID uuid : CombatManager.getPlayers()) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.UUID;
|
||||
* @param <T> the command sender type
|
||||
*/
|
||||
public final class AbstractSender<T> implements Sender {
|
||||
private final KnockbackSyncBase plugin;
|
||||
private final Base plugin;
|
||||
private final SenderFactory<?, T> factory;
|
||||
private final T sender;
|
||||
|
||||
@@ -18,7 +18,7 @@ public final class AbstractSender<T> implements Sender {
|
||||
private final String name;
|
||||
private final boolean isConsole;
|
||||
|
||||
AbstractSender(KnockbackSyncBase plugin, SenderFactory<?, T> factory, T sender) {
|
||||
AbstractSender(Base plugin, SenderFactory<?, T> factory, T sender) {
|
||||
this.plugin = plugin;
|
||||
this.factory = factory;
|
||||
this.sender = sender;
|
||||
@@ -28,7 +28,7 @@ public final class AbstractSender<T> implements Sender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KnockbackSyncBase getPlugin() {
|
||||
public Base getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -23,7 +23,7 @@ public interface Sender {
|
||||
*
|
||||
* @return the plugin
|
||||
*/
|
||||
KnockbackSyncBase getPlugin();
|
||||
Base getPlugin();
|
||||
|
||||
/**
|
||||
* Gets the sender's username
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
@@ -11,7 +11,7 @@ import java.util.UUID;
|
||||
* @param <P> the plugin type
|
||||
* @param <T> the command sender type
|
||||
*/
|
||||
public abstract class SenderFactory<P extends KnockbackSyncBase, T> implements AutoCloseable {
|
||||
public abstract class SenderFactory<P extends Base, T> implements AutoCloseable {
|
||||
private final P plugin;
|
||||
|
||||
public SenderFactory(P plugin) {
|
||||
|
||||
@@ -2,7 +2,8 @@ package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.common.BuildConfig;
|
||||
import me.caseload.knockbacksync.stats.SimplePie;
|
||||
import org.kohsuke.github.GHAsset;
|
||||
import org.kohsuke.github.GHRelease;
|
||||
@@ -18,8 +19,7 @@ public class BuildTypePie extends SimplePie {
|
||||
|
||||
private static final String RELEASES_FILE = "releases.txt";
|
||||
private static final String DEV_BUILDS_FILE = "dev-builds.txt";
|
||||
private static final File dataFolder = KnockbackSyncBase.INSTANCE.getDataFolder();
|
||||
public static URL jarUrl;
|
||||
private static final File dataFolder = Base.INSTANCE.getDataFolder();
|
||||
private static String cachedBuildType = null;
|
||||
|
||||
public BuildTypePie() {
|
||||
@@ -35,7 +35,7 @@ public class BuildTypePie extends SimplePie {
|
||||
|
||||
private static String calculateBuildType() {
|
||||
try {
|
||||
String currentHash = KnockbackSyncBase.INSTANCE.getPluginJarHashProvider().getPluginJarHash();
|
||||
String currentHash = Base.INSTANCE.getPluginJarHashProvider().getPluginJarHash();
|
||||
downloadBuildFiles();
|
||||
|
||||
if (isHashInFile(currentHash, new File(dataFolder, RELEASES_FILE))) {
|
||||
@@ -53,12 +53,12 @@ public class BuildTypePie extends SimplePie {
|
||||
|
||||
private static void downloadBuildFiles() throws IOException {
|
||||
GitHub gitHub = GitHub.connectAnonymously();
|
||||
GHRelease latestRelease = gitHub.getRepository("CASELOAD7000/knockback-sync")
|
||||
GHRelease latestRelease = gitHub.getRepository(BuildConfig.GITHUB_REPO)
|
||||
.getLatestRelease();
|
||||
List<GHAsset> assets = latestRelease.listAssets().toList();
|
||||
for (GHAsset asset : assets) {
|
||||
if (asset.getName().equals(RELEASES_FILE) || asset.getName().equals(DEV_BUILDS_FILE)) {
|
||||
KnockbackSyncBase.INSTANCE.getLogger().info("Downloading: " + asset.getName());
|
||||
Base.INSTANCE.getLogger().info("Downloading: " + asset.getName());
|
||||
|
||||
String jsonContent = readStringFromURL(asset.getUrl().toString());
|
||||
JsonObject jsonObject = JsonParser.parseString(jsonContent).getAsJsonObject();
|
||||
@@ -72,7 +72,7 @@ public class BuildTypePie extends SimplePie {
|
||||
}
|
||||
}
|
||||
|
||||
KnockbackSyncBase.INSTANCE.getLogger().info("Downloaded: " + asset.getName());
|
||||
Base.INSTANCE.getLogger().info("Downloaded: " + asset.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
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.stats.AdvancedPie;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClientBrandsPie extends AdvancedPie {
|
||||
|
||||
// Gets the client versions of players online
|
||||
public ClientBrandsPie() {
|
||||
super("client_brands", () -> {
|
||||
Map<String, Integer> valueMap = new HashMap<>();
|
||||
for (PlatformPlayer player : Base.INSTANCE.getPlatformServer().getOnlinePlayers()) {
|
||||
User user = player.getUser();
|
||||
if (user == null || user.getClientVersion() == null) {
|
||||
valueMap.put("vanilla", valueMap.getOrDefault("vanilla", 0) + 1);
|
||||
} else {
|
||||
valueMap.put(player.getClientBrand(), valueMap.getOrDefault(player.getClientBrand(), 0) + 1);
|
||||
}
|
||||
}
|
||||
return valueMap;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import me.caseload.knockbacksync.stats.CustomChart;
|
||||
|
||||
public interface Metrics {
|
||||
|
||||
void addCustomChart(CustomChart chart);
|
||||
void shutdown();
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
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;
|
||||
@@ -15,9 +17,13 @@ public class PlayerVersionsPie extends AdvancedPie {
|
||||
public PlayerVersionsPie() {
|
||||
super("player_version", () -> {
|
||||
Map<String, Integer> valueMap = new HashMap<>();
|
||||
for (PlatformPlayer player : KnockbackSyncBase.INSTANCE.platformServer.getOnlinePlayers()) {
|
||||
PlayerData playerData = PlayerDataManager.getPlayerData(player.getUUID());
|
||||
valueMap.put(playerData.getClientVersion().toString(), valueMap.getOrDefault(playerData.getClientVersion().toString(), 0) + 1);
|
||||
for (PlatformPlayer player : Base.INSTANCE.getPlatformServer().getOnlinePlayers()) {
|
||||
User user = player.getUser();
|
||||
if (user == null || user.getClientVersion() == null) {
|
||||
valueMap.put(ClientVersion.UNKNOWN.toString(), valueMap.getOrDefault(ClientVersion.UNKNOWN.toString(), 0) + 1);
|
||||
} else {
|
||||
valueMap.put(user.getClientVersion().toString(), valueMap.getOrDefault(user.getClientVersion().toString(), 0) + 1);
|
||||
}
|
||||
}
|
||||
return valueMap;
|
||||
});
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
@Getter
|
||||
public abstract class StatsManager {
|
||||
|
||||
Metrics metrics;
|
||||
|
||||
public StatsManager(Metrics metrics) {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
throw new IllegalStateException("Empty stats class!");
|
||||
Base.INSTANCE.getScheduler().runTaskAsynchronously(() -> {
|
||||
BuildTypePie.determineBuildType(); // Function to calculate hash
|
||||
metrics.addCustomChart(new PlayerVersionsPie());
|
||||
metrics.addCustomChart(new BuildTypePie());
|
||||
metrics.addCustomChart(new ClientBrandsPie());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
package me.caseload.knockbacksync.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.caseload.knockbacksync.manager.PlayerDataManager;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChatUtil {
|
||||
|
||||
public static @NotNull String translateAlternateColorCodes(char altColorChar, @NotNull String textToTranslate) {
|
||||
Preconditions.checkArgument(textToTranslate != null, "Cannot translate null text");
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
@@ -21,36 +18,4 @@ public class ChatUtil {
|
||||
|
||||
return new String(b);
|
||||
}
|
||||
|
||||
public static String getPingMessage(UUID senderUUID, UUID targetUUID) {
|
||||
|
||||
String noun = "";
|
||||
PlayerData playerData = null;
|
||||
if (senderUUID == null) {
|
||||
senderUUID = targetUUID;
|
||||
}
|
||||
|
||||
if (senderUUID.equals(targetUUID) || targetUUID == null) {
|
||||
playerData = PlayerDataManager.getPlayerData(senderUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', "&cKnockback synchronization is currently disabled for you!");
|
||||
}
|
||||
noun = "Your";
|
||||
} else {
|
||||
playerData = PlayerDataManager.getPlayerData(targetUUID);
|
||||
if (playerData == null) {
|
||||
return ChatUtil.translateAlternateColorCodes('&', "&cKnockback synchronization is currently disabled for the target player.");
|
||||
}
|
||||
noun = playerData.getPlatformPlayer().getName() + "'s";
|
||||
}
|
||||
|
||||
|
||||
String rawReturnString = null;
|
||||
if (playerData.getPing() == null) {
|
||||
rawReturnString = String.format("Pong not received. %s estimated ping is &b" + playerData.getPlatformPlayer().getPing() + "&rms.", noun);
|
||||
} else {
|
||||
rawReturnString = String.format("%s last ping packet took &b %.3f&rms. Jitter: &b%.3f&rms.", noun, playerData.getPing(), playerData.getJitter());
|
||||
}
|
||||
return ChatUtil.translateAlternateColorCodes('&', rawReturnString);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package me.caseload.knockbacksync.util;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.util.reflection.Reflection;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -19,7 +19,7 @@ public class GeyserUtil {
|
||||
public static boolean isGeyserPlayer(UUID uuid) {
|
||||
if (!CHECKED_FOR_GEYSER) {
|
||||
try {
|
||||
switch (KnockbackSyncBase.INSTANCE.platform) {
|
||||
switch (Base.INSTANCE.getPlatform()) {
|
||||
case BUKKIT:
|
||||
case FOLIA:
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
@@ -40,7 +40,7 @@ public class GeyserUtil {
|
||||
if (GEYSER_PRESENT) {
|
||||
if (GEYSER_API_CLASS == null) {
|
||||
try {
|
||||
switch (KnockbackSyncBase.INSTANCE.platform) {
|
||||
switch (Base.INSTANCE.getPlatform()) {
|
||||
case BUKKIT:
|
||||
case FOLIA:
|
||||
ClassLoader classLoader = PacketEvents.getAPI().getPlugin().getClass().getClassLoader();
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
package me.caseload.knockbacksync.util;
|
||||
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
public class MathUtil {
|
||||
|
||||
private static final double TERMINAL_VELOCITY = 3.92;
|
||||
private static final double MULTIPLIER = 0.98;
|
||||
private static final int MAX_TICKS = 20;
|
||||
private static final int MAX_TICKS = 30;
|
||||
|
||||
public static double getCompensatedVerticalVelocity(double velocity, double acceleration, int ticks) {
|
||||
while (ticks > 0) {
|
||||
velocity -= acceleration;
|
||||
velocity *= MULTIPLIER;
|
||||
ticks--;
|
||||
}
|
||||
|
||||
return velocity;
|
||||
}
|
||||
|
||||
public static double calculateDistanceTraveled(double velocity, int time, double acceleration) {
|
||||
double totalDistance = 0;
|
||||
@@ -36,17 +50,20 @@ public class MathUtil {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public static int calculateTimeToMaxVelocity(double targetVerticalVelocity, double acceleration) {
|
||||
double a = -acceleration * MULTIPLIER;
|
||||
double b = acceleration + TERMINAL_VELOCITY * MULTIPLIER;
|
||||
double c = -2 * targetVerticalVelocity;
|
||||
public static int calculateTimeToMaxVelocity(double velocity, double acceleration) {
|
||||
int ticks = 0;
|
||||
|
||||
double discriminant = b * b - 4 * a * c;
|
||||
if (discriminant < 0)
|
||||
return 0;
|
||||
while (velocity > 0) {
|
||||
if (ticks > MAX_TICKS)
|
||||
return -1;
|
||||
|
||||
double positiveRoot = (-b + Math.sqrt(discriminant)) / (2 * a);
|
||||
return (int) Math.ceil(positiveRoot * 20);
|
||||
velocity -= acceleration;
|
||||
velocity = Math.min(velocity, TERMINAL_VELOCITY);
|
||||
velocity *= MULTIPLIER;
|
||||
ticks++;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public static double clamp(double num, double min, double max) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#########################################
|
||||
|
||||
# Do not modify!
|
||||
config-version: 2
|
||||
config_version: 6
|
||||
|
||||
# Plugin enabled state
|
||||
# Toggleable using /knockbacksync toggle
|
||||
@@ -18,7 +18,7 @@ notify_updates: true
|
||||
# Disabling this may lead to inaccuracies in calculations
|
||||
runnable:
|
||||
enabled: true # Runnable enabled state
|
||||
interval: 20 # The interval in ticks between sending out pings to players
|
||||
interval: 5 # The interval in ticks between sending out pings to players
|
||||
combat_timer: 30 # The timer in ticks before being considered out of combat
|
||||
|
||||
# The minimum change in ping required for it to be considered a lag spike.
|
||||
@@ -38,17 +38,198 @@ spike_threshold: 20
|
||||
# ping measured in ms with PING/TRANSACTION is typically within ± 5ms of actual but can be up to 50ms off
|
||||
ping_strategy: "KEEPALIVE"
|
||||
|
||||
# This setting has yet to be implemented. Enable if you want it to be enabled as soon as the next update add support
|
||||
enable_experimental_offground: false
|
||||
# Whether to try to detect when player is offground and apply less kb
|
||||
enable_offground_synchronization: true
|
||||
|
||||
enable_message: "&aSuccessfully enabled KnockbackSync."
|
||||
disable_message: "&cSuccessfully disabled KnockbackSync."
|
||||
player_enable_message: "&aSuccessfully enabled KnockbackSync for %player%."
|
||||
player_disable_message: "&aSuccessfully &cdisabled &aKnockbackSync for %player%."
|
||||
player_ineligible_message: "&c%player% is ineligible for KnockbackSync. If you believe this is an error, please open an issue on the github page."
|
||||
reload_message: "&aSuccessfully reloaded KnockbackSync config."
|
||||
global_status_message: "&eGlobal KnockbackSync status: "
|
||||
player_status_message: "&e%player%'s KnockbackSync status: "
|
||||
player_disabled_global_message: "&cDisabled (Global toggle is off)"
|
||||
enable_experimental_offground_message: "&aSuccessfully enabled offground experiment."
|
||||
disable_experimental_offground_message: "&cSuccessfully disabled offground experiment."
|
||||
# This is the rate the server will use for (amongst other things) sending packets about entities to players
|
||||
# In other words it defines how often clients get updated velocity for other entities in the world
|
||||
# Note that certain events mark velocity as dirty and causes it to be immediately updated for 1 tick regardless of the tick interval
|
||||
# These include but are not limited to jumping, taking knockback, swimming, and a target entity being affected by a special mob attack
|
||||
# Such as the Ender Dragon's knockback attacks
|
||||
# If an entity below doesn't exist in the version of the game your server is running in, changing the config for it will do nothing
|
||||
# Currently only modifying the player's tick interval is supported on Bukkit/Spigot/Paper + Folia
|
||||
# Modifying the tick interval of any entity is supported on Fabric.
|
||||
entity_tick_intervals:
|
||||
player: 2 # This is 2 by default in vanilla and paper, change to 1 to test out the new feature
|
||||
allay: 2
|
||||
area_effect_cloud: Integer.MAX_VALUE
|
||||
armadillo: 3
|
||||
armor_stand: 3
|
||||
arrow: 20
|
||||
axolotl: 3
|
||||
bat: 3
|
||||
bee: 3
|
||||
blaze: 3
|
||||
block_display: 1
|
||||
boat: 3
|
||||
bogged: 3
|
||||
breeze: 3
|
||||
breeze_wind_charge: 10
|
||||
camel: 3
|
||||
cat: 3
|
||||
cave_spider: 3
|
||||
chest_boat: 3
|
||||
chest_minecart: 3
|
||||
chicken: 3
|
||||
cod: 3
|
||||
command_block_minecart: 3
|
||||
cow: 3
|
||||
creeper: 3
|
||||
dolphin: 3
|
||||
donkey: 3
|
||||
dragon_fireball: 10
|
||||
drowned: 3
|
||||
egg: 10
|
||||
elder_guardian: 3
|
||||
end_crystal: Integer.MAX_VALUE
|
||||
ender_dragon: 3
|
||||
ender_pearl: 10
|
||||
enderman: 3
|
||||
endermite: 3
|
||||
evoker: 3
|
||||
evoker_fangs: 2
|
||||
experience_bottle: 10
|
||||
experience_orb: 20
|
||||
eye_of_ender: 4
|
||||
falling_block: 20
|
||||
firework_rocket: 10
|
||||
fox: 3
|
||||
frog: 3
|
||||
furnace_minecart: 3
|
||||
ghast: 3
|
||||
giant: 3
|
||||
glow_item_frame: Integer.MAX_VALUE
|
||||
glow_squid: 3
|
||||
goat: 3
|
||||
guardian: 3
|
||||
hoglin: 3
|
||||
hopper_minecart: 3
|
||||
horse: 3
|
||||
husk: 3
|
||||
illusioner: 3
|
||||
interaction: 3
|
||||
iron_golem: 3
|
||||
item: 20
|
||||
item_display: 1
|
||||
item_frame: Integer.MAX_VALUE
|
||||
ominous_item_spawner: 3
|
||||
fireball: 10
|
||||
leash_knot: Integer.MAX_VALUE
|
||||
lightning_bolt: Integer.MAX_VALUE
|
||||
llama: 3
|
||||
llama_spit: 10
|
||||
magma_cube: 3
|
||||
marker: 3
|
||||
minecart: 3
|
||||
mooshroom: 3
|
||||
mule: 3
|
||||
ocelot: 3
|
||||
painting: Integer.MAX_VALUE
|
||||
panda: 3
|
||||
parrot: 3
|
||||
phantom: 3
|
||||
pig: 3
|
||||
piglin: 3
|
||||
piglin_brute: 3
|
||||
pillager: 3
|
||||
polar_bear: 3
|
||||
potion: 10
|
||||
pufferfish: 3
|
||||
rabbit: 3
|
||||
ravager: 3
|
||||
salmon: 3
|
||||
sheep: 3
|
||||
shulker: 3
|
||||
shulker_bullet: 3
|
||||
silverfish: 3
|
||||
skeleton: 3
|
||||
skeleton_horse: 3
|
||||
slime: 3
|
||||
small_fireball: 10
|
||||
sniffer: 3
|
||||
snow_golem: 3
|
||||
snowball: 10
|
||||
spawner_minecart: 3
|
||||
spectral_arrow: 20
|
||||
spider: 3
|
||||
squid: 3
|
||||
stray: 3
|
||||
strider: 3
|
||||
tadpole: 3
|
||||
text_display: 1
|
||||
tnt: 10
|
||||
tnt_minecart: 3
|
||||
trader_llama: 3
|
||||
trident: 20
|
||||
tropical_fish: 3
|
||||
turtle: 3
|
||||
vex: 3
|
||||
villager: 3
|
||||
vindicator: 3
|
||||
wandering_trader: 3
|
||||
warden: 3
|
||||
wind_charge: 10
|
||||
witch: 3
|
||||
wither: 3
|
||||
wither_skeleton: 3
|
||||
wither_skull: 10
|
||||
wolf: 3
|
||||
zoglin: 3
|
||||
zombie: 3
|
||||
zombie_horse: 3
|
||||
zombie_villager: 3
|
||||
zombified_piglin: 3
|
||||
fishing_bobber: 5
|
||||
|
||||
messages:
|
||||
toggle:
|
||||
global:
|
||||
enable: "&aSuccessfully enabled KnockbackSync."
|
||||
disable: "&cSuccessfully disabled KnockbackSync."
|
||||
player:
|
||||
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."
|
||||
permission:
|
||||
no_global: "&cYou don't have permission to toggle the global setting."
|
||||
no_self: "&cYou do not have permission to toggle your knockback."
|
||||
no_other: "&cYou do not have permission to toggle the knockback of other player's."
|
||||
self:
|
||||
enable: "&aSuccessfully enabled KnockbackSync."
|
||||
disable: "&cSuccessfully disabled KnockbackSync."
|
||||
other:
|
||||
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."
|
||||
server_disabled: "&cKnockbacksync is currently disabled on this server. Contact your server administrator for more information."
|
||||
offground:
|
||||
enable: "&aSuccessfully enabled offground synchronization."
|
||||
disable: "&cSuccessfully disabled offground synchronization."
|
||||
reload:
|
||||
success: "&aSuccessfully reloaded KnockbackSync."
|
||||
status:
|
||||
global:
|
||||
enabled: "&eKnockbackSync global status: &aEnabled"
|
||||
disabled: "&eKnockbackSync global status: &cDisabled"
|
||||
offground:
|
||||
enabled: "&eKnockbackSync off-ground status: &aEnabled"
|
||||
disabled: "&eKnockbackSync off-ground status: &cDisabled"
|
||||
player:
|
||||
enabled: "&e%player%'s KnockbackSync status: &aEnabled"
|
||||
disabled: "&e%player%'s KnockbackSync status: &cDisabled"
|
||||
global_disabled: "&e%player%'s KnockbackSync status: &cDisabled (Global toggle is off)"
|
||||
permission:
|
||||
no_self: "&cYou do not have permission to check your knockbacksync status."
|
||||
no_other: "&cYou do not have permission to check the status of other players!"
|
||||
console:
|
||||
must_specify_player: "&cYou must specify a player to use the knockbacksync ping command from the console."
|
||||
disabled:
|
||||
self: "&cKnockback synchronization is currently disabled for you!"
|
||||
target: "&cKnockback synchronization is currently disabled for the target player."
|
||||
ping:
|
||||
self:
|
||||
unavailable: "Accurate ping unavailable. Your estimated ping is &b%ping%&rms."
|
||||
available: "Your real ping is &b%ping%&rms. Jitter: &b%jitter%&rms. Spike: &b%spike%&r. Compensated ping: &b%compensated%&rms."
|
||||
other:
|
||||
unavailable: "Accurate ping unavailable. %player%'s estimated ping is &b%ping%&rms."
|
||||
available: "%player%'s real ping is &b%ping%&rms. Jitter: &b%jitter%&rms. Spike: &b%spike%&r. Compensated ping: &b%compensated%&rms."
|
||||
+14
-4
@@ -3,6 +3,10 @@ plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = file("src/main/resources/knockbacksync.accesswidener")
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("${rootProject.property("archives_base_name")}-fabric")
|
||||
}
|
||||
@@ -38,12 +42,18 @@ dependencies {
|
||||
modImplementation("net.fabricmc.fabric-api:fabric-api:${rootProject.property("fabric_version")}")
|
||||
|
||||
include(modImplementation("me.lucko:fabric-permissions-api:0.3.1")!!)
|
||||
include(modImplementation("com.github.retrooper:packetevents-fabric:2.5.8-SNAPSHOT")!!)
|
||||
include(modImplementation("com.github.retrooper:packetevents-fabric:2.6.0-SNAPSHOT")!!)
|
||||
include(modImplementation("org.incendo:cloud-fabric:2.0.0-beta.9")!!)
|
||||
|
||||
shadeThisThing(implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")!!)
|
||||
shadeThisThing(implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2")!!)
|
||||
shadeThisThing(implementation("org.kohsuke:github-api:1.326")!!)
|
||||
include(implementation("org.yaml:snakeyaml:2.0")!!)
|
||||
include(implementation("org.kohsuke:github-api:1.326")!!)
|
||||
// Required for org.kohsuke.github
|
||||
include(implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")!!)
|
||||
include(implementation("com.fasterxml.jackson.core:jackson-annotations:2.17.2")!!)
|
||||
include(implementation("com.fasterxml.jackson.core:jackson-core:2.17.2")!!)
|
||||
// Not requires in modern Minecraft. May be needed if fabric version is backported to older versions
|
||||
// include(implementation("org.apache.commons:commons-lang3:3.17.0")!!)
|
||||
// include(implementation("commons-io:commons-io:2.16.1")!!)
|
||||
|
||||
compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT")
|
||||
compileOnly("org.projectlombok:lombok:1.18.34")
|
||||
|
||||
+23
-5
@@ -1,8 +1,9 @@
|
||||
package me.caseload.knockbacksync;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsBuilder;
|
||||
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;
|
||||
@@ -16,6 +17,7 @@ import me.caseload.knockbacksync.sender.FabricSenderFactory;
|
||||
import me.caseload.knockbacksync.stats.custom.FabricStatsManager;
|
||||
import me.caseload.knockbacksync.stats.custom.PluginJarHashProvider;
|
||||
import me.caseload.knockbacksync.world.FabricServer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
@@ -30,14 +32,15 @@ import java.nio.file.Files;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class KBSyncFabricBase extends KnockbackSyncBase {
|
||||
public class FabricBase extends Base {
|
||||
|
||||
private final Logger logger = Logger.getLogger(KBSyncFabricLoaderMod.class.getName());
|
||||
private final Logger logger = Logger.getLogger(FabricLoaderMod.class.getName());
|
||||
private final FabricPermissionChecker permissionChecker = new FabricPermissionChecker();
|
||||
@Getter
|
||||
private final FabricSenderFactory fabricSenderFactory = new FabricSenderFactory(this);
|
||||
private float tickRate = 20.0F;
|
||||
|
||||
public KBSyncFabricBase() {
|
||||
public FabricBase() {
|
||||
super.configManager = new ConfigManager();
|
||||
super.playerSelectorParser = new FabricPlayerSelectorParser<>();
|
||||
super.commandManager = new FabricServerCommandManager<>(
|
||||
@@ -66,10 +69,15 @@ public class KBSyncFabricBase extends KnockbackSyncBase {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
PacketEvents.setAPI(FabricPacketEventsBuilder.build("knockbacksync"));
|
||||
PacketEvents.setAPI(new FabricPacketEventsAPI("knockbacksync", EnvType.SERVER));
|
||||
PacketEvents.getAPI().load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
super.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeScheduler() {
|
||||
scheduler = new FabricSchedulerAdapter();
|
||||
@@ -81,6 +89,7 @@ public class KBSyncFabricBase extends KnockbackSyncBase {
|
||||
new FabricPlayerDamageListener().register();
|
||||
new FabricPlayerKnockbackListener().register();
|
||||
new FabricTickRateChangeListener().register();
|
||||
super.eventBus.registerStaticListeners(EntityTickManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,6 +118,15 @@ public class KBSyncFabricBase extends KnockbackSyncBase {
|
||||
return permissionChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTickRate() {
|
||||
return this.tickRate;
|
||||
}
|
||||
|
||||
public void setTickRate(float tickRate) {
|
||||
this.tickRate = tickRate;
|
||||
}
|
||||
|
||||
private URL getJarURL() {
|
||||
URL jarUrl = null;
|
||||
Optional<ModContainer> modContainer = FabricLoader.getInstance().getModContainer("knockbacksync");
|
||||
+2
-6
@@ -6,9 +6,9 @@ import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
public class KBSyncFabricLoaderMod implements PreLaunchEntrypoint, ModInitializer {
|
||||
public class FabricLoaderMod implements PreLaunchEntrypoint, ModInitializer {
|
||||
|
||||
private final KnockbackSyncBase core = new KBSyncFabricBase();
|
||||
private final Base core = new FabricBase();
|
||||
|
||||
public static MinecraftServer getServer() {
|
||||
return (MinecraftServer) FabricLoader.getInstance().getGameInstance();
|
||||
@@ -22,10 +22,6 @@ public class KBSyncFabricLoaderMod implements PreLaunchEntrypoint, ModInitialize
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
core.enable();
|
||||
core.initializeScheduler();
|
||||
core.configManager.loadConfig(false);
|
||||
core.statsManager.init();
|
||||
core.checkForUpdates();
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
|
||||
core.scheduler.shutdown();
|
||||
core.statsManager.getMetrics().shutdown();
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.caseload.knockbacksync.entity;
|
||||
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.ConfigWrapper;
|
||||
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
|
||||
import me.caseload.knockbacksync.event.KBSyncEventHandler;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class EntityTickManager {
|
||||
private static final Map<EntityType<?>, Integer> customTickIntervals = new HashMap<>();
|
||||
|
||||
static {
|
||||
updateTickIntervals(Base.INSTANCE.getConfigManager().getConfigWrapper());
|
||||
}
|
||||
|
||||
@KBSyncEventHandler
|
||||
public static void updateTickIntervals(ConfigReloadEvent event) {
|
||||
ConfigWrapper configWrapper = event.getConfigManager().getConfigWrapper();
|
||||
updateTickIntervals(configWrapper);
|
||||
|
||||
// Map<String, Integer> entityIntervals = CustomUpdateIntervals.loadEntityIntervalsFromConfig();
|
||||
// for (ServerLevel level : FabricLoaderMod.getServer().getAllLevels()) {
|
||||
// CustomUpdateIntervals.updateIntervals(level, entityIntervals);
|
||||
// }
|
||||
}
|
||||
|
||||
private static void updateTickIntervals(ConfigWrapper configWrapper) {
|
||||
customTickIntervals.clear();
|
||||
for (String entityKey : configWrapper.getKeys("entity_tick_intervals")) {
|
||||
try {
|
||||
Optional<EntityType<?>> entityType = EntityType.byString(entityKey.toLowerCase());
|
||||
if (entityType.isPresent()) {
|
||||
int interval = configWrapper.getInt("entity_tick_intervals." + entityKey, entityType.get().updateInterval());
|
||||
customTickIntervals.put(entityType.get(), interval);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Invalid entity type in config: " + entityKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getCustomUpdateInterval(EntityType<?> entityType) {
|
||||
return customTickIntervals.getOrDefault(entityType, entityType.updateInterval());
|
||||
}
|
||||
}
|
||||
-12
@@ -17,18 +17,6 @@ public class FabricPlayerDamageListener extends PlayerDamageListener {
|
||||
onPlayerDamage((ServerPlayer) player, entity);
|
||||
return InteractionResult.PASS;
|
||||
});
|
||||
|
||||
// Sending ping packets on tick if runnable is disabled
|
||||
// ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
// if (!KnockbackSyncBase.INSTANCE.getConfigManager().isRunnableEnabled()) {
|
||||
// for (ServerPlayerEntity player : server.getPlayerList()) {
|
||||
// PlayerData playerData = PlayerDataManager.getPlayerData(player);
|
||||
// if (playerData != null) {
|
||||
// playerData.sendPing();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
private void onPlayerDamage(ServerPlayer attacker, Entity victimEntity) {
|
||||
|
||||
+3
-2
@@ -1,13 +1,14 @@
|
||||
package me.caseload.knockbacksync.listener.fabric;
|
||||
|
||||
import me.caseload.knockbacksync.Base;
|
||||
import me.caseload.knockbacksync.FabricBase;
|
||||
import me.caseload.knockbacksync.callback.TickRateChangeEvent;
|
||||
import me.caseload.knockbacksync.player.PlayerData;
|
||||
|
||||
public class FabricTickRateChangeListener {
|
||||
public void register() {
|
||||
TickRateChangeEvent.EVENT.register((oldTickRate, newTickRate) -> {
|
||||
if (oldTickRate != newTickRate) {
|
||||
PlayerData.TICK_RATE = newTickRate;
|
||||
((FabricBase) Base.INSTANCE).setTickRate(newTickRate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.entity.EntityTickManager;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public abstract class ChunkMapMixin {
|
||||
|
||||
@Redirect(method = "addEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/EntityType;updateInterval()I"))
|
||||
private int getCustomUpdateInterval(EntityType<?> entityType) {
|
||||
return EntityTickManager.getCustomUpdateInterval(entityType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//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);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -16,11 +16,11 @@ 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) {
|
||||
@@ -37,4 +37,23 @@ public class ServerEntityMixin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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,6 +1,7 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
import me.caseload.knockbacksync.callback.TickRateChangeEvent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.TickRateManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//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
|
||||
//// }
|
||||
//}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.caseload.knockbacksync.mixin;
|
||||
|
||||
public interface UpdateIntervalAccessor {
|
||||
void setUpdateInterval(int interval);
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
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 me.caseload.knockbacksync.world.FabricWorld;
|
||||
import me.caseload.knockbacksync.world.PlatformWorld;
|
||||
@@ -8,9 +12,12 @@ 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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -20,6 +27,7 @@ import java.util.UUID;
|
||||
|
||||
public class FabricPlayer implements PlatformPlayer {
|
||||
public final ServerPlayer fabricPlayer;
|
||||
private String clientBrand = "fabric";
|
||||
|
||||
public FabricPlayer(ServerPlayer player) {
|
||||
this.fabricPlayer = player;
|
||||
@@ -82,8 +90,7 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
|
||||
@Override
|
||||
public Vector3d getLocation() {
|
||||
Vec3 pos = fabricPlayer.position();
|
||||
return new Vector3d(pos.x, pos.y, pos.z);
|
||||
return new Vector3d(fabricPlayer.getX(), fabricPlayer.getY(), fabricPlayer.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,5 +132,41 @@ public class FabricPlayer implements PlatformPlayer {
|
||||
fabricPlayer.hurtMarked = true;
|
||||
}
|
||||
|
||||
// Implement other methods
|
||||
@Override
|
||||
public Vector3d getVelocity() {
|
||||
final Vec3 fabricVelocity = fabricPlayer.getDeltaMovement();
|
||||
return new Vector3d(fabricVelocity.x, fabricVelocity.y, fabricVelocity.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getJumpPower() {
|
||||
double jumpVelocity = 0.42;
|
||||
MobEffectInstance jumpEffect = fabricPlayer.getEffect(MobEffects.JUMP);
|
||||
if (jumpEffect != null) {
|
||||
int amplifier = jumpEffect.getAmplifier();
|
||||
jumpVelocity += (amplifier + 1) * 0.1F;
|
||||
}
|
||||
|
||||
return jumpVelocity;
|
||||
}
|
||||
@Override
|
||||
public BoundingBox getBoundingBox() {
|
||||
AABB 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientBrand(String brand) {
|
||||
this.clientBrand = brand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientBrand() {
|
||||
return this.clientBrand;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.scheduler;
|
||||
|
||||
import me.caseload.knockbacksync.KBSyncFabricLoaderMod;
|
||||
import me.caseload.knockbacksync.FabricLoaderMod;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class FabricSchedulerAdapter implements SchedulerAdapter {
|
||||
|
||||
@Override
|
||||
public AbstractTaskHandle runTask(Runnable task) {
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, KBSyncFabricLoaderMod.getServer().getTickCount(), 0, false);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount(), 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, KBSyncFabricLoaderMod.getServer().getTickCount() + delayTicks, 0, false);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount() + 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, KBSyncFabricLoaderMod.getServer().getTickCount() + delayTicks, periodTicks, true);
|
||||
ScheduledTask scheduledTask = new ScheduledTask(task, FabricLoaderMod.getServer().getTickCount() + delayTicks, periodTicks, true);
|
||||
Runnable cancellationTask = () -> taskMap.remove(scheduledTask);
|
||||
taskMap.put(scheduledTask, cancellationTask);
|
||||
return new FabricTaskHandle(cancellationTask);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.caseload.knockbacksync.sender;
|
||||
|
||||
import me.caseload.knockbacksync.KBSyncFabricBase;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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;
|
||||
@@ -13,16 +13,16 @@ import org.incendo.cloud.SenderMapper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FabricSenderFactory extends SenderFactory<KBSyncFabricBase, CommandSourceStack> implements SenderMapper<CommandSourceStack, Sender> {
|
||||
private final KBSyncFabricBase plugin;
|
||||
public class FabricSenderFactory extends SenderFactory<FabricBase, CommandSourceStack> implements SenderMapper<CommandSourceStack, Sender> {
|
||||
private final FabricBase plugin;
|
||||
|
||||
public FabricSenderFactory(KBSyncFabricBase kbSyncFabricBase) {
|
||||
public FabricSenderFactory(FabricBase kbSyncFabricBase) {
|
||||
super(kbSyncFabricBase);
|
||||
this.plugin = kbSyncFabricBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KBSyncFabricBase getPlugin() {
|
||||
protected FabricBase getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ public class FabricSenderFactory extends SenderFactory<KBSyncFabricBase, Command
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSourceStack commandSource, String node) {
|
||||
return ((FabricPermissionChecker) KnockbackSyncBase.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node);
|
||||
return ((FabricPermissionChecker) Base.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSourceStack commandSource, String node, boolean defaultIfUnset) {
|
||||
return ((FabricPermissionChecker) KnockbackSyncBase.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node, defaultIfUnset);
|
||||
return ((FabricPermissionChecker) Base.INSTANCE.getPermissionChecker()).hasPermission(commandSource, node, defaultIfUnset);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+3
-10
@@ -1,16 +1,9 @@
|
||||
package me.caseload.knockbacksync.stats.custom;
|
||||
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
import me.caseload.knockbacksync.Base;
|
||||
|
||||
public class FabricStatsManager extends StatsManager {
|
||||
@Override
|
||||
public void init() {
|
||||
KnockbackSyncBase.INSTANCE.getScheduler().runTaskAsynchronously(() -> {
|
||||
BuildTypePie.determineBuildType(); // Function to calculate hash
|
||||
MetricsFabric metrics = new MetricsFabric(23568);
|
||||
metrics.addCustomChart(new PlayerVersionsPie());
|
||||
metrics.addCustomChart(new BuildTypePie());
|
||||
super.metrics = metrics;
|
||||
});
|
||||
public FabricStatsManager() {
|
||||
super(new MetricsFabric(23568));
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,12 @@ package me.caseload.knockbacksync.stats.custom;
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import me.caseload.knockbacksync.KBSyncFabricLoaderMod;
|
||||
import me.caseload.knockbacksync.KnockbackSyncBase;
|
||||
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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MetricsFabric implements Metrics {
|
||||
@@ -40,34 +35,7 @@ public class MetricsFabric implements Metrics {
|
||||
*/
|
||||
public MetricsFabric(int serviceId) {
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(FabricLoader.getInstance().getConfigDir().toString(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
||||
Config config;
|
||||
|
||||
try {
|
||||
if (!configFile.exists()) {
|
||||
bStatsFolder.mkdirs(); // Ensure the directory exists
|
||||
|
||||
config = new Config();
|
||||
config.serverUuid = UUID.randomUUID().toString();
|
||||
|
||||
// Add header as a comment
|
||||
// mapper.writeValue(configFile,
|
||||
// "# bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
// + "# many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
// + "# enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
// + "# performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
// + "# anonymous.\n");
|
||||
mapper.writeValue(configFile, config); // Write the config object as YAML
|
||||
} else {
|
||||
config = mapper.readValue(configFile, Config.class);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// Handle the exception appropriately (e.g., log an error)
|
||||
config = new Config(); // Fallback to default values
|
||||
config.serverUuid = UUID.randomUUID().toString();
|
||||
}
|
||||
BStatsConfig.Config config = BStatsConfig.loadConfig();
|
||||
|
||||
// Load the data
|
||||
boolean enabled = config.enabled;
|
||||
@@ -91,10 +59,10 @@ public class MetricsFabric implements Metrics {
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> KnockbackSyncBase.INSTANCE.getScheduler().runTask(submitDataTask),
|
||||
submitDataTask -> Base.INSTANCE.getScheduler().runTask(submitDataTask),
|
||||
() -> true,
|
||||
(message, error) -> KnockbackSyncBase.INSTANCE.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> KnockbackSyncBase.INSTANCE.getLogger().log(Level.INFO, message),
|
||||
(message, error) -> Base.INSTANCE.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> Base.INSTANCE.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText,
|
||||
@@ -119,8 +87,8 @@ public class MetricsFabric implements Metrics {
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", KBSyncFabricLoaderMod.getServer().usesAuthentication() ? 0 : 1);
|
||||
builder.appendField("bukkitVersion", "Fabric " + FabricLoader.getInstance().getModContainer("fabricloader").get().getMetadata().getVersion().getFriendlyString() + " (MC: " + KBSyncFabricLoaderMod.getServer().getServerVersion() + ")");
|
||||
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"));
|
||||
@@ -134,18 +102,10 @@ public class MetricsFabric implements Metrics {
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
if (KBSyncFabricLoaderMod.getServer().isRunning()) {
|
||||
return KBSyncFabricLoaderMod.getServer().getPlayerCount();
|
||||
if (FabricLoaderMod.getServer().isRunning()) {
|
||||
return FabricLoaderMod.getServer().getPlayerCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Config {
|
||||
public boolean enabled = true;
|
||||
public String serverUuid;
|
||||
public boolean logFailedRequests = false;
|
||||
public boolean logSentData = false;
|
||||
public boolean logResponseStatusText = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CustomUpdateIntervals {
|
||||
private static final Map<String, Integer> entityIntervals = new HashMap<>();
|
||||
|
||||
public static void setInterval(String entityType, int interval) {
|
||||
entityIntervals.put(entityType, interval);
|
||||
}
|
||||
|
||||
public static int getInterval(String entityType, int defaultInterval) {
|
||||
return entityIntervals.getOrDefault(entityType, defaultInterval);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
entityIntervals.clear();
|
||||
}
|
||||
|
||||
public static void updateIntervals(ServerLevel level, Map<String, Integer> entityIntervals) {
|
||||
for (Entity entity : level.getAllEntities()) {
|
||||
String entityType = entity.getType().builtInRegistryHolder().getRegisteredName();
|
||||
if (entityIntervals.containsKey(entityType)) {
|
||||
ChunkMap.TrackedEntity serverEntity = ((ServerChunkCache) entity.level().getChunkSource()).chunkMap.entityMap.get(entity.getId());
|
||||
if (serverEntity != null) {
|
||||
((UpdateIntervalAccessor) serverEntity).setUpdateInterval(entityIntervals.get(entityType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Integer> loadEntityIntervalsFromConfig() {
|
||||
Map<String, Integer> intervals = new HashMap<>();
|
||||
ConfigWrapper config = Base.INSTANCE.getConfigManager().getConfigWrapper();
|
||||
|
||||
String basePath = "entity_update_intervals";
|
||||
if (config.contains(basePath)) {
|
||||
for (String entityType : config.getKeys(basePath)) {
|
||||
int interval = config.getInt(basePath + "." + entityType, -1);
|
||||
if (interval > 0) {
|
||||
intervals.put(entityType, interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intervals;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.caseload.knockbacksync.world;
|
||||
|
||||
import me.caseload.knockbacksync.KBSyncFabricLoaderMod;
|
||||
import me.caseload.knockbacksync.FabricLoaderMod;
|
||||
import me.caseload.knockbacksync.player.FabricPlayer;
|
||||
import me.caseload.knockbacksync.player.PlatformPlayer;
|
||||
|
||||
@@ -10,14 +10,14 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class FabricServer implements PlatformServer {
|
||||
public Collection<PlatformPlayer> getOnlinePlayers() {
|
||||
return KBSyncFabricLoaderMod.getServer().getPlayerList().getPlayers().stream()
|
||||
return FabricLoaderMod.getServer().getPlayerList().getPlayers().stream()
|
||||
.map(FabricPlayer::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformPlayer getPlayer(UUID uuid) {
|
||||
return new FabricPlayer(KBSyncFabricLoaderMod.getServer().getPlayerList().getPlayer(uuid));
|
||||
return new FabricPlayer(FabricLoaderMod.getServer().getPlayerList().getPlayer(uuid));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,10 +10,12 @@ 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;
|
||||
|
||||
public class FabricWorld implements PlatformWorld {
|
||||
private final Level world;
|
||||
@@ -25,7 +27,7 @@ public class FabricWorld implements PlatformWorld {
|
||||
@Override
|
||||
public WrappedBlockState getBlockStateAt(int x, int y, int z) {
|
||||
BlockState blockState = this.world.getBlockState(new BlockPos(x, y, z));
|
||||
return WrappedBlockState.getByString(blockState.getBlock().getName().toString());
|
||||
return WrappedBlockState.getByGlobalId(Block.BLOCK_STATE_REGISTRY.getId(blockState));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,17 +47,18 @@ public class FabricWorld implements PlatformWorld {
|
||||
fluidHandling == FluidHandling.NONE ? ClipContext.Fluid.NONE :
|
||||
fluidHandling == FluidHandling.SOURCE_ONLY ? ClipContext.Fluid.SOURCE_ONLY :
|
||||
ClipContext.Fluid.ANY,
|
||||
(Entity) null
|
||||
CollisionContext.empty()
|
||||
));
|
||||
|
||||
if (result.getType() == HitResult.Type.MISS) return null;
|
||||
|
||||
Vec3 hitLocation = result.getLocation();
|
||||
BlockPos blockPos = result.getBlockPos();
|
||||
return new RayTraceResult(
|
||||
new Vector3d(result.getLocation().x, result.getLocation().y, result.getLocation().z),
|
||||
new Vector3d(hitLocation.x, hitLocation.y, hitLocation.z),
|
||||
BlockFace.getBlockFaceByValue(result.getDirection().ordinal()),
|
||||
new Vector3i(result.getBlockPos().getX(), result.getBlockPos().getY(), result.getBlockPos().getZ()),
|
||||
WrappedBlockState.getByString(world.getBlockState(blockPos).getBlock().toString())
|
||||
new Vector3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
||||
getBlockStateAt(blockPos.getX(), blockPos.getY(), blockPos.getZ())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"me.caseload.knockbacksync.KBSyncFabricLoaderMod"
|
||||
"me.caseload.knockbacksync.FabricLoaderMod"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
@@ -20,6 +20,8 @@
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"fabric": "*",
|
||||
"minecraft": "~${minecraft_version}"
|
||||
}
|
||||
"minecraft": "~${minecraft_version}",
|
||||
"packetevents": "*"
|
||||
},
|
||||
"accessWidener": "knockbacksync.accesswidener"
|
||||
}
|
||||
@@ -0,0 +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
|
||||
@@ -4,6 +4,7 @@
|
||||
"package": "me.caseload.knockbacksync.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"ChunkMapMixin",
|
||||
"CommandStackSourceAccessor",
|
||||
"PlayerMixin",
|
||||
"ServerEntityMixin",
|
||||
|
||||
@@ -17,3 +17,5 @@ archives_base_name=knockbacksync
|
||||
# Dependencies
|
||||
# check this on https://modmuss50.me/fabric.html
|
||||
fabric_version=0.102.0+1.21
|
||||
|
||||
githubRepo=CASELOAD7000/knockback-sync
|
||||
Vendored
BIN
Binary file not shown.
+7
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -1,136 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.caseload</groupId>
|
||||
<artifactId>KnockbackSync</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>KnockbackSync</name>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!--outputTimestamp must be set to a consistent arbitrary value for reproducibility-->
|
||||
<!--Chosen value is the exact time caseload uploaded the first commit-->
|
||||
<project.build.outputTimestamp>2024-01-18T02:53:12Z</project.build.outputTimestamp>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>dev.jorel.commandapi</pattern>
|
||||
<shadedPattern>me.caseload.knockbacksync.commandapi</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.github.retrooper</pattern>
|
||||
<shadedPattern>me.caseload.knockbacksync.retrooper</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.projectlombok</pattern>
|
||||
<shadedPattern>me.caseload.knockbacksync.projectlombok</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.kohsuke</pattern>
|
||||
<shadedPattern>me.caseload.knockbacksync.kohsuke</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>me.caseload.knockbacksync.bstats</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigotmc-repo</id> <!--spigot-api-->
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository> <!--packetevents-spigot-->
|
||||
<id>codemc-releases</id>
|
||||
<url>https://repo.codemc.io/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<repository> <!--Backup repo for maven central, mainly for geysermc api, bstats-bukkit-->
|
||||
<id>opencollab</id>
|
||||
<url>https://repo.opencollab.dev/maven-snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.18.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.retrooper</groupId>
|
||||
<artifactId>packetevents-spigot</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.jorel</groupId>
|
||||
<artifactId>commandapi-bukkit-shade</artifactId>
|
||||
<version>9.5.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.34</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.326</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
Reference in new issue
Block a user