Compare commits
103
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b863655f1 | ||
|
|
631be06ec2 | ||
|
|
1c4361f2c6 | ||
|
|
497d72e13b | ||
|
|
02c5021ce2 | ||
|
|
83e06bce77 | ||
|
|
42fd3e1cc7 | ||
|
|
26a771e4da | ||
|
|
1dc4361938 | ||
|
|
3d5491de4f | ||
|
|
75ff92c326 | ||
|
|
c530dcfa17 | ||
|
|
d720b8864f | ||
|
|
3e3dbf0f30 | ||
|
|
0bd154974b | ||
|
|
3f57026960 | ||
|
|
e5bd42c15a | ||
|
|
67a25de202 | ||
|
|
be9841fb9f | ||
|
|
d8b529e837 | ||
|
|
187d70d1f1 | ||
|
|
31fd590968 | ||
|
|
2f57704bae | ||
|
|
4385073234 | ||
|
|
743a2d9cdb | ||
|
|
39a812c5ff | ||
|
|
367aac0274 | ||
|
|
c749a560fd | ||
|
|
3c6742d040 | ||
|
|
5dc3e08f22 | ||
|
|
1fb5762351 | ||
|
|
b51b661e23 | ||
|
|
a540a9abb7 | ||
|
|
9035180db8 | ||
|
|
5a6f87fe22 | ||
|
|
fe95424e28 | ||
|
|
d70e778f2d | ||
|
|
8faa0db0ed | ||
|
|
c4f133891d | ||
|
|
4b6eeadcc9 | ||
|
|
9028135f61 | ||
|
|
8307e58449 | ||
|
|
33766b11bc | ||
|
|
44fa35e6c7 | ||
|
|
26f205173e | ||
|
|
7ff1ed0307 | ||
|
|
e50348ee32 | ||
|
|
aee84e92cb | ||
|
|
a1f376bdb1 | ||
|
|
6483debd0a | ||
|
|
946c7a97a9 | ||
|
|
b2db9ed9ff | ||
|
|
3059f21ce6 | ||
|
|
b48f898c77 | ||
|
|
6f63743bd8 | ||
|
|
21e15f9b8a | ||
|
|
e505d2b2cd | ||
|
|
c83b374830 | ||
|
|
1bdca2dae5 | ||
|
|
91241dec7d | ||
|
|
651b0bd83e | ||
|
|
7f71f809c0 | ||
|
|
05444e6f56 | ||
|
|
e9e84f5548 | ||
|
|
62cb82de6a | ||
|
|
d10f7619e4 | ||
|
|
16e464719b | ||
|
|
21f22a8446 | ||
|
|
f02d1383c8 | ||
|
|
b65f42a480 | ||
|
|
e829c25c82 | ||
|
|
30c2d361a3 | ||
|
|
d0eb36c87a | ||
|
|
504e667d40 | ||
|
|
f8aa611a4b | ||
|
|
28be03b978 | ||
|
|
e3ccbe2449 | ||
|
|
5782d356fb | ||
|
|
999d7c7016 | ||
|
|
270db6995c | ||
|
|
bafb169b76 | ||
|
|
5bc0f06500 | ||
|
|
1e244ed37b | ||
|
|
8b8a9b5ce7 | ||
|
|
129549bf2d | ||
|
|
ed7f9d80f8 | ||
|
|
c1054142e0 | ||
|
|
9ee0b82eab | ||
|
|
76f5f81f6a | ||
|
|
0c51152477 | ||
|
|
9b8a0b4969 | ||
|
|
a2528beaf9 | ||
|
|
a3de70d498 | ||
|
|
bb08c78e8a | ||
|
|
4da8ddbd12 | ||
|
|
8f89d3615b | ||
|
|
8a09be435e | ||
|
|
7af6cfbb3b | ||
|
|
ce8aee5b8c | ||
|
|
a38f341965 | ||
|
|
262de24cbf | ||
|
|
16bb09aee0 | ||
|
|
f6ef1df29d |
No files matched your search
@@ -7,7 +7,7 @@ GrimAC is an open source Minecraft anticheat designed for 1.21 and supports 1.8-
|
||||
### Compiling through terminal/command prompt
|
||||
1. git clone https://github.com/GrimAnticheat/Grim.git (or click the green code button, download ZIP, then unzip it.)
|
||||
2. cd Grim
|
||||
3. gradlew build
|
||||
3. ./gradlew build
|
||||
4. The final jar is located in build/libs
|
||||
|
||||
### API Information
|
||||
|
||||
+23
-3
@@ -6,13 +6,32 @@ plugins {
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("io.freefair.lombok") version "8.6"
|
||||
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
|
||||
id("com.diffplug.spotless") version "6.25.0"
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
removeUnusedImports()
|
||||
trimTrailingWhitespace()
|
||||
targetExclude("build/generated/**/*")
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
trimTrailingWhitespace()
|
||||
}
|
||||
}
|
||||
|
||||
group = "ac.grim.grimac"
|
||||
version = "2.3.68"
|
||||
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."
|
||||
java.sourceCompatibility = JavaVersion.VERSION_17
|
||||
java.targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
// Set to false for debug builds
|
||||
// You cannot live reload classes if the jar relocates dependencies
|
||||
@@ -126,6 +145,7 @@ bukkit {
|
||||
|
||||
tasks.build {
|
||||
dependsOn(tasks.shadowJar)
|
||||
dependsOn(tasks.spotlessApply)
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
@@ -158,4 +178,4 @@ tasks.shadowJar {
|
||||
relocate("org.intellij", "ac.grim.grimac.shaded.intellij")
|
||||
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -25,6 +25,7 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
|
||||
private String checkName;
|
||||
private String configName;
|
||||
private String alternativeName;
|
||||
private String displayName;
|
||||
private String description;
|
||||
|
||||
private boolean experimental;
|
||||
@@ -53,6 +54,7 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
|
||||
this.alternativeName = checkData.alternativeName();
|
||||
this.experimental = checkData.experimental();
|
||||
this.description = checkData.description();
|
||||
this.displayName = this.checkName;
|
||||
}
|
||||
//
|
||||
reload();
|
||||
@@ -112,6 +114,8 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
|
||||
public void reload(ConfigManager configuration) {
|
||||
decay = configuration.getDoubleElse(configName + ".decay", decay);
|
||||
setbackVL = configuration.getDoubleElse(configName + ".setbackvl", setbackVL);
|
||||
displayName = configuration.getStringElse(configName + ".displayname", checkName);
|
||||
|
||||
if (setbackVL == -1) setbackVL = Double.MAX_VALUE;
|
||||
updateExempted();
|
||||
onReload(configuration);
|
||||
@@ -152,4 +156,3 @@ public class Check implements AbstractCheck, ConfigReloadObserver {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
|
||||
// It works on clients who % 360 their rotation.
|
||||
@CheckData(name = "AimModulo360", decay = 0.005)
|
||||
public class AimModulo360 extends Check implements RotationCheck {
|
||||
float lastDeltaYaw;
|
||||
|
||||
private float lastDeltaYaw;
|
||||
|
||||
public AimModulo360(GrimPlayer playerData) {
|
||||
super(playerData);
|
||||
@@ -19,7 +20,10 @@ public class AimModulo360 extends Check implements RotationCheck {
|
||||
|
||||
@Override
|
||||
public void process(final RotationUpdate rotationUpdate) {
|
||||
if (player.packetStateData.lastPacketWasTeleport) return;
|
||||
if (player.packetStateData.lastPacketWasTeleport) {
|
||||
lastDeltaYaw = rotationUpdate.getDeltaXRot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.xRot < 360 && player.xRot > -360 && Math.abs(rotationUpdate.getDeltaXRot()) > 320 && Math.abs(lastDeltaYaw) < 30) {
|
||||
flagAndAlert();
|
||||
|
||||
@@ -59,15 +59,15 @@ public class AimProcessor extends Check implements RotationCheck {
|
||||
|
||||
if (this.xRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
Pair<Double, Integer> modeX = this.xRotMode.getMode();
|
||||
if (modeX.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeX = modeX.getFirst();
|
||||
if (modeX.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeX = modeX.first();
|
||||
this.sensitivityX = convertToSensitivity(this.modeX);
|
||||
}
|
||||
}
|
||||
if (this.yRotMode.size() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
Pair<Double, Integer> modeY = this.yRotMode.getMode();
|
||||
if (modeY.getSecond() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeY = modeY.getFirst();
|
||||
if (modeY.second() > SIGNIFICANT_SAMPLES_THRESHOLD) {
|
||||
this.modeY = modeY.first();
|
||||
this.sensitivityY = convertToSensitivity(this.modeY);
|
||||
}
|
||||
}
|
||||
@@ -81,4 +81,4 @@ public class AimProcessor extends Check implements RotationCheck {
|
||||
double var9 = Math.cbrt(var11);
|
||||
return (var9 - 0.2f) / 0.6f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
@CheckData(name = "BadPacketsH")
|
||||
public class BadPacketsH extends Check implements PacketCheck {
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BadPacketsO extends Check implements PacketCheck {
|
||||
boolean hasID = false;
|
||||
|
||||
for (Pair<Long, Long> iterator : keepaliveMap) {
|
||||
if (iterator.getFirst() == id) {
|
||||
if (iterator.first() == id) {
|
||||
hasID = true;
|
||||
break;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class BadPacketsO extends Check implements PacketCheck {
|
||||
do {
|
||||
data = keepaliveMap.poll();
|
||||
if (data == null) break;
|
||||
} while (data.getFirst() != id);
|
||||
} while (data.first() != id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@@ -4,7 +4,6 @@ import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockBreakSpeed;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package ac.grim.grimac.checks.impl.combat;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@CheckData(name = "MultiInteractA", experimental = true)
|
||||
public class MultiInteractA extends Check implements PostPredictionCheck {
|
||||
public MultiInteractA(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayList<String> flags = new ArrayList<>();
|
||||
private int lastEntity;
|
||||
private boolean lastSneaking;
|
||||
private boolean hasInteracted = false;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event);
|
||||
int entity = packet.getEntityId();
|
||||
boolean sneaking = packet.isSneaking().orElse(false);
|
||||
|
||||
if (hasInteracted && entity != lastEntity) {
|
||||
String verbose = "lastEntity=" + lastEntity + ", entity=" + entity
|
||||
+ ", lastSneaking=" + lastSneaking + ", sneaking=" + sneaking;
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
lastEntity = entity;
|
||||
lastSneaking = sneaking;
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package ac.grim.grimac.checks.impl.combat;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@CheckData(name = "MultiInteractB", experimental = true)
|
||||
public class MultiInteractB extends Check implements PostPredictionCheck {
|
||||
public MultiInteractB(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private final ArrayList<String> flags = new ArrayList<>();
|
||||
private Vector3f lastPos;
|
||||
private boolean hasInteracted = false;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) {
|
||||
Vector3f pos = new WrapperPlayClientInteractEntity(event).getTarget().orElse(null);
|
||||
|
||||
if (pos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasInteracted && !pos.equals(lastPos)) {
|
||||
String verbose = "pos=" + MessageUtil.toUnlabledString(pos) + ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
} else {
|
||||
flags.add(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
lastPos = pos;
|
||||
hasInteracted = true;
|
||||
}
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType()) && player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8) && !player.packetStateData.lastPacketWasTeleport) {
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
// we don't need to check pre-1.9 players here (no tick skipping)
|
||||
if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_8)) return;
|
||||
|
||||
if (player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
flags.clear();
|
||||
hasInteracted = false;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
@@ -43,7 +45,7 @@ import java.util.*;
|
||||
public class Reach extends Check implements PacketCheck {
|
||||
// Only one flag per reach attack, per entity, per tick.
|
||||
// We store position because lastX isn't reliable on teleports.
|
||||
private final Map<Integer, Vector3d> playerAttackQueue = new HashMap<>();
|
||||
private final Int2ObjectMap<Vector3d> playerAttackQueue = new Int2ObjectOpenHashMap<>();
|
||||
private static final List<EntityType> blacklisted = Arrays.asList(
|
||||
EntityTypes.BOAT,
|
||||
EntityTypes.CHEST_BOAT,
|
||||
@@ -81,7 +83,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Dead entities cause false flags (https://github.com/GrimAnticheat/Grim/issues/546)
|
||||
if (entity.isDead) return;
|
||||
|
||||
@@ -142,8 +144,8 @@ public class Reach extends Check implements PacketCheck {
|
||||
}
|
||||
|
||||
private void tickBetterReachCheckWithAngle() {
|
||||
for (Map.Entry<Integer, Vector3d> attack : playerAttackQueue.entrySet()) {
|
||||
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getKey().intValue());
|
||||
for (Int2ObjectMap.Entry<Vector3d> attack : playerAttackQueue.int2ObjectEntrySet()) {
|
||||
PacketEntity reachEntity = player.compensatedEntities.entityMap.get(attack.getIntKey());
|
||||
if (reachEntity != null) {
|
||||
String result = checkReach(reachEntity, attack.getValue(), false);
|
||||
if (result != null) {
|
||||
@@ -208,7 +210,7 @@ public class Reach extends Check implements PacketCheck {
|
||||
Vector eyePos = new Vector(from.getX(), from.getY() + eye, from.getZ());
|
||||
Vector endReachPos = eyePos.clone().add(new Vector(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
|
||||
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).getFirst();
|
||||
Vector intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
|
||||
|
||||
if (ReachUtils.isVecInside(targetBox, eyePos)) {
|
||||
minDistance = 0;
|
||||
|
||||
@@ -18,7 +18,6 @@ public class CrashB extends Check implements PacketCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CREATIVE_INVENTORY_ACTION) {
|
||||
if (player.gamemode != GameMode.CREATIVE) {
|
||||
player.getSetbackTeleportUtil().executeViolationSetback();
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
flagAndAlert(); // Could be transaction split, no need to setback though
|
||||
|
||||
@@ -9,6 +9,7 @@ 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 org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ClientBrand extends Check implements PacketCheck {
|
||||
@@ -38,6 +39,7 @@ public class ClientBrand extends Check implements PacketCheck {
|
||||
System.arraycopy(data, 1, minusLength, 0, minusLength.length);
|
||||
|
||||
brand = new String(minusLength).replace(" (Velocity)", ""); //removes velocity's brand suffix
|
||||
brand = ChatColor.stripColor(brand); //strip color codes from client brand
|
||||
if (player.checkManager.getPrePredictionCheck(ExploitA.class).checkString(brand)) brand = "sent log4j";
|
||||
if (!GrimAPI.INSTANCE.getConfigManager().isIgnoredClient(brand)) {
|
||||
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("client-brand-format", "%prefix% &f%player% joined using %brand%");
|
||||
|
||||
@@ -63,15 +63,15 @@ public class FastBreak extends Check implements PacketCheck {
|
||||
|
||||
if (digging.getAction() == DiggingAction.START_DIGGING) {
|
||||
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(blockPosition);
|
||||
|
||||
|
||||
// Exempt all blocks that do not exist in the player version
|
||||
if (WrappedBlockState.getDefaultState(player.getClientVersion(), block.getType()).getType() == StateTypes.AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
startBreak = System.currentTimeMillis() - (targetBlock == null ? 50 : 0); // ???
|
||||
targetBlock = blockPosition;
|
||||
|
||||
|
||||
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, targetBlock);
|
||||
|
||||
double breakDelay = System.currentTimeMillis() - lastFinishBreak;
|
||||
|
||||
@@ -5,7 +5,6 @@ import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@CheckData(name = "TransactionOrder", experimental = false)
|
||||
public class TransactionOrder extends Check implements PacketCheck {
|
||||
@@ -14,4 +13,4 @@ public class TransactionOrder extends Check implements PacketCheck {
|
||||
super(player);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
|
||||
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = 10, experimental = true)
|
||||
@CheckData(name = "NegativeTimer", configName = "NegativeTimer", setback = -1, experimental = true)
|
||||
public class NegativeTimerCheck extends TimerCheck implements PostPredictionCheck {
|
||||
|
||||
public NegativeTimerCheck(GrimPlayer player) {
|
||||
@@ -24,7 +24,7 @@ public class NegativeTimerCheck extends TimerCheck implements PostPredictionChec
|
||||
|
||||
if (timerBalanceRealTime < lastMovementPlayerClock - clockDrift) {
|
||||
int lostMS = (int) ((System.nanoTime() - timerBalanceRealTime) / 1e6);
|
||||
flagAndAlert("-" + lostMS);
|
||||
if (flagAndAlert("-" + lostMS)) setbackIfAboveSetbackVL();
|
||||
timerBalanceRealTime += 50e6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ public class NoSlowA extends Check implements PostPredictionCheck {
|
||||
}
|
||||
|
||||
if (bestOffset > offsetToFlag) {
|
||||
if (flaggedLastTick) {
|
||||
flagWithSetback();
|
||||
if (flaggedLastTick && flagWithSetback()) {
|
||||
alert("");
|
||||
}
|
||||
flaggedLastTick = true;
|
||||
|
||||
@@ -35,4 +35,4 @@ public class NoSlowB extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,32 +2,17 @@ package ac.grim.grimac.checks.impl.movement;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "NoSlowC", setback = 5, experimental = true)
|
||||
public class NoSlowC extends Check implements PostPredictionCheck, PacketCheck {
|
||||
public class NoSlowC extends Check implements PostPredictionCheck {
|
||||
public NoSlowC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean startedSprintingBeforeSlowMovement = false;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
|
||||
startedSprintingBeforeSlowMovement = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (!predictionComplete.isChecked()) return;
|
||||
@@ -36,8 +21,7 @@ public class NoSlowC extends Check implements PostPredictionCheck, PacketCheck {
|
||||
ClientVersion client = player.getClientVersion();
|
||||
|
||||
// https://bugs.mojang.com/browse/MC-152728
|
||||
if (startedSprintingBeforeSlowMovement && client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
|
||||
reward();
|
||||
if (client.isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,42 +2,26 @@ package ac.grim.grimac.checks.impl.movement;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "NoSlowD", setback = 5, experimental = true)
|
||||
public class NoSlowD extends Check implements PostPredictionCheck, PacketCheck {
|
||||
public class NoSlowD extends Check implements PostPredictionCheck {
|
||||
public NoSlowD(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean startedSprintingBeforeUse = false;
|
||||
private boolean flaggedLastTick = false;
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
if (new WrapperPlayClientEntityAction(event).getAction() == WrapperPlayClientEntityAction.Action.START_SPRINTING) {
|
||||
startedSprintingBeforeUse = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
if (!predictionComplete.isChecked()) return;
|
||||
|
||||
if (player.packetStateData.isSlowedByUsingItem()) {
|
||||
// https://bugs.mojang.com/browse/MC-152728
|
||||
if (startedSprintingBeforeUse && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
|
||||
reward();
|
||||
flaggedLastTick = false;
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14_2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package ac.grim.grimac.checks.impl.movement;
|
||||
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.CheckData;
|
||||
import ac.grim.grimac.checks.type.PacketCheck;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
@@ -13,7 +12,7 @@ import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEn
|
||||
import static com.github.retrooper.packetevents.protocol.potion.PotionTypes.BLINDNESS;
|
||||
|
||||
@CheckData(name = "NoSlowE", setback = 5, experimental = true)
|
||||
public class NoSlowE extends Check implements PostPredictionCheck, PacketCheck {
|
||||
public class NoSlowE extends Check implements PostPredictionCheck {
|
||||
public NoSlowE(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerFlying;
|
||||
|
||||
public class SetbackBlocker extends Check implements PacketCheck {
|
||||
|
||||
@@ -7,8 +7,6 @@ import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class MultiPlace extends BlockPlaceCheck {
|
||||
|
||||
@Override
|
||||
public void onPredictionComplete(PredictionComplete predictionComplete) {
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && !player.skippedTickInActualMovement && predictionComplete.isChecked()) {
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8) && player.isTickingReliablyFor(3)) {
|
||||
for (String verbose : flags) {
|
||||
flagAndAlert(verbose);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@CheckData(name = "PositionPlace")
|
||||
public class PositionPlace extends BlockPlaceCheck {
|
||||
@@ -49,27 +48,21 @@ public class PositionPlace extends BlockPlaceCheck {
|
||||
|
||||
// So now we have the player's possible eye positions
|
||||
// So then look at the face that the player has clicked
|
||||
boolean flag = false;
|
||||
switch (place.getDirection()) {
|
||||
case NORTH: // Z- face
|
||||
flag = eyePositions.minZ > combined.minZ;
|
||||
break;
|
||||
case SOUTH: // Z+ face
|
||||
flag = eyePositions.maxZ < combined.maxZ;
|
||||
break;
|
||||
case EAST: // X+ face
|
||||
flag = eyePositions.maxX < combined.maxX;
|
||||
break;
|
||||
case WEST: // X- face
|
||||
flag = eyePositions.minX > combined.minX;
|
||||
break;
|
||||
case UP: // Y+ face
|
||||
flag = eyePositions.maxY < combined.maxY;
|
||||
break;
|
||||
case DOWN: // Y- face
|
||||
flag = eyePositions.minY > combined.minY;
|
||||
break;
|
||||
}
|
||||
boolean flag = switch (place.getDirection()) {
|
||||
case NORTH -> // Z- face
|
||||
eyePositions.minZ > combined.minZ;
|
||||
case SOUTH -> // Z+ face
|
||||
eyePositions.maxZ < combined.maxZ;
|
||||
case EAST -> // X+ face
|
||||
eyePositions.maxX < combined.maxX;
|
||||
case WEST -> // X- face
|
||||
eyePositions.minX > combined.minX;
|
||||
case UP -> // Y+ face
|
||||
eyePositions.maxY < combined.maxY;
|
||||
case DOWN -> // Y- face
|
||||
eyePositions.minY > combined.minY;
|
||||
default -> false;
|
||||
};
|
||||
|
||||
if (flag) {
|
||||
if (flagAndAlert() && shouldModifyPackets() && shouldCancel()) {
|
||||
|
||||
@@ -113,7 +113,7 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.getFirst() != null) return true;
|
||||
if (intercept.first() != null) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockPlace;
|
||||
import ac.grim.grimac.utils.collisions.HitboxData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
@@ -18,6 +19,7 @@ import java.util.List;
|
||||
public class BlockPlaceCheck extends Check implements RotationCheck, PostPredictionCheck {
|
||||
private static final List<StateType> weirdBoxes = new ArrayList<>();
|
||||
private static final List<StateType> buggyBoxes = new ArrayList<>();
|
||||
private final SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
|
||||
|
||||
protected int cancelVL;
|
||||
|
||||
@@ -69,11 +71,11 @@ public class BlockPlaceCheck extends Check implements RotationCheck, PostPredict
|
||||
Vector3i clicked = place.getPlacedAgainstBlockLocation();
|
||||
CollisionBox placedOn = HitboxData.getBlockHitbox(player, place.getMaterial(), player.getClientVersion(), player.compensatedWorld.getWrappedBlockStateAt(clicked), clicked.getX(), clicked.getY(), clicked.getZ());
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
placedOn.downCast(boxes);
|
||||
int size = placedOn.downCast(boxes);
|
||||
|
||||
SimpleCollisionBox combined = new SimpleCollisionBox(clicked.getX(), clicked.getY(), clicked.getZ());
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox box = boxes[i];
|
||||
double minX = Math.max(box.minX, combined.minX);
|
||||
double minY = Math.max(box.minY, combined.minY);
|
||||
double minZ = Math.max(box.minZ, combined.minZ);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package ac.grim.grimac.checks.type;
|
||||
|
||||
import ac.grim.grimac.api.AbstractCheck;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.RotationUpdate;
|
||||
|
||||
public interface RotationCheck extends AbstractCheck {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package ac.grim.grimac.checks.type;
|
||||
|
||||
import ac.grim.grimac.api.AbstractCheck;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.VehiclePositionUpdate;
|
||||
|
||||
public interface VehicleCheck extends AbstractCheck {
|
||||
|
||||
@@ -70,4 +70,4 @@ public class GrimDebug extends BaseCommand {
|
||||
|
||||
sender.sendMessage("Console output for " + (grimPlayer.bukkitPlayer == null ? grimPlayer.user.getProfile().getName() : grimPlayer.bukkitPlayer.getName()) + " is now " + isOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
package ac.grim.grimac.commands;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
|
||||
import ac.grim.grimac.checks.impl.misc.ClientBrand;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MultiLibUtil;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import co.aikar.commands.BaseCommand;
|
||||
import co.aikar.commands.annotation.CommandAlias;
|
||||
import co.aikar.commands.annotation.CommandCompletion;
|
||||
|
||||
@@ -23,8 +23,7 @@ public class GrimSpectate extends BaseCommand {
|
||||
@CommandPermission("grim.spectate")
|
||||
@CommandCompletion("@players")
|
||||
public void onSpectate(CommandSender sender, @Optional OnlinePlayer target) {
|
||||
if (!(sender instanceof Player)) return;
|
||||
Player player = (Player) sender;
|
||||
if (!(sender instanceof Player player)) return;
|
||||
|
||||
if (target != null && target.getPlayer().getUniqueId().equals(player.getUniqueId())) {
|
||||
String message = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("cannot-run-on-self", "%prefix% &cYou cannot use this command on yourself!");
|
||||
|
||||
@@ -17,8 +17,7 @@ public class GrimStopSpectating extends BaseCommand {
|
||||
@CommandCompletion("here")
|
||||
public void onStopSpectate(CommandSender sender, String[] args) {
|
||||
String string = args.length > 0 ? args[0] : null;
|
||||
if (!(sender instanceof Player)) return;
|
||||
Player player = (Player) sender;
|
||||
if (!(sender instanceof Player player)) return;
|
||||
if (GrimAPI.INSTANCE.getSpectateManager().isSpectating(player.getUniqueId())) {
|
||||
boolean teleportBack = string == null || !string.equalsIgnoreCase("here");
|
||||
GrimAPI.INSTANCE.getSpectateManager().disable(player, teleportBack);
|
||||
@@ -28,4 +27,3 @@ public class GrimStopSpectating extends BaseCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.bukkit.util.Vector;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
@@ -224,11 +225,10 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleBlockPlaceOrUseItem(PacketWrapper packet, GrimPlayer player) {
|
||||
private static void handleBlockPlaceOrUseItem(PacketWrapper<?> packet, GrimPlayer player) {
|
||||
// Legacy "use item" packet
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement &&
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place &&
|
||||
PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9)) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
|
||||
|
||||
@@ -243,9 +243,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof WrapperPlayClientUseItem) {
|
||||
WrapperPlayClientUseItem place = (WrapperPlayClientUseItem) packet;
|
||||
|
||||
if (packet instanceof WrapperPlayClientUseItem place) {
|
||||
if (player.gamemode == GameMode.SPECTATOR || player.gamemode == GameMode.ADVENTURE) return;
|
||||
|
||||
ItemStack placedWith = player.getInventory().getHeldItem();
|
||||
@@ -257,9 +255,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
// Check for interactable first (door, etc)
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
|
||||
ItemStack placedWith = player.getInventory().getHeldItem();
|
||||
ItemStack offhand = player.getInventory().getOffHand();
|
||||
|
||||
@@ -295,8 +291,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement) {
|
||||
WrapperPlayClientPlayerBlockPlacement place = (WrapperPlayClientPlayerBlockPlacement) packet;
|
||||
if (packet instanceof WrapperPlayClientPlayerBlockPlacement place) {
|
||||
Vector3i blockPosition = place.getBlockPosition();
|
||||
BlockFace face = place.getFace();
|
||||
|
||||
@@ -375,6 +370,11 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Manual filter on FINISH_DIGGING to prevent clients setting non-breakable blocks to air
|
||||
private static final Function<StateType, Boolean> BREAKABLE = type -> {
|
||||
return !type.isAir() && type.getHardness() != -1.0f && type != StateTypes.WATER && type != StateTypes.LAVA;
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getConnectionState() != ConnectionState.PLAY) return;
|
||||
@@ -456,22 +456,24 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
|
||||
WrapperPlayClientPlayerDigging dig = new WrapperPlayClientPlayerDigging(event);
|
||||
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(dig.getBlockPosition());
|
||||
final Vector3i digPosition = dig.getBlockPosition();
|
||||
WrappedBlockState block = player.compensatedWorld.getWrappedBlockStateAt(digPosition);
|
||||
final StateType type = block.getType();
|
||||
|
||||
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, block.getType());
|
||||
player.checkManager.getPacketCheck(BadPacketsX.class).handle(event, dig, type);
|
||||
player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig);
|
||||
|
||||
if (dig.getAction() == DiggingAction.FINISHED_DIGGING) {
|
||||
// Not unbreakable
|
||||
if (!block.getType().isAir() && block.getType().getHardness() != -1.0f && !event.isCancelled()) {
|
||||
if (BREAKABLE.apply(type) && !event.isCancelled()) {
|
||||
player.compensatedWorld.startPredicting();
|
||||
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
|
||||
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
|
||||
player.compensatedWorld.stopPredicting(dig);
|
||||
}
|
||||
}
|
||||
|
||||
if (dig.getAction() == DiggingAction.START_DIGGING && !event.isCancelled()) {
|
||||
double damage = BlockBreakSpeed.getBlockDamage(player, dig.getBlockPosition());
|
||||
double damage = BlockBreakSpeed.getBlockDamage(player, digPosition);
|
||||
|
||||
//Instant breaking, no damage means it is unbreakable by creative players (with swords)
|
||||
if (damage >= 1) {
|
||||
@@ -479,9 +481,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && Materials.isWaterSource(player.getClientVersion(), block)) {
|
||||
// Vanilla uses a method to grab water flowing, but as you can't break flowing water
|
||||
// We can simply treat all waterlogged blocks or source blocks as source blocks
|
||||
player.compensatedWorld.updateBlock(dig.getBlockPosition(), StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
|
||||
player.compensatedWorld.updateBlock(digPosition, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
|
||||
} else {
|
||||
player.compensatedWorld.updateBlock(dig.getBlockPosition().getX(), dig.getBlockPosition().getY(), dig.getBlockPosition().getZ(), 0);
|
||||
player.compensatedWorld.updateBlock(digPosition.getX(), digPosition.getY(), digPosition.getZ(), 0);
|
||||
}
|
||||
player.compensatedWorld.stopPredicting(dig);
|
||||
}
|
||||
@@ -797,14 +799,14 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
if (intercept.getFirst() == null) continue; // No intercept
|
||||
if (intercept.first() == null) continue; // No intercept
|
||||
|
||||
Vector hitLoc = intercept.getFirst();
|
||||
Vector hitLoc = intercept.first();
|
||||
|
||||
if (hitLoc.distanceSquared(startingVec) < bestHitResult) {
|
||||
bestHitResult = hitLoc.distanceSquared(startingVec);
|
||||
bestHitLoc = hitLoc;
|
||||
bestFace = intercept.getSecond();
|
||||
bestFace = intercept.second();
|
||||
}
|
||||
}
|
||||
if (bestHitLoc != null) {
|
||||
@@ -819,8 +821,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
Pair<Vector, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
|
||||
if (intercept.getFirst() != null) {
|
||||
return new HitData(vector3i, intercept.getFirst(), intercept.getSecond(), block);
|
||||
if (intercept.first() != null) {
|
||||
return new HitData(vector3i, intercept.first(), intercept.second(), block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowC;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowD;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowE;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
|
||||
@@ -227,9 +227,8 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
|
||||
if (status.getStatus() == 31) {
|
||||
PacketEntity hook = player.compensatedEntities.getEntity(status.getEntityId());
|
||||
if (!(hook instanceof PacketEntityHook)) return;
|
||||
if (!(hook instanceof PacketEntityHook hookEntity)) return;
|
||||
|
||||
PacketEntityHook hookEntity = (PacketEntityHook) hook;
|
||||
if (hookEntity.attached == player.entityID) {
|
||||
player.sendTransaction();
|
||||
// We don't transaction sandwich this, it's too rare to be a real problem.
|
||||
@@ -454,8 +453,7 @@ public class PacketEntityReplication extends Check implements PacketCheck {
|
||||
player.latencyUtils.addRealTimeTask(lastTrans, () -> {
|
||||
PacketEntity entity = player.compensatedEntities.getEntity(entityId);
|
||||
if (entity == null) return;
|
||||
if (entity instanceof PacketEntityTrackXRot && yaw != null) {
|
||||
PacketEntityTrackXRot xRotEntity = (PacketEntityTrackXRot) entity;
|
||||
if (entity instanceof PacketEntityTrackXRot xRotEntity && yaw != null) {
|
||||
xRotEntity.packetYaw = yaw;
|
||||
xRotEntity.steps = entity.isBoat() ? 10 : 3;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentTypes;
|
||||
@@ -72,6 +74,13 @@ public class PacketPlayerAttack extends PacketListenerAbstract {
|
||||
player.maxPlayerAttackSlow = 1;
|
||||
}
|
||||
} else if (!isLegacyPlayer && player.isSprinting) {
|
||||
// 1.9+ players who have attack speed cannot slow themselves twice in one tick because their attack cooldown gets reset on swing.
|
||||
if (player.maxPlayerAttackSlow > 0
|
||||
&& PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)
|
||||
&& player.compensatedEntities.getSelf().getAttributeValue(Attributes.GENERIC_ATTACK_SPEED) < 16) { // 16 is a reasonable limit
|
||||
return;
|
||||
}
|
||||
|
||||
// 1.9+ player who might have been slowed, but we can't be sure
|
||||
player.maxPlayerAttackSlow += 1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowA;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowD;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
@@ -23,7 +22,6 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.player.InteractionHand;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.*;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class PacketPlayerDigging extends PacketListenerAbstract {
|
||||
|
||||
@@ -172,10 +170,12 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
|
||||
|
||||
if (WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) {
|
||||
final GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser());
|
||||
if (player == null) return;
|
||||
|
||||
if (!player.packetStateData.lastPacketWasTeleport && !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
|
||||
if (player.packetStateData.isSlowedByUsingItem() && player.packetStateData.eatingHand != InteractionHand.OFF_HAND && player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected) {
|
||||
if (player != null && player.packetStateData.isSlowedByUsingItem()
|
||||
&& !player.packetStateData.lastPacketWasTeleport
|
||||
&& !player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
|
||||
if (player.packetStateData.eatingHand != InteractionHand.OFF_HAND
|
||||
&& player.packetStateData.getSlowedByUsingItemSlot() != player.packetStateData.lastSlotSelected
|
||||
|| player.getInventory().getItemInHand(player.packetStateData.eatingHand).isEmpty()) {
|
||||
player.packetStateData.setSlowedByUsingItem(false);
|
||||
player.checkManager.getPostPredictionCheck(NoSlowA.class).didSlotChangeLastTick = true;
|
||||
}
|
||||
@@ -220,13 +220,7 @@ public class PacketPlayerDigging extends PacketListenerAbstract {
|
||||
final ItemStack item = hand == InteractionHand.MAIN_HAND ?
|
||||
player.getInventory().getHeldItem() : player.getInventory().getOffHand();
|
||||
|
||||
final boolean wasSlow = player.packetStateData.isSlowedByUsingItem();
|
||||
|
||||
handleUseItem(player, item, hand);
|
||||
|
||||
if (!wasSlow) {
|
||||
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSteerVehicle;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowD;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.nmsutil.WatchableIndexUtil;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
@@ -209,8 +208,6 @@ public class PacketSelfMetadataListener extends PacketListenerAbstract {
|
||||
// The above line is a hack to fake activate use item
|
||||
player.packetStateData.setSlowedByUsingItem(isActive);
|
||||
|
||||
player.checkManager.getPostPredictionCheck(NoSlowD.class).startedSprintingBeforeUse = player.packetStateData.isSlowedByUsingItem() && player.isSprinting;
|
||||
|
||||
if (isActive) {
|
||||
player.packetStateData.eatingHand = isOffhand ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import ac.grim.grimac.utils.data.Pair;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTeleportConfirm;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerPositionAndLook;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerVehicleMove;
|
||||
import org.bukkit.Location;
|
||||
|
||||
@@ -99,4 +99,4 @@ public class ProxyAlertMessenger extends PacketListenerAbstract {
|
||||
if (!file.exists()) return false;
|
||||
return YamlConfiguration.loadConfiguration(file).getBoolean(pathToValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,8 @@ public class ResyncWorldUtil {
|
||||
|
||||
// Takes 0.15ms or so to complete. Not bad IMO. Unsure how I could improve this other than sending packets async.
|
||||
// But that's on PacketEvents.
|
||||
FoliaScheduler.getEntityScheduler().execute(player.bukkitPlayer, GrimAPI.INSTANCE.getPlugin(), () -> {
|
||||
FoliaScheduler.getRegionScheduler().execute(GrimAPI.INSTANCE.getPlugin(), player.bukkitPlayer.getWorld(),
|
||||
minBlockX >> 4, minBlockZ >> 4, () -> {
|
||||
boolean flat = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
|
||||
|
||||
if (player.bukkitPlayer == null) return;
|
||||
@@ -111,6 +112,6 @@ public class ResyncWorldUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -174,4 +174,4 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.compensatedWorld.updateBlock(blockChange.getX(), blockChange.getY(), blockChange.getZ(), blockChange.getBlockId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import java.util.BitSet;
|
||||
public class PacketWorldReaderEight extends BasePacketWorldReader {
|
||||
@Override
|
||||
public void handleMapChunkBulk(final GrimPlayer player, final PacketSendEvent event) {
|
||||
PacketWrapper wrapper = new PacketWrapper(event);
|
||||
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
|
||||
ByteBuf buffer = (ByteBuf) wrapper.getBuffer();
|
||||
|
||||
boolean skylight = wrapper.readBoolean();
|
||||
@@ -47,7 +47,7 @@ public class PacketWorldReaderEight extends BasePacketWorldReader {
|
||||
|
||||
@Override
|
||||
public void handleMapChunk(final GrimPlayer player, final PacketSendEvent event) {
|
||||
PacketWrapper wrapper = new PacketWrapper(event);
|
||||
PacketWrapper<?> wrapper = new PacketWrapper<>(event);
|
||||
|
||||
final int chunkX = wrapper.readInt();
|
||||
final int chunkZ = wrapper.readInt();
|
||||
|
||||
@@ -5,6 +5,8 @@ import ac.grim.grimac.checks.impl.aim.AimDuplicateLook;
|
||||
import ac.grim.grimac.checks.impl.aim.AimModulo360;
|
||||
import ac.grim.grimac.checks.impl.aim.processor.AimProcessor;
|
||||
import ac.grim.grimac.checks.impl.badpackets.*;
|
||||
import ac.grim.grimac.checks.impl.combat.MultiInteractA;
|
||||
import ac.grim.grimac.checks.impl.combat.MultiInteractB;
|
||||
import ac.grim.grimac.checks.impl.combat.Reach;
|
||||
import ac.grim.grimac.checks.impl.crash.*;
|
||||
import ac.grim.grimac.checks.impl.exploit.ExploitA;
|
||||
@@ -32,7 +34,6 @@ import ac.grim.grimac.manager.init.start.SuperDebug;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.GhostBlockDetector;
|
||||
import ac.grim.grimac.predictionengine.SneakingEstimator;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.*;
|
||||
import ac.grim.grimac.utils.latency.CompensatedCooldown;
|
||||
import ac.grim.grimac.utils.latency.CompensatedFireworks;
|
||||
@@ -46,8 +47,11 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class CheckManager {
|
||||
private static boolean inited;
|
||||
private static final AtomicBoolean initedAtomic = new AtomicBoolean(false);
|
||||
|
||||
ClassToInstanceMap<PacketCheck> packetChecks;
|
||||
ClassToInstanceMap<PositionCheck> positionCheck;
|
||||
@@ -134,6 +138,8 @@ public class CheckManager {
|
||||
.put(NoSlowC.class, new NoSlowC(player))
|
||||
.put(NoSlowD.class, new NoSlowD(player))
|
||||
.put(NoSlowE.class, new NoSlowE(player))
|
||||
.put(MultiInteractA.class, new MultiInteractA(player))
|
||||
.put(MultiInteractB.class, new MultiInteractB(player))
|
||||
.put(SetbackTeleportUtil.class, new SetbackTeleportUtil(player)) // Avoid teleporting to new position, update safe pos last
|
||||
.put(CompensatedFireworks.class, player.compensatedFireworks)
|
||||
.put(SneakingEstimator.class, new SneakingEstimator(player))
|
||||
@@ -333,7 +339,12 @@ public class CheckManager {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Fast non-thread safe check
|
||||
if (inited) return;
|
||||
// Slow thread safe check
|
||||
if (!initedAtomic.compareAndSet(false, true)) return;
|
||||
inited = true;
|
||||
|
||||
for (AbstractCheck check : allChecks.values()) {
|
||||
if (check.getCheckName() != null) {
|
||||
String permissionName = "grim.exempt." + check.getCheckName().toLowerCase();
|
||||
@@ -346,7 +357,5 @@ public class CheckManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
@@ -69,4 +69,4 @@ public class InitManager {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import io.github.retrooper.packetevents.util.folia.FoliaScheduler;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -105,7 +106,7 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
original = MessageUtil.format(original
|
||||
.replace("[alert]", alertString)
|
||||
.replace("[proxy]", alertString)
|
||||
.replace("%check_name%", check.getCheckName())
|
||||
.replace("%check_name%", check.getDisplayName())
|
||||
.replace("%experimental%", check.isExperimental() ? experimentalSymbol : "")
|
||||
.replace("%vl%", vl)
|
||||
.replace("%verbose%", verbose)
|
||||
@@ -149,7 +150,7 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
|
||||
if (command.command.equals("[webhook]")) {
|
||||
String vl = group.violations.values().stream().filter((e) -> e == check).count() + "";
|
||||
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getCheckName(), vl);
|
||||
GrimAPI.INSTANCE.getDiscordManager().sendAlert(player, verbose, check.getDisplayName(), vl);
|
||||
} else if (command.command.equals("[proxy]")) {
|
||||
ProxyAlertMessenger.sendPluginMessage(replaceAlertPlaceholders(command.getCommand(), group, check, proxyAlertString, verbose));
|
||||
} else {
|
||||
@@ -183,7 +184,7 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
|
||||
group.violations.put(currentTime, check);
|
||||
// Remove violations older than the defined time in the config
|
||||
group.violations.entrySet().removeIf(time -> currentTime - time.getKey() > group.removeViolationsAfter);
|
||||
group.violations.long2ObjectEntrySet().removeIf(time -> currentTime - time.getLongKey() > group.removeViolationsAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +196,7 @@ class PunishGroup {
|
||||
@Getter
|
||||
List<ParsedCommand> commands;
|
||||
@Getter
|
||||
HashMap<Long, Check> violations = new HashMap<>();
|
||||
Long2ObjectOpenHashMap<Check> violations = new Long2ObjectOpenHashMap<>();
|
||||
@Getter
|
||||
int removeViolationsAfter;
|
||||
|
||||
|
||||
@@ -153,12 +153,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
|
||||
|
||||
// Velocity sets
|
||||
if (futureKb.getFirst() != null) {
|
||||
clientVel = futureKb.getSecond();
|
||||
if (futureKb.first() != null) {
|
||||
clientVel = futureKb.second();
|
||||
}
|
||||
|
||||
// Explosion adds
|
||||
if (futureExplosion != null && (futureKb.getFirst() == null || futureKb.getFirst().transaction < futureExplosion.transaction)) {
|
||||
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
|
||||
clientVel.add(futureExplosion.vector);
|
||||
}
|
||||
|
||||
@@ -328,16 +328,16 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
while (true) {
|
||||
Pair<Integer, Vector3d> teleportPos = player.vehicleData.vehicleTeleports.peek();
|
||||
if (teleportPos == null) break;
|
||||
if (lastTransaction < teleportPos.getFirst()) {
|
||||
if (lastTransaction < teleportPos.first()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3d position = teleportPos.getSecond();
|
||||
Vector3d position = teleportPos.second();
|
||||
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
return true;
|
||||
} else if (lastTransaction > teleportPos.getFirst() + 1) {
|
||||
} else if (lastTransaction > teleportPos.first() + 1) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
|
||||
|
||||
@@ -71,14 +71,8 @@ public class SpectateManager implements Initable {
|
||||
spectatingPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
private static class PreviousState {
|
||||
public PreviousState(org.bukkit.GameMode gameMode, Location location) {
|
||||
this.gameMode = gameMode;
|
||||
this.location = location;
|
||||
}
|
||||
private record PreviousState(org.bukkit.GameMode gameMode, Location location) {
|
||||
|
||||
private final org.bukkit.GameMode gameMode;
|
||||
private final Location location;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import ac.grim.grimac.utils.lists.EvictingQueue;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -20,7 +22,7 @@ import java.util.*;
|
||||
public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
private static final StringBuilder[] flags = new StringBuilder[256]; // 17 MB of logs in memory
|
||||
|
||||
Map<StringBuilder, Integer> continuedDebug = new HashMap<>();
|
||||
Object2IntMap<StringBuilder> continuedDebug = new Object2IntOpenHashMap<>();
|
||||
|
||||
List<VectorData> predicted = new EvictingQueue<>(60);
|
||||
List<Vector> actually = new EvictingQueue<>(60);
|
||||
@@ -45,7 +47,7 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
|
||||
Location location = new Location(player.x, player.y, player.z, player.xRot, player.yRot, player.bukkitPlayer == null ? "null" : player.bukkitPlayer.getWorld().getName());
|
||||
|
||||
for (Iterator<Map.Entry<StringBuilder, Integer>> it = continuedDebug.entrySet().iterator(); it.hasNext(); ) {
|
||||
for (Iterator<Object2IntMap.Entry<StringBuilder>> it = continuedDebug.object2IntEntrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry<StringBuilder, Integer> debug = it.next();
|
||||
appendDebug(debug.getKey(), player.predictedVelocity, player.actualMovement, location, player.startTickClientVel, player.baseTickAddition, player.baseTickWaterPushing);
|
||||
debug.setValue(debug.getValue() - 1);
|
||||
@@ -287,4 +289,4 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
return "x: " + x + " y: " + y + " z: " + z + " xRot: " + xRot + " yRot: " + yRot + " world: " + world;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public class TickEndEvent implements Initable {
|
||||
// but whatever. At least plugins can't break it, I guess.
|
||||
//
|
||||
// Pledge injects into another list, so we should be safe injecting into this one
|
||||
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<Object>(endOfTickObject) {
|
||||
List<?> wrapper = Collections.synchronizedList(new HookedListWrapper<>(endOfTickObject) {
|
||||
@Override
|
||||
public void onIterator() {
|
||||
hasTicked = true;
|
||||
@@ -58,4 +58,4 @@ public class TickEndEvent implements Initable {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ public class GrimPlayer implements GrimUser {
|
||||
boolean hasID = false;
|
||||
int skipped = 0;
|
||||
for (Pair<Short, Long> iterator : transactionsSent) {
|
||||
if (iterator.getFirst() == id) {
|
||||
if (iterator.first() == id) {
|
||||
hasID = true;
|
||||
break;
|
||||
}
|
||||
@@ -347,9 +347,9 @@ public class GrimPlayer implements GrimUser {
|
||||
|
||||
lastTransactionReceived.incrementAndGet();
|
||||
lastTransReceived = System.currentTimeMillis();
|
||||
transactionPing = (System.nanoTime() - data.getSecond());
|
||||
playerClockAtLeast = data.getSecond();
|
||||
} while (data.getFirst() != id);
|
||||
transactionPing = (System.nanoTime() - data.second());
|
||||
playerClockAtLeast = data.second();
|
||||
} while (data.first() != id);
|
||||
|
||||
// A transaction means a new tick, so apply any block places
|
||||
CheckManagerListener.handleQueuedPlaces(this, false, 0, 0, System.currentTimeMillis());
|
||||
@@ -357,7 +357,7 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
// Were we the ones who sent the packet?
|
||||
return data != null && data.getFirst() == id;
|
||||
return data != null && data.first() == id;
|
||||
}
|
||||
|
||||
public void baseTickAddWaterPushing(Vector vector) {
|
||||
@@ -443,8 +443,7 @@ public class GrimPlayer implements GrimUser {
|
||||
|
||||
public void disconnect(Component reason) {
|
||||
String textReason;
|
||||
if (reason instanceof TranslatableComponent) {
|
||||
TranslatableComponent translatableComponent = (TranslatableComponent) reason;
|
||||
if (reason instanceof TranslatableComponent translatableComponent) {
|
||||
textReason = translatableComponent.key();
|
||||
} else {
|
||||
textReason = LegacyComponentSerializer.legacySection().serialize(reason);
|
||||
|
||||
@@ -6,7 +6,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
|
||||
public class GhostBlockDetector extends Check implements PostPredictionCheck {
|
||||
|
||||
@@ -258,8 +258,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// When in control of the entity, the player sets the entity position to their current position
|
||||
riding.setPositionRaw(GetBoundingBox.getPacketEntityBoundingBox(player, player.x, player.y, player.z, riding));
|
||||
|
||||
if (riding instanceof PacketEntityTrackXRot) {
|
||||
PacketEntityTrackXRot boat = (PacketEntityTrackXRot) riding;
|
||||
if (riding instanceof PacketEntityTrackXRot boat) {
|
||||
boat.packetYaw = player.xRot;
|
||||
boat.interpYaw = player.xRot;
|
||||
boat.steps = 0;
|
||||
@@ -339,7 +338,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
boolean clientClaimsRiptide = player.packetStateData.tryingToRiptide;
|
||||
if (player.packetStateData.tryingToRiptide) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.getFirst()));
|
||||
boolean isInWater = player.compensatedWorld.isRaining || Collisions.hasMaterial(player, player.boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
|
||||
|
||||
if (currentTime - player.packetStateData.lastRiptide < 450 || !isInWater) {
|
||||
player.packetStateData.tryingToRiptide = false;
|
||||
@@ -350,7 +349,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(0.03).offset(0, -1, 0);
|
||||
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
|
||||
WrappedBlockState data = pair.getFirst();
|
||||
WrappedBlockState data = pair.first();
|
||||
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
|
||||
player.uncertaintyHandler.isSteppingOnSlime = true;
|
||||
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
|
||||
@@ -396,8 +395,8 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
|
||||
player.uncertaintyHandler.isNearGlitchyBlock = player.getClientVersion().isOlderThan(ClientVersion.V_1_9)
|
||||
&& Collisions.hasMaterial(player, expandedBB.copy().expand(0.2),
|
||||
checkData -> BlockTags.ANVIL.contains(checkData.getFirst().getType())
|
||||
|| checkData.getFirst().getType() == StateTypes.CHEST || checkData.getFirst().getType() == StateTypes.TRAPPED_CHEST);
|
||||
checkData -> BlockTags.ANVIL.contains(checkData.first().getType())
|
||||
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
|
||||
|
||||
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
|
||||
player.uncertaintyHandler.checkForHardCollision();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package ac.grim.grimac.predictionengine;
|
||||
|
||||
import ac.grim.grimac.checks.impl.movement.NoSlowC;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.attribute.ValuedAttribute;
|
||||
@@ -88,8 +87,6 @@ public class PlayerBaseTick {
|
||||
|
||||
if (player.compensatedEntities.getSelf().inVehicle()) player.isSlowMovement = false;
|
||||
|
||||
if (wasSlowMovement != player.isSlowMovement) player.checkManager.getPostPredictionCheck(NoSlowC.class).startedSprintingBeforeSlowMovement = player.isSlowMovement && player.isSprinting;
|
||||
|
||||
// Players in boats don't care about being in blocks
|
||||
if (!player.compensatedEntities.getSelf().inVehicle()) {
|
||||
this.moveTowardsClosestSpace(player.lastX - (player.boundingBox.maxX - player.boundingBox.minX) * 0.35, player.lastZ + (player.boundingBox.maxZ - player.boundingBox.minZ) * 0.35);
|
||||
@@ -154,7 +151,7 @@ public class PlayerBaseTick {
|
||||
|
||||
// Might be null after respawn?
|
||||
final Optional<WrapperPlayServerUpdateAttributes.Property> property = playerSpeed.property();
|
||||
if (!property.isPresent()) return;
|
||||
if (property.isEmpty()) return;
|
||||
|
||||
// The client first desync's this attribute
|
||||
property.get().getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow"));
|
||||
@@ -363,22 +360,12 @@ public class PlayerBaseTick {
|
||||
double d7 = direction2 == BlockFace.WEST || direction2 == BlockFace.EAST ? relativeXMovement : relativeZMovement;
|
||||
d6 = direction2 == BlockFace.EAST || direction2 == BlockFace.SOUTH ? 1.0 - d7 : d7;
|
||||
// d7 and d6 flip the movement direction based on desired movement direction
|
||||
boolean doesSuffocate;
|
||||
switch (direction2) {
|
||||
case EAST:
|
||||
doesSuffocate = this.suffocatesAt(blockX + 1, blockZ);
|
||||
break;
|
||||
case WEST:
|
||||
doesSuffocate = this.suffocatesAt(blockX - 1, blockZ);
|
||||
break;
|
||||
case NORTH:
|
||||
doesSuffocate = this.suffocatesAt(blockX, blockZ - 1);
|
||||
break;
|
||||
default:
|
||||
case SOUTH:
|
||||
doesSuffocate = this.suffocatesAt(blockX, blockZ + 1);
|
||||
break;
|
||||
}
|
||||
boolean doesSuffocate = switch (direction2) {
|
||||
case EAST -> this.suffocatesAt(blockX + 1, blockZ);
|
||||
case WEST -> this.suffocatesAt(blockX - 1, blockZ);
|
||||
case NORTH -> this.suffocatesAt(blockX, blockZ - 1);
|
||||
default -> this.suffocatesAt(blockX, blockZ + 1);
|
||||
};
|
||||
|
||||
if (d6 >= lowestValue || doesSuffocate) continue;
|
||||
lowestValue = d6;
|
||||
|
||||
@@ -248,21 +248,21 @@ public class PointThreeEstimator {
|
||||
|
||||
// Check for flowing water
|
||||
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
|
||||
final WrappedBlockState state = pair.getFirst();
|
||||
final WrappedBlockState state = pair.first();
|
||||
final StateType stateType = state.getType();
|
||||
if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) {
|
||||
isNearClimbable = true;
|
||||
}
|
||||
|
||||
if (BlockTags.TRAPDOORS.contains(stateType)) {
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state);
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
|
||||
}
|
||||
|
||||
if (stateType == StateTypes.BUBBLE_COLUMN) {
|
||||
isNearBubbleColumn = true;
|
||||
}
|
||||
|
||||
if (Materials.isWater(player.getClientVersion(), pair.getFirst()) || pair.getFirst().getType() == StateTypes.LAVA) {
|
||||
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
|
||||
isNearFluid = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package ac.grim.grimac.predictionengine;
|
||||
import ac.grim.grimac.checks.Check;
|
||||
import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
|
||||
@@ -303,8 +303,7 @@ public class UncertaintyHandler {
|
||||
// This is the end of that section.
|
||||
|
||||
// I can't figure out how the client exactly tracks boost time
|
||||
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable) {
|
||||
PacketEntityRideable vehicle = (PacketEntityRideable) player.compensatedEntities.getSelf().getRiding();
|
||||
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityRideable vehicle) {
|
||||
if (vehicle.currentBoostTime < vehicle.boostTimeMax + 20)
|
||||
offset -= 0.01;
|
||||
}
|
||||
@@ -339,10 +338,9 @@ public class UncertaintyHandler {
|
||||
private boolean striderCollision(SimpleCollisionBox expandedBB) {
|
||||
// Stiders can walk on top of other striders
|
||||
if (player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityStrider) {
|
||||
for (Map.Entry<Integer, PacketEntity> entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) {
|
||||
PacketEntity entity = entityPair.getValue();
|
||||
if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding() && !entity.hasPassenger(entityPair.getValue())
|
||||
&& entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
|
||||
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
|
||||
if (entity.getType() == EntityTypes.STRIDER && entity != player.compensatedEntities.getSelf().getRiding()
|
||||
&& !entity.hasPassenger(entity) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -356,9 +354,9 @@ public class UncertaintyHandler {
|
||||
final PacketEntity riding = player.compensatedEntities.getSelf().getRiding();
|
||||
if (riding == null || !riding.isBoat()) return false;
|
||||
|
||||
for (Map.Entry<Integer, PacketEntity> entityPair : player.compensatedEntities.entityMap.int2ObjectEntrySet()) {
|
||||
PacketEntity entity = entityPair.getValue();
|
||||
if (entity != riding && entity.isPushable() && !riding.hasPassenger(entityPair.getValue()) && entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
|
||||
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
|
||||
if (entity != riding && entity.isPushable() && !riding.hasPassenger(entity)
|
||||
&& entity.getPossibleCollisionBoxes().isIntersected(expandedBB)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.github.retrooper.packetevents.protocol.world.states.defaulttags.Block
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import org.bukkit.Bukkit;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil;
|
||||
import org.bukkit.util.Vector;
|
||||
@@ -418,4 +417,4 @@ public class MovementTicker {
|
||||
public boolean canStandOnLava() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import ac.grim.grimac.utils.nmsutil.JumpPower;
|
||||
import ac.grim.grimac.utils.nmsutil.Riptide;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -134,8 +133,8 @@ public class PredictionEngine {
|
||||
|
||||
// Returns pair of primary push movement, and then outputvel
|
||||
Pair<Vector, Vector> output = doSeekingWallCollisions(player, primaryPushMovement, originalClientVel, clientVelAfterInput);
|
||||
primaryPushMovement = output.getFirst();
|
||||
Vector outputVel = clampMovementToHardBorder(player, output.getSecond());
|
||||
primaryPushMovement = output.first();
|
||||
Vector outputVel = clampMovementToHardBorder(player, output.second());
|
||||
|
||||
double resultAccuracy = outputVel.distanceSquared(player.actualMovement);
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
// And 0.03 didn't affect onGround status
|
||||
// The player cannot jump
|
||||
final OptionalInt jumpBoost = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.JUMP_BOOST);
|
||||
if (((!jumpBoost.isPresent() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
|
||||
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
|
||||
return;
|
||||
|
||||
JumpPower.jumpFromGround(player, jump);
|
||||
|
||||
+1
-3
@@ -17,9 +17,7 @@ import java.util.Set;
|
||||
|
||||
public class PredictionEngineRideableUtils {
|
||||
public static Set<VectorData> handleJumps(GrimPlayer player, Set<VectorData> possibleVectors) {
|
||||
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse)) return possibleVectors;
|
||||
|
||||
PacketEntityHorse horse = (PacketEntityHorse) player.compensatedEntities.getSelf().getRiding();
|
||||
if (!(player.compensatedEntities.getSelf().getRiding() instanceof PacketEntityHorse horse)) return possibleVectors;
|
||||
|
||||
// Setup player inputs
|
||||
float f = player.vehicleData.vehicleHorizontal * 0.5F;
|
||||
|
||||
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.collisions.AxisUtil;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.blocks.DoorHandler;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.HitData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
@@ -37,7 +38,6 @@ import lombok.Setter;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -72,6 +72,9 @@ public class BlockPlace {
|
||||
|
||||
@Getter private final boolean block;
|
||||
|
||||
// Allocated once instead of in functions to reduce new[] calls that need to be made. Since per-instance BlockPlace is always dealt with on the same thread we can use 1 buffer array
|
||||
private final SimpleCollisionBox[] collisions = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
|
||||
|
||||
public BlockPlace(GrimPlayer player, InteractionHand hand, Vector3i blockPosition, int faceId, BlockFace face, ItemStack itemStack, HitData hitData) {
|
||||
this.player = player;
|
||||
this.hand = hand;
|
||||
@@ -200,12 +203,12 @@ public class BlockPlace {
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
if (BlockTags.FENCE_GATES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
if (simpleBox.minX <= 7 / 16d && simpleBox.maxX >= 7 / 16d
|
||||
&& simpleBox.minY <= 0 && simpleBox.maxY >= 10 / 16d
|
||||
@@ -225,12 +228,12 @@ public class BlockPlace {
|
||||
if (isFullFace(facing)) return true;
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
if (simpleBox.minX <= 2 / 16d && simpleBox.maxX >= 14 / 16d
|
||||
&& simpleBox.minY <= 0 && simpleBox.maxY >= 1
|
||||
@@ -289,10 +292,10 @@ public class BlockPlace {
|
||||
}
|
||||
}
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
if (axis.modify(simpleBox).isFullBlockNoCache()) return true;
|
||||
}
|
||||
|
||||
@@ -318,12 +321,12 @@ public class BlockPlace {
|
||||
if (isFullFace(facing)) return true;
|
||||
if (BlockTags.LEAVES.contains(data.getType())) return false;
|
||||
|
||||
List<SimpleCollisionBox> collisions = new ArrayList<>();
|
||||
box.downCast(collisions);
|
||||
int size = box.downCast(collisions);
|
||||
|
||||
AxisSelect axis = AxisUtil.getAxis(facing.getOppositeFace());
|
||||
|
||||
for (SimpleCollisionBox simpleBox : collisions) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
SimpleCollisionBox simpleBox = collisions[i];
|
||||
simpleBox = axis.modify(simpleBox);
|
||||
// If all sides to the box have width, there is collision.
|
||||
switch (facing) {
|
||||
@@ -537,21 +540,14 @@ public class BlockPlace {
|
||||
}
|
||||
|
||||
public Vector3i getNormalBlockFace() {
|
||||
switch (face) {
|
||||
default:
|
||||
case UP:
|
||||
return new Vector3i(0, 1, 0);
|
||||
case DOWN:
|
||||
return new Vector3i(0, -1, 0);
|
||||
case SOUTH:
|
||||
return new Vector3i(0, 0, 1);
|
||||
case NORTH:
|
||||
return new Vector3i(0, 0, -1);
|
||||
case WEST:
|
||||
return new Vector3i(-1, 0, 0);
|
||||
case EAST:
|
||||
return new Vector3i(1, 0, 0);
|
||||
}
|
||||
return switch (face) {
|
||||
case DOWN -> new Vector3i(0, -1, 0);
|
||||
case SOUTH -> new Vector3i(0, 0, 1);
|
||||
case NORTH -> new Vector3i(0, 0, -1);
|
||||
case WEST -> new Vector3i(-1, 0, 0);
|
||||
case EAST -> new Vector3i(1, 0, 0);
|
||||
default -> new Vector3i(0, 1, 0);
|
||||
};
|
||||
}
|
||||
|
||||
public void set(StateType material) {
|
||||
@@ -672,7 +668,7 @@ public class BlockPlace {
|
||||
final double distance = player.compensatedEntities.getSelf().getAttributeValue(Attributes.PLAYER_BLOCK_INTERACTION_RANGE) + 3;
|
||||
Vector eyePos = new Vector(player.x, player.y + player.getEyeHeight(), player.z);
|
||||
Vector endReachPos = eyePos.clone().add(new Vector(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
|
||||
Vector intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).getFirst();
|
||||
Vector intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).first();
|
||||
|
||||
// Bring this back to relative to the block
|
||||
// The player didn't even click the block... (we should force resync BEFORE we get here!)
|
||||
|
||||
@@ -16,4 +16,4 @@ public final class PositionUpdate {
|
||||
private final SetBackData setback;
|
||||
private final TeleportData teleportData;
|
||||
private boolean isTeleport;
|
||||
}
|
||||
}
|
||||
@@ -107,17 +107,11 @@ public class ConsumesBlockPlace {
|
||||
Attachment attachment = bell.getAttachment();
|
||||
BlockFace dir2 = BlockFace.valueOf(direction.name());
|
||||
|
||||
switch (attachment) {
|
||||
case FLOOR:
|
||||
return AxisUtil.isSameAxis(dir, dir2);
|
||||
case SINGLE_WALL:
|
||||
case DOUBLE_WALL:
|
||||
return !AxisUtil.isSameAxis(dir, dir2);
|
||||
case CEILING:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return switch (attachment) {
|
||||
case FLOOR -> AxisUtil.isSameAxis(dir, dir2);
|
||||
case SINGLE_WALL, DOUBLE_WALL -> !AxisUtil.isSameAxis(dir, dir2);
|
||||
case CEILING -> true;
|
||||
};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,64 +18,42 @@ public class BlockFaceHelper {
|
||||
*/
|
||||
@Deprecated
|
||||
public static BlockFace fromBukkitFace(org.bukkit.block.BlockFace face) {
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.NORTH;
|
||||
case SOUTH:
|
||||
return BlockFace.SOUTH;
|
||||
case WEST:
|
||||
return BlockFace.WEST;
|
||||
case EAST:
|
||||
return BlockFace.EAST;
|
||||
case UP:
|
||||
return BlockFace.UP;
|
||||
case DOWN:
|
||||
return BlockFace.DOWN;
|
||||
default:
|
||||
return BlockFace.OTHER;
|
||||
}
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.NORTH;
|
||||
case SOUTH -> BlockFace.SOUTH;
|
||||
case WEST -> BlockFace.WEST;
|
||||
case EAST -> BlockFace.EAST;
|
||||
case UP -> BlockFace.UP;
|
||||
case DOWN -> BlockFace.DOWN;
|
||||
default -> BlockFace.OTHER;
|
||||
};
|
||||
}
|
||||
|
||||
public static BlockFace getClockWise(BlockFace face) {
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.EAST;
|
||||
case SOUTH:
|
||||
return BlockFace.WEST;
|
||||
case WEST:
|
||||
return BlockFace.NORTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.EAST;
|
||||
case SOUTH -> BlockFace.WEST;
|
||||
case WEST -> BlockFace.NORTH;
|
||||
default -> BlockFace.SOUTH;
|
||||
};
|
||||
}
|
||||
|
||||
public static BlockFace getPEClockWise(BlockFace face) {
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.EAST;
|
||||
case SOUTH:
|
||||
return BlockFace.WEST;
|
||||
case WEST:
|
||||
return BlockFace.NORTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.EAST;
|
||||
case SOUTH -> BlockFace.WEST;
|
||||
case WEST -> BlockFace.NORTH;
|
||||
default -> BlockFace.SOUTH;
|
||||
};
|
||||
}
|
||||
|
||||
public static BlockFace getCounterClockwise(BlockFace face) {
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return BlockFace.WEST;
|
||||
case SOUTH:
|
||||
return BlockFace.EAST;
|
||||
case WEST:
|
||||
return BlockFace.SOUTH;
|
||||
case EAST:
|
||||
default:
|
||||
return BlockFace.NORTH;
|
||||
}
|
||||
return switch (face) {
|
||||
case NORTH -> BlockFace.WEST;
|
||||
case SOUTH -> BlockFace.EAST;
|
||||
case WEST -> BlockFace.SOUTH;
|
||||
default -> BlockFace.NORTH;
|
||||
};
|
||||
}
|
||||
|
||||
public Vector offset(Vector toOffset, BlockFace face) {
|
||||
|
||||
@@ -4,4 +4,4 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
|
||||
public interface AxisSelect {
|
||||
SimpleCollisionBox modify(SimpleCollisionBox box);
|
||||
}
|
||||
}
|
||||
@@ -54,36 +54,22 @@ public enum AxisUtil {
|
||||
}
|
||||
|
||||
public static AxisSelect getAxis(BlockFace face) {
|
||||
switch (face) {
|
||||
case EAST:
|
||||
return EAST.select;
|
||||
case WEST:
|
||||
return WEST.select;
|
||||
case NORTH:
|
||||
return NORTH.select;
|
||||
case SOUTH:
|
||||
return SOUTH.select;
|
||||
case UP:
|
||||
return UP.select;
|
||||
case DOWN:
|
||||
default:
|
||||
return DOWN.select;
|
||||
}
|
||||
return switch (face) {
|
||||
case EAST -> EAST.select;
|
||||
case WEST -> WEST.select;
|
||||
case NORTH -> NORTH.select;
|
||||
case SOUTH -> SOUTH.select;
|
||||
case UP -> UP.select;
|
||||
default -> DOWN.select;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isSameAxis(BlockFace one, BlockFace two) {
|
||||
switch (one) {
|
||||
case WEST:
|
||||
case EAST:
|
||||
return two == BlockFace.WEST || two == BlockFace.EAST;
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return two == BlockFace.NORTH || two == BlockFace.SOUTH;
|
||||
case UP:
|
||||
case DOWN:
|
||||
return two == BlockFace.UP || two == BlockFace.DOWN;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return switch (one) {
|
||||
case WEST, EAST -> two == BlockFace.WEST || two == BlockFace.EAST;
|
||||
case NORTH, SOUTH -> two == BlockFace.NORTH || two == BlockFace.SOUTH;
|
||||
case UP, DOWN -> two == BlockFace.UP || two == BlockFace.DOWN;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,9 @@
|
||||
package ac.grim.grimac.utils.collisions;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicWall;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxFence;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxPane;
|
||||
import ac.grim.grimac.utils.collisions.blocks.connecting.DynamicHitboxWall;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
@@ -17,55 +18,41 @@ import java.util.*;
|
||||
|
||||
// Expansion to the CollisionData class, which is different than regular ray tracing hitboxes
|
||||
public enum HitboxData {
|
||||
SCAFFOLDING((player, item, version, data, x, y, z) -> {
|
||||
// If is holding scaffolding
|
||||
if (item == StateTypes.SCAFFOLDING) {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
RAILS((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getShape()) {
|
||||
case ASCENDING_NORTH, ASCENDING_SOUTH, ASCENDING_EAST, ASCENDING_WEST -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_8)) {
|
||||
StateType railType = data.getType();
|
||||
// Activator rails always appear as flat detector rails in 1.7.10 because of ViaVersion
|
||||
// Ascending power rails in 1.7 have flat rail hitbox https://bugs.mojang.com/browse/MC-9134
|
||||
if (railType == StateTypes.ACTIVATOR_RAIL || (railType == StateTypes.POWERED_RAIL && data.isPowered())) {
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F, false);
|
||||
}
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_9)) {
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F, false);
|
||||
} else if (version.isNewerThanOrEquals(ClientVersion.V_1_9) && version.isOlderThan(ClientVersion.V_1_10)) {
|
||||
// https://bugs.mojang.com/browse/MC-89552 sloped rails in 1.9 - it is slightly taller than a regular rail
|
||||
yield new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F, false);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_11)) {
|
||||
// https://bugs.mojang.com/browse/MC-102638 All sloped rails are full blocks in 1.10
|
||||
yield new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
yield new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
|
||||
}
|
||||
default -> new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
};
|
||||
}, BlockTags.RAILS.getStates().toArray(new StateType[0])),
|
||||
|
||||
END_PORTAL((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_9)) {
|
||||
return new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 0.0625D, 1.0D);
|
||||
} else if (version.isOlderThan(ClientVersion.V_1_17)) {
|
||||
return new SimpleCollisionBox(0.0, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
|
||||
}
|
||||
|
||||
// STABLE_SHAPE for the scaffolding
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(
|
||||
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 14.0D, 16.0D, 16.0D, 16.0D));
|
||||
|
||||
if (data.getHalf() == Half.LOWER) { // Add the unstable shape to the collision boxes
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 14.0D, 16.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 2.0D));
|
||||
}
|
||||
|
||||
return box;
|
||||
}, StateTypes.SCAFFOLDING),
|
||||
|
||||
DRIPLEAF((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
|
||||
if (data.getFacing() == BlockFace.NORTH) { // Stem
|
||||
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
|
||||
} else if (data.getFacing() == BlockFace.SOUTH) {
|
||||
box.add(new HexCollisionBox(5.0D, 0.0D, 1.0D, 11.0D, 15.0D, 7.0D));
|
||||
} else if (data.getFacing() == BlockFace.EAST) {
|
||||
box.add(new HexCollisionBox(1.0D, 0.0D, 5.0D, 7.0D, 15.0D, 11.0D));
|
||||
} else {
|
||||
box.add(new HexCollisionBox(9.0D, 0.0D, 5.0D, 15.0D, 15.0D, 11.0D));
|
||||
}
|
||||
|
||||
if (data.getTilt() == Tilt.NONE || data.getTilt() == Tilt.UNSTABLE) {
|
||||
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 15.0, 16.0));
|
||||
} else if (data.getTilt() == Tilt.PARTIAL) {
|
||||
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 13.0, 16.0));
|
||||
}
|
||||
|
||||
return box;
|
||||
|
||||
}, StateTypes.BIG_DRIPLEAF),
|
||||
return new HexCollisionBox(0.0D, 6.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
}, StateTypes.END_PORTAL),
|
||||
|
||||
FENCE_GATE((player, item, version, data, x, y, z) -> {
|
||||
// This technically should be taken from the block data/made multi-version/run block updates... but that's too far even for me
|
||||
@@ -90,41 +77,214 @@ public enum HitboxData {
|
||||
}, BlockTags.FENCE_GATES.getStates().toArray(new StateType[0])),
|
||||
|
||||
|
||||
FENCE((player, item, version, data, x, y, z) -> {
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
|
||||
FENCE(new DynamicHitboxFence(), BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_12_2)) {
|
||||
int i = 0;
|
||||
if (data.getSouth() == South.TRUE) {
|
||||
i |= 0b1;
|
||||
}
|
||||
if (data.getWest() == West.TRUE) {
|
||||
i |= 0b10;
|
||||
}
|
||||
if (data.getNorth() == North.TRUE) {
|
||||
i |= 0b100;
|
||||
}
|
||||
if (data.getEast() == East.TRUE) {
|
||||
i |= 0b1000;
|
||||
}
|
||||
PANE(new DynamicHitboxPane(), Materials.getPanes().toArray(new StateType[0])),
|
||||
|
||||
return DynamicFence.LEGACY_BOUNDING_BOXES[i].copy();
|
||||
LEVER(((player, item, version, data, x, y, z) -> {
|
||||
Face face = data.getFace();
|
||||
BlockFace facing = data.getFacing();
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
double f = 0.1875;
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(1.0 - f * 2.0, 0.2, 0.5 - f, 1.0, 0.8, 0.5 + f, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.2, 0.5 - f, f * 2.0, 0.8, 0.5 + f, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 1.0 - f * 2.0, 0.5 + f, 0.8, 1.0, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.5 - f, 0.2, 0.0, 0.5 + f, 0.8, f * 2.0, false);
|
||||
}
|
||||
case CEILING:
|
||||
return new SimpleCollisionBox(0.25, 0.4, 0.25, 0.75, 1.0, 0.75, false);
|
||||
case FLOOR:
|
||||
return new SimpleCollisionBox(0.25, 0.0, 0.25, 0.75, 0.6, 0.75, false);
|
||||
}
|
||||
}
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
CollisionData.getData(state.getType()).getMovementCollisionBox(player, version, state).downCast(boxes);
|
||||
return switch (face) {
|
||||
case FLOOR -> {
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
yield new SimpleCollisionBox(0.25, 0.0, 0.3125, 0.75, 0.375, 0.6875, false);
|
||||
}
|
||||
// Z-AXIS
|
||||
yield new SimpleCollisionBox(0.3125, 0.0, 0.25, 0.6875, 0.375, 0.75, false);
|
||||
// Z-AXIS
|
||||
}
|
||||
case WALL -> switch (facing) {
|
||||
case EAST -> new SimpleCollisionBox(0.0, 0.25, 0.3125, 0.375, 0.75, 0.6875, false);
|
||||
case WEST -> new SimpleCollisionBox(0.625, 0.25, 0.3125, 1.0, 0.75, 0.6875, false);
|
||||
case SOUTH -> new SimpleCollisionBox(0.3125, 0.25, 0.0, 0.6875, 0.75, 0.375, false);
|
||||
default -> new SimpleCollisionBox(0.3125, 0.25, 0.625, 0.6875, 0.75, 1.0, false);
|
||||
};
|
||||
default -> {
|
||||
// X-AXIS
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
yield new SimpleCollisionBox(0.25, 0.625, 0.3125, 0.75, 1.0, 0.6875, false);
|
||||
}
|
||||
// Z-Axis
|
||||
yield new SimpleCollisionBox(0.3125, 0.625, 0.25, 0.6875, 1.0, 0.75, false);
|
||||
}
|
||||
};
|
||||
}), StateTypes.LEVER),
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
box.maxY = 1;
|
||||
BUTTON((player, item, version, data, x, y, z) -> {
|
||||
final Face face = data.getFace();
|
||||
final BlockFace facing = data.getFacing();
|
||||
final boolean powered = data.isPowered();
|
||||
|
||||
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
double f2 = (float) (data.isPowered() ? 1 : 2) / 16.0;
|
||||
|
||||
switch (face) {
|
||||
case WALL:
|
||||
switch (facing) {
|
||||
case WEST:
|
||||
return new SimpleCollisionBox(1.0 - f2, 0.375, 0.3125, 1.0, 0.625, 0.6875, false);
|
||||
case EAST:
|
||||
return new SimpleCollisionBox(0.0, 0.375, 0.3125, f2, 0.625, 0.6875, false);
|
||||
case NORTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 1.0 - f2, 0.6875, 0.625, 1.0, false);
|
||||
case SOUTH:
|
||||
return new SimpleCollisionBox(0.3125, 0.375, 0.0, 0.6875, 0.625, f2, false);
|
||||
}
|
||||
case CEILING:
|
||||
return new SimpleCollisionBox(0.3125, 1.0 - f2, 0.375, 0.6875, 1.0, 0.625, false);
|
||||
case FLOOR:
|
||||
return new SimpleCollisionBox(0.3125, 0.0, 0.375, 0.6875, 0.0 + f2, 0.625, false);
|
||||
}
|
||||
}
|
||||
|
||||
return new ComplexCollisionBox(boxes.toArray(new SimpleCollisionBox[0]));
|
||||
}, BlockTags.FENCES.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL((player, item, version, data, x, y, z) -> {
|
||||
WrappedBlockState state = player.compensatedWorld.getWrappedBlockStateAt(x, y, z);
|
||||
return new DynamicWall().fetchRegularBox(player, state, version, x, y, z);
|
||||
}, BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
switch (face) {
|
||||
case WALL:
|
||||
return switch (facing) {
|
||||
case EAST ->
|
||||
powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
case WEST ->
|
||||
powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
case SOUTH ->
|
||||
powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
case NORTH, UP, DOWN ->
|
||||
powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
case CEILING:
|
||||
// ViaVersion shows lever
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
|
||||
} else {
|
||||
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
|
||||
}
|
||||
case FLOOR:
|
||||
// ViaVersion shows lever
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_8)) {
|
||||
return LEVER.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
|
||||
}
|
||||
|
||||
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
|
||||
default:
|
||||
throw new IllegalStateException("Impossible Hitbox State");
|
||||
}
|
||||
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL(new DynamicHitboxWall(), BlockTags.WALLS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_SIGN((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH -> new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST -> new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST -> new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH, SOUTH -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
};
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
STANDING_SIGN((player, item, version, data, x, y, z) ->
|
||||
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
|
||||
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
SAPLING(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
BlockTags.SAPLINGS.getStates().toArray(new StateType[0])),
|
||||
|
||||
ROOTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D),
|
||||
StateTypes.WARPED_ROOTS, StateTypes.CRIMSON_ROOTS),
|
||||
|
||||
BANNER(new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D),
|
||||
StateTypes.WHITE_BANNER, StateTypes.ORANGE_BANNER, StateTypes.MAGENTA_BANNER, StateTypes.LIGHT_BLUE_BANNER,
|
||||
StateTypes.YELLOW_BANNER, StateTypes.LIME_BANNER, StateTypes.PINK_BANNER, StateTypes.GRAY_BANNER,
|
||||
StateTypes.LIGHT_GRAY_BANNER, StateTypes.CYAN_BANNER, StateTypes.PURPLE_BANNER, StateTypes.BLUE_BANNER,
|
||||
StateTypes.BROWN_BANNER, StateTypes.GREEN_BANNER, StateTypes.RED_BANNER, StateTypes.BLACK_BANNER),
|
||||
|
||||
WALL_BANNER((player, item, version, data, x, y, z) -> {
|
||||
// ViaVersion replacement block
|
||||
if (version.isOlderThan(ClientVersion.V_1_8)) {
|
||||
return WALL_SIGN.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(0.0, 0.0, 14.0, 16.0, 12.5, 16.0);
|
||||
case EAST -> new HexCollisionBox(0.0, 0.0, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST -> new HexCollisionBox(14.0, 0.0, 0.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH -> new HexCollisionBox(0.0, 0.0, 0.0, 16.0, 12.5, 2.0);
|
||||
default -> throw new IllegalStateException("Impossible Banner Facing State; Something very wrong is going on");
|
||||
};
|
||||
}, StateTypes.WHITE_WALL_BANNER, StateTypes.ORANGE_WALL_BANNER, StateTypes.MAGENTA_WALL_BANNER,
|
||||
StateTypes.LIGHT_BLUE_WALL_BANNER, StateTypes.YELLOW_WALL_BANNER, StateTypes.LIME_WALL_BANNER,
|
||||
StateTypes.PINK_WALL_BANNER, StateTypes.GRAY_WALL_BANNER, StateTypes.LIGHT_GRAY_WALL_BANNER,
|
||||
StateTypes.CYAN_WALL_BANNER, StateTypes.PURPLE_WALL_BANNER, StateTypes.BLUE_WALL_BANNER,
|
||||
StateTypes.BROWN_WALL_BANNER, StateTypes.GREEN_WALL_BANNER, StateTypes.RED_WALL_BANNER, StateTypes.BLACK_WALL_BANNER),
|
||||
|
||||
BREWING_STAND((player, item, version, block, x, y, z) -> {
|
||||
// BEWARE OF https://bugs.mojang.com/browse/MC-85109 FOR 1.8 PLAYERS
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
return new SimpleCollisionBox(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
|
||||
} else {
|
||||
return new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 2.0, 15.0),
|
||||
new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625, false));
|
||||
}
|
||||
}, StateTypes.BREWING_STAND),
|
||||
|
||||
SMALL_FLOWER((player, item, version, data, x, y, z) -> player.getClientVersion().isOlderThan(ClientVersion.V_1_13)
|
||||
? new SimpleCollisionBox(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D)
|
||||
: new OffsetCollisionBox(data.getType(), 0.3125D, 0.0D, 0.3125D, 0.6875D, 0.625D, 0.6875D),
|
||||
BlockTags.SMALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
TALL_FLOWERS(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), BlockTags.TALL_FLOWERS.getStates().toArray(new StateType[0])),
|
||||
|
||||
FIRE((player, item, version, data, x, y, z) -> {
|
||||
// Since 1.16 fire has a small hitbox
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
|
||||
}
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
|
||||
|
||||
HONEY_BLOCK(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.HONEY_BLOCK),
|
||||
|
||||
@@ -132,14 +292,21 @@ public enum HitboxData {
|
||||
|
||||
SOUL_SAND(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SOUL_SAND),
|
||||
|
||||
CACTUS(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CACTUS),
|
||||
CACTUS((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
// https://bugs.mojang.com/browse/MC-59610
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
}, StateTypes.CACTUS),
|
||||
|
||||
SNOW((player, item, version, data, x, y, z) -> {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, data.getLayers() * 0.125, 1);
|
||||
}, StateTypes.SNOW),
|
||||
|
||||
LECTERN_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
ComplexCollisionBox common = new ComplexCollisionBox(5,
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
|
||||
new HexCollisionBox(4.0D, 2.0D, 4.0D, 12.0D, 14.0D, 12.0D));
|
||||
|
||||
if (data.getFacing() == BlockFace.WEST) {
|
||||
@@ -163,126 +330,261 @@ public enum HitboxData {
|
||||
return common;
|
||||
}, StateTypes.LECTERN),
|
||||
|
||||
WALL_HANGING_SIGNS((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
case WEST:
|
||||
case EAST:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
GLOW_LICHEN_SCULK_VEIN((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(6);
|
||||
|
||||
if (data.isUp()) {
|
||||
box.add(new HexCollisionBox(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D));
|
||||
}
|
||||
if (data.isDown()) {
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D));
|
||||
}
|
||||
if (data.getWest() == West.TRUE) {
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D));
|
||||
}
|
||||
if (data.getEast() == East.TRUE) {
|
||||
box.add(new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D));
|
||||
}
|
||||
if (data.getNorth() == North.TRUE) {
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D));
|
||||
}
|
||||
if (data.getSouth() == South.TRUE) {
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D));
|
||||
}
|
||||
|
||||
return box;
|
||||
} else { // ViaVersion just replaces this with... nothing
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
}, StateTypes.GLOW_LICHEN, StateTypes.SCULK_CATALYST),
|
||||
|
||||
SPORE_BLOSSOM((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_16_4)) {
|
||||
return new HexCollisionBox(2.0D, 13.0D, 2.0D, 14.0D, 16.0D, 14.0D);
|
||||
} else { // ViaVersion replacement is a Peony
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
}, StateTypes.SPORE_BLOSSOM),
|
||||
|
||||
PITCHER_CROP((player, item, version, data, x, y, z) -> {
|
||||
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
|
||||
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
|
||||
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
|
||||
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
|
||||
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
|
||||
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
|
||||
if (version.isNewerThan(ClientVersion.V_1_19_4)) {
|
||||
final SimpleCollisionBox FULL_UPPER_SHAPE = new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 15.0D, 13.0D);
|
||||
final SimpleCollisionBox FULL_LOWER_SHAPE = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 16.0D, 13.0D);
|
||||
final SimpleCollisionBox COLLISION_SHAPE_BULB = new HexCollisionBox(5.0D, -1.0D, 5.0D, 11.0D, 3.0D, 11.0D);
|
||||
final SimpleCollisionBox COLLISION_SHAPE_CROP = new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 5.0D, 13.0D);
|
||||
final SimpleCollisionBox[] UPPER_SHAPE_BY_AGE = new SimpleCollisionBox[]{new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 11.0D, 13.0D), FULL_UPPER_SHAPE};
|
||||
final SimpleCollisionBox[] LOWER_SHAPE_BY_AGE = new SimpleCollisionBox[]{COLLISION_SHAPE_BULB, new HexCollisionBox(3.0D, -1.0D, 3.0D, 13.0D, 14.0D, 13.0D), FULL_LOWER_SHAPE, FULL_LOWER_SHAPE, FULL_LOWER_SHAPE};
|
||||
|
||||
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
|
||||
return data.getHalf() == Half.UPPER ? UPPER_SHAPE_BY_AGE[Math.min(Math.abs(4 - (data.getAge() + 1)), UPPER_SHAPE_BY_AGE.length - 1)] : LOWER_SHAPE_BY_AGE[data.getAge()];
|
||||
} else {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
}, StateTypes.PITCHER_CROP),
|
||||
|
||||
BUTTON((player, item, version, data, x, y, z) -> {
|
||||
final BlockFace facing = data.getFacing();
|
||||
final boolean powered = data.isPowered();
|
||||
switch (data.getFace()) {
|
||||
case FLOOR:
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 1.0, 11.0) : new HexCollisionBox(6.0, 0.0, 5.0, 10.0, 2.0, 11.0);
|
||||
}
|
||||
WHEAT_BEETROOTS((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, (data.getAge() + 1) * 2, 16.0D);
|
||||
}, StateTypes.WHEAT, StateTypes.BEETROOTS),
|
||||
|
||||
return powered ? new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 1.0, 10.0) : new HexCollisionBox(5.0, 0.0, 6.0, 11.0, 2.0, 10.0);
|
||||
case WALL:
|
||||
CollisionBox shape;
|
||||
switch (facing) {
|
||||
case EAST:
|
||||
shape = powered ? new HexCollisionBox(0.0, 6.0, 5.0, 1.0, 10.0, 11.0) : new HexCollisionBox(0.0, 6.0, 5.0, 2.0, 10.0, 11.0);
|
||||
break;
|
||||
case WEST:
|
||||
shape = powered ? new HexCollisionBox(15.0, 6.0, 5.0, 16.0, 10.0, 11.0) : new HexCollisionBox(14.0, 6.0, 5.0, 16.0, 10.0, 11.0);
|
||||
break;
|
||||
case SOUTH:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 1.0) : new HexCollisionBox(5.0, 6.0, 0.0, 11.0, 10.0, 2.0);
|
||||
break;
|
||||
case NORTH:
|
||||
case UP:
|
||||
case DOWN:
|
||||
shape = powered ? new HexCollisionBox(5.0, 6.0, 15.0, 11.0, 10.0, 16.0) : new HexCollisionBox(5.0, 6.0, 14.0, 11.0, 10.0, 16.0);
|
||||
break;
|
||||
default:
|
||||
shape = NoCollisionBox.INSTANCE;
|
||||
}
|
||||
CARROT_POTATOES((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, data.getAge() + 2, 16.0D);
|
||||
}, StateTypes.CARROTS, StateTypes.POTATOES),
|
||||
|
||||
return shape;
|
||||
case CEILING:
|
||||
default:
|
||||
// x axis
|
||||
if (facing == BlockFace.EAST || facing == BlockFace.WEST) {
|
||||
return powered ? new HexCollisionBox(6.0, 15.0, 5.0, 10.0, 16.0, 11.0) : new HexCollisionBox(6.0, 14.0, 5.0, 10.0, 16.0, 11.0);
|
||||
} else {
|
||||
return powered ? new HexCollisionBox(5.0, 15.0, 6.0, 11.0, 16.0, 10.0) : new HexCollisionBox(5.0, 14.0, 6.0, 11.0, 16.0, 10.0);
|
||||
}
|
||||
NETHER_WART((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0, 5 + (data.getAge() * 3), 16.0D);
|
||||
}, StateTypes.NETHER_WART),
|
||||
|
||||
ATTACHED_PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13))
|
||||
return new HexCollisionBox(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D);
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case SOUTH -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 16.0D);
|
||||
case WEST -> new HexCollisionBox(0.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
case NORTH -> new HexCollisionBox(6.0D, 0.0D, 0.0D, 10.0D, 10.0D, 10.0D);
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 16.0D, 10.0D, 10.0D);
|
||||
};
|
||||
}, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM),
|
||||
|
||||
PUMPKIN_STEM((player, item, version, data, x, y, z) -> {
|
||||
return new HexCollisionBox(7, 0, 7, 9, 2 * (data.getAge() + 1), 9);
|
||||
}, StateTypes.PUMPKIN_STEM, StateTypes.MELON_STEM),
|
||||
|
||||
// Hitbox/Outline is Same as Collision
|
||||
COCOA_BEANS((player, item, version, data, x, y, z) -> {
|
||||
return CollisionData.getCocoa(version, data.getAge(), data.getFacing());
|
||||
}, StateTypes.COCOA),
|
||||
|
||||
|
||||
// Easier to just use no collision box
|
||||
// Redstone wire is very complex with its collision shapes and has many de-syncs
|
||||
REDSTONE_WIRE(NoCollisionBox.INSTANCE, StateTypes.REDSTONE_WIRE),
|
||||
|
||||
SWEET_BERRY((player, item, version, data, x, y, z) -> {
|
||||
if (data.getAge() == 0) {
|
||||
return new HexCollisionBox(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
|
||||
} else if (data.getAge() < 3) {
|
||||
return new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
}
|
||||
}, BlockTags.BUTTONS.getStates().toArray(new StateType[0])),
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}, StateTypes.SWEET_BERRY_BUSH),
|
||||
|
||||
WALL_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 14.0, 16.0, 12.5, 16.0);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 16.0, 12.5, 2.0);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0.0, 4.5, 0.0, 2.0, 12.5, 16.0);
|
||||
case WEST:
|
||||
return new HexCollisionBox(14.0, 4.5, 0.0, 16.0, 12.5, 16.0);
|
||||
default:
|
||||
return NoCollisionBox.INSTANCE;
|
||||
CORAL_FAN(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 4.0D, 14.0D), BlockTags.CORALS.getStates().toArray(new StateType[0])),
|
||||
|
||||
TORCHFLOWER_CROP((player, item, version, data, x, y, z) -> {
|
||||
if (data.getAge() == 0) {
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
|
||||
}
|
||||
}, BlockTags.WALL_SIGNS.getStates().toArray(new StateType[0])),
|
||||
// age is 1
|
||||
return new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 10.0D, 11.0D);
|
||||
}, StateTypes.TORCHFLOWER_CROP),
|
||||
|
||||
WALL_HANGING_SIGN((player, item, version, data, x, y, z) -> {
|
||||
switch (data.getFacing()) {
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0.0D, 14.0D, 6.0D, 16.0D, 16.0D, 10.0D),
|
||||
new HexCollisionBox(1.0D, 0.0D, 7.0D, 15.0D, 10.0D, 9.0D));
|
||||
default:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(6.0D, 14.0D, 0.0D, 10.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(7.0D, 0.0D, 1.0D, 9.0D, 10.0D, 15.0D));
|
||||
DEAD_BUSH(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D), StateTypes.DEAD_BUSH),
|
||||
|
||||
SUGARCANE(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.SUGAR_CANE),
|
||||
|
||||
NETHER_SPROUTS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 3.0D, 14.0D), StateTypes.NETHER_SPROUTS),
|
||||
|
||||
HANGING_ROOTS(new HexCollisionBox(2.0D, 10.0D, 2.0D, 14.0D, 16.0D, 14.0D), StateTypes.HANGING_ROOTS),
|
||||
|
||||
GRASS_FERN((player, item, version, data, x ,y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_13)) {
|
||||
return new SimpleCollisionBox(0.1F, 0.0F, 0.1F, 0.9F, 0.8F, 0.9F);
|
||||
}
|
||||
}, BlockTags.WALL_HANGING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
return new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 13.0D, 14.0D);
|
||||
}, StateTypes.SHORT_GRASS, StateTypes.FERN),
|
||||
|
||||
STANDING_SIGN((player, item, version, data, x, y, z) ->
|
||||
new HexCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0),
|
||||
BlockTags.STANDING_SIGNS.getStates().toArray(new StateType[0])),
|
||||
SEA_GRASS(new HexCollisionBox(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D),
|
||||
StateTypes.SEAGRASS),
|
||||
|
||||
REDSTONE_WIRE((player, item, version, data, x, y, z) ->
|
||||
// Easier to just use no collision box
|
||||
// Redstone wire is very complex with its collision shapes and has many de-syncs
|
||||
NoCollisionBox.INSTANCE,
|
||||
StateTypes.REDSTONE_WIRE),
|
||||
TALL_SEAGRASS(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 16.0, 14.0),
|
||||
StateTypes.TALL_SEAGRASS),
|
||||
|
||||
FIRE((player, item, version, data, x, y, z) -> {
|
||||
// Since 1.16 fire has a small hitbox
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_16)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D);
|
||||
SMALL_DRIPLEAF(new HexCollisionBox(2.0, 0.0, 2.0, 14.0, 13.0, 14.0), StateTypes.SMALL_DRIPLEAF),
|
||||
|
||||
CAVE_VINES(new HexCollisionBox(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D), StateTypes.CAVE_VINES, StateTypes.CAVE_VINES_PLANT),
|
||||
|
||||
// Then your enum entries become:
|
||||
TWISTING_VINES_BLOCK((player, item, version, data, x, y, z) ->
|
||||
getVineCollisionBox(version, false, true), StateTypes.TWISTING_VINES),
|
||||
|
||||
WEEPING_VINES_BLOCK((player, item, version, data, x, y, z) ->
|
||||
getVineCollisionBox(version, true, true), StateTypes.WEEPING_VINES),
|
||||
|
||||
TWISTING_VINES((player, item, version, data, x, y, z) ->
|
||||
getVineCollisionBox(version, false, false), StateTypes.TWISTING_VINES_PLANT),
|
||||
|
||||
WEEPING_VINES((player, item, version, data, x, y, z) ->
|
||||
getVineCollisionBox(version, true, false), StateTypes.WEEPING_VINES_PLANT),
|
||||
|
||||
TALL_PLANT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.TALL_GRASS, StateTypes.LARGE_FERN),
|
||||
|
||||
BAMBOO((player, item, version, data, x, y, z) -> data.getLeaves() == Leaves.LARGE
|
||||
? new HexOffsetCollisionBox(data.getType(), 3.0, 0.0, 3.0, 13.0, 16.0, 13.0)
|
||||
: new HexOffsetCollisionBox(data.getType(), 5.0, 0.0, 5.0, 11.0, 16.0, 11.0), StateTypes.BAMBOO),
|
||||
|
||||
BAMBOO_SAPLING((player, item, version, data, x, y, z) -> {
|
||||
return new HexOffsetCollisionBox(data.getType(), 4.0D, 0.0D, 4.0D, 12.0D, 12.0D, 12.0D);
|
||||
}, StateTypes.BAMBOO_SAPLING),
|
||||
|
||||
SCAFFOLDING((player, item, version, data, x, y, z) -> {
|
||||
// If is holding scaffolding or Via replacement - hay bale
|
||||
if (item == StateTypes.SCAFFOLDING || version.isOlderThan(ClientVersion.V_1_14)) {
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}, BlockTags.FIRE.getStates().toArray(new StateType[0])),
|
||||
// STABLE_SHAPE for the scaffolding
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(9,
|
||||
new HexCollisionBox(0.0D, 14.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 16.0D, 2.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 14.0D, 2.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(14.0D, 0.0D, 14.0D, 16.0D, 16.0D, 16.0D));
|
||||
|
||||
BANNER(((player, item, version, data, x, y, z) ->
|
||||
new SimpleCollisionBox(4.0, 0.0, 4.0, 12.0, 16.0, 12.0)),
|
||||
BlockTags.BANNERS.getStates().toArray(new StateType[0]));
|
||||
if (data.getHalf() == Half.LOWER) { // Add the unstable shape to the collision boxes
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 2.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(14.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 14.0D, 16.0D, 2.0D, 16.0D));
|
||||
box.add(new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 2.0D));
|
||||
}
|
||||
|
||||
return box;
|
||||
}, StateTypes.SCAFFOLDING),
|
||||
|
||||
DRIPLEAF((player, item, version, data, x, y, z) -> {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(2);
|
||||
|
||||
if (data.getFacing() == BlockFace.NORTH) { // Stem
|
||||
box.add(new HexCollisionBox(5.0D, 0.0D, 9.0D, 11.0D, 15.0D, 15.0D));
|
||||
} else if (data.getFacing() == BlockFace.SOUTH) {
|
||||
box.add(new HexCollisionBox(5.0D, 0.0D, 1.0D, 11.0D, 15.0D, 7.0D));
|
||||
} else if (data.getFacing() == BlockFace.EAST) {
|
||||
box.add(new HexCollisionBox(1.0D, 0.0D, 5.0D, 7.0D, 15.0D, 11.0D));
|
||||
} else {
|
||||
box.add(new HexCollisionBox(9.0D, 0.0D, 5.0D, 15.0D, 15.0D, 11.0D));
|
||||
}
|
||||
|
||||
if (data.getTilt() == Tilt.NONE || data.getTilt() == Tilt.UNSTABLE) {
|
||||
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 15.0, 16.0));
|
||||
} else if (data.getTilt() == Tilt.PARTIAL) {
|
||||
box.add(new HexCollisionBox(0.0, 11.0, 0.0, 16.0, 13.0, 16.0));
|
||||
}
|
||||
|
||||
return box;
|
||||
|
||||
}, StateTypes.BIG_DRIPLEAF),
|
||||
|
||||
PINK_PETALS_BLOCK((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_20_2)) {
|
||||
int flowerAmount = data.getFlowerAmount();
|
||||
int horizontalIndex = getHorizontalID(data.getFacing());
|
||||
|
||||
CollisionBox result = NoCollisionBox.INSTANCE;
|
||||
|
||||
// Pre-defined collision boxes for each quadrant
|
||||
HexCollisionBox[] boxes = new HexCollisionBox[] {
|
||||
new HexCollisionBox(8, 0, 8, 16, 3, 16), // SE
|
||||
new HexCollisionBox(8, 0, 0, 16, 3, 8), // NE
|
||||
new HexCollisionBox(0, 0, 0, 8, 3, 8), // NW
|
||||
new HexCollisionBox(0, 0, 8, 8, 3, 16) // SW
|
||||
};
|
||||
|
||||
// Add boxes based on flower amount and facing
|
||||
for (int i = 0; i < flowerAmount; i++) {
|
||||
int index = Math.floorMod(i - horizontalIndex, 4);
|
||||
result = result.union(boxes[index]);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else if (version.isNewerThan(ClientVersion.V_1_19_3)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D);
|
||||
} else if (version.isNewerThan(ClientVersion.V_1_12_2)) {
|
||||
return CORAL_FAN.box.copy();
|
||||
}
|
||||
return GRASS_FERN.dynamic.fetch(player, item, version, data, x, y, z);
|
||||
}, StateTypes.PINK_PETALS),
|
||||
|
||||
MANGROVE_PROPAGULE(((player, item, version, data, x, y, z) -> {
|
||||
if (data.isHanging()) {
|
||||
return new HexOffsetCollisionBox(data.getType(), 7.0, 0.0, 7.0, 9.0, 16.0, 9.0);
|
||||
} else {
|
||||
return new HexOffsetCollisionBox(data.getType(), 7.0, getPropaguleMinHeight(data.getAge()), 7.0, 9.0, 16.0, 9.0);
|
||||
}
|
||||
}), StateTypes.MANGROVE_PROPAGULE),
|
||||
|
||||
FROGSPAWN((player, item, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_18_2)) {
|
||||
return new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 1.5D, 16.0D);
|
||||
} else { // ViaVersion just replaces this with... nothing
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
}, StateTypes.FROGSPAWN),
|
||||
|
||||
// always a fullblock hitbox. Via replacement is obsidian
|
||||
SCULK_SHRIKER(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.SCULK_SHRIEKER);
|
||||
|
||||
private static final Map<StateType, HitboxData> lookup = new HashMap<>();
|
||||
|
||||
@@ -329,6 +631,48 @@ public enum HitboxData {
|
||||
return data.box.copy().offset(x, y, z);
|
||||
|
||||
// Allow this class to override collision boxes when they aren't the same as regular boxes
|
||||
return HitboxData.getData(block.getType()).dynamic.fetch(player, heldItem, version, block, x, y, z).offset(x, y, z);
|
||||
HitBoxFactory hitBoxFactory = data.dynamic;
|
||||
CollisionBox collisionBox = hitBoxFactory.fetch(player, heldItem, version, block, x, y, z);
|
||||
collisionBox.offset(x, y, z);
|
||||
return collisionBox;
|
||||
}
|
||||
|
||||
private static int getPropaguleMinHeight(int age) {
|
||||
return switch (age) {
|
||||
case 0, 1, 2 -> 13 - age * 3;
|
||||
case 3, 4 -> (4 - age) * 3;
|
||||
default -> throw new IllegalStateException("Impossible Propagule Height");
|
||||
};
|
||||
}
|
||||
|
||||
private static CollisionBox getVineCollisionBox(ClientVersion version, boolean isWeeping, boolean isBlock) {
|
||||
if (version.isNewerThan(ClientVersion.V_1_15_2)) {
|
||||
if (isWeeping) {
|
||||
return isBlock
|
||||
? new HexCollisionBox(4.0, 9.0, 4.0, 12.0, 16.0, 12.0)
|
||||
: new HexCollisionBox(1.0, 0.0, 1.0, 15.0, 16.0, 15.0);
|
||||
} else {
|
||||
return new HexCollisionBox(4.0D, 0.0D, 4.0D, 12.0D, isBlock ? 15.0D : 16.0D, 12.0D);
|
||||
}
|
||||
} else {
|
||||
// Via replacement - 4 sided vine
|
||||
return new ComplexCollisionBox(4,
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D),
|
||||
new HexCollisionBox(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getHorizontalID(BlockFace facing) {
|
||||
return switch (facing) {
|
||||
case DOWN, UP -> -1;
|
||||
case NORTH -> 2;
|
||||
case SOUTH -> 0;
|
||||
case WEST -> 1;
|
||||
case EAST -> 3;
|
||||
default -> throw new IllegalStateException("Impossible blockface for getHorizontalID");
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -21,18 +21,14 @@ public class DoorHandler implements CollisionFactory {
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
switch (fetchDirection(player, version, block, x, y, z)) {
|
||||
case NORTH:
|
||||
return NORTH_AABB.copy();
|
||||
case SOUTH:
|
||||
return SOUTH_AABB.copy();
|
||||
case EAST:
|
||||
return EAST_AABB.copy();
|
||||
case WEST:
|
||||
return WEST_AABB.copy();
|
||||
}
|
||||
return switch (fetchDirection(player, version, block, x, y, z)) {
|
||||
case NORTH -> NORTH_AABB.copy();
|
||||
case SOUTH -> SOUTH_AABB.copy();
|
||||
case EAST -> EAST_AABB.copy();
|
||||
case WEST -> WEST_AABB.copy();
|
||||
default -> NoCollisionBox.INSTANCE;
|
||||
};
|
||||
|
||||
return NoCollisionBox.INSTANCE;
|
||||
}
|
||||
|
||||
public BlockFace fetchDirection(GrimPlayer player, ClientVersion version, WrappedBlockState door, int x, int y, int z) {
|
||||
@@ -80,16 +76,11 @@ public class DoorHandler implements CollisionFactory {
|
||||
isRightHinge = door.getHinge() == Hinge.RIGHT;
|
||||
}
|
||||
|
||||
switch (facingDirection) {
|
||||
case EAST:
|
||||
default:
|
||||
return isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
|
||||
case SOUTH:
|
||||
return isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
|
||||
case WEST:
|
||||
return isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
|
||||
case NORTH:
|
||||
return isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
|
||||
}
|
||||
return switch (facingDirection) {
|
||||
case SOUTH -> isClosed ? BlockFace.SOUTH : (isRightHinge ? BlockFace.EAST : BlockFace.WEST);
|
||||
case WEST -> isClosed ? BlockFace.WEST : (isRightHinge ? BlockFace.SOUTH : BlockFace.NORTH);
|
||||
case NORTH -> isClosed ? BlockFace.NORTH : (isRightHinge ? BlockFace.WEST : BlockFace.EAST);
|
||||
default -> isClosed ? BlockFace.EAST : (isRightHinge ? BlockFace.NORTH : BlockFace.SOUTH);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class DynamicChorusPlant implements CollisionFactory {
|
||||
float f = 0.5F - (float) 0.3125;
|
||||
float f1 = 0.5F + (float) 0.3125;
|
||||
SimpleCollisionBox baseShape = new SimpleCollisionBox(f, f, f, f1, f1, f1, false);
|
||||
CollisionBox[] avoxelshape = new CollisionBox[directions.length];
|
||||
SimpleCollisionBox[] avoxelshape = new SimpleCollisionBox[directions.length];
|
||||
|
||||
for (int i = 0; i < directions.length; ++i) {
|
||||
BlockFace direction = directions[i];
|
||||
@@ -41,7 +41,7 @@ public class DynamicChorusPlant implements CollisionFactory {
|
||||
CollisionBox[] avoxelshape1 = new CollisionBox[64];
|
||||
|
||||
for (int k = 0; k < 64; ++k) {
|
||||
ComplexCollisionBox directionalShape = new ComplexCollisionBox(baseShape);
|
||||
ComplexCollisionBox directionalShape = new ComplexCollisionBox(7, baseShape); // how big is this one??
|
||||
|
||||
for (int j = 0; j < directions.length; ++j) {
|
||||
if ((k & 1 << j) != 0) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.HexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import ac.grim.grimac.utils.nmsutil.Materials;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
@@ -18,16 +15,16 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.Shape;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class DynamicStair implements CollisionFactory {
|
||||
protected static final CollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
|
||||
protected static final CollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
|
||||
protected static final CollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
|
||||
protected static final CollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
|
||||
protected static final CollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox TOP_AABB = new HexCollisionBox(0, 8, 0, 16, 16, 16);
|
||||
protected static final SimpleCollisionBox BOTTOM_AABB = new HexCollisionBox(0, 0, 0, 16, 8, 16);
|
||||
protected static final SimpleCollisionBox OCTET_NNN = new HexCollisionBox(0.0D, 0.0D, 0.0D, 8.0D, 8.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NNP = new HexCollisionBox(0.0D, 0.0D, 8.0D, 8.0D, 8.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NPN = new HexCollisionBox(0.0D, 8.0D, 0.0D, 8.0D, 16.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_NPP = new HexCollisionBox(0.0D, 8.0D, 8.0D, 8.0D, 16.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PNN = new HexCollisionBox(8.0D, 0.0D, 0.0D, 16.0D, 8.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PNP = new HexCollisionBox(8.0D, 0.0D, 8.0D, 16.0D, 8.0D, 16.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PPN = new HexCollisionBox(8.0D, 8.0D, 0.0D, 16.0D, 16.0D, 8.0D);
|
||||
protected static final SimpleCollisionBox OCTET_PPP = new HexCollisionBox(8.0D, 8.0D, 8.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final CollisionBox[] TOP_SHAPES = makeShapes(TOP_AABB, OCTET_NNN, OCTET_PNN, OCTET_NNP, OCTET_PNP);
|
||||
protected static final CollisionBox[] BOTTOM_SHAPES = makeShapes(BOTTOM_AABB, OCTET_NPN, OCTET_PPN, OCTET_NPP, OCTET_PPP);
|
||||
private static final int[] SHAPE_BY_STATE = new int[]{12, 5, 3, 10, 14, 13, 7, 11, 13, 7, 11, 14, 8, 4, 1, 2, 4, 1, 2, 8};
|
||||
@@ -77,25 +74,20 @@ public class DynamicStair implements CollisionFactory {
|
||||
}
|
||||
|
||||
private static BlockFace rotateYCCW(BlockFace face) {
|
||||
switch (face) {
|
||||
default:
|
||||
case NORTH:
|
||||
return BlockFace.WEST;
|
||||
case EAST:
|
||||
return BlockFace.NORTH;
|
||||
case SOUTH:
|
||||
return BlockFace.EAST;
|
||||
case WEST:
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
return switch (face) {
|
||||
case EAST -> BlockFace.NORTH;
|
||||
case SOUTH -> BlockFace.EAST;
|
||||
case WEST -> BlockFace.SOUTH;
|
||||
default -> BlockFace.WEST;
|
||||
};
|
||||
}
|
||||
|
||||
private static CollisionBox[] makeShapes(CollisionBox p_199779_0_, CollisionBox p_199779_1_, CollisionBox p_199779_2_, CollisionBox p_199779_3_, CollisionBox p_199779_4_) {
|
||||
private static CollisionBox[] makeShapes(SimpleCollisionBox p_199779_0_, SimpleCollisionBox p_199779_1_, SimpleCollisionBox p_199779_2_, SimpleCollisionBox p_199779_3_, SimpleCollisionBox p_199779_4_) {
|
||||
return IntStream.range(0, 16).mapToObj((p_199780_5_) -> makeStairShape(p_199780_5_, p_199779_0_, p_199779_1_, p_199779_2_, p_199779_3_, p_199779_4_)).toArray(CollisionBox[]::new);
|
||||
}
|
||||
|
||||
private static CollisionBox makeStairShape(int p_199781_0_, CollisionBox p_199781_1_, CollisionBox p_199781_2_, CollisionBox p_199781_3_, CollisionBox p_199781_4_, CollisionBox p_199781_5_) {
|
||||
ComplexCollisionBox voxelshape = new ComplexCollisionBox(p_199781_1_);
|
||||
private static CollisionBox makeStairShape(int p_199781_0_, SimpleCollisionBox p_199781_1_, SimpleCollisionBox p_199781_2_, SimpleCollisionBox p_199781_3_, SimpleCollisionBox p_199781_4_, SimpleCollisionBox p_199781_5_) {
|
||||
ComplexCollisionBox voxelshape = new ComplexCollisionBox(5, p_199781_1_); // can we do better than 5?
|
||||
if ((p_199781_0_ & 1) != 0) {
|
||||
voxelshape.add(p_199781_2_);
|
||||
}
|
||||
@@ -134,36 +126,23 @@ public class DynamicStair implements CollisionFactory {
|
||||
}
|
||||
|
||||
private int directionToValue(BlockFace face) {
|
||||
switch (face) {
|
||||
default:
|
||||
case UP:
|
||||
case DOWN:
|
||||
return -1;
|
||||
case NORTH:
|
||||
return 2;
|
||||
case SOUTH:
|
||||
return 0;
|
||||
case WEST:
|
||||
return 1;
|
||||
case EAST:
|
||||
return 3;
|
||||
}
|
||||
return switch (face) {
|
||||
case NORTH -> 2;
|
||||
case SOUTH -> 0;
|
||||
case WEST -> 1;
|
||||
case EAST -> 3;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
private EnumShape toEnumShape(Shape shape) {
|
||||
switch (shape) {
|
||||
default:
|
||||
case STRAIGHT:
|
||||
return EnumShape.STRAIGHT;
|
||||
case INNER_LEFT:
|
||||
return EnumShape.INNER_LEFT;
|
||||
case INNER_RIGHT:
|
||||
return EnumShape.INNER_RIGHT;
|
||||
case OUTER_LEFT:
|
||||
return EnumShape.OUTER_LEFT;
|
||||
case OUTER_RIGHT:
|
||||
return EnumShape.OUTER_RIGHT;
|
||||
}
|
||||
return switch (shape) {
|
||||
case INNER_LEFT -> EnumShape.INNER_LEFT;
|
||||
case INNER_RIGHT -> EnumShape.INNER_RIGHT;
|
||||
case OUTER_LEFT -> EnumShape.OUTER_LEFT;
|
||||
case OUTER_RIGHT -> EnumShape.OUTER_RIGHT;
|
||||
default -> EnumShape.STRAIGHT;
|
||||
};
|
||||
}
|
||||
|
||||
enum EnumShape {
|
||||
|
||||
@@ -14,20 +14,13 @@ public class PistonBaseCollision implements CollisionFactory {
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
if (!block.isExtended()) return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
|
||||
switch (block.getFacing()) {
|
||||
default:
|
||||
case DOWN:
|
||||
return new HexCollisionBox(0, 4, 0, 16, 16, 16);
|
||||
case UP:
|
||||
return new HexCollisionBox(0, 0, 0, 16, 12, 16);
|
||||
case NORTH:
|
||||
return new HexCollisionBox(0, 0, 4, 16, 16, 16);
|
||||
case SOUTH:
|
||||
return new HexCollisionBox(0, 0, 0, 16, 16, 12);
|
||||
case WEST:
|
||||
return new HexCollisionBox(4, 0, 0, 16, 16, 16);
|
||||
case EAST:
|
||||
return new HexCollisionBox(0, 0, 0, 12, 16, 16);
|
||||
}
|
||||
return switch (block.getFacing()) {
|
||||
case UP -> new HexCollisionBox(0, 0, 0, 16, 12, 16);
|
||||
case NORTH -> new HexCollisionBox(0, 0, 4, 16, 16, 16);
|
||||
case SOUTH -> new HexCollisionBox(0, 0, 0, 16, 16, 12);
|
||||
case WEST -> new HexCollisionBox(4, 0, 0, 16, 16, 16);
|
||||
case EAST -> new HexCollisionBox(0, 0, 0, 12, 16, 16);
|
||||
default -> new HexCollisionBox(0, 4, 0, 16, 16, 16);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -38,38 +38,43 @@ public class PistonHeadCollision implements CollisionFactory {
|
||||
longAmount = 0;
|
||||
|
||||
|
||||
switch (block.getFacing()) {
|
||||
case DOWN:
|
||||
default:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 4, 16),
|
||||
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
|
||||
case UP:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 12, 0, 16, 16, 16),
|
||||
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
|
||||
case NORTH:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 16, 16, 4),
|
||||
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
|
||||
case SOUTH:
|
||||
return switch (block.getFacing()) {
|
||||
case UP -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 12, 0, 16, 16, 16),
|
||||
new HexCollisionBox(6, 0 - longAmount, 6, 10, 12, 10));
|
||||
case NORTH -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 16, 16, 4),
|
||||
new HexCollisionBox(6, 6, 4, 10, 10, 16 + longAmount));
|
||||
case SOUTH -> {
|
||||
// SOUTH piston is glitched in 1.7 and 1.8, fixed in 1.9
|
||||
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
yield new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
new HexCollisionBox(4, 6, 0, 12, 10, 12));
|
||||
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
yield new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 12, 16, 16, 16),
|
||||
new HexCollisionBox(6, 6, 0 - longAmount, 10, 10, 12));
|
||||
case WEST:
|
||||
}
|
||||
case WEST -> {
|
||||
// WEST piston is glitched in 1.7 and 1.8, fixed in 1.9
|
||||
// Don't bother with short piston boxes as 1.7/1.8 clients don't have them
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_8))
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
yield new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
new HexCollisionBox(6, 4, 4, 10, 12, 16));
|
||||
|
||||
return new ComplexCollisionBox(new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
yield new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 4, 16, 16),
|
||||
new HexCollisionBox(4, 6, 6, 16 + longAmount, 10, 10));
|
||||
case EAST:
|
||||
return new ComplexCollisionBox(new HexCollisionBox(12, 0, 0, 16, 16, 16),
|
||||
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
|
||||
}
|
||||
}
|
||||
case EAST -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(12, 0, 0, 16, 16, 16),
|
||||
new HexCollisionBox(0 - longAmount, 6, 4, 12, 10, 12));
|
||||
default -> new ComplexCollisionBox(2,
|
||||
new HexCollisionBox(0, 0, 0, 16, 4, 16),
|
||||
new HexCollisionBox(6, 4, 6, 10, 16 + longAmount, 10));
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -4,7 +4,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionFactory;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
@@ -18,10 +17,8 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicFence extends DynamicConnecting implements CollisionFactory {
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true);
|
||||
|
||||
public static SimpleCollisionBox[] LEGACY_BOUNDING_BOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
|
||||
public class DynamicCollisionFence extends DynamicConnecting implements CollisionFactory {
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
+3
-3
@@ -19,9 +19,9 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicPane extends DynamicConnecting implements CollisionFactory {
|
||||
public class DynamicCollisionPane extends DynamicConnecting implements CollisionFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
@@ -52,7 +52,7 @@ public class DynamicPane extends DynamicConnecting implements CollisionFactory {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_9)) {
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
} else { // 1.8 and below clients have pane bounding boxes one pixel less
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox();
|
||||
ComplexCollisionBox boxes = new ComplexCollisionBox(2);
|
||||
if ((!west || !east) && (west || east || north || south)) {
|
||||
if (west) {
|
||||
boxes.add(new SimpleCollisionBox(0.0F, 0.0F, 0.4375F, 0.5F, 1.0F, 0.5625F));
|
||||
+9
-8
@@ -15,12 +15,15 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.South;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.enums.West;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicWall extends DynamicConnecting implements CollisionFactory {
|
||||
public static final CollisionBox[] BOXES = makeShapes(4.0F, 3.0F, 16.0F, 0.0F, 16.0F, false);
|
||||
public class DynamicCollisionWall extends DynamicConnecting implements CollisionFactory {
|
||||
// https://bugs.mojang.com/browse/MC-9565
|
||||
// https://bugs.mojang.com/browse/MC-94016
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false);
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(4.0F, 3.0F, 24.0F, 0.0F, 24.0F, false, 1);
|
||||
|
||||
/**
|
||||
* @deprecated use DynamicHitboxWall
|
||||
*/
|
||||
@Deprecated
|
||||
public CollisionBox fetchRegularBox(GrimPlayer player, WrappedBlockState state, ClientVersion version, int x, int y, int z) {
|
||||
int north, south, west, east, up;
|
||||
north = south = west = east = up = 0;
|
||||
@@ -49,12 +52,11 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
|
||||
|
||||
// On 1.13+ clients the bounding box is much more complicated
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox();
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(5);
|
||||
|
||||
// Proper and faster way would be to compute all this beforehand
|
||||
if (up == 1) {
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
|
||||
return box;
|
||||
}
|
||||
|
||||
if (north == 1) {
|
||||
@@ -77,6 +79,7 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
|
||||
} else if (east == 2) {
|
||||
box.add(new HexCollisionBox(5, 0, 5, 16, 16, 11));
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
// Magic 1.8 code for walls that I copied over, 1.12 below uses this mess
|
||||
@@ -139,9 +142,7 @@ public class DynamicWall extends DynamicConnecting implements CollisionFactory {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
// Proper and faster way would be to compute all this beforehand
|
||||
if (up) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy());
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 24, 12));
|
||||
return box;
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy().union(new HexCollisionBox(4, 0, 4, 12, 24, 12));
|
||||
}
|
||||
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
+13
-6
@@ -18,7 +18,7 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicConnecting {
|
||||
|
||||
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter) {
|
||||
public static CollisionBox[] makeShapes(float p_196408_1_, float p_196408_2_, float p_196408_3_, float p_196408_4_, float p_196408_5_, boolean includeCenter, int additionalMaxIndex) {
|
||||
float middleMin = 8.0F - p_196408_1_;
|
||||
float middleMax = 8.0F + p_196408_1_;
|
||||
float f2 = 8.0F - p_196408_2_;
|
||||
@@ -29,14 +29,21 @@ public class DynamicConnecting {
|
||||
SimpleCollisionBox voxelshape3 = new HexCollisionBox(0.0D, p_196408_4_, f2, f3, p_196408_5_, f3);
|
||||
SimpleCollisionBox voxelshape4 = new HexCollisionBox(f2, p_196408_4_, f2, 16.0D, p_196408_5_, f3);
|
||||
|
||||
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(voxelshape1, voxelshape4);
|
||||
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(voxelshape2, voxelshape3);
|
||||
ComplexCollisionBox voxelshape5 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape1, voxelshape4);
|
||||
ComplexCollisionBox voxelshape6 = new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape3);
|
||||
|
||||
CollisionBox[] avoxelshape = new CollisionBox[]{NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1, new ComplexCollisionBox(voxelshape2, voxelshape1), new ComplexCollisionBox(voxelshape3, voxelshape1), new ComplexCollisionBox(voxelshape6, voxelshape1), voxelshape4, new ComplexCollisionBox(voxelshape2, voxelshape4), new ComplexCollisionBox(voxelshape3, voxelshape4), new ComplexCollisionBox(voxelshape6, voxelshape4), voxelshape5, new ComplexCollisionBox(voxelshape2, voxelshape5), new ComplexCollisionBox(voxelshape3, voxelshape5), new ComplexCollisionBox(voxelshape6, voxelshape5)};
|
||||
CollisionBox[] avoxelshape = new CollisionBox[]{
|
||||
NoCollisionBox.INSTANCE, voxelshape2, voxelshape3, voxelshape6, voxelshape1,
|
||||
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape1), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape1),
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape1), voxelshape4,
|
||||
new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape2, voxelshape4), new ComplexCollisionBox(2 + additionalMaxIndex, voxelshape3, voxelshape4),
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape3, voxelshape4), voxelshape5,
|
||||
new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape2, voxelshape1, voxelshape4), new ComplexCollisionBox(3 + additionalMaxIndex, voxelshape3, voxelshape1, voxelshape4),
|
||||
new ComplexCollisionBox(4 + additionalMaxIndex, voxelshape1, voxelshape2, voxelshape3, voxelshape4)};
|
||||
|
||||
if (includeCenter) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
avoxelshape[i] = new ComplexCollisionBox(up, avoxelshape[i]);
|
||||
avoxelshape[i] = avoxelshape[i].union(up);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,4 +136,4 @@ public class DynamicConnecting {
|
||||
public boolean canConnectToGate(StateType fence) {
|
||||
return !Materials.isGlassPane(fence);
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.CollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.ComplexCollisionBox;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.HitBoxFactory;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
|
||||
public class DynamicHitboxFence extends DynamicConnecting implements HitBoxFactory {
|
||||
private static final CollisionBox[] MODERN_HITBOXES = makeShapes(2.0F, 2.0F, 24.0F, 0.0F, 24.0F, true, 1);
|
||||
|
||||
public static SimpleCollisionBox[] LEGACY_HITBOXES = new SimpleCollisionBox[] {new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 0.625D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.375D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.375D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.625D), new SimpleCollisionBox(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D)};
|
||||
|
||||
// no ComplexCollisionBox produced by makeShapes is every larger than 5 SimpleCollisionBoxes
|
||||
private static final int MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE = 5;
|
||||
|
||||
static {
|
||||
SimpleCollisionBox[] boxes = new SimpleCollisionBox[MAX_MODERN_HITBOX_COMPLEX_COLLISION_BOX_SIZE];
|
||||
|
||||
// we start from one because MODERN_HITBOXES[0] is a NoCollisionBox
|
||||
for (int i = 1; i < MODERN_HITBOXES.length; i++) {
|
||||
CollisionBox collisionBox = MODERN_HITBOXES[i];
|
||||
int size = collisionBox.downCast(boxes);
|
||||
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (boxes[j].maxY > 1) {
|
||||
boxes[j].maxY = 1;
|
||||
}
|
||||
}
|
||||
|
||||
MODERN_HITBOXES[i] = size == 1 ? boxes[0] : new ComplexCollisionBox(size, boxes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
boolean east;
|
||||
boolean north;
|
||||
boolean south;
|
||||
boolean west;
|
||||
|
||||
// 1.13+ servers on 1.13+ clients send the full fence data
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
|
||||
&& version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
east = block.getEast() != East.FALSE;
|
||||
north = block.getNorth() != North.FALSE;
|
||||
south = block.getSouth() != South.FALSE;
|
||||
west = block.getWest() != West.FALSE;
|
||||
} else {
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
|
||||
}
|
||||
|
||||
return version.isNewerThanOrEquals(ClientVersion.V_1_12_2)
|
||||
? getModernCollisionBox(north, east, south, west)
|
||||
: getLegacyCollisionBox(north, east, south, west);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return LEGACY_HITBOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return MODERN_HITBOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
if (BlockTags.FENCES.contains(one))
|
||||
return !(one == StateTypes.NETHER_BRICK_FENCE) && !(two == StateTypes.NETHER_BRICK_FENCE);
|
||||
else
|
||||
return BlockTags.FENCES.contains(one) || CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
public class DynamicHitboxPane extends DynamicConnecting implements HitBoxFactory {
|
||||
|
||||
private static final CollisionBox[] COLLISION_BOXES = makeShapes(1.0F, 1.0F, 16.0F, 0.0F, 16.0F, true, 1);
|
||||
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType item, ClientVersion version, WrappedBlockState block, int x, int y, int z) {
|
||||
boolean east, north, south, west;
|
||||
|
||||
// 1.13+ servers on 1.13+ clients send the full fence data
|
||||
if (isModernVersion(version)) {
|
||||
east = block.getEast() != East.FALSE;
|
||||
north = block.getNorth() != North.FALSE;
|
||||
south = block.getSouth() != South.FALSE;
|
||||
west = block.getWest() != West.FALSE;
|
||||
} else {
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST);
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH);
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH);
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST);
|
||||
}
|
||||
|
||||
// On 1.7 and 1.8 clients, and 1.13+ clients on 1.7 and 1.8 servers, the glass pane is + instead of |
|
||||
if (shouldUseOldPaneShape(version, north, south, east, west)) {
|
||||
north = south = east = west = true;
|
||||
}
|
||||
|
||||
return version.isNewerThanOrEquals(ClientVersion.V_1_9)
|
||||
? getModernCollisionBox(north, east, south, west)
|
||||
: getLegacyCollisionBox(north, east, south, west);
|
||||
}
|
||||
|
||||
private boolean isModernVersion(ClientVersion version) {
|
||||
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)
|
||||
&& version.isNewerThanOrEquals(ClientVersion.V_1_13);
|
||||
}
|
||||
|
||||
private boolean shouldUseOldPaneShape(ClientVersion version, boolean north, boolean south, boolean east, boolean west) {
|
||||
return (!north && !south && !east && !west) &&
|
||||
(version.isOlderThanOrEquals(ClientVersion.V_1_8) ||
|
||||
(PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8) &&
|
||||
version.isNewerThanOrEquals(ClientVersion.V_1_13)));
|
||||
}
|
||||
|
||||
private CollisionBox getModernCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
return COLLISION_BOXES[getAABBIndex(north, east, south, west)].copy();
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyCollisionBox(boolean north, boolean east, boolean south, boolean west) {
|
||||
float minX = 0.4375F;
|
||||
float maxX = 0.5625F;
|
||||
float minZ = 0.4375F;
|
||||
float maxZ = 0.5625F;
|
||||
|
||||
if ((!west || !east) && (west || east || north || south)) {
|
||||
if (west) {
|
||||
minX = 0.0F;
|
||||
} else if (east) {
|
||||
maxX = 1.0F;
|
||||
}
|
||||
} else {
|
||||
minX = 0.0F;
|
||||
maxX = 1.0F;
|
||||
}
|
||||
|
||||
if ((!north || !south) && (west || east || north || south)) {
|
||||
if (north) {
|
||||
minZ = 0.0F;
|
||||
} else if (south) {
|
||||
maxZ = 1.0F;
|
||||
}
|
||||
} else {
|
||||
minZ = 0.0F;
|
||||
maxZ = 1.0F;
|
||||
}
|
||||
|
||||
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, 1.0F, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConnectToGlassBlock() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
if (BlockTags.GLASS_PANES.contains(one) || one == StateTypes.IRON_BARS) {
|
||||
return true;
|
||||
} else {
|
||||
return CollisionData.getData(one)
|
||||
.getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0)
|
||||
.isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package ac.grim.grimac.utils.collisions.blocks.connecting;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.CollisionData;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.*;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.enums.*;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
public class DynamicHitboxWall extends DynamicConnecting implements HitBoxFactory {
|
||||
@Override
|
||||
public CollisionBox fetch(GrimPlayer player, StateType heldItem, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
|
||||
int[] connections = getConnections(player, version, state, x, y, z);
|
||||
int north = connections[0], south = connections[1], west = connections[2], east = connections[3], up = connections[4];
|
||||
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
return getModernHitBox(north, south, west, east, up);
|
||||
} else {
|
||||
return getLegacyHitBox(north, south, west, east);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getConnections(GrimPlayer player, ClientVersion version, WrappedBlockState state, int x, int y, int z) {
|
||||
int north = 0, south = 0, west = 0, east = 0, up = 0;
|
||||
|
||||
if (isModernServer()) {
|
||||
boolean sixteen = PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_16);
|
||||
north = getConnectionValue(state.getNorth(), sixteen);
|
||||
east = getConnectionValue(state.getEast(), sixteen);
|
||||
south = getConnectionValue(state.getSouth(), sixteen);
|
||||
west = getConnectionValue(state.getWest(), sixteen);
|
||||
up = state.isUp() ? 1 : 0;
|
||||
} else {
|
||||
north = connectsTo(player, version, x, y, z, BlockFace.NORTH) ? 1 : 0;
|
||||
south = connectsTo(player, version, x, y, z, BlockFace.SOUTH) ? 1 : 0;
|
||||
west = connectsTo(player, version, x, y, z, BlockFace.WEST) ? 1 : 0;
|
||||
east = connectsTo(player, version, x, y, z, BlockFace.EAST) ? 1 : 0;
|
||||
up = 1;
|
||||
}
|
||||
|
||||
return new int[]{north, south, west, east, up};
|
||||
}
|
||||
|
||||
private boolean isModernServer() {
|
||||
return PacketEvents.getAPI().getServerManager().getVersion().isNewerThan(ServerVersion.V_1_12_2);
|
||||
}
|
||||
|
||||
private int getConnectionValue(Enum<?> direction, boolean sixteen) {
|
||||
if (direction == North.NONE || direction == East.NONE || direction == South.NONE || direction == West.NONE) {
|
||||
return 0;
|
||||
}
|
||||
return (direction == North.LOW || direction == East.LOW || direction == South.LOW || direction == West.LOW || sixteen) ? 1 : 2;
|
||||
}
|
||||
|
||||
private CollisionBox getModernHitBox(int north, int south, int west, int east, int up) {
|
||||
ComplexCollisionBox box = new ComplexCollisionBox(5);
|
||||
if (up == 1) {
|
||||
box.add(new HexCollisionBox(4, 0, 4, 12, 16, 12));
|
||||
}
|
||||
|
||||
addDirectionalBox(box, north, 5, 0, 0.0D, 11, 14, 11);
|
||||
addDirectionalBox(box, south, 5, 0, 5, 11, 14, 16);
|
||||
addDirectionalBox(box, west, 0, 0, 5, 11, 14, 11);
|
||||
addDirectionalBox(box, east, 5, 0, 5, 16, 14, 11);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
private void addDirectionalBox(ComplexCollisionBox box, int direction, double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
if (direction == 1) {
|
||||
box.add(new HexCollisionBox(x1, y1, z1, x2, y2, z2));
|
||||
} else if (direction == 2) {
|
||||
box.add(new HexCollisionBox(x1, y1, z1, x2, 16, z2));
|
||||
}
|
||||
}
|
||||
|
||||
private CollisionBox getLegacyHitBox(int north, int south, int west, int east) {
|
||||
float minX = 0.25F, maxX = 0.75F, minZ = 0.25F, maxZ = 0.75F;
|
||||
float maxY = 1.0F;
|
||||
|
||||
if (north == 1) minZ = 0.0F;
|
||||
if (south == 1) maxZ = 1.0F;
|
||||
if (west == 1) minX = 0.0F;
|
||||
if (east == 1) maxX = 1.0F;
|
||||
|
||||
if (north == 1 && south == 1 && west == 0 && east == 0) {
|
||||
maxY = 0.8125F;
|
||||
minX = 0.3125F;
|
||||
maxX = 0.6875F;
|
||||
} else if (west == 1 && east == 1 && north == 0 && south == 0) {
|
||||
maxY = 0.8125F;
|
||||
minZ = 0.3125F;
|
||||
maxZ = 0.6875F;
|
||||
}
|
||||
|
||||
return new SimpleCollisionBox(minX, 0.0F, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanConnect(GrimPlayer player, WrappedBlockState state, StateType one, StateType two, BlockFace direction) {
|
||||
return BlockTags.WALLS.contains(one) ||
|
||||
CollisionData.getData(one).getMovementCollisionBox(player, player.getClientVersion(), state, 0, 0, 0).isSideFullBlock(direction);
|
||||
}
|
||||
}
|
||||
@@ -228,4 +228,4 @@ public class BoundingBox {
|
||||
public String toString() {
|
||||
return "[" + minX + ", " + minY + ", " + minZ + ", " + maxX + ", " + maxY + ", " + maxZ + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import java.util.List;
|
||||
|
||||
public interface CollisionBox {
|
||||
CollisionBox union(SimpleCollisionBox other);
|
||||
|
||||
boolean isCollided(SimpleCollisionBox other);
|
||||
|
||||
boolean isIntersected(SimpleCollisionBox other);
|
||||
@@ -15,6 +17,13 @@ public interface CollisionBox {
|
||||
|
||||
void downCast(List<SimpleCollisionBox> list);
|
||||
|
||||
/**
|
||||
* @param list - A list that will contain all of the SimpleCollisionBoxes representing the CollisionBox.
|
||||
* @return - int representing the size of elements in the array that belong to this downcast.
|
||||
* The contents of elements starting from list[size] and above are undefined and should not be iterated over.
|
||||
*/
|
||||
int downCast(SimpleCollisionBox[] list);
|
||||
|
||||
boolean isNull();
|
||||
|
||||
boolean isFullBlock();
|
||||
@@ -22,4 +31,4 @@ public interface CollisionBox {
|
||||
default boolean isSideFullBlock(BlockFace axis) {
|
||||
return isFullBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,4 @@ import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState
|
||||
|
||||
public interface CollisionFactory {
|
||||
CollisionBox fetch(GrimPlayer player, ClientVersion version, WrappedBlockState block, int x, int y, int z);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,91 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ComplexCollisionBox implements CollisionBox {
|
||||
|
||||
private final List<CollisionBox> boxes = new ArrayList<>();
|
||||
// Most complex shape is the Modern MC Cauldron which is made up of 15 boxes
|
||||
public static int DEFAULT_MAX_COLLISION_BOX_SIZE = 15; // increase if we somehow have a shape made of more than 15 parts.
|
||||
private final SimpleCollisionBox[] boxes;
|
||||
private int currentLength;
|
||||
|
||||
public ComplexCollisionBox(CollisionBox... boxes) {
|
||||
Collections.addAll(this.boxes, boxes);
|
||||
public ComplexCollisionBox(SimpleCollisionBox... boxes) {
|
||||
this(DEFAULT_MAX_COLLISION_BOX_SIZE, boxes);
|
||||
}
|
||||
|
||||
public boolean add(CollisionBox collisionBox) {
|
||||
return boxes.add(collisionBox);
|
||||
public ComplexCollisionBox(int maxIndex) {
|
||||
this.boxes = new SimpleCollisionBox[maxIndex];
|
||||
}
|
||||
|
||||
public ComplexCollisionBox(int maxIndex, SimpleCollisionBox... boxes) {
|
||||
this.boxes = new SimpleCollisionBox[maxIndex];
|
||||
System.arraycopy(boxes, 0, this.boxes, 0, Math.min(maxIndex, boxes.length));
|
||||
currentLength = boxes.length;
|
||||
}
|
||||
|
||||
public boolean add(SimpleCollisionBox collisionBox) {
|
||||
boxes[currentLength] = collisionBox;
|
||||
currentLength++;
|
||||
return currentLength <= boxes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox union(SimpleCollisionBox other) {
|
||||
add(other);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollided(SimpleCollisionBox other) {
|
||||
for (CollisionBox box : boxes) {
|
||||
if (box.isCollided(other)) return true;
|
||||
for (int i = 0; i < currentLength; i++) {
|
||||
if (boxes[i].isCollided(other)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntersected(SimpleCollisionBox other) {
|
||||
for (CollisionBox box : boxes) {
|
||||
if (box.isIntersected(other))
|
||||
return true;
|
||||
for (int i = 0; i < currentLength; i++) {
|
||||
if (boxes[i].isIntersected(other)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox copy() {
|
||||
ComplexCollisionBox cc = new ComplexCollisionBox();
|
||||
for (CollisionBox b : boxes)
|
||||
cc.boxes.add(b.copy());
|
||||
return cc;
|
||||
ComplexCollisionBox copy = new ComplexCollisionBox(boxes.length);
|
||||
for (int i = 0; i < currentLength; i++) {
|
||||
copy.boxes[i] = boxes[i].copy();
|
||||
}
|
||||
copy.currentLength = this.currentLength;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox offset(double x, double y, double z) {
|
||||
for (CollisionBox b : boxes)
|
||||
b.offset(x, y, z);
|
||||
for (int i = 0; i < currentLength; i++) {
|
||||
boxes[i].offset(x, y ,z);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downCast(List<SimpleCollisionBox> list) {
|
||||
for (CollisionBox box : boxes)
|
||||
box.downCast(list);
|
||||
list.addAll(Arrays.asList(boxes).subList(0, currentLength));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int downCast(SimpleCollisionBox[] list) {
|
||||
System.arraycopy(boxes, 0, list, 0, currentLength);
|
||||
return currentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
for (CollisionBox box : boxes)
|
||||
if (!box.isNull())
|
||||
return false;
|
||||
for (int i = 0; i < currentLength; i++) {
|
||||
if (!boxes[i].isNull()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,4 +93,4 @@ public class ComplexCollisionBox implements CollisionBox {
|
||||
public boolean isFullBlock() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,14 @@ public class DynamicCollisionBox implements CollisionBox {
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
// Untested but currently unused
|
||||
// *should* work because every single one of these eventually becomes a Complex, Simple, or NoCollision Box
|
||||
@Override
|
||||
public CollisionBox union(SimpleCollisionBox other) {
|
||||
CollisionBox dynamicBox = box.fetch(player, version, block, x, y, z).offset(x, y, z);
|
||||
return dynamicBox.union(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollided(SimpleCollisionBox other) {
|
||||
return box.fetch(player, version, block, x, y, z).offset(x, y, z).isCollided(other);
|
||||
@@ -44,6 +52,11 @@ public class DynamicCollisionBox implements CollisionBox {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int downCast(SimpleCollisionBox[] list) {
|
||||
return box.fetch(player, version, block, x, y, z).offset(x, y, z).downCast(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downCast(List<SimpleCollisionBox> list) {
|
||||
box.fetch(player, version, block, x, y, z).offset(x, y, z).downCast(list);
|
||||
@@ -66,4 +79,4 @@ public class DynamicCollisionBox implements CollisionBox {
|
||||
public void setVersion(ClientVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,18 @@ package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
public class HexCollisionBox extends SimpleCollisionBox {
|
||||
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* Mojang's block hitbox values are all based on chunks, so they're stored in game as 16 * the actual size
|
||||
* When copying block hitbox values, it may be easier to simple copy the multiplied values and use this class
|
||||
* If your min/max values are < 1 you should probably check out {@link SimpleCollisionBox}
|
||||
* @param minX 16 * x position of first corner
|
||||
* @param minY 16 * y position of first corner
|
||||
* @param minZ 16 * z position of first corner
|
||||
* @param maxX 16 * x position of second corner
|
||||
* @param maxY 16 * y position of second corner
|
||||
* @param maxZ 16 * z position of second corner
|
||||
*/
|
||||
public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
this.minX = minX / 16d;
|
||||
this.minY = minY / 16d;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
|
||||
// Exists for the same reason as HexCollisionBox but for offset blocks; read comments there if unsure
|
||||
public class HexOffsetCollisionBox extends OffsetCollisionBox {
|
||||
public HexOffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
super(block, minX / 16d, minY / 16d, minZ / 16d, maxX / 16d, maxY / 16d, maxZ / 16d);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,11 @@ public class NoCollisionBox implements CollisionBox {
|
||||
private NoCollisionBox() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollisionBox union(SimpleCollisionBox other) {
|
||||
return other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollided(SimpleCollisionBox other) {
|
||||
return false;
|
||||
@@ -27,6 +32,9 @@ public class NoCollisionBox implements CollisionBox {
|
||||
@Override
|
||||
public void downCast(List<SimpleCollisionBox> list) { /**/ }
|
||||
|
||||
@Override
|
||||
public int downCast(SimpleCollisionBox[] list) { return 0; }
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
return true;
|
||||
@@ -41,4 +49,4 @@ public class NoCollisionBox implements CollisionBox {
|
||||
public CollisionBox copy() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class OffsetCollisionBox extends SimpleCollisionBox {
|
||||
|
||||
public static enum OffsetType {
|
||||
NONE,
|
||||
XZ,
|
||||
XYZ,
|
||||
}
|
||||
|
||||
float maxHorizontalModelOffset = 0.25F;
|
||||
float maxVerticalModelOffset = 0.2F;
|
||||
double offsetX = 0;
|
||||
double offsetY = 0;
|
||||
double offsetZ = 0;
|
||||
|
||||
OffsetType offsetType;
|
||||
|
||||
private static final HashSet<StateType> XZ_OFFSET_BLOCKSTATES = new HashSet<>();
|
||||
private static final HashSet<StateType> XYZ_OFFSET_BLOCKSTATES = new HashSet<>();
|
||||
|
||||
static {
|
||||
// Can we add a hasOffSet to StateType() ?
|
||||
// Or a new BlockTag for XZ and XYZ Offset ?
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.MANGROVE_PROPAGULE);
|
||||
|
||||
XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.SMALL_FLOWERS.getStates());
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO_SAPLING);
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.BAMBOO);
|
||||
XZ_OFFSET_BLOCKSTATES.add(StateTypes.POINTED_DRIPSTONE);
|
||||
// Only offsets rendering HitBox on XZ // we should document this somewhere for future reference
|
||||
// XZ_OFFSET_BLOCKSTATES.addAll(BlockTags.TALL_FLOWERS.getStates());
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_SEAGRASS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.TALL_GRASS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.LARGE_FERN);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.WARPED_ROOTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.NETHER_SPROUTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.CRIMSON_ROOTS);
|
||||
// XZ_OFFSET_BLOCKSTATES.add(StateTypes.HANGING_ROOTS);
|
||||
|
||||
// Only offsets rendering on XYZ, not HitBox
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SHORT_GRASS);
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.FERN);
|
||||
// XYZ_OFFSET_BLOCKSTATES.add(StateTypes.SMALL_DRIPLEAF);
|
||||
}
|
||||
|
||||
public OffsetCollisionBox(StateType block, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
super(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
if (block.equals(StateTypes.POINTED_DRIPSTONE)) {
|
||||
maxHorizontalModelOffset = 0.125F;
|
||||
}
|
||||
// else if (block.equals(StateTypes.SMALL_DRIPLEAF)) {
|
||||
// maxVerticalModelOffset = 0.1F;
|
||||
// }
|
||||
|
||||
if (XZ_OFFSET_BLOCKSTATES.contains(block)) {
|
||||
offsetType = OffsetType.XZ;
|
||||
return;
|
||||
} else if (XYZ_OFFSET_BLOCKSTATES.contains(block)) {
|
||||
offsetType = OffsetType.XYZ;
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Invalid State Type for OffSetCollisionBox: " + block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCollisionBox offset(double x, double y, double z) {
|
||||
// In case you want to call .offset() again or get the box values without offset.
|
||||
resetBlockStateOffSet();
|
||||
long l;
|
||||
switch (offsetType) {
|
||||
case NONE:
|
||||
return super.offset(x, y, z);
|
||||
case XZ:
|
||||
l = GrimMath.hashCode(x, 0, z);
|
||||
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
return super.offset(x + offsetX, y, z + offsetZ);
|
||||
case XYZ:
|
||||
l = GrimMath.hashCode(x, 0, z);
|
||||
offsetY = ((double)((float)(l >> 4 & 15L) / 15.0F) - 1.0) * (double) maxVerticalModelOffset;
|
||||
offsetX = GrimMath.clamp(((double)((float)(l & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
offsetZ = GrimMath.clamp(((double)((float)(l >> 8 & 15L) / 15.0F) - 0.5) * 0.5, (double)(-maxHorizontalModelOffset), (double)maxHorizontalModelOffset);
|
||||
return super.offset(x + offsetX, offsetY, z + offsetZ);
|
||||
}
|
||||
// You *really* shouldn't be using this class if offsetType = null
|
||||
return null;
|
||||
}
|
||||
|
||||
public void resetBlockStateOffSet() {
|
||||
this.minX += offsetX;
|
||||
this.minY += offsetY;
|
||||
this.minZ += offsetZ;
|
||||
this.maxX += offsetX;
|
||||
this.maxY += offsetY;
|
||||
this.maxZ += offsetZ;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleCollisionBox implements CollisionBox {
|
||||
@@ -20,10 +19,23 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
public double minX, minY, minZ, maxX, maxY, maxZ;
|
||||
private boolean isFullBlock = false;
|
||||
|
||||
SimpleCollisionBox[] boxes = new SimpleCollisionBox[ComplexCollisionBox.DEFAULT_MAX_COLLISION_BOX_SIZE];
|
||||
|
||||
public SimpleCollisionBox() {
|
||||
this(0, 0, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
|
||||
* @param minX x position of first corner
|
||||
* @param minY y position of first corner
|
||||
* @param minZ z position of first corner
|
||||
* @param maxX x position of second corner
|
||||
* @param maxY y position of second corner
|
||||
* @param maxZ z position of second corner
|
||||
* @param fullBlock - whether on not the box is a perfect 1x1x1 sized block
|
||||
*/
|
||||
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, boolean fullBlock) {
|
||||
this.minX = minX;
|
||||
this.maxX = maxX;
|
||||
@@ -47,7 +59,17 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
this(minX, minY, minZ, minX + 1, minY + 1, minZ + 1, true);
|
||||
}
|
||||
|
||||
// Use only if you don't know the fullBlock status, which is rare
|
||||
/**
|
||||
* Creates a box defined by two points in 3d space; used to represent hitboxes and collision boxes.
|
||||
* If your min/max values are > 1 you should probably check out {@link HexCollisionBox}
|
||||
* Use only if you don't know the fullBlock status, which is rare
|
||||
* @param minX x position of first corner
|
||||
* @param minY y position of first corner
|
||||
* @param minZ z position of first corner
|
||||
* @param maxX x position of second corner
|
||||
* @param maxY y position of second corner
|
||||
* @param maxZ z position of second corner
|
||||
*/
|
||||
public SimpleCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
this.minX = minX;
|
||||
this.maxX = maxX;
|
||||
@@ -142,7 +164,8 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
return vectors;
|
||||
}
|
||||
|
||||
public SimpleCollisionBox union(SimpleCollisionBox other) {
|
||||
@Override
|
||||
public CollisionBox union(SimpleCollisionBox other) {
|
||||
this.minX = Math.min(this.minX, other.minX);
|
||||
this.minY = Math.min(this.minY, other.minY);
|
||||
this.minZ = Math.min(this.minZ, other.minZ);
|
||||
@@ -211,11 +234,10 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
return isIntersected((SimpleCollisionBox) other);
|
||||
}
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
other.downCast(boxes);
|
||||
int size = other.downCast(boxes);
|
||||
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
if (isIntersected(box)) return true;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (isIntersected(boxes[i])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -246,6 +268,12 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
list.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int downCast(SimpleCollisionBox[] list) {
|
||||
list[0] = this;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
return false;
|
||||
@@ -264,19 +292,13 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
|
||||
// Get the direction of block we are trying to connect to -> towards the block that is trying to connect
|
||||
final BlockFace faceToSourceConnector = axis.getOppositeFace();
|
||||
switch (faceToSourceConnector) {
|
||||
case EAST:
|
||||
case WEST:
|
||||
return this.minX == 0 && this.maxX == 1;
|
||||
case UP:
|
||||
case DOWN:
|
||||
return this.minY == 0 && this.maxY == 1;
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
return this.minZ == 0 && this.maxZ == 1;
|
||||
}
|
||||
return switch (faceToSourceConnector) {
|
||||
case EAST, WEST -> this.minX == 0 && this.maxX == 1;
|
||||
case UP, DOWN -> this.minY == 0 && this.maxY == 1;
|
||||
case NORTH, SOUTH -> this.minZ == 0 && this.maxZ == 1;
|
||||
default -> false;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFullBlockNoCache() {
|
||||
@@ -501,4 +523,4 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
", isFullBlock=" + isFullBlock +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,25 +20,10 @@ package ac.grim.grimac.utils.data;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Pair<A, B> {
|
||||
private final A first;
|
||||
private final B second;
|
||||
|
||||
public Pair(A first, B second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
public record Pair<A, B>(A first, B second) {
|
||||
|
||||
public static <T, K> Pair<T, K> of(T a, K b) {
|
||||
return new Pair<T, K>(a, b);
|
||||
}
|
||||
|
||||
public A getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public B getSecond() {
|
||||
return second;
|
||||
return new Pair<>(a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,4 +34,4 @@ public class Pair<A, B> {
|
||||
Pair b = (Pair) o;
|
||||
return Objects.equals(this.first, b.first) && Objects.equals(this.second, b.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package ac.grim.grimac.utils.data;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
|
||||
public final class TrackedPosition {
|
||||
@@ -64,4 +62,4 @@ public final class TrackedPosition {
|
||||
public void setPos(Vector3d pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,11 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.potion.PotionType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
@@ -54,7 +55,7 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
private ReachInterpolationData oldPacketLocation;
|
||||
private ReachInterpolationData newPacketLocation;
|
||||
|
||||
private Map<PotionType, Integer> potionsMap = null;
|
||||
private Object2IntMap<PotionType> potionsMap = null;
|
||||
protected final Map<Attribute, ValuedAttribute> attributeMap = new IdentityHashMap<>();
|
||||
|
||||
public PacketEntity(GrimPlayer player, EntityType type) {
|
||||
@@ -202,8 +203,8 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
}
|
||||
|
||||
public OptionalInt getPotionEffectLevel(PotionType effect) {
|
||||
final Integer amplifier = potionsMap == null ? null : potionsMap.get(effect);
|
||||
return amplifier == null ? OptionalInt.empty() : OptionalInt.of(amplifier);
|
||||
final int amplifier = potionsMap == null ? -1 : potionsMap.getInt(effect);
|
||||
return amplifier == -1 ? OptionalInt.empty() : OptionalInt.of(amplifier);
|
||||
}
|
||||
|
||||
public boolean hasPotionEffect(PotionType effect) {
|
||||
@@ -212,13 +213,14 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
|
||||
public void addPotionEffect(PotionType effect, int amplifier) {
|
||||
if (potionsMap == null) {
|
||||
potionsMap = new HashMap<>();
|
||||
potionsMap = new Object2IntOpenHashMap<>();
|
||||
potionsMap.defaultReturnValue(-1);
|
||||
}
|
||||
potionsMap.put(effect, amplifier);
|
||||
}
|
||||
|
||||
public void removePotionEffect(PotionType effect) {
|
||||
if (potionsMap == null) return;
|
||||
potionsMap.remove(effect);
|
||||
potionsMap.removeInt(effect);
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,8 @@ public class PacketEntitySelf extends PacketEntity {
|
||||
final ValuedAttribute movementSpeed = ValuedAttribute.ranged(Attributes.GENERIC_MOVEMENT_SPEED, 0.1f, 0, 1024);
|
||||
movementSpeed.with(new WrapperPlayServerUpdateAttributes.Property("MOVEMENT_SPEED", 0.1f, new ArrayList<>()));
|
||||
trackAttribute(movementSpeed);
|
||||
trackAttribute(ValuedAttribute.ranged(Attributes.GENERIC_ATTACK_SPEED, 4, 0, 1024)
|
||||
.requiredVersion(player, ClientVersion.V_1_9));
|
||||
trackAttribute(ValuedAttribute.ranged(Attributes.GENERIC_JUMP_STRENGTH, 0.42f, 0, 32)
|
||||
.requiredVersion(player, ClientVersion.V_1_20_5));
|
||||
trackAttribute(ValuedAttribute.ranged(Attributes.PLAYER_BLOCK_BREAK_SPEED, 1.0, 0, 1024)
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class PacketEntityEnderDragonPart extends PacketEntity {
|
||||
|
||||
|
||||
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user