Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf80d63c4a | ||
|
|
540fc454b9 | ||
|
|
cad28d6342 | ||
|
|
978cbb293c | ||
|
|
42ce2122a8 | ||
|
|
096b01213c | ||
|
|
f8a5315fff | ||
|
|
22e9a649c5 | ||
|
|
8e470d82bc | ||
|
|
de083b7b84 | ||
|
|
3f86a097de | ||
|
|
1b3b3d7b1d | ||
|
|
40aa1eadbe | ||
|
|
fac7d98530 | ||
|
|
b9f16762f1 | ||
|
|
441af405b7 | ||
|
|
71fb934a7e | ||
|
|
fe1d3b2b93 | ||
|
|
a408946c0e | ||
|
|
f1192069ae | ||
|
|
09139062c6 | ||
|
|
813551a093 | ||
|
|
3028b4d274 | ||
|
|
7daaca95eb | ||
|
|
102b5842e7 | ||
|
|
a76bf9ce09 | ||
|
|
4f3e715f94 | ||
|
|
1b47e31776 | ||
|
|
b69906f809 | ||
|
|
9ba4052c35 | ||
|
|
a86bf57a39 | ||
|
|
a82f2b888a | ||
|
|
89cf181772 | ||
|
|
6e5ec332dd | ||
|
|
79bf7e4fd1 | ||
|
|
d0aafdd9f7 | ||
|
|
3f19abc859 | ||
|
|
8a6863b9d3 | ||
|
|
26492fd8e3 | ||
|
|
15e809835b | ||
|
|
1eae5484c7 | ||
|
|
90036e2bf2 | ||
|
|
faf2fb3afb | ||
|
|
7461aca13f | ||
|
|
f381805cad | ||
|
|
2fa022eb00 | ||
|
|
44900e584b | ||
|
|
a2f4dc1979 | ||
|
|
0237f956ff | ||
|
|
e222df8e87 | ||
|
|
bbab7e222c | ||
|
|
41c7e41a0e | ||
|
|
5551ef68e1 | ||
|
|
ca6e83de4d | ||
|
|
4d6ac75c73 | ||
|
|
1317ae0bb4 | ||
|
|
d538c59a79 | ||
|
|
6a223a30c7 | ||
|
|
2e1c79b4e3 | ||
|
|
bb143d7993 | ||
|
|
322d2567c0 | ||
|
|
8d7a4cce87 | ||
|
|
832194a1e9 | ||
|
|
0321e0bceb | ||
|
|
63854605b6 | ||
|
|
5c4602188e | ||
|
|
163e120f00 | ||
|
|
ce6e1d3e9f |
No files matched your search
@@ -7,6 +7,8 @@ indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
tab_width = 4
|
||||
end_of_line = crlf
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# intellij specific configuration
|
||||
ij_continuation_indent_size = 8
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import kotlinx.serialization.json.*
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
@@ -27,26 +28,35 @@ fun parseFabricModJson(jarFile: File, project: Project): Set<DependencyIdentifie
|
||||
val nestedJars = mutableSetOf<DependencyIdentifier>()
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
val fabricModJsonEntry = jar.getEntry("fabric.mod.json") ?: return@use
|
||||
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonObject = Json.parseToJsonElement(reader.readText()).jsonObject
|
||||
val jars = jsonObject["jars"]?.jsonArray ?: return@use
|
||||
jars.forEach { jarEntry ->
|
||||
val filePath = jarEntry.jsonObject["file"]?.jsonPrimitive?.content ?: return@forEach
|
||||
project.logger.debug("Found nested JAR in fabric.mod.json: {} in {}", filePath, jarFile)
|
||||
val jarName = filePath.substringAfterLast("/")
|
||||
val parts = jarName.removeSuffix(".jar").split("-")
|
||||
if (parts.size >= 2) {
|
||||
val inferredName = parts.dropLast(1).joinToString("-")
|
||||
val inferredVersion = parts.last()
|
||||
nestedJars.add(DependencyIdentifier("", inferredName, inferredVersion))
|
||||
project.logger.debug("Inferred nested dependency from fabric.mod.json: :$inferredName:$inferredVersion")
|
||||
val fabricModJsonEntry: ZipEntry? = jar.getEntry("fabric.mod.json")
|
||||
if (fabricModJsonEntry != null) {
|
||||
jar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonContent = reader.readText()
|
||||
val jsonElement = Json.parseToJsonElement(jsonContent)
|
||||
val jsonObject = jsonElement.jsonObject
|
||||
val jarsArray = jsonObject["jars"]?.jsonArray ?: return@use
|
||||
jarsArray.forEach { jarEntry: JsonElement ->
|
||||
val filePath =
|
||||
jarEntry.jsonObject["file"]?.jsonPrimitive?.content ?: return@forEach
|
||||
project.logger.debug(
|
||||
"Found nested JAR in fabric.mod.json: {} in {}",
|
||||
filePath,
|
||||
jarFile
|
||||
)
|
||||
val jarName = filePath.substringAfterLast("/")
|
||||
val parts = jarName.removeSuffix(".jar").split("-")
|
||||
if (parts.size >= 2) {
|
||||
val inferredName = parts.dropLast(1).joinToString("-")
|
||||
val inferredVersion = parts.last()
|
||||
nestedJars.add(DependencyIdentifier("", inferredName, inferredVersion))
|
||||
project.logger.debug("Inferred nested dependency from fabric.mod.json: :$inferredName:$inferredVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
project.logger.error("Failed to parse fabric.mod.json in {}: {}", jarFile, e.message)
|
||||
project.logger.error("Failed to parse fabric.mod.json in $jarFile: ${e.message}")
|
||||
}
|
||||
return nestedJars
|
||||
}
|
||||
@@ -56,41 +66,50 @@ fun extractEmbeddedJars(jarFile: File, project: Project): Set<DependencyIdentifi
|
||||
embeddedJars.addAll(parseFabricModJson(jarFile, project)) // Retain existing logic for top-level fabric.mod.json parsing
|
||||
try {
|
||||
JarFile(jarFile).use { jar ->
|
||||
for (entry in jar.entries()) {
|
||||
val entries = jar.entries()
|
||||
while (entries.hasMoreElements()) {
|
||||
val entry = entries.nextElement()
|
||||
val entryName = entry.name
|
||||
if (!entryName.endsWith(".jar") || !entryName.startsWith("META-INF/jarjar/")
|
||||
&& !entryName.startsWith("libs/") && !entryName.startsWith("META-INF/jars/")
|
||||
) continue
|
||||
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
|
||||
val tempFile = File.createTempFile("embedded-", ".jar").also(File::deleteOnExit)
|
||||
jar.getInputStream(entry).use { input ->
|
||||
tempFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
if (entryName.endsWith(".jar") && (entryName.startsWith("META-INF/jarjar/") || entryName.startsWith(
|
||||
"libs/"
|
||||
) || entryName.startsWith("META-INF/jars/"))
|
||||
) {
|
||||
project.logger.debug("Found embedded JAR: {} in {}", entryName, jarFile)
|
||||
val tempFile = File.createTempFile("embedded-", ".jar")
|
||||
tempFile.deleteOnExit()
|
||||
jar.getInputStream(entry).use { input ->
|
||||
tempFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
JarFile(tempFile).use { embeddedJar ->
|
||||
val fabricModJsonEntry = embeddedJar.getEntry("fabric.mod.json") ?: run {
|
||||
project.logger.error("No fabric.mod.json found in embedded JAR: {}. Skipping.", entryName)
|
||||
return@use
|
||||
}
|
||||
embeddedJar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonObject = Json.parseToJsonElement(reader.readText()).jsonObject
|
||||
JarFile(tempFile).use { embeddedJar ->
|
||||
val fabricModJsonEntry: ZipEntry? = embeddedJar.getEntry("fabric.mod.json")
|
||||
if (fabricModJsonEntry != null) {
|
||||
embeddedJar.getInputStream(fabricModJsonEntry).bufferedReader().use { reader ->
|
||||
val jsonContent = reader.readText()
|
||||
val jsonElement = Json.parseToJsonElement(jsonContent)
|
||||
val jsonObject = jsonElement.jsonObject
|
||||
|
||||
// Extract id and parse into group
|
||||
val id = jsonObject["id"]?.jsonPrimitive?.content
|
||||
?: error("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
|
||||
// Extract id and parse into group
|
||||
val id = jsonObject["id"]?.jsonPrimitive?.content
|
||||
?: throw IllegalStateException("Missing 'id' in fabric.mod.json of embedded JAR: $entryName")
|
||||
val idWithDots = id.replace("_", ".")
|
||||
val lastDotIndex = idWithDots.lastIndexOf(".")
|
||||
val group = if (lastDotIndex != -1) idWithDots.take(lastDotIndex) else ""
|
||||
|
||||
val group = id.replace('_', '.').dropLastWhile { it != '.' }.dropLast(1)
|
||||
// Extract name and version from the JAR filename
|
||||
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
|
||||
val (name, version) = parseNameAndVersionFromJarName(jarName)
|
||||
?: throw IllegalStateException("Could not parse name and version from JAR filename: $jarName")
|
||||
|
||||
// Extract name and version from the JAR filename
|
||||
val jarName = entryName.substringAfterLast("/").removeSuffix(".jar")
|
||||
val (name, version) = parseNameAndVersionFromJarName(jarName)
|
||||
?: error("Could not parse name and version from JAR filename: $jarName")
|
||||
|
||||
// Create DependencyIdentifier (classifier is empty for now)
|
||||
val depId = DependencyIdentifier(group, name, version)
|
||||
embeddedJars.add(depId)
|
||||
project.logger.debug("Identified embedded dependency from fabric.mod.json and filename: {}", depId)
|
||||
// Create DependencyIdentifier (classifier is empty for now)
|
||||
val depId = DependencyIdentifier(group, name, version)
|
||||
embeddedJars.add(depId)
|
||||
project.logger.debug("Identified embedded dependency from fabric.mod.json and filename: {}", depId)
|
||||
}
|
||||
} else {
|
||||
project.logger.error("No fabric.mod.json found in embedded JAR: {}. Skipping.", entryName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,13 +126,15 @@ fun parseNameAndVersionFromJarName(jarName: String): Pair<String, String>? {
|
||||
val versionStartMatch = versionStartRegex.find(jarName) ?: return null
|
||||
val versionStartIndex = versionStartMatch.range.first
|
||||
|
||||
if (versionStartIndex == 0 || versionStartIndex + 1 >= jarName.length) return null
|
||||
|
||||
// Split the JAR name into name and version at the version start index
|
||||
val name = jarName.take(versionStartIndex)
|
||||
val version = jarName.drop(versionStartIndex + 1) // Skip the hyphen
|
||||
val version = jarName.substring(versionStartIndex + 1) // Skip the hyphen
|
||||
|
||||
return Pair(name, version)
|
||||
return if (name.isNotEmpty() && version.isNotEmpty()) {
|
||||
Pair(name, version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldExcludeDependency(
|
||||
@@ -166,12 +187,15 @@ fun isJijTarget(
|
||||
project: Project
|
||||
): Boolean {
|
||||
val classifier = dependency.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
project.logger.debug("Checking JIJ target: {}", DependencyIdentifier(
|
||||
dependency.moduleGroup,
|
||||
dependency.moduleName,
|
||||
dependency.moduleVersion,
|
||||
classifier
|
||||
))
|
||||
project.logger.debug(
|
||||
"Checking JIJ target: {}",
|
||||
DependencyIdentifier(
|
||||
dependency.moduleGroup,
|
||||
dependency.moduleName,
|
||||
dependency.moduleVersion,
|
||||
classifier
|
||||
)
|
||||
)
|
||||
return !shouldExcludeDependency(
|
||||
dependency,
|
||||
allEmbeddedDependencies,
|
||||
@@ -189,14 +213,14 @@ fun includeDependencyWithExclusions(
|
||||
project.logger.debug("Including external dependency as JIJ with dynamic exclusions: {}", depId)
|
||||
project.dependencies {
|
||||
// Include dependency with classifier (if present)
|
||||
"include"(depId.toString()) {
|
||||
"include"("${depId.group}:${depId.name}:${depId.version}${if (depId.classifier.isNotEmpty()) ":${depId.classifier}" else ""}") {
|
||||
allEmbeddedDependencies.forEach { embeddedDep ->
|
||||
if (embeddedDep.group.isNotEmpty()) {
|
||||
exclude(group = embeddedDep.group, module = embeddedDep.name)
|
||||
} else {
|
||||
exclude(module = embeddedDep.name)
|
||||
}
|
||||
project.logger.debug("Excluding embedded transitive dependency: {}", embeddedDep)
|
||||
project.logger.debug("Excluding embedded transitive dependency: ${embeddedDep.name}:${embeddedDep.version}${if (embeddedDep.classifier.isNotEmpty()) ":${embeddedDep.classifier}" else ""}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,52 +236,59 @@ fun processDependencies(
|
||||
) {
|
||||
dependencies.forEach { dep: ResolvedDependency ->
|
||||
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion, classifier)
|
||||
val depKey = depId.toString()
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
project.logger.debug("Resolved dependency: {}", depId)
|
||||
|
||||
if (!processed.add(depKey)) return
|
||||
|
||||
project.logger.debug("Resolved dependency: {}", depKey)
|
||||
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
}
|
||||
|
||||
if (isJijTarget(dep, allEmbeddedDependencies.keys, actuallyIncludedDependencies, project)) {
|
||||
project.logger.debug("Processing JIJ dependency: {}", depId)
|
||||
val isProjectDependency = project.rootProject.allprojects.any { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}
|
||||
if (isProjectDependency) {
|
||||
val projectPath = project.rootProject.allprojects.find { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}?.path ?: error("Project dependency not found: ${dep.moduleName}")
|
||||
project.logger.debug("Including project dependency as JIJ: {}", projectPath)
|
||||
project.dependencies {
|
||||
"include"(project.project(projectPath))
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
} else {
|
||||
includeDependencyWithExclusions(
|
||||
depId,
|
||||
project,
|
||||
actuallyIncludedDependencies,
|
||||
allEmbeddedDependencies.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processDependencies(
|
||||
dep.children,
|
||||
processed,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
) {
|
||||
project.logger.debug("Processing JIJ dependency: {}", depId)
|
||||
val isProjectDependency = project.rootProject.allprojects.any { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}
|
||||
if (isProjectDependency) {
|
||||
val projectPath = project.rootProject.allprojects.find { subproject ->
|
||||
subproject.name == dep.moduleName && (subproject.group == dep.moduleGroup || dep.moduleGroup.isEmpty())
|
||||
}?.path
|
||||
?: throw IllegalStateException("Project dependency not found: ${dep.moduleName}")
|
||||
project.logger.debug("Including project dependency as JIJ: $projectPath")
|
||||
project.dependencies {
|
||||
"include"(project.project(projectPath))
|
||||
}
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
} else {
|
||||
includeDependencyWithExclusions(
|
||||
depId,
|
||||
project,
|
||||
actuallyIncludedDependencies,
|
||||
allEmbeddedDependencies.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processDependencies(
|
||||
dep.children,
|
||||
processed,
|
||||
allEmbeddedDependencies,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,16 +304,22 @@ project.afterEvaluate {
|
||||
dependencies.forEach { dep ->
|
||||
val classifier = dep.moduleArtifacts.firstOrNull()?.classifier ?: ""
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}${if (classifier.isNotEmpty()) ":$classifier" else ""}"
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug("Collected embedded dependency: {} from {}", embeddedDep, jarFile)
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug(
|
||||
"Collected embedded dependency: {} from {}",
|
||||
embeddedDep,
|
||||
jarFile
|
||||
)
|
||||
}
|
||||
}
|
||||
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
|
||||
}
|
||||
collectAllEmbeddedDependenciesForAfterEvaluate(dep.children)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +339,8 @@ project.afterEvaluate {
|
||||
tasks.withType<Jar>().configureEach {
|
||||
doFirst {
|
||||
project.logger.debug("Resolving JIJ dependencies for JAR inclusion")
|
||||
val resolvedDependencies = jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
val resolvedDependencies =
|
||||
jijDependencies.resolvedConfiguration.firstLevelModuleDependencies
|
||||
val processed = mutableSetOf<String>()
|
||||
val allEmbeddedDependencies = mutableMapOf<DependencyIdentifier, File>()
|
||||
val actuallyIncludedDependencies = mutableSetOf<DependencyIdentifier>()
|
||||
@@ -310,20 +348,22 @@ tasks.withType<Jar>().configureEach {
|
||||
fun collectAllEmbeddedDependenciesForJar(dependencies: Set<ResolvedDependency>) {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug(
|
||||
"Collected embedded dependency for JAR: {} from {}",
|
||||
embeddedDep,
|
||||
jarFile
|
||||
)
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
project.logger.debug(
|
||||
"Collected embedded dependency for JAR: {} from {}",
|
||||
embeddedDep,
|
||||
jarFile
|
||||
)
|
||||
}
|
||||
}
|
||||
collectAllEmbeddedDependenciesForJar(dep.children)
|
||||
}
|
||||
collectAllEmbeddedDependenciesForJar(dep.children)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,27 +376,30 @@ tasks.withType<Jar>().configureEach {
|
||||
dependencies.forEach { dep ->
|
||||
val depKey = "${dep.moduleGroup}:${dep.moduleName}:${dep.moduleVersion}"
|
||||
val depId = DependencyIdentifier(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
|
||||
if (!processed.add(depKey)) return@forEach
|
||||
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
|
||||
if (!processed.contains(depKey)) {
|
||||
processed.add(depKey)
|
||||
project.logger.debug("Resolved JIJ dependency for JAR: {}", depId)
|
||||
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
val jarFile = dep.moduleArtifacts.firstOrNull()?.file
|
||||
if (jarFile != null && jarFile.exists()) {
|
||||
val embeddedJars = extractEmbeddedJars(jarFile, project)
|
||||
embeddedJars.forEach { embeddedDep ->
|
||||
allEmbeddedDependencies[embeddedDep] = jarFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)) {
|
||||
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
if (isJijTarget(
|
||||
dep,
|
||||
allEmbeddedDependencies.keys,
|
||||
actuallyIncludedDependencies,
|
||||
project
|
||||
)
|
||||
) {
|
||||
project.logger.debug("Including JIJ dependency in JAR: {}", depId)
|
||||
actuallyIncludedDependencies.add(depId)
|
||||
}
|
||||
logDependencies(dep.children)
|
||||
}
|
||||
logDependencies(dep.children)
|
||||
}
|
||||
}
|
||||
logDependencies(resolvedDependencies)
|
||||
|
||||
@@ -60,7 +60,8 @@ object VersionUtil {
|
||||
.apply { waitFor() }
|
||||
.inputStream
|
||||
.use { stdout.writeBytes(it.readAllBytes()) }
|
||||
return stdout.toString().trim()
|
||||
val fullCommit = stdout.toString().trim()
|
||||
return fullCommit.take(minOf(fullCommit.length, 7))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@ import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.platform.bukkit.world.BukkitPlatformWorld;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -15,6 +17,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitGrimEntity implements GrimEntity {
|
||||
|
||||
protected static final boolean CAN_USE_DIRECT_GETTERS = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_1) && PaperUtils.PAPER;
|
||||
|
||||
private final Entity entity;
|
||||
private BukkitPlatformWorld bukkitPlatformWorld;
|
||||
|
||||
@@ -75,4 +79,26 @@ public class BukkitGrimEntity implements GrimEntity {
|
||||
location.getPitch()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double oX, double oY, double oZ) {
|
||||
if (CAN_USE_DIRECT_GETTERS) {
|
||||
double x = this.entity.getX();
|
||||
double y = this.entity.getY();
|
||||
double z = this.entity.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
} else {
|
||||
org.bukkit.Location location = this.entity.getLocation();
|
||||
double x = location.getX();
|
||||
double y = location.getY();
|
||||
double z = location.getZ();
|
||||
double distX = (x - oX) * (x - oX);
|
||||
double distY = (y - oY) * (y - oY);
|
||||
double distZ = (z - oZ) * (z - oZ);
|
||||
return distX + distY + distZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.PistonData;
|
||||
import ac.grim.grimac.utils.data.TrackedPosition;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -28,10 +27,11 @@ public class PistonEvent implements Listener {
|
||||
private static final double MAX_VERTICAL_DISTANCE = 64.0;
|
||||
|
||||
// accuracy isn't that important, it's close enough and performant
|
||||
private static boolean isCloseEnough(Vector3i vectorA, Vector3d vectorB) {
|
||||
return Math.abs(vectorA.getX() - vectorB.getX()) <= MAX_HORIZONTAL_DISTANCE
|
||||
&& Math.abs(vectorA.getY() - vectorB.getY()) <= MAX_VERTICAL_DISTANCE
|
||||
&& Math.abs(vectorA.getZ() - vectorB.getZ()) <= MAX_HORIZONTAL_DISTANCE;
|
||||
private static boolean isCloseEnough(final int aX, final int aY, final int aZ,
|
||||
final double bX, final double bY, final double bZ) {
|
||||
return Math.abs(aX - bX) <= MAX_HORIZONTAL_DISTANCE
|
||||
&& Math.abs(aY - bY) <= MAX_VERTICAL_DISTANCE
|
||||
&& Math.abs(aZ - bZ) <= MAX_HORIZONTAL_DISTANCE;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@@ -71,10 +71,13 @@ public class PistonEvent implements Listener {
|
||||
final int chunkX = event.getBlock().getX() >> 4;
|
||||
final int chunkZ = event.getBlock().getZ() >> 4;
|
||||
final BlockFace blockFace = BukkitConversionUtils.fromBukkitFace(event.getDirection());
|
||||
final Vector3i sourcePos = new Vector3i(piston.getX(), piston.getY(), piston.getZ());
|
||||
final int sourceX = piston.getX();
|
||||
final int sourceY = piston.getY();
|
||||
final int sourceZ = piston.getZ();
|
||||
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
|
||||
TrackedPosition position = player.compensatedEntities.self.trackedServerPosition;
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourceX, sourceY, sourceZ, position.getX(), position.getY(), position.getZ())) {
|
||||
final int lastTrans = player.lastTransactionSent.get();
|
||||
PistonData data = new PistonData(blockFace, boxes, lastTrans, true, hasSlimeBlock, hasHoneyBlock);
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
|
||||
@@ -127,12 +130,16 @@ public class PistonEvent implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
final int chunkX = event.getBlock().getX() >> 4;
|
||||
final int chunkZ = event.getBlock().getZ() >> 4;
|
||||
Vector3i sourcePos = new Vector3i(event.getBlock().getX(), event.getBlock().getY(), event.getBlock().getZ());
|
||||
final Block block = event.getBlock();
|
||||
final int chunkX = block.getX() >> 4;
|
||||
final int chunkZ = block.getZ() >> 4;
|
||||
final int sourceX = block.getX();
|
||||
final int sourceY = block.getY();
|
||||
final int sourceZ = block.getZ();
|
||||
|
||||
for (GrimPlayer player : GrimAPI.INSTANCE.getPlayerDataManager().getEntries()) {
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourcePos, player.compensatedEntities.self.trackedServerPosition.getPos())) {
|
||||
TrackedPosition position = player.compensatedEntities.self.trackedServerPosition;
|
||||
if (player.compensatedWorld.isChunkLoaded(chunkX, chunkZ) && isCloseEnough(sourceX, sourceY, sourceZ, position.getX(), position.getY(), position.getZ())) {
|
||||
int lastTrans = player.lastTransactionSent.get();
|
||||
PistonData data = new PistonData(face, boxes, lastTrans, false, hasSlimeBlock, hasHoneyBlock);
|
||||
player.latencyUtils.addRealTimeTaskAsync(lastTrans, () -> player.compensatedWorld.activePistons.add(data));
|
||||
|
||||
+2
-1
@@ -177,10 +177,11 @@ public class BukkitItemResetHandler implements ItemResetHandler {
|
||||
if (legacy) { // 1.8.8
|
||||
resetItemUsage = player -> {
|
||||
try {
|
||||
method.invoke(getHandle.invoke(player));
|
||||
|
||||
// in 1.8 we need to resync item usage manually,
|
||||
// only do so if the player is using an item
|
||||
if (isUsingItem.test(player)) player.updateInventory();
|
||||
method.invoke(getHandle.invoke(player));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
+28
-4
@@ -9,8 +9,11 @@ import ac.grim.grimac.platform.bukkit.entity.BukkitGrimEntity;
|
||||
import ac.grim.grimac.platform.bukkit.utils.anticheat.MultiLibUtil;
|
||||
import ac.grim.grimac.platform.bukkit.utils.convert.BukkitConversionUtils;
|
||||
import ac.grim.grimac.platform.bukkit.utils.reflection.PaperUtils;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.player.User;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lombok.Getter;
|
||||
@@ -27,6 +30,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPlayer {
|
||||
|
||||
private static final BukkitAudiences audiences = BukkitAudiences.create(GrimACBukkitLoaderPlugin.LOADER);
|
||||
|
||||
@Getter
|
||||
@@ -34,10 +38,18 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
@Getter
|
||||
private final PlatformInventory inventory;
|
||||
|
||||
private final @Nullable User user;
|
||||
|
||||
public BukkitPlatformPlayer(Player bukkitPlayer) {
|
||||
super(bukkitPlayer);
|
||||
this.bukkitPlayer = bukkitPlayer;
|
||||
this.inventory = new BukkitPlatformInventory(bukkitPlayer);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value()) {
|
||||
Object channel = PacketEvents.getAPI().getProtocolManager().getChannel(bukkitPlayer.getUniqueId());
|
||||
this.user = PacketEvents.getAPI().getProtocolManager().getUser(channel);
|
||||
} else {
|
||||
this.user = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,12 +79,20 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
bukkitPlayer.sendMessage(message);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
bukkitPlayer.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
if (CommonGrimArguments.USE_CHAT_FAST_BYPASS.value() && user != null) {
|
||||
user.sendMessage(message);
|
||||
} else {
|
||||
audiences.player(bukkitPlayer).sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,8 +112,12 @@ public class BukkitPlatformPlayer extends BukkitGrimEntity implements PlatformPl
|
||||
|
||||
@Override
|
||||
public Vector3d getPosition() {
|
||||
org.bukkit.Location location = this.bukkitPlayer.getLocation();
|
||||
return new Vector3d(location.getX(), location.getY(), location.getZ());
|
||||
if (CAN_USE_DIRECT_GETTERS) {
|
||||
return new Vector3d(this.bukkitPlayer.getX(), this.bukkitPlayer.getY(), this.bukkitPlayer.getZ());
|
||||
} else {
|
||||
org.bukkit.Location location = this.bukkitPlayer.getLocation();
|
||||
return new Vector3d(location.getX(), location.getY(), location.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -98,7 +98,7 @@ dependencies {
|
||||
}
|
||||
|
||||
compileOnly(libs.floodgate.api)
|
||||
compileOnly(libs.via.version.api)
|
||||
compileOnly(libs.viaversion)
|
||||
compileOnly(libs.netty)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import ac.grim.grimac.platform.api.scheduler.PlatformScheduler;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.platform.api.sender.SenderFactory;
|
||||
import ac.grim.grimac.utils.anticheat.PlayerDataManager;
|
||||
import ac.grim.grimac.utils.common.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import ac.grim.grimac.utils.reflection.ReflectionUtils;
|
||||
import lombok.Getter;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
@@ -67,7 +67,7 @@ public final class GrimAPI {
|
||||
|
||||
// the order matters
|
||||
private static Platform detectPlatform() {
|
||||
Platform override = Platform.getByName(GrimArguments.PLATFORM_OVERRIDE);
|
||||
Platform override = CommonGrimArguments.PLATFORM_OVERRIDE.value();
|
||||
if (override != null) return override;
|
||||
if (ReflectionUtils.hasClass("io.papermc.paper.threadedregions.RegionizedServer")) return Platform.FOLIA;
|
||||
if (ReflectionUtils.hasClass("org.bukkit.Bukkit")) return Platform.BUKKIT;
|
||||
|
||||
@@ -169,6 +169,10 @@ public class Check extends GrimProcessor implements AbstractCheck {
|
||||
packetType == PacketType.Play.Client.WINDOW_CONFIRMATION;
|
||||
}
|
||||
|
||||
public static boolean isAsync(PacketTypeCommon packetType) {
|
||||
return packetType == PacketType.Play.Client.KEEP_ALIVE;
|
||||
}
|
||||
|
||||
public boolean isUpdate(PacketTypeCommon packetType) {
|
||||
return isFlying(packetType)
|
||||
|| packetType == PacketType.Play.Client.CLIENT_TICK_END
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package ac.grim.grimac.checks.impl.badpackets;
|
||||
|
||||
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 com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "BadPacketsZ", experimental = true)
|
||||
public class BadPacketsZ extends Check implements PacketCheck {
|
||||
private boolean sent;
|
||||
|
||||
public BadPacketsZ(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
|
||||
sent = false;
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Client.PLAYER_INPUT) {
|
||||
if (sent) {
|
||||
flagAndAlert();
|
||||
}
|
||||
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
|
||||
private int lastTick;
|
||||
private boolean didLastFlag;
|
||||
// Initialize to non-null values to prevent NPE when checking for blockType properties and if position equals old position
|
||||
private @NotNull Vector3i lastBreakLoc = new Vector3i();
|
||||
private @NotNull StateType lastBlockType = StateTypes.AIR;
|
||||
private int lastX, lastY, lastZ;
|
||||
private float lastHardness, lastBlastResistance;
|
||||
|
||||
public AirLiquidBreak(GrimPlayer player) {
|
||||
super(player);
|
||||
@@ -40,15 +40,20 @@ public class AirLiquidBreak extends Check implements BlockBreakCheck {
|
||||
// I am explicitly making this patch as narrow and specific as possible to potentially discover other blocks that exhibit similar behaviour
|
||||
int newTick = GrimAPI.INSTANCE.getTickManager().currentTick;
|
||||
if (lastTick == newTick
|
||||
&& lastBreakLoc.equals(blockBreak.position)
|
||||
&& lastX == blockBreak.x
|
||||
&& lastY == blockBreak.y
|
||||
&& lastZ == blockBreak.z
|
||||
&& !didLastFlag
|
||||
&& lastBlockType.getHardness() == 0.0F
|
||||
&& lastBlockType.getBlastResistance() == 0.0F
|
||||
&& lastHardness == 0.0F
|
||||
&& lastBlastResistance == 0.0F
|
||||
&& block == StateTypes.WATER
|
||||
) return;
|
||||
lastTick = newTick;
|
||||
lastBreakLoc = blockBreak.position;
|
||||
lastBlockType = block;
|
||||
lastX = blockBreak.x;
|
||||
lastY = blockBreak.y;
|
||||
lastZ = blockBreak.z;
|
||||
lastHardness = block.getHardness();
|
||||
lastBlastResistance = block.getBlastResistance();
|
||||
|
||||
// the block does not have a hitbox
|
||||
boolean invalid = (block == StateTypes.LIGHT && !(player.inventory.getHeldItem().is(ItemTypes.LIGHT) || player.inventory.getOffHand().is(ItemTypes.LIGHT)))
|
||||
|
||||
@@ -25,10 +25,9 @@ public class FarBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
double min = Double.MAX_VALUE;
|
||||
for (double d : player.getPossibleEyeHeights()) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
|
||||
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
min = Math.min(min, eyes.distanceSquared(best));
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.x, blockBreak.y, blockBreak.z);
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
|
||||
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
|
||||
}
|
||||
|
||||
// getPickRange() determines this?
|
||||
|
||||
@@ -36,8 +36,11 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
super(playerData);
|
||||
}
|
||||
|
||||
// The block the player is currently breaking
|
||||
Vector3i targetBlockPosition = null;
|
||||
// The block the player is currently breaking, represented by primitives for performance.
|
||||
// `targetBlockIsSet` tracks nullness of the position, since primitives can't be null
|
||||
private boolean targetBlockIsSet;
|
||||
private int targetBlockX, targetBlockY, targetBlockZ;
|
||||
|
||||
// The maximum amount of damage the player deals to the block
|
||||
//
|
||||
double maximumBlockDamage = 0;
|
||||
@@ -67,8 +70,11 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
// * can we translate back "up" to server version and run check against server version to avoid loading older registries?
|
||||
WrappedBlockState block = clientOlderThanServer ? WrappedBlockState.getByGlobalId(player.getClientVersion(), player.getViaTranslatedClientBlockID(blockBreak.block.getGlobalId())) : blockBreak.block;
|
||||
|
||||
startBreak = System.currentTimeMillis() - (targetBlockPosition == null ? 50 : 0); // ???
|
||||
targetBlockPosition = blockBreak.position;
|
||||
startBreak = System.currentTimeMillis() - (!targetBlockIsSet ? 50 : 0); // ???
|
||||
targetBlockIsSet = true;
|
||||
targetBlockX = blockBreak.x;
|
||||
targetBlockY = blockBreak.y;
|
||||
targetBlockZ = blockBreak.z;
|
||||
|
||||
maximumBlockDamage = BlockBreakSpeed.getBlockDamage(player, block);
|
||||
|
||||
@@ -87,9 +93,7 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
}
|
||||
|
||||
clampBalance();
|
||||
}
|
||||
|
||||
if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockPosition != null) {
|
||||
} else if (blockBreak.action == DiggingAction.FINISHED_DIGGING && targetBlockIsSet) {
|
||||
double predictedTime = Math.ceil(1 / maximumBlockDamage) * 50;
|
||||
double realTime = System.currentTimeMillis() - startBreak;
|
||||
double diff = predictedTime - realTime;
|
||||
@@ -117,8 +121,8 @@ public class FastBreak extends Check implements BlockBreakCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
// Find the most optimal block damage using the animation packet, which is sent at least once a tick when breaking blocks
|
||||
// On 1.8 clients, via screws with this packet meaning we must fall back to the 1.8 idle flying packet
|
||||
if ((player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? event.getPacketType() == PacketType.Play.Client.ANIMATION : WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) && targetBlockPosition != null) {
|
||||
maximumBlockDamage = Math.max(maximumBlockDamage, BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(targetBlockPosition)));
|
||||
if ((player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? event.getPacketType() == PacketType.Play.Client.ANIMATION : WrapperPlayClientPlayerFlying.isFlying(event.getPacketType())) && targetBlockIsSet) {
|
||||
maximumBlockDamage = Math.max(maximumBlockDamage, BlockBreakSpeed.getBlockDamage(player, player.compensatedWorld.getBlock(targetBlockX, targetBlockY, targetBlockZ)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ public class MultiBreak extends Check implements BlockBreakCheck {
|
||||
private final List<String> flags = new ArrayList<>();
|
||||
private boolean hasBroken;
|
||||
private BlockFace lastFace;
|
||||
private Vector3i lastPos;
|
||||
private int lastX;
|
||||
private int lastY;
|
||||
private int lastZ;
|
||||
|
||||
public MultiBreak(GrimPlayer player) {
|
||||
super(player);
|
||||
@@ -33,10 +35,22 @@ public class MultiBreak extends Check implements BlockBreakCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasBroken && (blockBreak.face != lastFace || !blockBreak.position.equals(lastPos))) {
|
||||
final int currentX = blockBreak.x;
|
||||
final int currentY = blockBreak.y;
|
||||
final int currentZ = blockBreak.z;
|
||||
|
||||
if (hasBroken && (blockBreak.face != lastFace ||
|
||||
currentX != lastX ||
|
||||
currentY != lastY ||
|
||||
currentZ != lastZ)) {
|
||||
|
||||
final String currentPosString = MessageUtil.toUnlabeledString(currentX, currentY, currentZ);
|
||||
final String lastPosString = MessageUtil.toUnlabeledString(lastX, lastY, lastZ);
|
||||
|
||||
final String verbose = "face=" + blockBreak.face + ", lastFace=" + lastFace
|
||||
+ ", pos=" + MessageUtil.toUnlabledString(blockBreak.position)
|
||||
+ ", lastPos=" + MessageUtil.toUnlabledString(lastPos);
|
||||
+ ", pos=" + currentPosString
|
||||
+ ", lastPos=" + lastPosString;
|
||||
|
||||
if (!player.canSkipTicks()) {
|
||||
if (flagAndAlert(verbose) && shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
@@ -47,7 +61,9 @@ public class MultiBreak extends Check implements BlockBreakCheck {
|
||||
}
|
||||
|
||||
lastFace = blockBreak.face;
|
||||
lastPos = blockBreak.position;
|
||||
lastX = currentX;
|
||||
lastY = currentY;
|
||||
lastZ = currentZ;
|
||||
hasBroken = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
|
||||
@CheckData(name = "PositionBreakB", experimental = true)
|
||||
@CheckData(name = "PositionBreakB")
|
||||
public class PositionBreakB extends Check implements BlockBreakCheck {
|
||||
private final int releaseFace = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8) ? 0 : 255;
|
||||
private BlockFace lastFace;
|
||||
|
||||
@@ -7,6 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.update.BlockBreak;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.TeleportData;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
@@ -14,6 +15,7 @@ import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3f;
|
||||
@@ -70,7 +72,7 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
}
|
||||
|
||||
private boolean didRayTraceHit(BlockBreak blockBreak) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.position);
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockBreak.x, blockBreak.y, blockBreak.z);
|
||||
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
|
||||
@@ -111,7 +113,8 @@ public class RotationBreak extends Check implements BlockBreakCheck {
|
||||
for (Vector3f lookDir : possibleLookDirs) {
|
||||
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
Vector3dm pointAtDistance = trace.getPointAtDistance(distance);
|
||||
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOriginX(), trace.getOriginY(), trace.getOriginZ(), pointAtDistance.getX(), pointAtDistance.getY(), pointAtDistance.getZ());
|
||||
|
||||
if (intercept.first() != null) return true;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,14 @@ import static ac.grim.grimac.utils.nmsutil.BlockBreakSpeed.getBlockDamage;
|
||||
public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
private final int exemptedY = player.getClientVersion().isOlderThan(ClientVersion.V_1_8) ? 255 : (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14) ? -1 : 4095);
|
||||
private boolean lastBlockWasInstantBreak = false;
|
||||
private Vector3i lastBlock, lastCancelledBlock, lastLastBlock = null;
|
||||
|
||||
private boolean lastBlockIsSet;
|
||||
private int lastBlockX, lastBlockY, lastBlockZ;
|
||||
|
||||
private boolean lastCancelledBlockIsSet;
|
||||
private int lastCancelledBlockX, lastCancelledBlockY, lastCancelledBlockZ;
|
||||
|
||||
private boolean lastLastBlockIsSet;
|
||||
|
||||
public WrongBreak(final GrimPlayer player) {
|
||||
super(player);
|
||||
@@ -27,8 +34,7 @@ public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
// The client sometimes sends a wierd cancel packet
|
||||
private boolean shouldExempt(final WrappedBlockState block, int yPos) {
|
||||
// lastLastBlock is always null when this happens, and lastBlock isn't
|
||||
if (lastLastBlock != null || lastBlock == null)
|
||||
if (lastLastBlockIsSet || !lastBlockIsSet)
|
||||
return false;
|
||||
|
||||
// on pre 1.14.4 clients, the YPos of this packet is always the same
|
||||
@@ -41,22 +47,29 @@ public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreak blockBreak) {
|
||||
final int x = blockBreak.x;
|
||||
final int y = blockBreak.y;
|
||||
final int z = blockBreak.z;
|
||||
|
||||
if (blockBreak.action == DiggingAction.START_DIGGING) {
|
||||
final Vector3i pos = blockBreak.position;
|
||||
|
||||
lastBlockWasInstantBreak = getBlockDamage(player, blockBreak.block) >= 1;
|
||||
lastCancelledBlock = null;
|
||||
lastLastBlock = lastBlock;
|
||||
lastBlock = pos;
|
||||
}
|
||||
lastCancelledBlockIsSet = false;
|
||||
|
||||
if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
|
||||
final Vector3i pos = blockBreak.position;
|
||||
lastLastBlockIsSet = lastBlockIsSet;
|
||||
|
||||
if (!shouldExempt(blockBreak.block, pos.y) && !pos.equals(lastBlock)) {
|
||||
lastBlockIsSet = true;
|
||||
lastBlockX = x;
|
||||
lastBlockY = y;
|
||||
lastBlockZ = z;
|
||||
} else if (blockBreak.action == DiggingAction.CANCELLED_DIGGING) {
|
||||
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
|
||||
|
||||
if (!shouldExempt(blockBreak.block, y) && notLastBlock) {
|
||||
// https://github.com/GrimAnticheat/Grim/issues/1512
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && pos.equals(lastCancelledBlock))) {
|
||||
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
|
||||
boolean isLastCancelled = lastCancelledBlockIsSet && x == lastCancelledBlockX && y == lastCancelledBlockY && z == lastCancelledBlockZ;
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4) || (!lastBlockWasInstantBreak && isLastCancelled)) {
|
||||
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
|
||||
if (flagAndAlert("action=CANCELLED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
|
||||
if (shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
@@ -64,18 +77,20 @@ public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
}
|
||||
}
|
||||
|
||||
lastCancelledBlock = pos;
|
||||
lastLastBlock = null;
|
||||
lastBlock = null;
|
||||
return;
|
||||
}
|
||||
lastCancelledBlockIsSet = true;
|
||||
lastCancelledBlockX = x;
|
||||
lastCancelledBlockY = y;
|
||||
lastCancelledBlockZ = z;
|
||||
|
||||
if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
|
||||
final Vector3i pos = blockBreak.position;
|
||||
lastLastBlockIsSet = lastBlockIsSet = false;
|
||||
} else if (blockBreak.action == DiggingAction.FINISHED_DIGGING) {
|
||||
boolean notLastCancelled = !lastCancelledBlockIsSet || x != lastCancelledBlockX || y != lastCancelledBlockY || z != lastCancelledBlockZ;
|
||||
boolean notLastBlock = !lastBlockIsSet || x != lastBlockX || y != lastBlockY || z != lastBlockZ;
|
||||
|
||||
// when a player looks away from the mined block, they send a cancel, and if they look at it again, they don't send another start. (thanks mojang!)
|
||||
if (!pos.equals(lastCancelledBlock) && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && !pos.equals(lastBlock)) {
|
||||
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + MessageUtil.toUnlabledString(lastBlock) + ", pos=" + MessageUtil.toUnlabledString(pos))) {
|
||||
if (notLastCancelled && (!lastBlockWasInstantBreak || player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) && notLastBlock) {
|
||||
String lastBlockStr = lastBlockIsSet ? MessageUtil.toUnlabeledString(lastBlockX, lastBlockY, lastBlockZ) : "null";
|
||||
if (flagAndAlert("action=FINISHED_DIGGING" + ", last=" + lastBlockStr + ", pos=" + MessageUtil.toUnlabeledString(x, y, z))) {
|
||||
if (shouldModifyPackets()) {
|
||||
blockBreak.cancel();
|
||||
}
|
||||
@@ -84,9 +99,7 @@ public class WrongBreak extends Check implements BlockBreakCheck {
|
||||
|
||||
// 1.14.4+ clients don't send another start break in protected regions
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_14_4)) {
|
||||
lastCancelledBlock = null;
|
||||
lastLastBlock = null;
|
||||
lastBlock = null;
|
||||
lastCancelledBlockIsSet = lastLastBlockIsSet = lastBlockIsSet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.reflection.ViaVersionUtil;
|
||||
import ac.grim.grimac.utils.via.ViaHooks;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
@@ -24,11 +26,8 @@ public class ChatB extends Check implements PacketCheck {
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHAT_MESSAGE) {
|
||||
String message = new WrapperPlayClientChatMessage(event).getMessage();
|
||||
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
if (flagAndAlert("message=" + message)) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
}
|
||||
if (checkChatMessage(message)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +52,21 @@ public class ChatB extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns whether the packet should be cancelled
|
||||
public boolean checkChatMessage(String message) {
|
||||
if (message.isEmpty() || !message.trim().equals(message) || message.startsWith("/") && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
if (flagAndAlert("message=" + message) && shouldModifyPackets()) {
|
||||
player.onPacketCancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static {
|
||||
if (ViaVersionUtil.isAvailable && PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_11)) {
|
||||
ViaHooks.injectVia();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,10 @@ public class Reach extends Check implements PacketCheck {
|
||||
} else {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) {
|
||||
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
final double x = reachEntity.trackedServerPosition.getX();
|
||||
final double y = reachEntity.trackedServerPosition.getX();
|
||||
final double z = reachEntity.trackedServerPosition.getX();
|
||||
targetBox = new SimpleCollisionBox(x - 1, y, z - 1, x + 1, y + 2, z + 1);
|
||||
}
|
||||
return ReachUtils.getMinReachToBox(player, targetBox) > player.compensatedEntities.self.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
|
||||
}
|
||||
@@ -175,7 +178,10 @@ public class Reach extends Check implements PacketCheck {
|
||||
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
|
||||
|
||||
if (reachEntity.type == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
|
||||
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
|
||||
final double x = reachEntity.trackedServerPosition.getX();
|
||||
final double y = reachEntity.trackedServerPosition.getX();
|
||||
final double z = reachEntity.trackedServerPosition.getX();
|
||||
targetBox = new SimpleCollisionBox(x - 1, y, z - 1, x + 1, y + 2, z + 1);
|
||||
}
|
||||
|
||||
// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
|
||||
@@ -218,21 +224,27 @@ public class Reach extends Check implements PacketCheck {
|
||||
// +3 would be 3 + 3 = 6, which is the pre-1.20.5 behaviour, preventing "Missed Hitbox"
|
||||
final double distance = maxReach + 3;
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
final Vector3dm eyePos = new Vector3dm(from.getX(), 0, from.getZ());
|
||||
|
||||
final double eyePosX = from.getX();
|
||||
final double eyePosZ = from.getZ();
|
||||
|
||||
for (Vector3dm lookVec : possibleLookDirs) {
|
||||
for (double eye : possibleEyeHeights) {
|
||||
eyePos.setY(from.getY() + eye);
|
||||
Vector3dm endReachPos = eyePos.clone().add(new Vector3dm(lookVec.getX() * distance, lookVec.getY() * distance, lookVec.getZ() * distance));
|
||||
final double eyePosY = from.getY() + eye;
|
||||
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePos, endReachPos).first();
|
||||
final double endX = eyePosX + lookVec.getX() * distance;
|
||||
final double endY = eyePosY + lookVec.getY() * distance;
|
||||
final double endZ = eyePosZ + lookVec.getZ() * distance;
|
||||
|
||||
if (ReachUtils.isVecInside(targetBox, eyePos)) {
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(targetBox, eyePosX, eyePosY, eyePosZ, endX, endY, endZ).first();
|
||||
|
||||
if (ReachUtils.isVecInside(targetBox, eyePosX, eyePosY, eyePosZ)) {
|
||||
minDistance = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (intercept != null) {
|
||||
minDistance = Math.min(eyePos.distance(intercept), minDistance);
|
||||
minDistance = Math.min(intercept.distance(eyePosX, eyePosY, eyePosZ), minDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 = "ElytraA", description = "Started gliding while already gliding", experimental = true)
|
||||
@CheckData(name = "ElytraA", description = "Started gliding while already gliding")
|
||||
public class ElytraA extends Check implements PostPredictionCheck {
|
||||
private boolean setback;
|
||||
|
||||
|
||||
@@ -9,7 +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.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraB", description = "Started gliding without jumping", experimental = true)
|
||||
@CheckData(name = "ElytraB", description = "Started gliding without jumping")
|
||||
public class ElytraB extends Check implements PostPredictionCheck {
|
||||
private boolean glide;
|
||||
private boolean setback;
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientEntityAction;
|
||||
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently", experimental = true)
|
||||
@CheckData(name = "ElytraC", description = "Started gliding too frequently")
|
||||
public class ElytraC extends Check implements PostPredictionCheck {
|
||||
private boolean glideThisTick, glideLastTick, setback;
|
||||
private int flags;
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 = "ElytraF", description = "Started gliding while on ground", experimental = true)
|
||||
@CheckData(name = "ElytraF", description = "Started gliding while on ground")
|
||||
public class ElytraF extends Check implements PostPredictionCheck {
|
||||
private boolean setback;
|
||||
|
||||
|
||||
+3
-1
@@ -15,6 +15,8 @@ public class VehiclePredictionRunner extends Check implements VehicleCheck {
|
||||
public void process(final VehiclePositionUpdate vehicleUpdate) {
|
||||
// Vehicle onGround = false always
|
||||
// We don't do vehicle setbacks because vehicle netcode sucks.
|
||||
player.movementCheckRunner.processAndCheckMovementPacket(new PositionUpdate(vehicleUpdate.from(), vehicleUpdate.to(), false, null, null, vehicleUpdate.isTeleport()));
|
||||
player.movementCheckRunner.processAndCheckMovementPacket(new PositionUpdate(
|
||||
vehicleUpdate.fromX(), vehicleUpdate.fromY(), vehicleUpdate.fromZ(), vehicleUpdate.toX(), vehicleUpdate.toY(), vehicleUpdate.toZ(),
|
||||
false, null, null, vehicleUpdate.isTeleport()));
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@CheckData(name = "MultiActionsC", description = "Clicked in inventory while moving", experimental = true)
|
||||
@CheckData(name = "MultiActionsC", description = "Clicked in inventory while moving")
|
||||
public class MultiActionsC extends Check implements PacketCheck {
|
||||
public MultiActionsC(GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
// TODO: move this to a bett spot? not sure where to put this
|
||||
@Contract(pure = true)
|
||||
public static String getVerbose(@NotNull GrimPlayer player) {
|
||||
StringJoiner verbose = new StringJoiner(", ");
|
||||
|
||||
@@ -7,7 +7,7 @@ import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
|
||||
@CheckData(name = "MultiActionsD", description = "Closed inventory while moving", experimental = true)
|
||||
@CheckData(name = "MultiActionsD", description = "Closed inventory while moving")
|
||||
public class MultiActionsD extends Check implements PacketCheck {
|
||||
public MultiActionsD(GrimPlayer player) {
|
||||
super(player);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class MultiActionsE extends Check implements PacketCheck {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE) {
|
||||
if (!isAsync(event.getPacketType())) {
|
||||
dropping = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public class PacketOrderB extends Check implements PacketCheck {
|
||||
return; // do not set sentAnimation to false
|
||||
}
|
||||
|
||||
if (event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE) {
|
||||
if (!isAsync(event.getPacketType())) {
|
||||
if (sentAttack && is1_9) {
|
||||
flagAndAlert("post-attack");
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ public class PacketOrderO extends Check implements PacketCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flying && event.getPacketType() != PacketType.Play.Client.KEEP_ALIVE
|
||||
&& event.getPacketType() != PacketType.Play.Client.VEHICLE_MOVE
|
||||
) {
|
||||
if (flying && !isAsync(event.getPacketType()) && event.getPacketType() != PacketType.Play.Client.VEHICLE_MOVE) {
|
||||
if (player.inVehicle() && event.getPacketType() == PacketType.Play.Client.ENTITY_ACTION) {
|
||||
WrapperPlayClientEntityAction.Action action = new WrapperPlayClientEntityAction(event).getAction();
|
||||
if (action == WrapperPlayClientEntityAction.Action.START_SPRINTING || action == WrapperPlayClientEntityAction.Action.STOP_SPRINTING) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package ac.grim.grimac.checks.impl.packetorder;
|
||||
|
||||
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 com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBundle;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
|
||||
@CheckData(name = "PacketOrderP", experimental = true)
|
||||
public class PacketOrderP extends Check implements PacketCheck {
|
||||
public PacketOrderP(final GrimPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private byte trimTimer; // let the list shrink eventually
|
||||
private final IntArrayList transactions = new IntArrayList(0);
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Client.CHUNK_BATCH_ACK) {
|
||||
if (!transactions.rem(player.getLastTransactionReceived())) {
|
||||
flagAndAlert("invalid response");
|
||||
}
|
||||
} else if (!isAsync(event.getPacketType()) && !isTransaction(event.getPacketType())) {
|
||||
if (transactions.rem(player.getLastTransactionReceived())) {
|
||||
flagAndAlert("skipped response, type=" + event.getPacketType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (event.getPacketType() == PacketType.Play.Server.CHUNK_BATCH_END) {
|
||||
boolean sendingBundlePacket = player.packetStateData.sendingBundlePacket;
|
||||
if (!sendingBundlePacket) player.user.sendPacket(new WrapperPlayServerBundle());
|
||||
|
||||
player.sendTransaction();
|
||||
int transaction = player.getLastTransactionSent();
|
||||
transactions.add(transaction);
|
||||
if (++trimTimer == 0) transactions.trim();
|
||||
player.addRealTimeTaskNext(() -> {
|
||||
if (transactions.rem(transaction))
|
||||
flagAndAlert("skipped response, type=TRANSACTION");
|
||||
});
|
||||
|
||||
if (!sendingBundlePacket) {
|
||||
event.getTasksAfterSend().add(() -> player.user.sendPacket(new WrapperPlayServerBundle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,24 +42,26 @@ public class DebugHandler extends AbstractDebugHandler implements PostPrediction
|
||||
double offset = predictionComplete.getOffset();
|
||||
|
||||
if (listeners.isEmpty() && !outputToConsole) return;
|
||||
if (player.predictedVelocity.vector.lengthSquared() == 0 && offset == 0) return;
|
||||
|
||||
final double predictedX = player.predictedVelocity.vectorX;
|
||||
final double predictedY = player.predictedVelocity.vectorY;
|
||||
final double predictedZ = player.predictedVelocity.vectorZ;
|
||||
if (predictedX == 0.0 && predictedY == 0.0 && predictedZ == 0.0 && offset == 0) return;
|
||||
|
||||
String color = pickColor(offset, offset);
|
||||
|
||||
Vector3dm predicted = player.predictedVelocity.vector;
|
||||
Vector3dm actually = player.actualMovement;
|
||||
|
||||
String xColor = pickColor(Math.abs(predicted.getX() - actually.getX()), offset);
|
||||
String yColor = pickColor(Math.abs(predicted.getY() - actually.getY()), offset);
|
||||
String zColor = pickColor(Math.abs(predicted.getZ() - actually.getZ()), offset);
|
||||
String xColor = pickColor(Math.abs(predictedX - actually.getX()), offset);
|
||||
String yColor = pickColor(Math.abs(predictedY - actually.getY()), offset);
|
||||
String zColor = pickColor(Math.abs(predictedZ - actually.getZ()), offset);
|
||||
|
||||
Component p = Component.empty()
|
||||
.append(P_PREFIX.color(NamedTextColor.NAMES.value(color)))
|
||||
.append(Component.text(predicted.getX()).color(NamedTextColor.NAMES.value(xColor)))
|
||||
.append(Component.text(predictedX).color(NamedTextColor.NAMES.value(xColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(predicted.getY()).color(NamedTextColor.NAMES.value(yColor)))
|
||||
.append(Component.text(predictedY).color(NamedTextColor.NAMES.value(yColor)))
|
||||
.append(Component.space())
|
||||
.append(Component.text(predicted.getZ()).color(NamedTextColor.NAMES.value(zColor)));
|
||||
.append(Component.text(predictedZ).color(NamedTextColor.NAMES.value(zColor)));
|
||||
|
||||
Component a = Component.empty()
|
||||
.append(A_PREFIX.color(NamedTextColor.NAMES.value(color)))
|
||||
|
||||
@@ -59,13 +59,16 @@ public class AirLiquidPlace extends BlockPlaceCheck {
|
||||
if (player.gamemode == GameMode.CREATIVE) return;
|
||||
|
||||
Vector3i blockPos = place.position;
|
||||
StateType placeAgainst = player.compensatedWorld.getBlockType(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
final int x = blockPos.getX();
|
||||
final int y = blockPos.getY();
|
||||
final int z = blockPos.getZ();
|
||||
StateType placeAgainst = player.compensatedWorld.getBlockType(x, y, z);
|
||||
|
||||
int currentTick = GrimAPI.INSTANCE.getTickManager().currentTick;
|
||||
// this is actual more lenient than we need to be, We can check up to 1 ticks for all changes at location sand up to 0 ticks for first change
|
||||
// But for such tiny differences in legitness it's not worth it.
|
||||
Iterable<BlockModification> blockModifications = player.blockHistory.getRecentModifications((blockModification) -> currentTick - blockModification.tick() < 2
|
||||
&& blockPos.equals(blockModification.location())
|
||||
&& x == blockModification.x() && y == blockModification.y() && z == blockModification.z()
|
||||
&& (blockModification.cause() == BlockModification.Cause.START_DIGGING || blockModification.cause() == BlockModification.Cause.HANDLE_NETTY_SYNC_TRANSACTION));
|
||||
|
||||
// Check if old block from instant breaking in same tick as the current placement was valid
|
||||
|
||||
@@ -30,9 +30,8 @@ public class FarPlace extends BlockPlaceCheck {
|
||||
final double[] possibleEyeHeights = player.getPossibleEyeHeights();
|
||||
for (double d : possibleEyeHeights) {
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(blockPos);
|
||||
Vector3dm eyes = new Vector3dm(player.x, player.y + d, player.z);
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(eyes, box);
|
||||
min = Math.min(min, eyes.distanceSquared(best));
|
||||
Vector3dm best = VectorUtils.cutBoxToVector(player.x, player.y + d, player.z, box);
|
||||
min = Math.min(min, best.distanceSquared(player.x, player.y + d, player.z));
|
||||
}
|
||||
|
||||
// getPickRange() determines this?
|
||||
|
||||
@@ -112,10 +112,10 @@ public class RotationPlace extends BlockPlaceCheck {
|
||||
for (double d : possibleEyeHeights) {
|
||||
for (Vector3f lookDir : possibleLookDirs) {
|
||||
// x, y, z are correct for the block placement even after post tick because of code elsewhere
|
||||
Vector3d starting = new Vector3d(player.x, player.y + d, player.z);
|
||||
// xRot and yRot are a tick behind
|
||||
Ray trace = new Ray(player, starting.getX(), starting.getY(), starting.getZ(), lookDir.getX(), lookDir.getY());
|
||||
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOrigin(), trace.getPointAtDistance(distance));
|
||||
Ray trace = new Ray(player, player.x, player.y + d, player.z, lookDir.getX(), lookDir.getY());
|
||||
Vector3dm pointAtDistance = trace.getPointAtDistance(distance);
|
||||
Pair<Vector3dm, BlockFace> intercept = ReachUtils.calculateIntercept(box, trace.getOriginX(), trace.getOriginY(), trace.getOriginZ(), pointAtDistance.getX(), pointAtDistance.getY(), pointAtDistance.getZ());
|
||||
|
||||
if (intercept.first() != null) return true;
|
||||
}
|
||||
|
||||
@@ -91,8 +91,7 @@ public class ExplosionHandler extends Check implements PostPredictionCheck {
|
||||
}
|
||||
|
||||
// Otherwise try and flip/open it.
|
||||
final Object poweredValue = state.getInternalData().get(StateValue.POWERED);
|
||||
final boolean canFlip = (poweredValue != null && !(Boolean) poweredValue) || type == StateTypes.LEVER;
|
||||
final boolean canFlip = state.hasProperty(StateValue.POWERED) && !state.isPowered() || type == StateTypes.LEVER;
|
||||
if (canFlip) {
|
||||
player.compensatedWorld.tickOpenable(record.x, record.y, record.z);
|
||||
}
|
||||
|
||||
@@ -64,11 +64,13 @@ public class KnockbackHandler extends Check implements PostPredictionCheck {
|
||||
|
||||
// Blacklist problemated vector until mojang fixes a client-sided bug
|
||||
if (playerVelocity.getY() == -0.04) {
|
||||
velocity.setVelocity(playerVelocity.add(new Vector3d(0, 1 / 8000D, 0)));
|
||||
velocity.setVelocity(playerVelocity.add(0, 1 / 8000D, 0));
|
||||
playerVelocity = velocity.getVelocity();
|
||||
event.markForReEncode(true);
|
||||
}
|
||||
|
||||
playerVelocity = VectorPrecisionConverter.convert(player.getClientVersion(), playerVelocity);
|
||||
|
||||
// Wrap velocity between two transactions
|
||||
player.sendTransaction();
|
||||
addPlayerKnockback(entityId, player.lastTransactionSent.get(), new Vector3dm(playerVelocity.getX(), playerVelocity.getY(), playerVelocity.getZ()));
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package ac.grim.grimac.checks.impl.velocity;
|
||||
|
||||
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.util.LpVector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class VectorPrecisionConverter {
|
||||
|
||||
private static final ServerVersion SERVER_VERSION = PacketEvents.getAPI().getServerManager().getVersion();
|
||||
|
||||
public static Vector3d convert(ClientVersion version, Vector3d vector) {
|
||||
if (version.isNewerThanOrEquals(ClientVersion.V_1_21_9) && SERVER_VERSION.isOlderThanOrEquals(ServerVersion.V_1_21_8)) {
|
||||
return VectorPrecisionConverter.legacyToLp(vector);
|
||||
} else if (version.isOlderThanOrEquals(ClientVersion.V_1_21_7) && SERVER_VERSION.isNewerThanOrEquals(ServerVersion.V_1_21_9)) {
|
||||
return VectorPrecisionConverter.lpToLegacy(vector);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3d legacyToLp(Vector3d legacy) {
|
||||
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(Unpooled.buffer());
|
||||
LpVector3d.write(wrapper, legacy);
|
||||
return LpVector3d.read(wrapper);
|
||||
}
|
||||
|
||||
private static final double PRECISION_LOSS_FIX = 1e-11d;
|
||||
|
||||
public static Vector3d lpToLegacy(Vector3d lp) {
|
||||
int xi = (int) (lp.x * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.x));
|
||||
int yi = (int) (lp.y * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.y));
|
||||
int zi = (int) (lp.z * 8000d + Math.copySign(PRECISION_LOSS_FIX, lp.z));
|
||||
|
||||
short x = (short) xi;
|
||||
short y = (short) yi;
|
||||
short z = (short) zi;
|
||||
|
||||
return new Vector3d(
|
||||
x / 8000d,
|
||||
y / 8000d,
|
||||
z / 8000d
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,12 +46,19 @@ public class GrimDebug implements BuildableCommand {
|
||||
PlayerSelector playerSelector = context.getOrDefault("target", null);
|
||||
|
||||
GrimPlayer targetGrimPlayer = parseTarget(sender, playerSelector == null ? sender : playerSelector.getSinglePlayer());
|
||||
if (targetGrimPlayer == null) return;
|
||||
if (targetGrimPlayer == null) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender, "player-not-found", "%prefix% &cPlayer is exempt or offline!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender.isConsole()) {
|
||||
targetGrimPlayer.checkManager.getDebugHandler().toggleConsoleOutput();
|
||||
} else if (sender.isPlayer()) {
|
||||
GrimPlayer senderGrimPlayer = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(sender.getUniqueId());
|
||||
if (senderGrimPlayer == null) {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender, "sender-not-found", "%prefix% &cYou cannot be exempt to use this command!"));
|
||||
return;
|
||||
}
|
||||
targetGrimPlayer.checkManager.getDebugHandler().toggleListener(senderGrimPlayer);
|
||||
} else {
|
||||
sender.sendMessage(MessageUtil.getParsedComponent(sender,
|
||||
|
||||
@@ -6,7 +6,7 @@ import ac.grim.grimac.manager.init.start.SuperDebug;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
@@ -39,7 +39,7 @@ public class GrimLog implements BuildableCommand {
|
||||
}
|
||||
|
||||
private static void sendLog(Sender sender, String log, String success, String failure, Consumer<String> consumer, String type) throws IOException {
|
||||
URL mUrl = new URL(GrimArguments.PASTE_URL + "data/post");
|
||||
URL mUrl = new URL(CommonGrimArguments.PASTE_URL.value() + "data/post");
|
||||
HttpURLConnection urlConn = (HttpURLConnection) mUrl.openConnection();
|
||||
try {
|
||||
urlConn.setDoOutput(true);
|
||||
@@ -53,7 +53,7 @@ public class GrimLog implements BuildableCommand {
|
||||
final int response = urlConn.getResponseCode();
|
||||
if (response == HttpURLConnection.HTTP_CREATED) {
|
||||
String responseURL = urlConn.getHeaderField("Location");
|
||||
String message = success.replace("%url%", GrimArguments.PASTE_URL + responseURL);
|
||||
String message = success.replace("%url%", CommonGrimArguments.PASTE_URL.value() + responseURL);
|
||||
consumer.accept(message);
|
||||
message = MessageUtil.replacePlaceholders(sender, message);
|
||||
sender.sendMessage(MessageUtil.miniMessage(message));
|
||||
|
||||
@@ -5,7 +5,7 @@ import ac.grim.grimac.command.BuildableCommand;
|
||||
import ac.grim.grimac.platform.api.sender.Sender;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.common.GrimArguments;
|
||||
import ac.grim.grimac.utils.common.arguments.CommonGrimArguments;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -56,7 +56,7 @@ public class GrimVersion implements BuildableCommand {
|
||||
try {
|
||||
//
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(GrimArguments.API_URL + "updates"))
|
||||
.uri(URI.create(CommonGrimArguments.API_URL.value() + "updates"))
|
||||
.GET()
|
||||
.header("User-Agent", "GrimAC/" + GrimAPI.INSTANCE.getExternalAPI().getGrimVersion())
|
||||
.header("Content-Type", "application/json")
|
||||
@@ -65,11 +65,11 @@ public class GrimVersion implements BuildableCommand {
|
||||
|
||||
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
final int statusCode = response.statusCode();
|
||||
if (statusCode != 200) {
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
Component msg = updateMessage.get();
|
||||
sender.sendMessage(Objects.requireNonNullElseGet(msg, () -> Component.text()
|
||||
.append(MessageUtil.miniMessage("%prefix%"))
|
||||
.append(Component.text(" Failed to check latest GrimAC version. Update server responded with code: ")
|
||||
.append(Component.text(" Failed to check latest GrimAC version. Update server responded with status code: ")
|
||||
.color(NamedTextColor.YELLOW))
|
||||
.append(Component.text(statusCode)
|
||||
.color(getColorForStatusCode(statusCode))
|
||||
|
||||
@@ -68,7 +68,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (Materials.isPlaceableWaterBucket(blockPlace.itemStack.getType()) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
blockPlace.replaceClicked = true; // See what's in the existing place
|
||||
WrappedBlockState existing = blockPlace.getExistingBlockData();
|
||||
if (!(boolean) existing.getInternalData().getOrDefault(StateValue.WATERLOGGED, true)) {
|
||||
if (existing.hasProperty(StateValue.WATERLOGGED) && !existing.isWaterlogged()) {
|
||||
// Strangely, the client does not predict waterlogged placements
|
||||
didPlace = true;
|
||||
}
|
||||
@@ -308,7 +308,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
WrappedBlockState existing = blockPlace.getExistingBlockData();
|
||||
if (existing.getInternalData().containsKey(StateValue.WATERLOGGED)) { // waterloggable
|
||||
if (existing.hasProperty(StateValue.WATERLOGGED)) { // waterloggable
|
||||
existing.setWaterlogged(false);
|
||||
blockPlace.set(existing);
|
||||
placed = true;
|
||||
@@ -483,7 +483,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.yaw = move.getYaw();
|
||||
player.pitch = move.getPitch();
|
||||
|
||||
final VehiclePositionUpdate update = new VehiclePositionUpdate(clamp, position, move.getYaw(), move.getPitch(), player.packetStateData.lastPacketWasTeleport);
|
||||
final VehiclePositionUpdate update = new VehiclePositionUpdate(clamp.getX(), clamp.getY(), clamp.getZ(),
|
||||
position.getX(), position.getY(), position.getZ(), move.getYaw(), move.getPitch(), player.packetStateData.lastPacketWasTeleport);
|
||||
player.checkManager.onVehiclePositionUpdate(update);
|
||||
|
||||
player.packetStateData.receivedSteerVehicle = false;
|
||||
@@ -532,14 +533,17 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.onPacketCancel();
|
||||
}
|
||||
|
||||
Vector3i facePos = new Vector3i(packet.getBlockPosition().getX() + packet.getFace().getModX(), packet.getBlockPosition().getY() + packet.getFace().getModY(), packet.getBlockPosition().getZ() + packet.getFace().getModZ());
|
||||
|
||||
// Ends the client prediction introduced in 1.19+
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_19) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19)) {
|
||||
player.user.sendPacket(new WrapperPlayServerAcknowledgeBlockChanges(packet.getSequence()));
|
||||
} else { // The client isn't smart enough to revert changes
|
||||
player.resyncPosition(packet.getBlockPosition());
|
||||
player.resyncPosition(facePos);
|
||||
final Vector3i blockPosition = packet.getBlockPosition();
|
||||
final int x = blockPosition.getX();
|
||||
final int y = blockPosition.getY();
|
||||
final int z = blockPosition.getZ();
|
||||
player.resyncPosition(x, y ,z);
|
||||
final BlockFace face = packet.getFace();
|
||||
player.resyncPosition(x + face.getModX(), y + face.getModY(), z + face.getModZ());
|
||||
}
|
||||
|
||||
// Stop inventory desync from cancelling place
|
||||
@@ -599,6 +603,10 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.serverOpenedInventoryThisTick = true);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.BUNDLE) {
|
||||
player.packetStateData.sendingBundlePacket = !player.packetStateData.sendingBundlePacket;
|
||||
}
|
||||
|
||||
player.checkManager.onPacketSend(event);
|
||||
}
|
||||
|
||||
@@ -720,9 +728,8 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
}
|
||||
|
||||
if (hasPosition) {
|
||||
Vector3d position = new Vector3d(x, y, z);
|
||||
Vector3d clampVector = VectorUtils.clampVector(position);
|
||||
final PositionUpdate update = new PositionUpdate(new Vector3d(player.x, player.y, player.z), position, onGround, teleportData.getSetback(), teleportData.getTeleportData(), teleportData.isTeleport());
|
||||
Vector3d clampVector = VectorUtils.clampVector(x, y, z);
|
||||
final PositionUpdate update = new PositionUpdate(player.x, player.y, player.z, x, y ,z, onGround, teleportData.getSetback(), teleportData.getTeleportData(), teleportData.isTeleport());
|
||||
|
||||
// Stupidity doesn't care about 0.03
|
||||
if (!player.packetStateData.lastPacketWasOnePointSeventeenDuplicate) {
|
||||
@@ -766,14 +773,15 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
return;
|
||||
}
|
||||
|
||||
final BlockBreak blockBreak = new BlockBreak(player, packet.getBlockPosition(), packet.getBlockFace(), packet.getBlockFaceId(), action, packet.getSequence(), player.compensatedWorld.getBlock(packet.getBlockPosition()));
|
||||
final Vector3i blockPosition = packet.getBlockPosition();
|
||||
final BlockBreak blockBreak = new BlockBreak(player, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), packet.getBlockFace(), packet.getBlockFaceId(), action, packet.getSequence(), player.compensatedWorld.getBlock(packet.getBlockPosition()));
|
||||
|
||||
player.checkManager.onBlockBreak(blockBreak);
|
||||
|
||||
if (blockBreak.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
player.onPacketCancel();
|
||||
player.resyncPosition(blockBreak.position, packet.getSequence());
|
||||
player.resyncPosition(blockBreak.x, blockBreak.y, blockBreak.z, packet.getSequence());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -781,7 +789,7 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
|
||||
if (action == DiggingAction.FINISHED_DIGGING && BREAKABLE.apply(blockBreak.block.getType())) {
|
||||
player.compensatedWorld.startPredicting();
|
||||
player.compensatedWorld.updateBlock(blockBreak.position.x, blockBreak.position.y, blockBreak.position.z, 0);
|
||||
player.compensatedWorld.updateBlock(blockBreak.x, blockBreak.y, blockBreak.z, 0);
|
||||
player.compensatedWorld.stopPredicting(packet);
|
||||
}
|
||||
|
||||
@@ -791,11 +799,14 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
// Instant breaking, no damage means it is unbreakable by creative players (with swords)
|
||||
if (damage >= 1) {
|
||||
player.compensatedWorld.startPredicting();
|
||||
final int x = blockBreak.x;
|
||||
final int y = blockBreak.y;
|
||||
final int z = blockBreak.z;
|
||||
player.blockHistory.add(
|
||||
new BlockModification(
|
||||
player.compensatedWorld.getBlock(blockBreak.position),
|
||||
player.compensatedWorld.getBlock(x, y, z),
|
||||
WrappedBlockState.getByGlobalId(0),
|
||||
blockBreak.position,
|
||||
x, y, z,
|
||||
GrimAPI.INSTANCE.getTickManager().currentTick,
|
||||
BlockModification.Cause.START_DIGGING
|
||||
)
|
||||
@@ -803,9 +814,9 @@ public class CheckManagerListener extends PacketListenerAbstract {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && Materials.isWaterSource(player.getClientVersion(), blockBreak.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(blockBreak.position, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion));
|
||||
player.compensatedWorld.updateBlock(x, y, z, StateTypes.WATER.createBlockState(CompensatedWorld.blockVersion).getGlobalId());
|
||||
} else {
|
||||
player.compensatedWorld.updateBlock(blockBreak.position.x, blockBreak.position.y, blockBreak.position.z, 0);
|
||||
player.compensatedWorld.updateBlock(x, y, z, 0);
|
||||
}
|
||||
player.compensatedWorld.stopPredicting(packet);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ package ac.grim.grimac.events.packets;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.RotationData;
|
||||
import ac.grim.grimac.utils.data.VehicleData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
@@ -35,22 +34,26 @@ public class PacketServerTeleport extends PacketListenerAbstract {
|
||||
|
||||
WrapperPlayServerPlayerPositionAndLook teleport = new WrapperPlayServerPlayerPositionAndLook(event);
|
||||
|
||||
Vector3d pos = new Vector3d(teleport.getX(), teleport.getY(), teleport.getZ());
|
||||
double posX = teleport.getX();
|
||||
double posY = teleport.getY();
|
||||
double posZ = teleport.getZ();
|
||||
final float yaw = teleport.getYaw();
|
||||
final float pitch = teleport.getPitch();
|
||||
|
||||
// This is the first packet sent to the client which we need to track
|
||||
if (player.getSetbackTeleportUtil().getRequiredSetBack() == null) {
|
||||
// Player teleport event gets called AFTER player join event
|
||||
player.x = teleport.getX();
|
||||
player.y = teleport.getY();
|
||||
player.z = teleport.getZ();
|
||||
player.yaw = teleport.getYaw();
|
||||
player.pitch = teleport.getPitch();
|
||||
player.x = posX;
|
||||
player.y = posY;
|
||||
player.z = posZ;
|
||||
player.yaw = yaw;
|
||||
player.pitch = pitch;
|
||||
|
||||
player.lastX = teleport.getX();
|
||||
player.lastY = teleport.getY();
|
||||
player.lastZ = teleport.getZ();
|
||||
player.lastYaw = teleport.getYaw();
|
||||
player.lastPitch = teleport.getPitch();
|
||||
player.lastX = posX;
|
||||
player.lastY = posY;
|
||||
player.lastZ = posZ;
|
||||
player.lastYaw = yaw;
|
||||
player.lastPitch = pitch;
|
||||
|
||||
player.pollData();
|
||||
}
|
||||
@@ -67,24 +70,24 @@ public class PacketServerTeleport extends PacketListenerAbstract {
|
||||
relativeZ = teleport.isRelativeFlag(RelativeFlag.Z);
|
||||
|
||||
if (relativeX) {
|
||||
pos = pos.add(new Vector3d(player.x, 0, 0));
|
||||
posX += player.x;
|
||||
teleport.setRelative(RelativeFlag.X, false);
|
||||
}
|
||||
|
||||
if (relativeY) {
|
||||
pos = pos.add(new Vector3d(0, player.y, 0));
|
||||
posY += player.y;
|
||||
teleport.setRelative(RelativeFlag.Y, false);
|
||||
}
|
||||
|
||||
if (relativeZ) {
|
||||
pos = pos.add(new Vector3d(0, 0, player.z));
|
||||
posZ += player.z;
|
||||
teleport.setRelative(RelativeFlag.Z, false);
|
||||
}
|
||||
|
||||
if (relativeX || relativeY || relativeZ) {
|
||||
teleport.setX(pos.getX());
|
||||
teleport.setY(pos.getY());
|
||||
teleport.setZ(pos.getZ());
|
||||
teleport.setX(posX);
|
||||
teleport.setY(posY);
|
||||
teleport.setZ(posZ);
|
||||
|
||||
event.markForReEncode(true);
|
||||
}
|
||||
@@ -124,10 +127,13 @@ public class PacketServerTeleport extends PacketListenerAbstract {
|
||||
|
||||
// For some reason teleports on 1.7 servers are offset by 1.62?
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_8))
|
||||
pos = pos.withY(pos.getY() - 1.62);
|
||||
posY -= 1.62;
|
||||
|
||||
Location target = new Location(null, pos.getX(), pos.getY(), pos.getZ());
|
||||
player.getSetbackTeleportUtil().addSentTeleport(target, teleport.getDeltaMovement(), lastTransactionSent, teleport.getRelativeFlags(), true, teleport.getTeleportId());
|
||||
final Vector3d deltaMovement = teleport.getDeltaMovement();
|
||||
player.getSetbackTeleportUtil().addSentTeleport(posX, posY, posZ,
|
||||
deltaMovement.getX(), deltaMovement.getY(), deltaMovement.getZ(),
|
||||
lastTransactionSent, teleport.getRelativeFlags(), true, teleport.getTeleportId()
|
||||
);
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.PLAYER_ROTATION) {
|
||||
@@ -157,9 +163,12 @@ public class PacketServerTeleport extends PacketListenerAbstract {
|
||||
|
||||
player.sendTransaction();
|
||||
event.getTasksAfterSend().add(player::sendTransaction);
|
||||
player.vehicleData.vehicleTeleports.add(new Pair<>(
|
||||
Vector3d position = new WrapperPlayServerVehicleMove(event).getPosition();
|
||||
player.vehicleData.vehicleTeleports.add(new VehicleData.VehicleTeleport(
|
||||
player.lastTransactionSent.get(),
|
||||
new WrapperPlayServerVehicleMove(event).getPosition()
|
||||
position.getX(),
|
||||
position.getY(),
|
||||
position.getZ()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -77,7 +77,11 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
|
||||
if (player == null) return;
|
||||
|
||||
WrapperPlayServerAcknowledgePlayerDigging ack = new WrapperPlayServerAcknowledgePlayerDigging(event);
|
||||
player.compensatedWorld.handleBlockBreakAck(ack.getBlockPosition(), ack.getBlockId(), ack.getAction(), ack.isSuccessful());
|
||||
Vector3i blockPosition = ack.getBlockPosition();
|
||||
final int x = blockPosition.getX();
|
||||
final int y = blockPosition.getY();
|
||||
final int z = blockPosition.getZ();
|
||||
player.compensatedWorld.handleBlockBreakAck(x, y, z, ack.getBlockId(), ack.getAction(), ack.isSuccessful());
|
||||
}
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.CHANGE_GAME_STATE) {
|
||||
@@ -121,7 +125,7 @@ public class BasePacketWorldReader extends PacketListenerAbstract {
|
||||
if (teleports.getFlags().getMask() != 0) {
|
||||
continue; // Worse that will happen is people will get an extra setback...
|
||||
}
|
||||
shouldPostTrans = shouldPostTrans || (Math.abs(teleports.getLocation().getX() - chunkCenterX) < 16 && Math.abs(teleports.getLocation().getZ() - chunkCenterZ) < 16);
|
||||
shouldPostTrans = shouldPostTrans || (Math.abs(teleports.getLocationX() - chunkCenterX) < 16 && Math.abs(teleports.getLocationZ() - chunkCenterZ) < 16);
|
||||
}
|
||||
|
||||
if (shouldPostTrans) {
|
||||
|
||||
@@ -116,6 +116,7 @@ public class CheckManager {
|
||||
.put(BadPacketsU.class, new BadPacketsU(player))
|
||||
.put(BadPacketsV.class, new BadPacketsV(player))
|
||||
.put(BadPacketsY.class, new BadPacketsY(player))
|
||||
.put(BadPacketsZ.class, new BadPacketsZ(player))
|
||||
.put(MultiActionsA.class, new MultiActionsA(player))
|
||||
.put(MultiActionsC.class, new MultiActionsC(player))
|
||||
.put(MultiActionsD.class, new MultiActionsD(player))
|
||||
@@ -124,6 +125,7 @@ public class CheckManager {
|
||||
.put(PacketOrderC.class, new PacketOrderC(player))
|
||||
.put(PacketOrderD.class, new PacketOrderD(player))
|
||||
.put(PacketOrderO.class, new PacketOrderO(player))
|
||||
.put(PacketOrderP.class, new PacketOrderP(player))
|
||||
.put(SprintA.class, new SprintA(player))
|
||||
.put(VehicleA.class, new VehicleA(player))
|
||||
.put(VehicleB.class, new VehicleB(player))
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package ac.grim.grimac.manager;
|
||||
|
||||
import ac.grim.grimac.GrimAPI;
|
||||
import ac.grim.grimac.api.config.ConfigManager;
|
||||
import ac.grim.grimac.manager.init.ReloadableInitable;
|
||||
import ac.grim.grimac.manager.init.start.StartableInitable;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.webhook.Embed;
|
||||
import ac.grim.grimac.utils.webhook.EmbedField;
|
||||
import ac.grim.grimac.utils.webhook.EmbedFooter;
|
||||
import ac.grim.grimac.utils.webhook.WebhookMessage;
|
||||
import ac.grim.grimac.utils.webhook.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.net.URI;
|
||||
@@ -18,7 +20,6 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -26,8 +27,8 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
public static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("https://discord.com/api/webhooks/\\d+/[\\w-]+").asMatchPredicate();
|
||||
public static final Duration timeout = Duration.ofSeconds(15);
|
||||
private static final Predicate<String> WEBHOOK_REGEX = Pattern.compile("https://discord.com/api/webhooks/\\d+/[\\w-]+").asMatchPredicate();
|
||||
private static final Duration timeout = Duration.ofSeconds(15);
|
||||
private static final HttpClient client = HttpClient.newBuilder().connectTimeout(timeout).build();
|
||||
private static final ConcurrentLinkedDeque<HttpRequest> requests = new ConcurrentLinkedDeque<>();
|
||||
private static final AtomicBoolean taskStarted = new AtomicBoolean();
|
||||
@@ -38,6 +39,24 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
private String staticContent = "";
|
||||
private String embedTitle = "";
|
||||
private boolean includeTimestamp;
|
||||
private boolean includeVerbose;
|
||||
private @Nullable String embedImageUrl;
|
||||
private @Nullable String embedThumbnailUrl;
|
||||
private @Nullable String embedFooterUrl;
|
||||
private String embedFooterText = "";
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile("^https?://(?:www\\.)?[-a-z0-9@:%._+~#=]{1,256}\\.[a-z0-9()]{1,6}\\b[-a-z0-9()@:%_+.~#?&/=]*$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static String validatedConfigURL(String configPath, String defaultURL) {
|
||||
String url = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-image-url", defaultURL);
|
||||
if (url == null || url.isBlank()) return null;
|
||||
if (URL_PATTERN.matcher(url).matches()) {
|
||||
return url;
|
||||
} else {
|
||||
LogUtil.warn("Invalid embed url for config path " + configPath + ": " + configPath);
|
||||
return defaultURL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
@@ -47,12 +66,13 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
@Override
|
||||
public void reload() {
|
||||
try {
|
||||
if (!GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("enabled", false)) {
|
||||
ConfigManager config = GrimAPI.INSTANCE.getConfigManager().getConfig();
|
||||
if (!config.getBooleanElse("enabled", false)) {
|
||||
url = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String webhook = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("webhook", "");
|
||||
String webhook = config.getStringElse("webhook", "");
|
||||
|
||||
if (!WEBHOOK_REGEX.test(webhook)) {
|
||||
LogUtil.error("Discord webhook url does not follow expected format (https://discord.com/api/webhooks/<id>/<token>): " + webhook);
|
||||
@@ -60,36 +80,43 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
} else {
|
||||
url = new URI(webhook);
|
||||
}
|
||||
|
||||
embedTitle = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-title", "**Grim Alert**");
|
||||
// not adding these to the config since they may change in the future
|
||||
// mainly for just for allowing more customization
|
||||
embedImageUrl = validatedConfigURL("embed-image-url", null);
|
||||
embedThumbnailUrl = validatedConfigURL("embed-thumbnail-url", "https://crafthead.net/helm/%uuid%");
|
||||
embedFooterUrl = validatedConfigURL("embed-footer-url", "https://grim.ac/images/grim.png");
|
||||
embedFooterText = config.getStringElse("embed-footer-text", "v%grim_version%");
|
||||
embedTitle = config.getStringElse("embed-title", "**Grim Alert**");
|
||||
|
||||
try {
|
||||
embedColor = Color.decode(GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("embed-color", "#00FFFF")).getRGB();
|
||||
embedColor = Color.decode(config.getStringElse("embed-color", "#00FFFF")).getRGB();
|
||||
} catch (NumberFormatException e) {
|
||||
LogUtil.warn("Discord embed color is invalid");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String string : GrimAPI.INSTANCE.getConfigManager().getConfig().getStringListElse("violation-content", getDefaultContents())) {
|
||||
for (String string : config.getStringListElse("violation-content", getDefaultContents())) {
|
||||
sb.append(string).append("\n");
|
||||
}
|
||||
staticContent = sb.toString();
|
||||
includeTimestamp = GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("include-timestamp", true);
|
||||
includeTimestamp = config.getBooleanElse("include-timestamp", true);
|
||||
includeVerbose = config.getBooleanElse("include-verbose", true);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Failed to load Discord webhook configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDefaultContents() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("**Player**: %player%");
|
||||
list.add("**Check**: %check%");
|
||||
list.add("**Violations**: %violations%");
|
||||
list.add("**Client Version**: %version%");
|
||||
list.add("**Brand**: %brand%");
|
||||
list.add("**Ping**: %ping%");
|
||||
list.add("**TPS**: %tps%");
|
||||
return list;
|
||||
@Contract(value = " -> new", pure = true)
|
||||
private @NotNull @Unmodifiable List<@NotNull String> getDefaultContents() {
|
||||
return List.of(
|
||||
"**Player**: %player%",
|
||||
"**Check**: %check%",
|
||||
"**Violations**: %violations%",
|
||||
"**Client Version**: %version%",
|
||||
"**Brand**: %brand%",
|
||||
"**Ping**: %ping%",
|
||||
"**TPS**: %tps%"
|
||||
);
|
||||
}
|
||||
|
||||
public void sendAlert(GrimPlayer player, String verbose, String checkName, int violations) {
|
||||
@@ -103,17 +130,18 @@ public class DiscordManager implements StartableInitable, ReloadableInitable {
|
||||
content = MessageUtil.replacePlaceholders(player, content, true);
|
||||
|
||||
Embed embed = new Embed(content)
|
||||
.imageURL("https://i.stack.imgur.com/Fzh0w.png")
|
||||
.thumbnailURL("https://crafthead.net/helm/" + player.user.getProfile().getUUID())
|
||||
.color(embedColor)
|
||||
.title(embedTitle)
|
||||
.footer(new EmbedFooter("", "https://grim.ac/images/grim.png"));
|
||||
.imageURL(MessageUtil.replacePlaceholders(player, embedImageUrl, false))
|
||||
.thumbnailURL(MessageUtil.replacePlaceholders(player, embedThumbnailUrl, false))
|
||||
.footer(new EmbedFooter(
|
||||
MessageUtil.replacePlaceholders(player, embedFooterText, true),
|
||||
MessageUtil.replacePlaceholders(player, embedFooterUrl, false)
|
||||
));
|
||||
|
||||
if (includeTimestamp) {
|
||||
embed.timestamp(Instant.now());
|
||||
}
|
||||
if (includeTimestamp) embed.timestamp(Instant.now());
|
||||
|
||||
if (!verbose.isEmpty()) {
|
||||
if (!verbose.isEmpty() && includeVerbose) {
|
||||
embed.addFields(new EmbedField("Verbose", MessageUtil.filterDiscordText(verbose), true));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import ac.grim.grimac.platform.api.player.PlatformPlayer;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.anticheat.LogUtil;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
@@ -179,7 +181,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +199,7 @@ public class PunishmentManager implements ConfigReloadable {
|
||||
class PunishGroup {
|
||||
public final List<AbstractCheck> checks;
|
||||
public final List<ParsedCommand> commands;
|
||||
public final Map<Long, Check> violations = new HashMap<>();
|
||||
public final Long2ObjectMap<Check> violations = new Long2ObjectOpenHashMap<>();
|
||||
public final int removeViolationsAfter; // time to remove violations after in milliseconds
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ import ac.grim.grimac.utils.data.SetBackData;
|
||||
import ac.grim.grimac.utils.data.TeleportAcceptData;
|
||||
import ac.grim.grimac.utils.data.TeleportData;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.VehicleData;
|
||||
import ac.grim.grimac.utils.data.VelocityData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
@@ -74,7 +74,9 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
@Override
|
||||
public void onPredictionComplete(final PredictionComplete predictionComplete) {
|
||||
// Grab friction now when we know player on ground and other variables
|
||||
Vector3dm afterTickFriction = player.clientVelocity.clone();
|
||||
final double afterTickFrictionX = player.clientVelocity.getX();
|
||||
final double afterTickFrictionY = player.clientVelocity.getY();
|
||||
final double afterTickFrictionZ = player.clientVelocity.getZ();
|
||||
|
||||
// We must first check if the player has accepted their setback
|
||||
// If the setback isn't complete, then this position is illegitimate
|
||||
@@ -82,12 +84,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
// The player needs to now wait for their vehicle to go into the right place before getting back in
|
||||
if (cheatVehicleInterpolationDelay > 0) cheatVehicleInterpolationDelay = 10;
|
||||
// Teleport, let velocity be reset
|
||||
lastKnownGoodPosition = new SetbackPosWithVector(new Vector3d(player.x, player.y, player.z), afterTickFriction);
|
||||
lastKnownGoodPosition = new SetbackPosWithVector(player.x, player.y, player.z, afterTickFrictionX, afterTickFrictionY, afterTickFrictionZ);
|
||||
} else if (requiredSetBack == null || requiredSetBack.isComplete()) {
|
||||
cheatVehicleInterpolationDelay--;
|
||||
// No simulation... we can do that later. We just need to know the valid position.
|
||||
// As we didn't setback here, the new position is known to be safe!
|
||||
lastKnownGoodPosition = new SetbackPosWithVector(new Vector3d(player.x, player.y, player.z), afterTickFriction);
|
||||
lastKnownGoodPosition = new SetbackPosWithVector(player.x, player.y, player.z, afterTickFrictionX, afterTickFrictionY, afterTickFrictionZ);
|
||||
}
|
||||
|
||||
if (requiredSetBack != null) requiredSetBack.tick();
|
||||
@@ -123,27 +125,6 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
return player.platformPlayer != null && player.noSetbackPermission;
|
||||
}
|
||||
|
||||
private void simulateFriction(Vector3dm vector) {
|
||||
// We must always do this before simulating positions, as this is the last actual (safe) movement
|
||||
// We must not do this for knockback or explosions, as they are at the start of the tick
|
||||
if (player.wasTouchingWater) {
|
||||
PredictionEngineWater.staticVectorEndOfTick(player, vector, 0.8F, player.gravity, true);
|
||||
} else if (player.wasTouchingLava) {
|
||||
vector.multiply(0.5D);
|
||||
if (player.hasGravity)
|
||||
vector.add(new Vector3dm(0.0D, -player.gravity / 4.0D, 0.0D));
|
||||
} else if (player.isGliding) {
|
||||
PredictionEngineElytra.getElytraMovement(player, vector, ReachUtils.getLook(player, player.yaw, player.pitch)).multiply(player.stuckSpeedMultiplier).multiply(new Vector3dm(0.99F, 0.98F, 0.99F));
|
||||
vector.setY(vector.getY() - 0.05); // Make the player fall a bit
|
||||
} else { // Gliding doesn't have friction, we handle it differently
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, vector); // Lava and normal movement
|
||||
vector.multiply(player.stuckSpeedMultiplier); // Prevent abusing setbacks to move out of blocks like webs
|
||||
}
|
||||
|
||||
// stop 1.8 players from stepping onto 1.25 high blocks, because why not?
|
||||
new PredictionEngine().applyMovementThreshold(player, new HashSet<>(Collections.singletonList(new VectorData(vector, VectorData.VectorType.BestVelPicked))));
|
||||
}
|
||||
|
||||
private void blockMovementsUntilResync(boolean simulateNextTickPosition, boolean isResync) {
|
||||
if (requiredSetBack == null) return; // Hasn't spawned
|
||||
if (player.platformPlayer != null && player.noSetbackPermission)
|
||||
@@ -157,64 +138,115 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
lastWorldResync = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
Vector3dm clientVel = lastKnownGoodPosition.vector.clone();
|
||||
double clientVelX = lastKnownGoodPosition.getVectorX();
|
||||
double clientVelY = lastKnownGoodPosition.getVectorY();
|
||||
double clientVelZ = lastKnownGoodPosition.getVectorZ();
|
||||
|
||||
Pair<VelocityData, Vector3dm> futureKb = player.checkManager.getKnockbackHandler().getFutureKnockback();
|
||||
VelocityData futureExplosion = player.checkManager.getExplosionHandler().getFutureExplosion();
|
||||
|
||||
// Velocity sets
|
||||
if (futureKb.first() != null) {
|
||||
clientVel = futureKb.second();
|
||||
clientVelX = futureKb.second().getX();
|
||||
clientVelY = futureKb.second().getY();
|
||||
clientVelZ = futureKb.second().getZ();
|
||||
}
|
||||
|
||||
// Explosion adds
|
||||
if (futureExplosion != null && (futureKb.first() == null || futureKb.first().transaction < futureExplosion.transaction)) {
|
||||
clientVel.add(futureExplosion.vector);
|
||||
clientVelX += futureExplosion.vector.getX();
|
||||
clientVelY += futureExplosion.vector.getY();
|
||||
clientVelZ += futureExplosion.vector.getZ();
|
||||
}
|
||||
|
||||
Vector3d position = lastKnownGoodPosition.pos;
|
||||
double x = lastKnownGoodPosition.posX;
|
||||
double y = lastKnownGoodPosition.posY;
|
||||
double z = lastKnownGoodPosition.posZ;
|
||||
|
||||
SimpleCollisionBox oldBB = player.boundingBox;
|
||||
player.boundingBox = GetBoundingBox.getPlayerBoundingBox(player, position.getX(), position.getY(), position.getZ());
|
||||
player.boundingBox = GetBoundingBox.getPlayerBoundingBox(player, x, y, z);
|
||||
|
||||
// Mini prediction engine - simulate collisions
|
||||
if (simulateNextTickPosition) {
|
||||
Vector3dm collide = Collisions.collide(player, clientVel.getX(), clientVel.getY(), clientVel.getZ());
|
||||
Vector3dm collide = Collisions.collide(player, clientVelX, clientVelY, clientVelZ);
|
||||
|
||||
position = position.withX(position.getX() + collide.getX());
|
||||
position = position.withY(position.getY() + collide.getY());
|
||||
x += collide.getX();
|
||||
y += collide.getY();
|
||||
// TODO: Is this even needed? Can't reproduce any phasing on vanilla 1.8 when being setback.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
// 1.8 players need the collision epsilon to not phase into blocks when being setback
|
||||
// Due to simulation, this will not allow a flight bypass by sending a billion invalid movements
|
||||
position = position.withY(position.getY() + SimpleCollisionBox.COLLISION_EPSILON);
|
||||
y += SimpleCollisionBox.COLLISION_EPSILON;
|
||||
}
|
||||
position = position.withZ(position.getZ() + collide.getZ());
|
||||
z += collide.getZ();
|
||||
|
||||
if (clientVel.getX() != collide.getX()) clientVel.setX(0);
|
||||
if (clientVel.getY() != collide.getY()) clientVel.setY(0);
|
||||
if (clientVel.getZ() != collide.getZ()) clientVel.setZ(0);
|
||||
if (clientVelX != collide.getX()) clientVelX = 0;
|
||||
if (clientVelY != collide.getY()) clientVelY = 0;
|
||||
if (clientVelZ != collide.getZ()) clientVelZ = 0;
|
||||
|
||||
simulateFriction(clientVel);
|
||||
// BEGIN SIMULATING FRICTION
|
||||
// We must always do this before simulating positions, as this is the last actual (safe) movement
|
||||
// We must not do this for knockback or explosions, as they are at the start of the tick
|
||||
double[] vector = {clientVelX, clientVelY, clientVelZ};
|
||||
if (player.wasTouchingWater) {
|
||||
PredictionEngineWater.staticVectorEndOfTick(player, vector, 0.8F, player.gravity, true);
|
||||
} else if (player.wasTouchingLava) {
|
||||
vector[0] *= 0.5D;
|
||||
vector[1] *= 0.5D;
|
||||
vector[2] *= 0.5D;
|
||||
if (player.hasGravity)
|
||||
vector[1] += -player.gravity / 4.0D;
|
||||
} else if (player.isGliding) {
|
||||
Vector3dm getLook = ReachUtils.getLook(player, player.yaw, player.pitch);
|
||||
PredictionEngineElytra.applyElytraMovementInPlace(player, vector, getLook.getX(), getLook.getY(), getLook.getZ());
|
||||
vector[0] *= player.stuckSpeedMultiplier.getX() * 0.99F;
|
||||
vector[1] *= player.stuckSpeedMultiplier.getY() * 0.98F;
|
||||
vector[2] *= player.stuckSpeedMultiplier.getZ() * 0.99F;
|
||||
|
||||
vector[1] -= 0.05; // Make the player fall a bit
|
||||
} else { // Gliding doesn't have friction, we handle it differently
|
||||
|
||||
// Lava and normal movement
|
||||
vector[0] *= player.friction;
|
||||
vector[1] = PredictionEngineNormal.staticVectorEndOfTickY(player, vector[1]);
|
||||
vector[2] *= player.friction;
|
||||
|
||||
// Prevent abusing setbacks to move out of blocks like webs
|
||||
vector[0] *= player.stuckSpeedMultiplier.getX();
|
||||
vector[1] *= player.stuckSpeedMultiplier.getY();
|
||||
vector[2] *= player.stuckSpeedMultiplier.getZ();
|
||||
}
|
||||
|
||||
// stop 1.8 players from stepping onto 1.25 high blocks, because why not?
|
||||
new PredictionEngine().applyMovementThreshold(player, new HashSet<>(Collections.singletonList(new VectorData(vector[0], vector[1], vector[2], VectorData.VectorType.BestVelPicked))));
|
||||
// END SIMULATING FRICTION
|
||||
|
||||
clientVelX = vector[0];
|
||||
clientVelY = vector[1];
|
||||
clientVelZ = vector[2];
|
||||
}
|
||||
|
||||
player.boundingBox = oldBB; // reset back to the new bounding box
|
||||
|
||||
boolean sendVelocity = true;
|
||||
|
||||
if (!hasAcceptedSpawnTeleport || player.isFlying)
|
||||
clientVel = null; // if the player is flying or hasn't spawned... don't force kb
|
||||
sendVelocity = false; // if the player is flying or hasn't spawned... don't force kb
|
||||
|
||||
// Something weird has occurred in the player's movement, block offsets until we resync
|
||||
if (isResync) {
|
||||
blockOffsets = true;
|
||||
}
|
||||
|
||||
SetBackData data = new SetBackData(new TeleportData(position, new Vector3d(), new RelativeFlag(0b11000), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVel, player.inVehicle(), false);
|
||||
SetBackData data = new SetBackData(new TeleportData(x, y, z, 0, 0, 0, new RelativeFlag(0b11000), player.lastTransactionSent.get(), 0), player.yaw, player.pitch, clientVelX, clientVelY, clientVelZ, sendVelocity, player.inVehicle(), false);
|
||||
sendSetback(data);
|
||||
}
|
||||
|
||||
private void sendSetback(SetBackData data) {
|
||||
isSendingSetback = true;
|
||||
Vector3d position = data.getTeleportData().getLocation();
|
||||
double positionX = data.getTeleportData().getLocationX();
|
||||
double positionY = data.getTeleportData().getLocationY();
|
||||
double positionZ = data.getTeleportData().getLocationZ();
|
||||
|
||||
try {
|
||||
// Player is in a vehicle
|
||||
@@ -230,7 +262,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
|
||||
// Stop the player from being able to teleport vehicles and simply re-enter them to continue,
|
||||
// therefore, teleport the entity
|
||||
player.user.sendPacket(new WrapperPlayServerEntityTeleport(vehicleId, new Vector3d(position.getX(), position.getY(), position.getZ()), player.yaw % 360, 0, false));
|
||||
player.user.sendPacket(new WrapperPlayServerEntityTeleport(vehicleId, new Vector3d(positionX, positionY, positionZ), player.yaw % 360, 0, false));
|
||||
player.getSetbackTeleportUtil().cheatVehicleInterpolationDelay = Integer.MAX_VALUE; // Set to max until player accepts the new position
|
||||
|
||||
// Make sure bukkit also knows the player got teleported out of their vehicle, can't do this async
|
||||
@@ -245,7 +277,7 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
}
|
||||
}
|
||||
|
||||
double y = position.getY();
|
||||
double y = positionY;
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_7_10)) {
|
||||
y += 1.62; // 1.7 teleport offset if grim ever supports 1.7 again
|
||||
}
|
||||
@@ -260,15 +292,15 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
data.getTeleportData().setTransaction(player.lastTransactionSent.get());
|
||||
|
||||
// Use provided transaction ID to make sure it can never desync, although there's no reason to do this
|
||||
addSentTeleport(new Location(null, position.getX(), y, position.getZ(), player.yaw % 360, player.pitch % 360), new Vector3d(), data.getTeleportData().getTransaction(), new RelativeFlag(0b11000), false, teleportId);
|
||||
addSentTeleport(positionX, y, positionZ, 0, 0, 0, data.getTeleportData().getTransaction(), new RelativeFlag(0b11000), false, teleportId);
|
||||
// This must be done after setting the sent teleport, otherwise we lose velocity data
|
||||
requiredSetBack = data;
|
||||
// Send after tracking to fix race condition
|
||||
PacketEvents.getAPI().getProtocolManager().sendPacketSilently(player.user.getChannel(), new WrapperPlayServerPlayerPositionAndLook(position.getX(), position.getY(), position.getZ(), 0, 0, data.getTeleportData().getFlags().getMask(), teleportId, false));
|
||||
PacketEvents.getAPI().getProtocolManager().sendPacketSilently(player.user.getChannel(), new WrapperPlayServerPlayerPositionAndLook(positionX, positionY, positionZ, 0, 0, data.getTeleportData().getFlags().getMask(), teleportId, false));
|
||||
player.sendTransaction();
|
||||
|
||||
if (data.getVelocity() != null && data.getVelocity().lengthSquared() > 0) {
|
||||
player.user.sendPacket(new WrapperPlayServerEntityVelocity(player.entityID, new Vector3d(data.getVelocity().getX(), data.getVelocity().getY(), data.getVelocity().getZ())));
|
||||
if (data.isHasVelocity() && (data.getVelocityX() != 0 || data.getVelocityY() != 0 || data.getVelocityZ() != 0)) {
|
||||
player.user.sendPacket(new WrapperPlayServerEntityVelocity(player.entityID, new Vector3d(data.getVelocityX(), data.getVelocityY(), data.getVelocityZ())));
|
||||
}
|
||||
} finally {
|
||||
isSendingSetback = false;
|
||||
@@ -288,12 +320,12 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
|
||||
TeleportData teleportPos;
|
||||
while ((teleportPos = pendingTeleports.peek()) != null) {
|
||||
double trueTeleportX = (teleportPos.isRelativeX() ? player.x : 0) + teleportPos.getLocation().getX();
|
||||
double trueTeleportY = (teleportPos.isRelativeY() ? player.y : 0) + teleportPos.getLocation().getY();
|
||||
double trueTeleportZ = (teleportPos.isRelativeZ() ? player.z : 0) + teleportPos.getLocation().getZ();
|
||||
double trueTeleportX = (teleportPos.isRelativeX() ? player.x : 0) + teleportPos.getLocationX();
|
||||
double trueTeleportY = (teleportPos.isRelativeY() ? player.y : 0) + teleportPos.getLocationY();
|
||||
double trueTeleportZ = (teleportPos.isRelativeZ() ? player.z : 0) + teleportPos.getLocationZ();
|
||||
|
||||
// There seems to be a version difference in teleports past 30 million... just clamp the vector
|
||||
Vector3d clamped = VectorUtils.clampVector(new Vector3d(trueTeleportX, trueTeleportY, trueTeleportZ));
|
||||
Vector3d clamped = VectorUtils.clampVector(trueTeleportX, trueTeleportY, trueTeleportZ);
|
||||
double threshold = teleportPos.isRelativePos() ? player.getMovementThreshold() : 0;
|
||||
boolean closeEnoughY = Math.abs(clamped.getY() - y) <= 1e-7 + threshold; // 1.7 rounding
|
||||
|
||||
@@ -339,18 +371,17 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
int lastTransaction = player.lastTransactionReceived.get();
|
||||
|
||||
while (true) {
|
||||
Pair<Integer, Vector3d> teleportPos = player.vehicleData.vehicleTeleports.peek();
|
||||
VehicleData.VehicleTeleport teleportPos = player.vehicleData.vehicleTeleports.peek();
|
||||
if (teleportPos == null) break;
|
||||
if (lastTransaction < teleportPos.first()) {
|
||||
if (lastTransaction < teleportPos.teleportId()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3d position = teleportPos.second();
|
||||
if (position.getX() == x && position.getY() == y && position.getZ() == z) {
|
||||
if (teleportPos.x() == x && teleportPos.y() == y && teleportPos.z() == z) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
return true;
|
||||
} else if (lastTransaction > teleportPos.first() + 1) {
|
||||
} else if (lastTransaction > teleportPos.teleportId() + 1) {
|
||||
player.vehicleData.vehicleTeleports.poll();
|
||||
|
||||
// Vehicles have terrible netcode so just ignore it if the teleport wasn't from us setting the player back
|
||||
@@ -396,36 +427,37 @@ public class SetbackTeleportUtil extends Check implements PostPredictionCheck {
|
||||
!player.getSetbackTeleportUtil().hasAcceptedSpawnTeleport);
|
||||
}
|
||||
|
||||
public void addSentTeleport(Location position, Vector3d velocity, int transaction, RelativeFlag flags, boolean plugin, int teleportId) {
|
||||
TeleportData data = new TeleportData(new Vector3d(position.getX(), position.getY(), position.getZ()), velocity, flags, transaction, teleportId);
|
||||
public void addSentTeleport(double x, double y, double z, double velocityX, double velocityY, double velocityZ, int transaction, RelativeFlag flags, boolean plugin, int teleportId) {
|
||||
TeleportData data = new TeleportData(x, y, z, velocityX, velocityY, velocityZ, flags, transaction, teleportId);
|
||||
pendingTeleports.add(data);
|
||||
|
||||
Vector3d safePosition = new Vector3d(position.getX(), position.getY(), position.getZ());
|
||||
|
||||
// We must convert relative teleports to avoid them becoming client controlled in the case of setback
|
||||
if (flags.has(RelativeFlag.X)) {
|
||||
safePosition = safePosition.withX(safePosition.getX() + lastKnownGoodPosition.pos.getX());
|
||||
x += lastKnownGoodPosition.posX;
|
||||
}
|
||||
|
||||
if (flags.has(RelativeFlag.Y)) {
|
||||
safePosition = safePosition.withY(safePosition.getY() + lastKnownGoodPosition.pos.getY());
|
||||
y += lastKnownGoodPosition.posY;
|
||||
}
|
||||
|
||||
if (flags.has(RelativeFlag.Z)) {
|
||||
safePosition = safePosition.withZ(safePosition.getZ() + lastKnownGoodPosition.pos.getZ());
|
||||
z += lastKnownGoodPosition.posZ;
|
||||
}
|
||||
|
||||
data = new TeleportData(safePosition, velocity, new RelativeFlag(0b11000), transaction, teleportId);
|
||||
requiredSetBack = new SetBackData(data, player.yaw, player.pitch, null, false, plugin);
|
||||
data = new TeleportData(x, y, z, velocityX, velocityY, velocityZ, new RelativeFlag(0b11000), transaction, teleportId);
|
||||
requiredSetBack = new SetBackData(data, player.yaw, player.pitch, false, plugin);
|
||||
|
||||
this.lastKnownGoodPosition = new SetbackPosWithVector(safePosition, new Vector3dm());
|
||||
this.lastKnownGoodPosition = new SetbackPosWithVector(x, y, z, 0, 0, 0);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class SetbackPosWithVector {
|
||||
private final Vector3d pos;
|
||||
private Vector3dm vector;
|
||||
private final double posX;
|
||||
private final double posY;
|
||||
private final double posZ;
|
||||
private final double vectorY;
|
||||
private double vectorX, vectorZ;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,12 @@ import ac.grim.grimac.checks.type.PostPredictionCheck;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.UncertaintyHandler;
|
||||
import ac.grim.grimac.predictionengine.predictions.PredictionEngine;
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.lists.EvictingQueue;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vec2;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
@@ -54,7 +56,12 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
|
||||
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);
|
||||
appendDebug(debug.getKey(), player.predictedVelocity,
|
||||
player.actualMovement.getX(), player.actualMovement.getY(), player.actualMovement.getZ(),
|
||||
location,
|
||||
player.startTickClientVel.getX(), player.startTickClientVel.getY(), player.startTickClientVel.getZ(),
|
||||
player.baseTickAddition.getX(), player.baseTickAddition.getY(), player.baseTickAddition.getZ(),
|
||||
player.baseTickWaterPushing.getX(), player.baseTickWaterPushing.getY(), player.baseTickWaterPushing.getZ());
|
||||
debug.setValue(debug.getValue() - 1);
|
||||
if (debug.getValue() <= 0) it.remove();
|
||||
}
|
||||
@@ -90,7 +97,14 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
Vector3dm startTickVel = startTickClientVel.get(i);
|
||||
Vector3dm addition = baseTickAddition.get(i);
|
||||
Vector3dm water = baseTickWater.get(i);
|
||||
appendDebug(sb, predict, actual, loc, startTickVel, addition, water);
|
||||
appendDebug(sb,
|
||||
predict,
|
||||
actual.getX(), actual.getY(), actual.getZ(),
|
||||
loc,
|
||||
startTickVel.getX(), startTickVel.getY(), startTickVel.getZ(),
|
||||
addition.getX(), addition.getY(), addition.getZ(),
|
||||
water.getX(), water.getY(), water.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
UncertaintyHandler uncertaintyHandler = player.uncertaintyHandler;
|
||||
@@ -194,7 +208,13 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
continuedDebug.put(sb, 40);
|
||||
}
|
||||
|
||||
private void appendDebug(StringBuilder sb, VectorData predict, Vector3dm actual, Location location, Vector3dm startTick, Vector3dm addition, Vector3dm water) {
|
||||
private void appendDebug(final StringBuilder sb, final VectorData predict,
|
||||
final double actualX, final double actualY, final double actualZ,
|
||||
final Location location,
|
||||
final double startTickX, final double startTickY, final double startTickZ,
|
||||
final double additionX, final double additionY, final double additionZ,
|
||||
final double waterX, final double waterY, final double waterZ) {
|
||||
|
||||
if (predict.isZeroPointZeroThree()) {
|
||||
sb.append("Movement threshold/tick skipping\n");
|
||||
}
|
||||
@@ -228,36 +248,45 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
}
|
||||
|
||||
// Apply 0.003/0.005 to make numbers more accurate
|
||||
Set<VectorData> set = new HashSet<>(Collections.singletonList(new VectorData(startTick.clone(), VectorData.VectorType.BestVelPicked)));
|
||||
Set<VectorData> set = new HashSet<>(Collections.singletonList(new VectorData(startTickX, startTickY, startTickZ, VectorData.VectorType.BestVelPicked)));
|
||||
new PredictionEngine().applyMovementThreshold(player, set);
|
||||
Vector3dm trueStartVel = ((VectorData) set.toArray()[0]).vector;
|
||||
VectorData tempVectorData = ((VectorData) set.toArray()[0]);
|
||||
double trueStartVelX = tempVectorData.vectorX;
|
||||
double trueStartVelY = tempVectorData.vectorY;
|
||||
double trueStartVelZ = tempVectorData.vectorZ;
|
||||
|
||||
Vector3dm clientMovement = getPlayerMathMovement(player, actual.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector3dm simulatedMovement = getPlayerMathMovement(player, predict.vector.clone().subtract(trueStartVel), location.xRot);
|
||||
Vector3dm offset = actual.clone().subtract(predict.vector);
|
||||
trueStartVel.add(addition);
|
||||
trueStartVel.add(water);
|
||||
final double offsetX = actualX - trueStartVelX;
|
||||
final double offsetY = actualY - trueStartVelY;
|
||||
final double offsetZ = actualZ - trueStartVelZ;
|
||||
|
||||
double offsetLength = Math.sqrt(offsetX*offsetX + offsetY*offsetY + offsetZ*offsetZ);
|
||||
|
||||
Vec2 clientMovement = getPlayerMathMovement(player, offsetX, offsetZ, location.xRot);
|
||||
Vec2 simulatedMovement = getPlayerMathMovement(player, predict.vectorX - trueStartVelX, predict.vectorZ - trueStartVelZ, location.xRot);
|
||||
trueStartVelX += additionX + waterX;
|
||||
trueStartVelY += additionY + waterY;
|
||||
trueStartVelZ += additionZ + waterZ;
|
||||
|
||||
sb.append("Simulated: ");
|
||||
sb.append(predict.vector.toString());
|
||||
sb.append(MessageUtil.toUnlabeledString(trueStartVelX, trueStartVelY, trueStartVelZ));
|
||||
sb.append("\nActually: ");
|
||||
sb.append(actual);
|
||||
sb.append(MessageUtil.toUnlabeledString(actualX, actualY, actualZ));
|
||||
sb.append("\nOffset Vector: ");
|
||||
sb.append(offset);
|
||||
sb.append(MessageUtil.toUnlabeledString(offsetX, offsetY, offsetZ));
|
||||
sb.append("\nOffset: ");
|
||||
sb.append(offset.length());
|
||||
sb.append(offsetLength);
|
||||
sb.append("\nLocation: ");
|
||||
sb.append(location);
|
||||
sb.append("\nInitial velocity: ");
|
||||
sb.append(startTick);
|
||||
sb.append(MessageUtil.toUnlabeledString(startTickX, startTickY, startTickZ));
|
||||
|
||||
if (addition.lengthSquared() > 0) {
|
||||
if (additionX != 0 || additionY != 0 || additionZ != 0) {
|
||||
sb.append("\nInitial vel addition: ");
|
||||
sb.append(addition);
|
||||
sb.append(MessageUtil.toUnlabeledString(additionX, additionY, additionZ));
|
||||
}
|
||||
if (water.lengthSquared() > 0) {
|
||||
if (waterX != 0 || waterY != 0 || waterZ != 0) {
|
||||
sb.append("\nWater vel addition: ");
|
||||
sb.append(water);
|
||||
sb.append(MessageUtil.toUnlabeledString(waterX, waterY, waterZ));
|
||||
}
|
||||
|
||||
sb.append("\nClient input: ");
|
||||
@@ -273,14 +302,14 @@ public final class SuperDebug extends Check implements PostPredictionCheck {
|
||||
sb.append("\n\n");
|
||||
}
|
||||
|
||||
private Vector3dm getPlayerMathMovement(GrimPlayer player, Vector3dm wantedMovement, float f2) {
|
||||
private Vec2 getPlayerMathMovement(GrimPlayer player, double x, double z, float f2) {
|
||||
float f3 = player.trigHandler.sin(f2 * 0.017453292f);
|
||||
float f4 = player.trigHandler.cos(f2 * 0.017453292f);
|
||||
|
||||
float bestTheoreticalX = (float) (f3 * wantedMovement.getZ() + f4 * wantedMovement.getX()) / (f3 * f3 + f4 * f4);
|
||||
float bestTheoreticalZ = (float) (-f3 * wantedMovement.getX() + f4 * wantedMovement.getZ()) / (f3 * f3 + f4 * f4);
|
||||
float bestTheoreticalX = (float) (f3 * z + f4 * x) / (f3 * f3 + f4 * f4);
|
||||
float bestTheoreticalZ = (float) (-f3 * x + f4 * z) / (f3 * f3 + f4 * f4);
|
||||
|
||||
return new Vector3dm(bestTheoreticalX, 0, bestTheoreticalZ);
|
||||
return new Vec2(bestTheoreticalX, bestTheoreticalZ);
|
||||
}
|
||||
|
||||
private record Location(double x, double y, double z, float xRot, float yRot, String world) {
|
||||
|
||||
+1
-2
@@ -7,7 +7,6 @@ import ac.grim.grimac.platform.api.world.PlatformWorld;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAcknowledgeBlockChanges;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockChange;
|
||||
@@ -116,7 +115,7 @@ public class DefaultResyncHandler implements ResyncHandler {
|
||||
GrimAPI.INSTANCE.getScheduler().getRegionScheduler().execute(GrimAPI.INSTANCE.getGrimPlugin(), world, chunkX, chunkZ, () -> {
|
||||
if (!player.platformPlayer.isOnline() || !player.getSetbackTeleportUtil().hasAcceptedSpawnTeleport)
|
||||
return;
|
||||
if (player.platformPlayer.getPosition().distance(new Vector3d(x, y, z)) >= 64)
|
||||
if (player.platformPlayer.distanceSquared(x, y, z) >= 64 * 64)
|
||||
return;
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) return; // Don't load chunks sync
|
||||
|
||||
|
||||
@@ -25,4 +25,6 @@ public interface GrimEntity extends GrimIdentity {
|
||||
PlatformWorld getWorld();
|
||||
|
||||
Location getLocation();
|
||||
|
||||
double distanceSquared(double x, double y, double z);
|
||||
}
|
||||
@@ -364,12 +364,15 @@ public class GrimPlayer implements GrimUser {
|
||||
// It's very difficult to test precedence so if there's issues with this bouncy implementation let me know
|
||||
for (VectorData data : new HashSet<>(possibleMovements)) {
|
||||
for (BlockFace direction : uncertaintyHandler.slimePistonBounces) {
|
||||
final double x = data.vectorX;
|
||||
final double y = data.vectorY;
|
||||
final double z = data.vectorZ;
|
||||
if (direction.getModX() != 0) {
|
||||
possibleMovements.add(data.returnNewModified(data.vector.clone().setX(direction.getModX()), VectorData.VectorType.SlimePistonBounce));
|
||||
possibleMovements.add(data.returnNewModified(direction.getModX(), y, z, VectorData.VectorType.SlimePistonBounce));
|
||||
} else if (direction.getModY() != 0) {
|
||||
possibleMovements.add(data.returnNewModified(data.vector.clone().setY(direction.getModY()), VectorData.VectorType.SlimePistonBounce));
|
||||
possibleMovements.add(data.returnNewModified(x, direction.getModY(), z, VectorData.VectorType.SlimePistonBounce));
|
||||
} else if (direction.getModZ() != 0) {
|
||||
possibleMovements.add(data.returnNewModified(data.vector.clone().setZ(direction.getModZ()), VectorData.VectorType.SlimePistonBounce));
|
||||
possibleMovements.add(data.returnNewModified(x, y, direction.getModZ(), VectorData.VectorType.SlimePistonBounce));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -796,7 +799,10 @@ public class GrimPlayer implements GrimUser {
|
||||
}
|
||||
|
||||
public boolean isInWaterOrRain() {
|
||||
return compensatedWorld.isRaining || Collisions.hasMaterial(this, boundingBox.copy().expand(0.1f), (block) -> Materials.isWater(CompensatedWorld.blockVersion, block.first()));
|
||||
return compensatedWorld.isRaining
|
||||
|| Collisions.hasMaterial(this, boundingBox.copy().expand(0.1f),
|
||||
(block, x, y, z) -> Materials.isWater(CompensatedWorld.blockVersion, block)
|
||||
);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@@ -925,12 +931,12 @@ public class GrimPlayer implements GrimUser {
|
||||
if (platformPlayer != null) platformPlayer.sendMessage(message);
|
||||
}
|
||||
|
||||
public void resyncPosition(Vector3i pos) {
|
||||
this.resyncHandler.resync(pos.getX(), pos.getY(), pos.getZ(), pos.getX(), pos.getY(), pos.getZ());
|
||||
public void resyncPosition(final int x, final int y, final int z) {
|
||||
this.resyncHandler.resync(x, y, z, x, y, z);
|
||||
}
|
||||
|
||||
public void resyncPosition(Vector3i pos, int sequenceID) {
|
||||
this.resyncHandler.resyncPosition(pos.x, pos.y, pos.z, sequenceID);
|
||||
public void resyncPosition(final int x, final int y, final int z, final int sequenceID) {
|
||||
this.resyncHandler.resyncPosition(x, y, z, sequenceID);
|
||||
}
|
||||
|
||||
public void resyncPositions(SimpleCollisionBox box) {
|
||||
@@ -942,14 +948,24 @@ public class GrimPlayer implements GrimUser {
|
||||
if (this.movementThisTick.size() >= 100) {
|
||||
GrimPlayer.Movement movement1 = this.movementThisTick.removeFirst();
|
||||
GrimPlayer.Movement movement2 = this.movementThisTick.removeFirst();
|
||||
GrimPlayer.Movement movement3 = new GrimPlayer.Movement(movement1.from(), movement2.to(), false);
|
||||
GrimPlayer.Movement movement3 = new GrimPlayer.Movement(movement1.from(), movement2.to());
|
||||
this.movementThisTick.addFirst(movement3);
|
||||
}
|
||||
|
||||
this.movementThisTick.add(movement);
|
||||
}
|
||||
|
||||
public record Movement(Vector3d from, Vector3d to, boolean axisIndependant) {}
|
||||
public record Movement(Vector3d from, Vector3d to, Vector3d axisDependentOriginalMovement) {
|
||||
|
||||
public Movement(Vector3d from, Vector3d to) {
|
||||
this(from, to, null);
|
||||
}
|
||||
|
||||
public boolean axisIndependant() {
|
||||
return axisDependentOriginalMovement != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO (Cross-platform) keep track of world at packet level; do not rely on potentially non-lag-compensated platformPlayer.getWorld()
|
||||
public Location getLocation() {
|
||||
|
||||
@@ -18,6 +18,7 @@ import ac.grim.grimac.predictionengine.predictions.rideable.PredictionEngineBoat
|
||||
import ac.grim.grimac.utils.anticheat.update.PositionUpdate;
|
||||
import ac.grim.grimac.utils.anticheat.update.PredictionComplete;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.SetBackData;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityCamel;
|
||||
@@ -196,7 +197,6 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.vehicleData.wasVehicleSwitch = false;
|
||||
|
||||
if (riding != null) {
|
||||
Vector3dm pos = new Vector3dm(player.x, player.y, player.z);
|
||||
SimpleCollisionBox interTruePositions = riding.getPossibleCollisionBoxes();
|
||||
|
||||
// We shrink the expanded bounding box to what the packet positions can be, for a smaller box
|
||||
@@ -206,7 +206,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
interTruePositions.expand(-width, 0, -width);
|
||||
interTruePositions.expandMax(0, -height, 0);
|
||||
|
||||
Vector3dm cutTo = VectorUtils.cutBoxToVector(pos, interTruePositions);
|
||||
Vector3dm cutTo = VectorUtils.cutBoxToVector(player.x, player.y, player.z, interTruePositions);
|
||||
|
||||
// Now we need to simulate a tick starting at the most optimal position
|
||||
// The start position is never sent, so we assume the most optimal start position
|
||||
@@ -231,9 +231,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
handleTeleport(update);
|
||||
|
||||
if (player.isClimbing) {
|
||||
Vector3dm ladder = player.clientVelocity.clone().setY(0.2);
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, ladder);
|
||||
player.lastWasClimbing = ladder.getY();
|
||||
player.lastWasClimbing = PredictionEngineNormal.staticVectorEndOfTickY(player, 0.2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -363,8 +361,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
}
|
||||
|
||||
SimpleCollisionBox steppingOnBB = GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z).expand(player.getMovementThreshold()).offset(0, -1, 0);
|
||||
Collisions.hasMaterial(player, steppingOnBB, (pair) -> {
|
||||
WrappedBlockState data = pair.first();
|
||||
Collisions.hasMaterial(player, steppingOnBB, (data) -> {
|
||||
if (data.getType() == StateTypes.SLIME_BLOCK && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) {
|
||||
player.uncertaintyHandler.isSteppingOnSlime = true;
|
||||
player.uncertaintyHandler.isSteppingOnBouncyBlock = true;
|
||||
@@ -410,8 +407,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.first().getType())
|
||||
|| checkData.first().getType() == StateTypes.CHEST || checkData.first().getType() == StateTypes.TRAPPED_CHEST);
|
||||
checkData -> BlockTags.ANVIL.contains(checkData.getType())
|
||||
|| checkData.getType() == StateTypes.CHEST || checkData.getType() == StateTypes.TRAPPED_CHEST);
|
||||
|
||||
player.uncertaintyHandler.isOrWasNearGlitchyBlock = isGlitchy || player.uncertaintyHandler.isNearGlitchyBlock;
|
||||
player.uncertaintyHandler.checkForHardCollision();
|
||||
@@ -453,7 +450,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
player.clientVelocity = player.actualMovement.clone();
|
||||
player.gravity = 0;
|
||||
player.friction = 0.91f;
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, player.clientVelocity);
|
||||
player.clientVelocity.setX(player.clientVelocity.getX() * player.friction);
|
||||
player.clientVelocity.setY(PredictionEngineNormal.staticVectorEndOfTickY(player, player.clientVelocity.getY()));
|
||||
player.clientVelocity.setZ(player.clientVelocity.getZ() * player.friction);
|
||||
} else if (riding == null) {
|
||||
wasChecked = true;
|
||||
|
||||
@@ -522,7 +521,7 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
} // If it isn't any of these cases, the player is on a mob they can't control and therefore is exempt
|
||||
|
||||
// No, don't comment about the sqrt call. It doesn't matter unless you run sqrt thousands of times a second.
|
||||
double offset = player.predictedVelocity.vector.distance(player.actualMovement);
|
||||
double offset = player.actualMovement.distance(player.predictedVelocity.vectorX, player.predictedVelocity.vectorY, player.predictedVelocity.vectorZ);
|
||||
offset = player.uncertaintyHandler.reduceOffset(offset);
|
||||
|
||||
if (player.packetStateData.tryingToRiptide != clientClaimsRiptide) {
|
||||
@@ -535,19 +534,19 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
//
|
||||
// Checking for oldClientVel being too high fixes BleachHack vertical scaffold
|
||||
if (player.getSetbackTeleportUtil().getRequiredSetBack() != null && player.getSetbackTeleportUtil().getRequiredSetBack().getTicksComplete() == 1) {
|
||||
Vector3dm setbackVel = player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity();
|
||||
SetBackData setback = player.getSetbackTeleportUtil().getRequiredSetBack();
|
||||
// A player must have velocity going INTO the ground to be able to jump
|
||||
// Otherwise they could ignore upwards velocity that isn't useful into more useful upwards velocity (towering)
|
||||
// So if they are supposed to be going upwards, or are supposed to be off the ground, resync
|
||||
if (player.predictedVelocity.isJump()
|
||||
&& !player.wasTouchingLava && !player.wasTouchingWater
|
||||
&& ((setbackVel != null && setbackVel.getY() >= 0) || !Collisions.slowCouldPointThreeHitGround(player, player.lastX, player.lastY, player.lastZ))) {
|
||||
&& ((setback.isHasVelocity() && setback.getVelocityY() >= 0) || !Collisions.slowCouldPointThreeHitGround(player, player.lastX, player.lastY, player.lastZ))) {
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
}
|
||||
boolean lavaBugFix = player.wasTouchingLava && player.predictedVelocity.isJump() &&
|
||||
player.predictedVelocity.vector.getY() < 0.06 && player.predictedVelocity.vector.getY() > -0.02;
|
||||
player.predictedVelocity.vectorY < 0.06 && player.predictedVelocity.vectorY > -0.02;
|
||||
// Player ignored the knockback or is delaying it a tick... bad!
|
||||
if (!player.predictedVelocity.isKnockback() && !lavaBugFix && player.getSetbackTeleportUtil().getRequiredSetBack().getVelocity() != null) {
|
||||
if (!player.predictedVelocity.isKnockback() && !lavaBugFix && player.getSetbackTeleportUtil().getRequiredSetBack().isHasVelocity()) {
|
||||
// And then send it again!
|
||||
player.getSetbackTeleportUtil().executeForceResync();
|
||||
}
|
||||
@@ -567,7 +566,9 @@ public class MovementCheckRunner extends Check implements PositionCheck {
|
||||
// Patch sprint jumping with elytra exploit
|
||||
if (player.platformPlayer != null && player.isGliding && player.predictedVelocity.isJump() && player.isSprinting && !allowSprintJumpingWithElytra) {
|
||||
SetbackTeleportUtil.SetbackPosWithVector lastKnownGoodPosition = player.getSetbackTeleportUtil().lastKnownGoodPosition;
|
||||
lastKnownGoodPosition.setVector(lastKnownGoodPosition.getVector().multiply(new Vector3dm(0.6 * 0.91, 1, 0.6 * 0.91)));
|
||||
lastKnownGoodPosition.setVectorX(lastKnownGoodPosition.getVectorX() * 0.6 * 0.91);
|
||||
lastKnownGoodPosition.setVectorZ(lastKnownGoodPosition.getVectorZ() * 0.6 * 0.91);
|
||||
|
||||
player.getSetbackTeleportUtil().executeNonSimulatingSetback();
|
||||
}
|
||||
|
||||
|
||||
@@ -268,22 +268,21 @@ public class PointThreeEstimator {
|
||||
isNearFluid = false;
|
||||
|
||||
// Check for flowing water
|
||||
Collisions.hasMaterial(player, pointThreeBox, (pair) -> {
|
||||
final WrappedBlockState state = pair.first();
|
||||
Collisions.hasMaterial(player, pointThreeBox, (state, x, y, z) -> {
|
||||
final StateType stateType = state.getType();
|
||||
if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.inVehicle() && player.inventory.getBoots().getType() == ItemTypes.LEATHER_BOOTS)) {
|
||||
isNearClimbable = true;
|
||||
}
|
||||
|
||||
if (BlockTags.TRAPDOORS.contains(stateType)) {
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.second().getX(), pair.second().getY(), pair.second().getZ(), state);
|
||||
isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, x, y, z, state);
|
||||
}
|
||||
|
||||
if (stateType == StateTypes.BUBBLE_COLUMN && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
isNearBubbleColumn = true;
|
||||
}
|
||||
|
||||
if (Materials.isWater(player.getClientVersion(), pair.first()) || pair.first().getType() == StateTypes.LAVA) {
|
||||
if (Materials.isWater(player.getClientVersion(), state) || state.getType() == StateTypes.LAVA) {
|
||||
isNearFluid = true;
|
||||
}
|
||||
|
||||
@@ -365,7 +364,7 @@ public class PointThreeEstimator {
|
||||
|
||||
// If this tick is the tick after y velocity was by 0, a stepping movement is POSSIBLE to have been hidden
|
||||
// A bit hacky... is there a better way? I'm unsure...
|
||||
boolean likelyStepSkip = player.isPointThree() && (data.vector.getY() > -0.08 && data.vector.getY() < 0.06) && couldStep;
|
||||
boolean likelyStepSkip = player.isPointThree() && (data.vectorY > -0.08 && data.vectorY < 0.06) && couldStep;
|
||||
|
||||
// We need to do hypot calculations for all 3 axis
|
||||
// sqrt(sqrt(x^2 + z^2)^2 + y^2) = hypot(x, z, y)
|
||||
@@ -411,7 +410,7 @@ public class PointThreeEstimator {
|
||||
wasAlwaysCertain = false;
|
||||
// Head hitters return the vector to 0, and then apply gravity to it.
|
||||
// Not much room for abuse for this, so keep it lenient
|
||||
return -Math.max(0, vector.vector.getY()) - 0.1 - fluidAddition;
|
||||
return -Math.max(0, vector.vectorY) - 0.1 - fluidAddition;
|
||||
} else if (player.uncertaintyHandler.wasAffectedByStuckSpeed()) {
|
||||
wasAlwaysCertain = false;
|
||||
// This shouldn't be needed but stuck speed can desync very easily with 0.03...
|
||||
@@ -425,7 +424,7 @@ public class PointThreeEstimator {
|
||||
double minMovement = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? 0.003 : 0.005;
|
||||
|
||||
// This should likely be refactored, but it works well.
|
||||
double yVel = vector.vector.getY();
|
||||
double yVel = vector.vectorY;
|
||||
double maxYTraveled = 0;
|
||||
boolean first = true;
|
||||
do {
|
||||
@@ -452,7 +451,7 @@ public class PointThreeEstimator {
|
||||
|
||||
// We aren't making progress, avoid infinite loop (This can be due to the player not having gravity)
|
||||
if (yVel == 0) break;
|
||||
} while (Math.abs(maxYTraveled + vector.vector.getY()) < player.getMovementThreshold()); // Account for uncertainty, don't stop until we simulate past uncertainty point
|
||||
} while (Math.abs(maxYTraveled + vector.vectorY) < player.getMovementThreshold()); // Account for uncertainty, don't stop until we simulate past uncertainty point
|
||||
|
||||
if (maxYTraveled != 0) {
|
||||
wasAlwaysCertain = false;
|
||||
|
||||
@@ -66,13 +66,13 @@ public class SneakingEstimator extends Check implements PostPredictionCheck {
|
||||
if (data.isJump() == player.predictedVelocity.isJump() && data.isKnockback() == player.predictedVelocity.isKnockback() && data.isExplosion() == player.predictedVelocity.isExplosion()) {
|
||||
// Fuck, we are compounding this which is very dangerous. After light testing seems fine... can we do better?
|
||||
if (player.uncertaintyHandler.lastStuckWest.hasOccurredSince(0) || player.uncertaintyHandler.lastStuckNorth.hasOccurredSince(0)) {
|
||||
sneakingPotentialHiddenVelocity.minX = Math.min(sneakingPotentialHiddenVelocity.minX, data.vector.getX());
|
||||
sneakingPotentialHiddenVelocity.minZ = Math.min(sneakingPotentialHiddenVelocity.minZ, data.vector.getZ());
|
||||
sneakingPotentialHiddenVelocity.minX = Math.min(sneakingPotentialHiddenVelocity.minX, data.vectorX);
|
||||
sneakingPotentialHiddenVelocity.minZ = Math.min(sneakingPotentialHiddenVelocity.minZ, data.vectorZ);
|
||||
}
|
||||
|
||||
if (player.uncertaintyHandler.lastStuckEast.hasOccurredSince(0) || player.uncertaintyHandler.lastStuckSouth.hasOccurredSince(0)) {
|
||||
sneakingPotentialHiddenVelocity.maxX = Math.max(sneakingPotentialHiddenVelocity.maxX, data.vector.getX());
|
||||
sneakingPotentialHiddenVelocity.maxZ = Math.max(sneakingPotentialHiddenVelocity.maxZ, data.vector.getZ());
|
||||
sneakingPotentialHiddenVelocity.maxX = Math.max(sneakingPotentialHiddenVelocity.maxX, data.vectorX);
|
||||
sneakingPotentialHiddenVelocity.maxZ = Math.max(sneakingPotentialHiddenVelocity.maxZ, data.vectorZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,12 +157,12 @@ public class UncertaintyHandler {
|
||||
Vector3dm maxLocation = new Vector3dm(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
|
||||
Vector3dm minLocation = new Vector3dm(entityBox.minX, entityBox.minY, entityBox.minZ);
|
||||
|
||||
Vector3dm diff = minLocation.subtract(new Vector3dm(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
Vector3dm diff = minLocation.subtract(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ).multiply(0.1);
|
||||
fishingRodPullBox.minX = Math.min(0, diff.getX());
|
||||
fishingRodPullBox.minY = Math.min(0, diff.getY());
|
||||
fishingRodPullBox.minZ = Math.min(0, diff.getZ());
|
||||
|
||||
diff = maxLocation.subtract(new Vector3dm(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ)).multiply(0.1);
|
||||
diff = maxLocation.subtract(player.lastX, player.lastY + 0.8 * 1.8, player.lastZ).multiply(0.1);
|
||||
fishingRodPullBox.maxX = Math.max(0, diff.getX());
|
||||
fishingRodPullBox.maxY = Math.max(0, diff.getY());
|
||||
fishingRodPullBox.maxZ = Math.max(0, diff.getZ());
|
||||
@@ -264,7 +264,7 @@ public class UncertaintyHandler {
|
||||
return 0.06;
|
||||
|
||||
// Not worth my time to fix this because checking flying generally sucks - if player was flying in last 2 ticks
|
||||
if ((lastFlyingTicks.hasOccurredSince(5)) && Math.abs(data.vector.getY()) < (4.5 * player.flySpeed - 0.25))
|
||||
if ((lastFlyingTicks.hasOccurredSince(5)) && Math.abs(data.vectorY) < (4.5 * player.flySpeed - 0.25))
|
||||
return 0.06;
|
||||
|
||||
double pointThree = player.getMovementThreshold();
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import com.github.retrooper.packetevents.protocol.world.Direction;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@UtilityClass
|
||||
public class BlockCollisions {
|
||||
|
||||
public static final Vector3d X_AXIS = new Vector3d(1.0, 0.0, 0.0);
|
||||
public static final Vector3d Y_AXIS = new Vector3d(0.0, 1.0, 0.0);
|
||||
public static final Vector3d Z_AXIS = new Vector3d(0.0, 0.0, 1.0);
|
||||
|
||||
public static Vector3i getFurthestCorner(Vector3d vector) {
|
||||
double xDot = Math.abs(X_AXIS.dot(vector));
|
||||
double yDot = Math.abs(Y_AXIS.dot(vector));
|
||||
double zDot = Math.abs(Z_AXIS.dot(vector));
|
||||
int xSign = vector.x >= 0.0 ? 1 : -1;
|
||||
int ySign = vector.y >= 0.0 ? 1 : -1;
|
||||
int zSign = vector.z >= 0.0 ? 1 : -1;
|
||||
if (xDot <= yDot && xDot <= zDot) {
|
||||
return new Vector3i(-xSign, -zSign, ySign);
|
||||
} else {
|
||||
return yDot <= zDot ? new Vector3i(zSign, -ySign, -xSign) : new Vector3i(-ySign, xSign, -zSign);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Vector3d> clip(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, Vector3d start, Vector3d end) {
|
||||
double[] minDistance = new double[]{1.0};
|
||||
double deltaX = end.x - start.x;
|
||||
double deltaY = end.y - start.y;
|
||||
double deltaZ = end.z - start.z;
|
||||
Direction direction = getDirection(minX, minY, minZ, maxX, maxY, maxZ, start, minDistance, null, deltaX, deltaY, deltaZ);
|
||||
if (direction == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
double distance = minDistance[0];
|
||||
return Optional.of(start.add(distance * deltaX, distance * deltaY, distance * deltaZ));
|
||||
}
|
||||
}
|
||||
|
||||
private static Direction getDirection(
|
||||
double minX,
|
||||
double minY,
|
||||
double minZ,
|
||||
double maxX,
|
||||
double maxY,
|
||||
double maxZ,
|
||||
Vector3d start,
|
||||
double[] minDistance,
|
||||
Direction facing,
|
||||
double deltaX,
|
||||
double deltaY,
|
||||
double deltaZ
|
||||
) {
|
||||
if (deltaX > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaX, deltaY, deltaZ, minX, minY, maxY, minZ, maxZ, Direction.WEST, start.x, start.y, start.z);
|
||||
} else if (deltaX < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaX, deltaY, deltaZ, maxX, minY, maxY, minZ, maxZ, Direction.EAST, start.x, start.y, start.z);
|
||||
}
|
||||
|
||||
if (deltaY > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaY, deltaZ, deltaX, minY, minZ, maxZ, minX, maxX, Direction.DOWN, start.y, start.z, start.x);
|
||||
} else if (deltaY < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaY, deltaZ, deltaX, maxY, minZ, maxZ, minX, maxX, Direction.UP, start.y, start.z, start.x);
|
||||
}
|
||||
|
||||
if (deltaZ > Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaZ, deltaX, deltaY, minZ, minX, maxX, minY, maxY, Direction.NORTH, start.z, start.x, start.y);
|
||||
} else if (deltaZ < -Collisions.COLLISION_EPSILON) {
|
||||
facing = clipPoint(minDistance, facing, deltaZ, deltaX, deltaY, maxZ, minX, maxX, minY, maxY, Direction.SOUTH, start.z, start.x, start.y);
|
||||
}
|
||||
|
||||
return facing;
|
||||
}
|
||||
|
||||
public static Direction clipPoint(
|
||||
double[] minDistance,
|
||||
Direction prevDirection,
|
||||
double distanceSide,
|
||||
double distanceOtherA,
|
||||
double distanceOtherB,
|
||||
double minSide,
|
||||
double minOtherA,
|
||||
double maxOtherA,
|
||||
double minOtherB,
|
||||
double maxOtherB,
|
||||
Direction hitSide,
|
||||
double startSide,
|
||||
double startOtherA,
|
||||
double startOtherB
|
||||
) {
|
||||
double sideDistance = (minSide - startSide) / distanceSide;
|
||||
double otherDistanceA = startOtherA + sideDistance * distanceOtherA;
|
||||
double otherDistanceB = startOtherB + sideDistance * distanceOtherB;
|
||||
if (sideDistance > 0.0 && sideDistance < minDistance[0]
|
||||
&& minOtherA - Collisions.COLLISION_EPSILON < otherDistanceA
|
||||
&& otherDistanceA < maxOtherA + Collisions.COLLISION_EPSILON
|
||||
&& minOtherB - Collisions.COLLISION_EPSILON < otherDistanceB
|
||||
&& otherDistanceB < maxOtherB + Collisions.COLLISION_EPSILON) {
|
||||
minDistance[0] = sideDistance;
|
||||
return hitSide;
|
||||
} else {
|
||||
return prevDirection;
|
||||
}
|
||||
}
|
||||
|
||||
public static final ImmutableList<Collisions.Axis> YXZ_AXIS_ORDER = ImmutableList.of(Collisions.Axis.Y, Collisions.Axis.X, Collisions.Axis.Z);
|
||||
public static final ImmutableList<Collisions.Axis> YZX_AXIS_ORDER = ImmutableList.of(Collisions.Axis.Y, Collisions.Axis.Z, Collisions.Axis.X);
|
||||
|
||||
public static ImmutableList<Collisions.Axis> axisStepOrder(Vector3d vector) {
|
||||
return Math.abs(vector.getX()) < Math.abs(vector.getZ()) ? YZX_AXIS_ORDER : YXZ_AXIS_ORDER;
|
||||
}
|
||||
|
||||
public static Vector3d relative(Vector3d curr, Direction direction, double value) {
|
||||
Vector3i vec = direction.getVector();
|
||||
return new Vector3d(
|
||||
curr.x + value * vec.getX(), curr.y + value * vec.getY(), curr.z + value * vec.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
|
||||
public interface BlockEffectsResolver {
|
||||
|
||||
void applyEffectsFromBlocks(GrimPlayer player);
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects;
|
||||
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BlockStepVisitor {
|
||||
|
||||
boolean visit(Vector3i blockPos, int index);
|
||||
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
// 1.21.10 ONLY - 1.21.9 is impossible to implement due to Mojang awesomeness
|
||||
public class BlockEffectsResolverV1_21_10 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_10();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
|
||||
int iterationCount = 16;
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(movement.axisDependentOriginalMovement())) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
iterationCount -= checkInsideBlocks(player, from, vector, visitedBlocks, iterationCount);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iterationCount -= checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks, 16);
|
||||
}
|
||||
|
||||
if (iterationCount <= 0) {
|
||||
checkInsideBlocks(player, movement.to(), movement.to(), visitedBlocks, 1);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
public static int checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks, int count) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
boolean isFarEnough = from.distanceSquared(to) > GrimMath.square(0.9999900000002526);
|
||||
|
||||
AtomicInteger blockCount = new AtomicInteger();
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, localCount) -> {
|
||||
if (localCount >= count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blockCount.set(localCount);
|
||||
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
boolean shouldApply = isFarEnough || boundingBox.intersects(blockPos);
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, shouldApply);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return blockCount.get() + 1;
|
||||
}
|
||||
|
||||
public static boolean forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor visitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (direction.lengthSquared() < (double) GrimMath.square(1.0E-5F)) {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!visitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(boundingBox.move(direction.multiply(-1.0)), direction)) {
|
||||
if (!visitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
alreadyVisited.add(GrimMath.asLong(blockPos));
|
||||
}
|
||||
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, direction, boundingBox, visitor);
|
||||
if (iterationCount < 0) {
|
||||
return false;
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(boundingBox, direction)) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(blockPos)) && !visitor.visit(blockPos, iterationCount + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d direction, SimpleCollisionBox boundingBox, BlockStepVisitor visitor) {
|
||||
double sizeX = boundingBox.getXSize();
|
||||
double sizeY = boundingBox.getYSize();
|
||||
double sizeZ = boundingBox.getZSize();
|
||||
|
||||
Vector3i furthestCorner = BlockCollisions.getFurthestCorner(direction);
|
||||
|
||||
Vector3d center = boundingBox.getCenter();
|
||||
Vector3d end = new Vector3d(
|
||||
center.x + sizeX * 0.5 * furthestCorner.getX(),
|
||||
center.y + sizeY * 0.5 * furthestCorner.getY(),
|
||||
center.z + sizeZ * 0.5 * furthestCorner.getZ()
|
||||
);
|
||||
Vector3d start = end.subtract(direction);
|
||||
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
iterationCount++;
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX - sizeX * furthestCorner.getX());
|
||||
int endY = GrimMath.floor(clampedY - sizeY * furthestCorner.getY());
|
||||
int endZ = GrimMath.floor(clampedZ - sizeZ * furthestCorner.getZ());
|
||||
int copyIterationCount = iterationCount;
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenCornersInDirection(currentX, currentY, currentZ, endX, endY, endZ, direction)) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(blockPos)) && !visitor.visit(blockPos, copyIterationCount)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
// 1.21.2-1.21.3
|
||||
public class BlockEffectsResolverV1_21_2 implements BlockEffectsResolver {
|
||||
|
||||
public static BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_2();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
SimpleCollisionBox boundingBox = (player.inVehicle()
|
||||
? GetBoundingBox.getCollisionBoxForPlayer(player, player.x, player.y, player.z)
|
||||
: player.boundingBox.copy()).expand(-1.0E-5F);
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to();
|
||||
|
||||
for (Vector3i blockPos : boxTraverseBlocks(from, to, boundingBox)) {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static Iterable<Vector3i> boxTraverseBlocks(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
Iterable<Vector3i> initialBlocks = SimpleCollisionBox.betweenClosed(boundingBox);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
return initialBlocks;
|
||||
} else {
|
||||
Set<Vector3i> traversedBlocks = new ObjectLinkedOpenHashSet<>();
|
||||
Vector3d normalizedDirection = direction.normalize().multiply(Collisions.COLLISION_EPSILON);
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d().add(normalizedDirection);
|
||||
Vector3d subtractedMinPosition = boundingBox.min().toVector3d().subtract(direction).subtract(normalizedDirection);
|
||||
addCollisionsAlongTravel(traversedBlocks, subtractedMinPosition, boxMinPosition, boundingBox);
|
||||
|
||||
for (Vector3i blockPos : initialBlocks) {
|
||||
traversedBlocks.add(blockPos);
|
||||
}
|
||||
|
||||
return traversedBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addCollisionsAlongTravel(Set<Vector3i> output, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
output.add(new Vector3i(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
// 1.21.4
|
||||
public class BlockEffectsResolverV1_21_4 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_4();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to();
|
||||
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
for (Vector3i blockPos : boxTraverseBlocks(from, to, boundingBox)) {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static Iterable<Vector3i> boxTraverseBlocks(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
Iterable<Vector3i> initialBlocks = SimpleCollisionBox.betweenClosed(boundingBox);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
return initialBlocks;
|
||||
} else {
|
||||
Set<Vector3i> traversedBlocks = new ObjectLinkedOpenHashSet<>();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
BlockEffectsResolverV1_21_2.addCollisionsAlongTravel(traversedBlocks, subtractedMinPosition, boxMinPosition, boundingBox);
|
||||
|
||||
for (Vector3i blockPos : initialBlocks) {
|
||||
traversedBlocks.add(blockPos);
|
||||
}
|
||||
|
||||
return traversedBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
// 1.21.5
|
||||
public class BlockEffectsResolverV1_21_5 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_5();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(to)) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
checkInsideBlocks(player, from, vector, visitedBlocks);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static void checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, i) -> {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static void forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (!(direction.lengthSquared() < GrimMath.square(0.99999F))) {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, subtractedMinPosition, boxMinPosition, boundingBox, blockStepVisitor);
|
||||
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!alreadyVisited.contains(GrimMath.asLong(blockPos))) {
|
||||
blockStepVisitor.visit(blockPos, iterationCount + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
blockStepVisitor.visit(blockPos, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(x, y, z))) {
|
||||
blockStepVisitor.visit(new Vector3i(x, y, z), iterationCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package ac.grim.grimac.predictionengine.blockeffects.impl;
|
||||
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockEffectsResolver;
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockStepVisitor;
|
||||
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
// 1.21.6-1.21.8
|
||||
public class BlockEffectsResolverV1_21_6 implements BlockEffectsResolver {
|
||||
|
||||
public static final BlockEffectsResolver INSTANCE = new BlockEffectsResolverV1_21_6();
|
||||
|
||||
@Override
|
||||
public void applyEffectsFromBlocks(GrimPlayer player) {
|
||||
LongSet visitedBlocks = player.visitedBlocks;
|
||||
|
||||
for (GrimPlayer.Movement movement : player.finalMovementsThisTick) {
|
||||
Vector3d from = movement.from();
|
||||
Vector3d to = movement.to().subtract(movement.from());
|
||||
if (movement.axisIndependant() && to.lengthSquared() > 0.0) {
|
||||
for (Collisions.Axis axis : BlockCollisions.axisStepOrder(to)) {
|
||||
double value = axis.get(to);
|
||||
if (value != 0.0) {
|
||||
Vector3d vector = BlockCollisions.relative(from, axis.getPositive(), value);
|
||||
checkInsideBlocks(player, from, vector, visitedBlocks);
|
||||
from = vector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkInsideBlocks(player, movement.from(), movement.to(), visitedBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
visitedBlocks.clear();
|
||||
}
|
||||
|
||||
private static void checkInsideBlocks(GrimPlayer player, Vector3d from, Vector3d to, LongSet visitedBlocks) {
|
||||
SimpleCollisionBox boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, to.x, to.y, to.z).expand(-1.0E-5F);
|
||||
forEachBlockIntersectedBetween(from, to, boundingBox, (blockPos, i) -> {
|
||||
WrappedBlockState blockState = player.compensatedWorld.getBlock(blockPos);
|
||||
StateType blockType = blockState.getType();
|
||||
|
||||
if (blockType.isAir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitedBlocks.add(GrimMath.asLong(blockPos))) {
|
||||
Collisions.onInsideBlock(player, blockType, blockState, blockPos.x, blockPos.y, blockPos.z, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean forEachBlockIntersectedBetween(Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
if (direction.lengthSquared() < GrimMath.square(0.99999F)) {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!blockStepVisitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LongSet alreadyVisited = new LongOpenHashSet();
|
||||
Vector3d boxMinPosition = boundingBox.min().toVector3d();
|
||||
Vector3d subtractedMinPosition = boxMinPosition.subtract(direction);
|
||||
|
||||
int iterationCount = addCollisionsAlongTravel(alreadyVisited, subtractedMinPosition, boxMinPosition, boundingBox, blockStepVisitor);
|
||||
if (iterationCount < 0) {
|
||||
return false;
|
||||
} else {
|
||||
for (Vector3i blockPos : SimpleCollisionBox.betweenClosed(boundingBox)) {
|
||||
if (!alreadyVisited.contains(GrimMath.asLong(blockPos)) && !blockStepVisitor.visit(blockPos, iterationCount + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int addCollisionsAlongTravel(LongSet alreadyVisited, Vector3d start, Vector3d end, SimpleCollisionBox boundingBox, BlockStepVisitor blockStepVisitor) {
|
||||
Vector3d direction = end.subtract(start);
|
||||
int currentX = GrimMath.floor(start.x);
|
||||
int currentY = GrimMath.floor(start.y);
|
||||
int currentZ = GrimMath.floor(start.z);
|
||||
int stepX = GrimMath.sign(direction.x);
|
||||
int stepY = GrimMath.sign(direction.y);
|
||||
int stepZ = GrimMath.sign(direction.z);
|
||||
double tMaxX = stepX == 0 ? Double.MAX_VALUE : stepX / direction.x;
|
||||
double tMaxY = stepY == 0 ? Double.MAX_VALUE : stepY / direction.y;
|
||||
double tMaxZ = stepZ == 0 ? Double.MAX_VALUE : stepZ / direction.z;
|
||||
double tDeltaX = tMaxX * (stepX > 0 ? 1.0 - GrimMath.frac(start.x) : GrimMath.frac(start.x));
|
||||
double tDeltaY = tMaxY * (stepY > 0 ? 1.0 - GrimMath.frac(start.y) : GrimMath.frac(start.y));
|
||||
double tDeltaZ = tMaxZ * (stepZ > 0 ? 1.0 - GrimMath.frac(start.z) : GrimMath.frac(start.z));
|
||||
int iterationCount = 0;
|
||||
|
||||
while (tDeltaX <= 1.0 || tDeltaY <= 1.0 || tDeltaZ <= 1.0) {
|
||||
if (tDeltaX < tDeltaY) {
|
||||
if (tDeltaX < tDeltaZ) {
|
||||
currentX += stepX;
|
||||
tDeltaX += tMaxX;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
} else if (tDeltaY < tDeltaZ) {
|
||||
currentY += stepY;
|
||||
tDeltaY += tMaxY;
|
||||
} else {
|
||||
currentZ += stepZ;
|
||||
tDeltaZ += tMaxZ;
|
||||
}
|
||||
|
||||
if (iterationCount++ > 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<Vector3d> collisionPoint = BlockCollisions.clip(currentX, currentY, currentZ, currentX + 1, currentY + 1, currentZ + 1, start, end);
|
||||
if (!collisionPoint.isEmpty()) {
|
||||
Vector3d collisionVec = collisionPoint.get();
|
||||
double clampedX = GrimMath.clamp(collisionVec.x, currentX + 1.0E-5F, currentX + 1.0 - 1.0E-5F);
|
||||
double clampedY = GrimMath.clamp(collisionVec.y, currentY + 1.0E-5F, currentY + 1.0 - 1.0E-5F);
|
||||
double clampedZ = GrimMath.clamp(collisionVec.z, currentZ + 1.0E-5F, currentZ + 1.0 - 1.0E-5F);
|
||||
int endX = GrimMath.floor(clampedX + boundingBox.getXSize());
|
||||
int endY = GrimMath.floor(clampedY + boundingBox.getYSize());
|
||||
int endZ = GrimMath.floor(clampedZ + boundingBox.getZSize());
|
||||
|
||||
for (int x = currentX; x <= endX; x++) {
|
||||
for (int y = currentY; y <= endY; y++) {
|
||||
for (int z = currentZ; z <= endZ; z++) {
|
||||
if (alreadyVisited.add(GrimMath.asLong(x, y, z)) && !blockStepVisitor.visit(new Vector3i(x, y, z), iterationCount)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
}
|
||||
+37
-73
@@ -89,12 +89,12 @@ public class MovementTicker {
|
||||
player.uncertaintyHandler.collidingEntities.add(possibleCollidingEntities);
|
||||
}
|
||||
|
||||
private boolean isHorizontalCollisionSoft(Vector3dm collide) {
|
||||
double horizontalLengthSquared = collide.getX() * collide.getX() + collide.getZ() * collide.getZ();
|
||||
private boolean isHorizontalCollisionSoft(double x, double y, double z) {
|
||||
double horizontalLengthSquared = x * x + z * z;
|
||||
if (horizontalLengthSquared < 1E-5F) return false;
|
||||
|
||||
float xxa = (float) player.predictedVelocity.input.getX();
|
||||
float zza = (float) player.predictedVelocity.input.getZ();
|
||||
float xxa = (float) player.predictedVelocity.input.x();
|
||||
float zza = (float) player.predictedVelocity.input.z();
|
||||
|
||||
float yawInRadians = player.yaw * (float) (Math.PI / 180.0);
|
||||
double sin = player.trigHandler.sin(yawInRadians);
|
||||
@@ -102,17 +102,17 @@ public class MovementTicker {
|
||||
double g = xxa * cos - zza * sin;
|
||||
double h = zza * cos + xxa * sin;
|
||||
double i = g * g + h * h;
|
||||
return i >= 1E-5F && Math.acos((g * collide.getX() + h * collide.getZ()) / Math.sqrt(i * horizontalLengthSquared)) < 0.13962634F;
|
||||
return i >= 1E-5F && Math.acos((g * x + h * z) / Math.sqrt(i * horizontalLengthSquared)) < 0.13962634F;
|
||||
}
|
||||
|
||||
public void move(Vector3dm inputVel, Vector3dm collide) {
|
||||
public void move(Vector3dm inputVel, double x, double y, double z) {
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.99) {
|
||||
player.clientVelocity = new Vector3dm();
|
||||
}
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_18_2)) {
|
||||
boolean xAxis = !GrimMath.equal(inputVel.getX(), collide.getX());
|
||||
boolean zAxis = !GrimMath.equal(inputVel.getZ(), collide.getZ());
|
||||
boolean xAxis = !GrimMath.equal(inputVel.getX(), x);
|
||||
boolean zAxis = !GrimMath.equal(inputVel.getZ(), z);
|
||||
|
||||
if (xAxis) {
|
||||
player.clientVelocity.setX(0);
|
||||
@@ -123,20 +123,20 @@ public class MovementTicker {
|
||||
}
|
||||
|
||||
player.horizontalCollision = xAxis || zAxis;
|
||||
player.softHorizontalCollision = player.horizontalCollision && isHorizontalCollisionSoft(collide);
|
||||
player.softHorizontalCollision = player.horizontalCollision && isHorizontalCollisionSoft(x, y, z);
|
||||
} else {
|
||||
if (inputVel.getX() != collide.getX()) {
|
||||
if (inputVel.getX() != x) {
|
||||
player.clientVelocity.setX(0);
|
||||
}
|
||||
|
||||
if (inputVel.getZ() != collide.getZ()) {
|
||||
if (inputVel.getZ() != z) {
|
||||
player.clientVelocity.setZ(0);
|
||||
}
|
||||
|
||||
player.horizontalCollision = inputVel.getX() != collide.getX() || inputVel.getZ() != collide.getZ();
|
||||
player.horizontalCollision = inputVel.getX() != x || inputVel.getZ() != z;
|
||||
}
|
||||
|
||||
player.verticalCollision = inputVel.getY() != collide.getY();
|
||||
player.verticalCollision = inputVel.getY() != y;
|
||||
|
||||
// Avoid order of collisions being wrong because 0.03 movements
|
||||
// Stepping movement USUALLY means the vehicle in on the ground as vehicles can't jump
|
||||
@@ -145,7 +145,7 @@ public class MovementTicker {
|
||||
boolean calculatedOnGround = (player.verticalCollision && inputVel.getY() < 0.0D);
|
||||
|
||||
// If the player is on the ground with a y velocity of 0, let the player decide (too close to call)
|
||||
if (inputVel.getY() == -SimpleCollisionBox.COLLISION_EPSILON && collide.getY() > -SimpleCollisionBox.COLLISION_EPSILON && collide.getY() <= 0 && !player.inVehicle())
|
||||
if (inputVel.getY() == -SimpleCollisionBox.COLLISION_EPSILON && y > -SimpleCollisionBox.COLLISION_EPSILON && y <= 0 && !player.inVehicle())
|
||||
calculatedOnGround = player.onGround;
|
||||
player.clientClaimsLastOnGround = player.onGround;
|
||||
|
||||
@@ -177,9 +177,9 @@ public class MovementTicker {
|
||||
|
||||
if (player.onGround) {
|
||||
player.fallDistance = 0;
|
||||
} else if (collide.getY() < 0) {
|
||||
player.fallDistance = (player.fallDistance) - collide.getY();
|
||||
player.vehicleData.lastYd = collide.getY();
|
||||
} else if (y < 0) {
|
||||
player.fallDistance = (player.fallDistance) - y;
|
||||
player.vehicleData.lastYd = y;
|
||||
}
|
||||
|
||||
// Striders call the method for inside blocks AGAIN!
|
||||
@@ -187,11 +187,11 @@ public class MovementTicker {
|
||||
Collisions.handleInsideBlocks(player);
|
||||
}
|
||||
|
||||
player.mainSupportingBlockData = MainSupportingBlockPosFinder.findMainSupportingBlockPos(player, player.mainSupportingBlockData, new Vector3d(collide.getX(), collide.getY(), collide.getZ()), player.boundingBox, player.onGround);
|
||||
player.mainSupportingBlockData = MainSupportingBlockPosFinder.findMainSupportingBlockPos(player, player.mainSupportingBlockData, new Vector3d(x, y, z), player.boundingBox, player.onGround);
|
||||
StateType onBlock = BlockProperties.getOnPos(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
|
||||
|
||||
// Hack with 1.14+ poses issue
|
||||
if (inputVel.getY() != collide.getY()) {
|
||||
if (inputVel.getY() != y) {
|
||||
// If the client supports slime blocks
|
||||
// And the block is a slime block
|
||||
// Or the block is honey and was replaced by viaversion
|
||||
@@ -214,26 +214,27 @@ public class MovementTicker {
|
||||
player.clientVelocity.setY(0);
|
||||
}
|
||||
}
|
||||
|
||||
collide = PredictionEngine.clampMovementToHardBorder(player, collide);
|
||||
// TODO reimplement? the comment about reimplementing clampMovementToHardBorder is 3 years old...
|
||||
// collide = PredictionEngine.clampMovementToHardBorder(player, collide);
|
||||
|
||||
// The game disregards movements smaller than 1e-7 (such as in boats)
|
||||
if (collide.lengthSquared() <= 1e-7
|
||||
double collideLengthSquared = GrimMath.lengthSquared(x, y, z);
|
||||
if (collideLengthSquared <= 1e-7
|
||||
// New condition added in 1.21.2
|
||||
&& (player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2) || inputVel.lengthSquared() - collide.lengthSquared() >= 1e-7)) {
|
||||
collide = new Vector3dm();
|
||||
&& (player.getClientVersion().isOlderThan(ClientVersion.V_1_21_2) || inputVel.lengthSquared() - collideLengthSquared >= 1e-7)) {
|
||||
x = y = z = 0;
|
||||
} else if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5)) {
|
||||
Vector3d from = new Vector3d(player.lastX, player.lastY, player.lastZ);
|
||||
Vector3d to = new Vector3d(player.x, player.y, player.z);
|
||||
|
||||
player.addMovementThisTick(new GrimPlayer.Movement(from, to, true));
|
||||
player.addMovementThisTick(new GrimPlayer.Movement(from, to, new Vector3d(inputVel.getX(), inputVel.getY(), inputVel.getZ())));
|
||||
}
|
||||
|
||||
// This is where vanilla moves the bounding box and sets it
|
||||
player.predictedVelocity = new VectorData(collide.clone(), player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
|
||||
player.predictedVelocity = new VectorData(x, y, z, player.predictedVelocity.lastVector, player.predictedVelocity.vectorType);
|
||||
|
||||
float f = BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z));
|
||||
player.clientVelocity.multiply(new Vector3dm(f, 1, f));
|
||||
player.clientVelocity.multiply(f, 1, f);
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2)) {
|
||||
return;
|
||||
@@ -306,10 +307,10 @@ public class MovementTicker {
|
||||
// Meaning we should scan upwards!
|
||||
oldBB.expand(-SimpleCollisionBox.COLLISION_EPSILON);
|
||||
|
||||
double posX = Math.max(0, player.predictedVelocity.vector.getX()) + SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double negX = Math.min(0, player.predictedVelocity.vector.getX()) - SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double posZ = Math.max(0, player.predictedVelocity.vector.getZ()) + SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double negZ = Math.min(0, player.predictedVelocity.vector.getZ()) - SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double posX = Math.max(0, player.predictedVelocity.vectorX) + SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double negX = Math.min(0, player.predictedVelocity.vectorX) - SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double posZ = Math.max(0, player.predictedVelocity.vectorZ) + SimpleCollisionBox.COLLISION_EPSILON;
|
||||
double negZ = Math.min(0, player.predictedVelocity.vectorZ) - SimpleCollisionBox.COLLISION_EPSILON;
|
||||
|
||||
boolean xAxisCollision = !Collisions.isEmpty(player, oldBB.expandMin(negX, 0, 0).expandMax(posX, 0, 0));
|
||||
boolean zAxisCollision = !Collisions.isEmpty(player, oldBB.expandMin(0, 0, negZ).expandMax(0, 0, posZ));
|
||||
@@ -435,7 +436,7 @@ public class MovementTicker {
|
||||
// 1.13 and below players can't climb ladders while touching water
|
||||
// yes, 1.13 players cannot climb ladders underwater
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) && player.isClimbing) {
|
||||
player.lastWasClimbing = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity.clone().setY(0.2D * 0.8F)).getY();
|
||||
player.lastWasClimbing = FluidFallingAdjustedMovement.getAdjustedY(player, playerGravity, isFalling, 0.2D * 0.8F);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -446,14 +447,14 @@ public class MovementTicker {
|
||||
|
||||
// Lava movement changed in 1.16
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16) && player.slightlyTouchingLava) {
|
||||
player.clientVelocity = player.clientVelocity.multiply(new Vector3dm(0.5D, 0.800000011920929D, 0.5D));
|
||||
player.clientVelocity = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity);
|
||||
player.clientVelocity = player.clientVelocity.multiply(0.5D, 0.800000011920929D, 0.5D);
|
||||
player.clientVelocity.setY(FluidFallingAdjustedMovement.getAdjustedY(player, playerGravity, isFalling, player.clientVelocity.getY()));
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5D);
|
||||
}
|
||||
|
||||
if (player.hasGravity)
|
||||
player.clientVelocity.add(new Vector3dm(0.0D, -playerGravity / 4.0D, 0.0D));
|
||||
player.clientVelocity.add(0.0D, -playerGravity / 4.0D, 0.0D);
|
||||
|
||||
} else if (player.isGliding) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && Collisions.onClimbable(player, player.lastX, player.lastY, player.lastZ)) {
|
||||
@@ -481,44 +482,7 @@ public class MovementTicker {
|
||||
}
|
||||
}
|
||||
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_2)) {
|
||||
// Reset stuck speed so it can update
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.99) {
|
||||
player.uncertaintyHandler.lastStuckSpeedMultiplier.reset();
|
||||
}
|
||||
|
||||
player.stuckSpeedMultiplier = new Vector3dm(1, 1, 1);
|
||||
player.finalMovementsThisTick.clear();
|
||||
|
||||
Vector3d from = new Vector3d(player.lastX, player.lastY, player.lastZ);
|
||||
Vector3d to = new Vector3d(player.x, player.y, player.z);
|
||||
|
||||
ClientVersion clientVersion = player.getClientVersion();
|
||||
if (clientVersion.isOlderThan(ClientVersion.V_1_21_5)) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(from, to, false));
|
||||
} else if (clientVersion.isNewerThanOrEquals(ClientVersion.V_1_21_5)) {
|
||||
player.finalMovementsThisTick.addAll(player.movementThisTick);
|
||||
player.movementThisTick.clear();
|
||||
|
||||
if (player.finalMovementsThisTick.isEmpty()) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(from, to, false));
|
||||
} else if (player.finalMovementsThisTick.get(player.finalMovementsThisTick.size() - 1).to().distanceSquared(to) > 9.9999994E-11F) {
|
||||
player.finalMovementsThisTick.add(new GrimPlayer.Movement(player.finalMovementsThisTick.get(player.finalMovementsThisTick.size() - 1).to(), to, false));
|
||||
}
|
||||
}
|
||||
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
|
||||
if (player.stuckSpeedMultiplier.getX() < 0.9) {
|
||||
// Reset fall distance if stuck in block
|
||||
player.fallDistance = 0;
|
||||
}
|
||||
|
||||
// Flying players are not affected by cobwebs/sweet berry bushes
|
||||
if (player.isFlying) {
|
||||
player.stuckSpeedMultiplier = new Vector3dm(1, 1, 1);
|
||||
}
|
||||
}
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
}
|
||||
|
||||
public boolean canStandOnLava() {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class MovementTickerStrider extends MovementTickerRideable {
|
||||
if (isAbove(player) && player.compensatedWorld.getLavaFluidLevelAt((int) Math.floor(player.x), (int) Math.floor(player.y + 1), (int) Math.floor(player.z)) == 0) {
|
||||
player.onGround = true;
|
||||
} else {
|
||||
player.clientVelocity.multiply(0.5).add(new Vector3dm(0, 0.05, 0));
|
||||
player.clientVelocity.multiply(0.5).add(0, 0.05, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+159
-102
@@ -9,6 +9,7 @@ import ac.grim.grimac.utils.data.KnownInput;
|
||||
import ac.grim.grimac.utils.data.Pair;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vec2d;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.math.VectorUtils;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
@@ -31,22 +32,22 @@ public class PredictionEngine {
|
||||
return outputVel;
|
||||
}
|
||||
|
||||
public static Vector3dm transformInputsToVector(GrimPlayer player, Vector3dm theoreticalInput) {
|
||||
public static Vec2d transformInputsToVector(GrimPlayer player, int strafe, int forward) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5)) {
|
||||
Vec2 moveVector = new Vec2((float) theoreticalInput.getX(), (float) theoreticalInput.getZ()).normalized();
|
||||
Vec2 moveVector = new Vec2((float) strafe, (float) forward).normalized();
|
||||
Vec2 input = modifyInput(player, moveVector);
|
||||
return new Vector3dm(input.x(), 0, input.y());
|
||||
return new Vec2d(input.x(), input.y());
|
||||
}
|
||||
float bestPossibleX;
|
||||
float bestPossibleZ;
|
||||
|
||||
// Slow movement was determined by the previous pose
|
||||
if (player.isSlowMovement) {
|
||||
bestPossibleX = (float) (theoreticalInput.getX() * player.sneakingSpeedMultiplier);
|
||||
bestPossibleZ = (float) (theoreticalInput.getZ() * player.sneakingSpeedMultiplier);
|
||||
bestPossibleX = (float) (strafe * player.sneakingSpeedMultiplier);
|
||||
bestPossibleZ = (float) (forward * player.sneakingSpeedMultiplier);
|
||||
} else {
|
||||
bestPossibleX = Math.min(Math.max(-1f, Math.round(theoreticalInput.getX())), 1f);
|
||||
bestPossibleZ = Math.min(Math.max(-1f, Math.round(theoreticalInput.getZ())), 1f);
|
||||
bestPossibleX = Math.min(Math.max(-1f, strafe), 1f);
|
||||
bestPossibleZ = Math.min(Math.max(-1f, forward), 1f);
|
||||
}
|
||||
|
||||
if (player.packetStateData.isSlowedByUsingItem()) {
|
||||
@@ -54,18 +55,23 @@ public class PredictionEngine {
|
||||
bestPossibleZ *= 0.2F;
|
||||
}
|
||||
|
||||
Vector3dm inputVector = new Vector3dm(bestPossibleX, 0, bestPossibleZ);
|
||||
inputVector.multiply(0.98F);
|
||||
double inputVectorX = bestPossibleX;
|
||||
double inputVectorZ = bestPossibleZ;
|
||||
inputVectorX *= 0.98F;
|
||||
inputVectorZ *= 0.98F;
|
||||
|
||||
// Simulate float rounding imprecision
|
||||
inputVector = new Vector3dm((float) inputVector.getX(), (float) inputVector.getY(), (float) inputVector.getZ());
|
||||
inputVectorX = (float) inputVectorX;
|
||||
inputVectorZ = (float) inputVectorZ;
|
||||
|
||||
if (inputVector.lengthSquared() > 1) {
|
||||
double d0 = Math.sqrt(inputVector.getX() * inputVector.getX() + inputVector.getY() * inputVector.getY() + inputVector.getZ() * inputVector.getZ());
|
||||
inputVector = new Vector3dm(inputVector.getX() / d0, inputVector.getY() / d0, inputVector.getZ() / d0);
|
||||
double lengthSquared = GrimMath.lengthSquared(inputVectorX, inputVectorZ);
|
||||
if (lengthSquared > 1) {
|
||||
double d0 = Math.sqrt(lengthSquared);
|
||||
inputVectorX = inputVectorX / d0;
|
||||
inputVectorZ = inputVectorZ / d0;
|
||||
}
|
||||
|
||||
return inputVector;
|
||||
return new Vec2d(inputVectorX, inputVectorZ);
|
||||
}
|
||||
|
||||
public static Vec2 modifyInput(GrimPlayer player, Vec2 moveVector) {
|
||||
@@ -140,7 +146,7 @@ public class PredictionEngine {
|
||||
|
||||
// Client velocity - before collision and carried into the next tick
|
||||
// Predicted velocity - after collision and not carried into the next tick
|
||||
new MovementTickerPlayer(player).move(player.clientVelocity.clone(), player.predictedVelocity.vector);
|
||||
new MovementTickerPlayer(player).move(player.clientVelocity.clone(), player.predictedVelocity.vectorX, player.predictedVelocity.vectorY, player.predictedVelocity.vectorZ);
|
||||
endOfTick(player, player.gravity);
|
||||
}
|
||||
|
||||
@@ -234,7 +240,7 @@ public class PredictionEngine {
|
||||
beforeCollisionMovement = primaryPushMovement;
|
||||
|
||||
// We basically want to avoid falsing ground spoof, try to find a vector that works
|
||||
if (player.wouldCollisionResultFlagGroundSpoof(primaryPushMovement.getY(), bestCollisionVel.vector.getY()))
|
||||
if (player.wouldCollisionResultFlagGroundSpoof(primaryPushMovement.getY(), bestCollisionVel.vectorY))
|
||||
resultAccuracy += 0.0001 * 0.0001;
|
||||
|
||||
bestInput = resultAccuracy;
|
||||
@@ -259,7 +265,7 @@ public class PredictionEngine {
|
||||
}
|
||||
|
||||
private Pair<Vector3dm, Vector3dm> doSeekingWallCollisions(GrimPlayer player, Vector3dm primaryPushMovement, Vector3dm originalClientVel, VectorData clientVelAfterInput) {
|
||||
boolean vehicleKB = player.inVehicle() && clientVelAfterInput.isKnockback() && clientVelAfterInput.vector.getY() == 0;
|
||||
boolean vehicleKB = player.inVehicle() && clientVelAfterInput.isKnockback() && clientVelAfterInput.vectorY == 0;
|
||||
// Extra collision epsilon required for vehicles to be accurate
|
||||
double xAdditional = Math.signum(primaryPushMovement.getX()) * SimpleCollisionBox.COLLISION_EPSILON;
|
||||
// The server likes sending y=0 kb "lifting" the player off the ground.
|
||||
@@ -329,10 +335,9 @@ public class PredictionEngine {
|
||||
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5)))) {
|
||||
|
||||
// Calculate the Y velocity after friction
|
||||
Vector3dm hackyClimbVector = new Vector3dm(0, 0.2, 0);
|
||||
PredictionEngineNormal.staticVectorEndOfTick(player, hackyClimbVector);
|
||||
double y = PredictionEngineNormal.staticVectorEndOfTickY(player, 0.2);
|
||||
|
||||
pointThreePossibilities.add(new VectorData(hackyClimbVector, VectorData.VectorType.ZeroPointZeroThree));
|
||||
pointThreePossibilities.add(new VectorData(0, y, 0, VectorData.VectorType.ZeroPointZeroThree));
|
||||
}
|
||||
|
||||
// This is a secure method to add jumping vectors to this list
|
||||
@@ -357,19 +362,24 @@ public class PredictionEngine {
|
||||
for (VectorData vectorData : data) {
|
||||
// Sneaking in water
|
||||
if (vectorData.isKnockback() && player.baseTickAddition.lengthSquared() != 0) {
|
||||
vectorData.vector = vectorData.vector.add(player.baseTickAddition);
|
||||
vectorData.vectorX += player.baseTickAddition.getX();
|
||||
vectorData.vectorY += player.baseTickAddition.getY();
|
||||
vectorData.vectorZ += player.baseTickAddition.getZ();
|
||||
}
|
||||
// Water pushing movement is affected by initial velocity due to 0.003 eating pushing in the past
|
||||
if (vectorData.isKnockback() && player.baseTickWaterPushing.lengthSquared() != 0) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13)) {
|
||||
Vector3dm vec3 = player.baseTickWaterPushing.clone();
|
||||
if (Math.abs(vectorData.vector.getX()) < 0.003 && Math.abs(vectorData.vector.getZ()) < 0.003 && player.baseTickWaterPushing.length() < 0.0045000000000000005D) {
|
||||
if (Math.abs(vectorData.vectorX) < 0.003 && Math.abs(vectorData.vectorZ) < 0.003 && player.baseTickWaterPushing.length() < 0.0045000000000000005D) {
|
||||
vec3 = vec3.normalize().multiply(0.0045000000000000005);
|
||||
}
|
||||
|
||||
vectorData.vector = vectorData.vector.add(vec3);
|
||||
vectorData.vectorX += vec3.getX();
|
||||
vectorData.vectorY += vec3.getY();
|
||||
vectorData.vectorZ += vec3.getZ();
|
||||
} else {
|
||||
vectorData.vector = vectorData.vector.add(player.baseTickWaterPushing);
|
||||
vectorData.vectorX += player.baseTickWaterPushing.getX();
|
||||
vectorData.vectorY += player.baseTickWaterPushing.getY();
|
||||
vectorData.vectorZ += player.baseTickWaterPushing.getZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,7 +415,9 @@ public class PredictionEngine {
|
||||
if (!player.inVehicle() || player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5)) return;
|
||||
|
||||
for (VectorData vectorData : data) {
|
||||
vectorData.vector = vectorData.vector.clone().multiply(0.98);
|
||||
vectorData.vectorX = vectorData.vectorX * 0.98;
|
||||
vectorData.vectorY = vectorData.vectorY * 0.98;
|
||||
vectorData.vectorZ = vectorData.vectorZ * 0.98;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,11 +425,11 @@ public class PredictionEngine {
|
||||
for (int x = 1; x <= Math.min(player.maxAttackSlow, 5); x++) {
|
||||
for (VectorData data : new HashSet<>(velocities)) {
|
||||
if (player.minAttackSlow > 0) {
|
||||
data.vector.setX(data.vector.getX() * 0.6);
|
||||
data.vector.setZ(data.vector.getZ() * 0.6);
|
||||
data.vectorX = data.vectorX * 0.6;
|
||||
data.vectorZ = data.vectorZ * 0.6;
|
||||
data.addVectorType(VectorData.VectorType.AttackSlow);
|
||||
} else {
|
||||
velocities.add(data.returnNewModified(data.vector.clone().multiply(new Vector3dm(0.6, 1, 0.6)), VectorData.VectorType.AttackSlow));
|
||||
velocities.add(data.returnNewModified(data.vectorX * 0.6, data.vectorY, data.vectorZ * 0.6, VectorData.VectorType.AttackSlow));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,37 +449,61 @@ public class PredictionEngine {
|
||||
minimumMovement = 0.005D;
|
||||
}
|
||||
|
||||
for (VectorData vector : velocities) {
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && !player.inVehicle()) {
|
||||
if (Collisions.getHorizontalDistanceSqr(vector.vector) < 9.0E-6) {
|
||||
vector.vector.setX(0D);
|
||||
vector.vector.setZ(0D);
|
||||
boolean stupidVectors = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21_5) && !player.inVehicle();
|
||||
boolean stuckOnEdge = player.uncertaintyHandler.stuckOnEdge.hasOccurredSince(2);
|
||||
Set<VectorData> vectors = stupidVectors && stuckOnEdge ? new HashSet<>(velocities) : velocities;
|
||||
for (VectorData vector : vectors) {
|
||||
if (stupidVectors) {
|
||||
if (Collisions.getHorizontalDistanceSqr(vector.vectorX, vector.vectorZ) < 9.0E-6) {
|
||||
if (stuckOnEdge) {
|
||||
VectorData edgeVector = vector.returnNewModified(vector.vectorX, vector.vectorY, vector.vectorZ, vector.vectorType);
|
||||
if (Math.abs(edgeVector.vectorY) < minimumMovement) {
|
||||
edgeVector.setY(0D);
|
||||
}
|
||||
|
||||
velocities.add(edgeVector);
|
||||
}
|
||||
|
||||
vector.setX(0D);
|
||||
vector.setZ(0D);
|
||||
}
|
||||
} else {
|
||||
if (Math.abs(vector.vector.getX()) < minimumMovement) {
|
||||
vector.vector.setX(0D);
|
||||
if (Math.abs(vector.vectorX) < minimumMovement) {
|
||||
vector.setX(0D);
|
||||
}
|
||||
|
||||
if (Math.abs(vector.vector.getZ()) < minimumMovement) {
|
||||
vector.vector.setZ(0D);
|
||||
if (Math.abs(vector.vectorZ) < minimumMovement) {
|
||||
vector.setZ(0D);
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(vector.vector.getY()) < minimumMovement) {
|
||||
vector.vector.setY(0D);
|
||||
if (Math.abs(vector.vectorY) < minimumMovement) {
|
||||
vector.setY(0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addExplosionToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
if (player.likelyExplosions == null && player.firstBreadExplosion == null) return;
|
||||
final VectorData[] vectorsToProcess = existingVelocities.toArray(new VectorData[0]);
|
||||
for (VectorData vector : vectorsToProcess) {
|
||||
if (player.likelyExplosions != null) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(player.likelyExplosions.vector), vector, VectorData.VectorType.Explosion));
|
||||
final double newX = player.likelyExplosions.vector.getX() + vector.vectorX;
|
||||
final double newY = player.likelyExplosions.vector.getY() + vector.vectorY;
|
||||
final double newZ = player.likelyExplosions.vector.getZ() + vector.vectorZ;
|
||||
existingVelocities.add(new VectorData(newX, newY, newZ, vector, VectorData.VectorType.Explosion));
|
||||
}
|
||||
|
||||
if (player.firstBreadExplosion != null) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(player.firstBreadExplosion.vector), vector, VectorData.VectorType.Explosion)
|
||||
.returnNewModified(vector.vector.clone().add(player.firstBreadExplosion.vector), VectorData.VectorType.FirstBreadExplosion));
|
||||
// Previously we called Vector3dm.add(player.likelyExplosions.vector) after checking (player.firstBreadExplosion != null)
|
||||
// which would've thrown an NPE if player.likelyExplosions could be null here, we can ignore this
|
||||
// empirically this means when firstBreadExplosion != null then likelyExplosions is also != null
|
||||
final double newX = player.likelyExplosions.vector.getX() + vector.vectorX;
|
||||
final double newY = player.likelyExplosions.vector.getY() + vector.vectorY;
|
||||
final double newZ = player.likelyExplosions.vector.getZ() + vector.vectorZ;
|
||||
VectorData intermediateData = new VectorData(newX, newY, newZ, vector, VectorData.VectorType.Explosion);
|
||||
VectorData finalData = new VectorData(newX, newY, newZ, intermediateData, VectorData.VectorType.FirstBreadExplosion);
|
||||
existingVelocities.add(finalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,16 +556,18 @@ public class PredictionEngine {
|
||||
bScore -= 1;
|
||||
|
||||
// If the player is on the ground but the vector leads the player off the ground
|
||||
if ((player.inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && a.vector.getY() >= 0)
|
||||
if ((player.inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && a.vectorY >= 0)
|
||||
aScore += 2;
|
||||
|
||||
if ((player.inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && b.vector.getY() >= 0)
|
||||
if ((player.inVehicle() ? player.clientControlledVerticalCollision : player.onGround) && b.vectorY >= 0)
|
||||
bScore += 2;
|
||||
|
||||
if (aScore != bScore)
|
||||
return Integer.compare(aScore, bScore);
|
||||
|
||||
return Double.compare(a.vector.distanceSquared(player.actualMovement), b.vector.distanceSquared(player.actualMovement));
|
||||
return Double.compare(
|
||||
GrimMath.distanceSquared(player.actualMovement.getX(), player.actualMovement.getY(), player.actualMovement.getZ(), a.vectorX, a.vectorY, a.vectorZ),
|
||||
GrimMath.distanceSquared(player.actualMovement.getX(), player.actualMovement.getY(), player.actualMovement.getZ(), b.vectorX, b.vectorY, b.vectorZ));
|
||||
}
|
||||
|
||||
public Vector3dm handleStartingVelocityUncertainty(GrimPlayer player, VectorData vector, Vector3dm targetVec) {
|
||||
@@ -583,69 +621,81 @@ public class PredictionEngine {
|
||||
// 0.075 seems safe?
|
||||
//
|
||||
// Be somewhat careful as there is an antikb (for horizontal) that relies on this lenience
|
||||
Vector3dm uncertainty = new Vector3dm(avgColliding * 0.08, additionVertical, avgColliding * 0.08);
|
||||
final double uncertaintyX = avgColliding * 0.08;
|
||||
final double uncertaintyY = additionVertical;
|
||||
final double uncertaintyZ = avgColliding * 0.08;
|
||||
|
||||
Vector3dm min = new Vector3dm(player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal, -bonusY + player.uncertaintyHandler.yNegativeUncertainty, player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal);
|
||||
Vector3dm max = new Vector3dm(player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal, bonusY + player.uncertaintyHandler.yPositiveUncertainty, player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal);
|
||||
final double minUncertaintyX = player.uncertaintyHandler.xNegativeUncertainty - additionHorizontal;
|
||||
final double minUncertaintyY = player.uncertaintyHandler.yNegativeUncertainty - bonusY;
|
||||
final double minUncertaintyZ = player.uncertaintyHandler.zNegativeUncertainty - additionHorizontal;
|
||||
|
||||
Vector3dm minVector = vector.vector.clone().add(min.subtract(uncertainty));
|
||||
Vector3dm maxVector = vector.vector.clone().add(max.add(uncertainty));
|
||||
final double maxUncertaintyX = player.uncertaintyHandler.xPositiveUncertainty + additionHorizontal;
|
||||
final double maxUncertaintyY = player.uncertaintyHandler.yPositiveUncertainty + bonusY;
|
||||
final double maxUncertaintyZ = player.uncertaintyHandler.zPositiveUncertainty + additionHorizontal;
|
||||
|
||||
double minVecX = vector.vectorX + (minUncertaintyX - uncertaintyX);
|
||||
double minVecY = vector.vectorY + (minUncertaintyY - uncertaintyY);
|
||||
double minVecZ = vector.vectorZ + (minUncertaintyZ - uncertaintyZ);
|
||||
|
||||
double maxVecX = vector.vectorX + (maxUncertaintyX + uncertaintyX);
|
||||
double maxVecY = vector.vectorY + (maxUncertaintyY + uncertaintyY);
|
||||
double maxVecZ = vector.vectorZ + (maxUncertaintyZ + uncertaintyZ);
|
||||
|
||||
// Handle the player landing within 0.03 movement, which resets Y velocity
|
||||
if (player.uncertaintyHandler.onGroundUncertain && vector.vector.getY() < 0) {
|
||||
maxVector.setY(0);
|
||||
if (player.uncertaintyHandler.onGroundUncertain && vector.vectorY < 0) {
|
||||
maxVecY = 0;
|
||||
}
|
||||
|
||||
// Handles stuff like missing idle packet causing gravity to be missed (plus 0.03 of course)
|
||||
double gravityOffset = player.pointThreeEstimator.getAdditionalVerticalUncertainty(vector);
|
||||
if (gravityOffset > 0) {
|
||||
maxVector.setY(maxVector.getY() + gravityOffset);
|
||||
maxVecY += gravityOffset;
|
||||
} else {
|
||||
minVector.setY(minVector.getY() + gravityOffset);
|
||||
minVecY += gravityOffset;
|
||||
}
|
||||
|
||||
// Handle vertical fluid pushing within 0.03
|
||||
double verticalFluid = player.pointThreeEstimator.getVerticalFluidPushingUncertainty(vector);
|
||||
minVector.setY(minVector.getY() - verticalFluid);
|
||||
minVecY -= verticalFluid;
|
||||
|
||||
// Handle vertical bubble column stupidity within 0.03
|
||||
double bubbleFluid = player.pointThreeEstimator.getVerticalBubbleUncertainty(vector);
|
||||
maxVector.setY(maxVector.getY() + bubbleFluid);
|
||||
minVector.setY(minVector.getY() - bubbleFluid);
|
||||
maxVecY += bubbleFluid;
|
||||
minVecY -= bubbleFluid;
|
||||
|
||||
// We can't simulate the player's Y velocity, unknown number of ticks with a gravity change
|
||||
// Feel free to simulate all 104857600000000000000000000 possibilities!
|
||||
if (!player.pointThreeEstimator.canPredictNextVerticalMovement()) {
|
||||
minVector.setY(minVector.getY() - player.compensatedEntities.self.getAttributeValue(Attributes.GRAVITY));
|
||||
minVecY -= player.compensatedEntities.self.getAttributeValue(Attributes.GRAVITY);
|
||||
}
|
||||
|
||||
// Hidden slime block bounces by missing idle tick and 0.03
|
||||
if (player.actualMovement.getY() >= 0 && player.uncertaintyHandler.influencedByBouncyBlock()) {
|
||||
if (player.uncertaintyHandler.thisTickSlimeBlockUncertainty != 0 && !vector.isJump()) { // jumping overrides slime block
|
||||
if (player.uncertaintyHandler.thisTickSlimeBlockUncertainty > maxVector.getY()) {
|
||||
maxVector.setY(player.uncertaintyHandler.thisTickSlimeBlockUncertainty);
|
||||
if (player.uncertaintyHandler.thisTickSlimeBlockUncertainty > maxVecY) {
|
||||
maxVecY = player.uncertaintyHandler.thisTickSlimeBlockUncertainty;
|
||||
}
|
||||
if (minVector.getY() > 0) minVector.setY(0);
|
||||
if (minVecY > 0) minVecY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (vector.isZeroPointZeroThree() && vector.isSwimHop()) {
|
||||
minVector.setY(minVector.getY() - 0.06); // Fluid pushing downwards hidden by 0.03
|
||||
minVecY -= 0.06; // Fluid pushing downwards hidden by 0.03
|
||||
}
|
||||
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(minVector, maxVector);
|
||||
SimpleCollisionBox box = new SimpleCollisionBox(minVecX, minVecY, minVecZ, maxVecX, maxVecY, maxVecZ);
|
||||
box.sort();
|
||||
|
||||
// https://github.com/MWHunter/Grim/issues/398
|
||||
// Thank mojang for removing the idle packet resulting in this hacky mess
|
||||
|
||||
double levitation = player.pointThreeEstimator.positiveLevitation(maxVector.getY());
|
||||
double levitation = player.pointThreeEstimator.positiveLevitation(maxVecY);
|
||||
box.combineToMinimum(box.minX, levitation, box.minZ);
|
||||
levitation = player.pointThreeEstimator.positiveLevitation(minVector.getY());
|
||||
levitation = player.pointThreeEstimator.positiveLevitation(minVecY);
|
||||
box.combineToMinimum(box.minX, levitation, box.minZ);
|
||||
levitation = player.pointThreeEstimator.negativeLevitation(maxVector.getY());
|
||||
levitation = player.pointThreeEstimator.negativeLevitation(maxVecY);
|
||||
box.combineToMinimum(box.minX, levitation, box.minZ);
|
||||
levitation = player.pointThreeEstimator.negativeLevitation(minVector.getY());
|
||||
levitation = player.pointThreeEstimator.negativeLevitation(minVecY);
|
||||
box.combineToMinimum(box.minX, levitation, box.minZ);
|
||||
|
||||
|
||||
@@ -656,12 +706,12 @@ public class PredictionEngine {
|
||||
box.maxZ += sneaking.getSneakingPotentialHiddenVelocity().maxZ;
|
||||
|
||||
if (player.uncertaintyHandler.fireworksBox != null) {
|
||||
double minXdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vector.getX());
|
||||
double minYdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vector.getY());
|
||||
double minZdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vector.getZ());
|
||||
double maxXdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxX - originalVec.vector.getX());
|
||||
double maxYdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxY - originalVec.vector.getY());
|
||||
double maxZdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxZ - originalVec.vector.getZ());
|
||||
double minXdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minX - originalVec.vectorX);
|
||||
double minYdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minY - originalVec.vectorY);
|
||||
double minZdiff = Math.min(0, player.uncertaintyHandler.fireworksBox.minZ - originalVec.vectorZ);
|
||||
double maxXdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxX - originalVec.vectorX);
|
||||
double maxYdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxY - originalVec.vectorY);
|
||||
double maxZdiff = Math.max(0, player.uncertaintyHandler.fireworksBox.maxZ - originalVec.vectorZ);
|
||||
|
||||
box.expandMin(minXdiff, minYdiff, minZdiff);
|
||||
box.expandMax(maxXdiff, maxYdiff, maxZdiff);
|
||||
@@ -714,7 +764,7 @@ public class PredictionEngine {
|
||||
//
|
||||
// Or the player is switching in and out of controlling a vehicle, in which friction messes it up
|
||||
//
|
||||
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && vector.vector.getY() > 0 && vector.isZeroPointZeroThree() && !Collisions.isEmpty(player, GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, vector.vector.getY() + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
|
||||
if (player.uncertaintyHandler.lastVehicleSwitch.hasOccurredSince(0) || player.uncertaintyHandler.lastHardCollidingLerpingEntity.hasOccurredSince(3) || (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_13) && vector.vectorY > 0 && vector.isZeroPointZeroThree() && !Collisions.isEmpty(player, GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.lastX, vector.vectorY + player.lastY + 0.6, player.lastZ, 0.6f, 1.26f)))) {
|
||||
box.expandToAbsoluteCoordinates(0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -740,22 +790,26 @@ public class PredictionEngine {
|
||||
box.expand(0.001); // Ignore 1e-3 offsets as we don't know starting vel
|
||||
}
|
||||
|
||||
minVector = box.min();
|
||||
maxVector = box.max();
|
||||
minVecX = box.minX;
|
||||
minVecY = box.minY;
|
||||
minVecZ = box.minZ;
|
||||
maxVecX = box.maxX;
|
||||
maxVecY = box.maxY;
|
||||
maxVecZ = box.maxZ;
|
||||
|
||||
if (pistonX != 0) {
|
||||
minVector.setX(Math.min(minVector.getX() - pistonX, pistonX));
|
||||
maxVector.setX(Math.max(maxVector.getX() + pistonX, pistonX));
|
||||
minVecX = Math.min(minVecX - pistonX, pistonX);
|
||||
maxVecX = Math.max(maxVecX + pistonX, pistonX);
|
||||
}
|
||||
if (pistonY != 0) {
|
||||
minVector.setY(Math.min(minVector.getY() - pistonY, pistonY));
|
||||
maxVector.setY(Math.max(maxVector.getY() + pistonY, pistonY));
|
||||
minVecY = Math.min(minVecY - pistonY, pistonY);
|
||||
maxVecY = Math.max(maxVecY + pistonY, pistonY);
|
||||
}
|
||||
if (pistonZ != 0) {
|
||||
minVector.setZ(Math.min(minVector.getZ() - pistonZ, pistonZ));
|
||||
maxVector.setZ(Math.max(maxVector.getZ() + pistonZ, pistonZ));
|
||||
minVecZ = Math.min(minVecZ - pistonZ, pistonZ);
|
||||
maxVecZ = Math.max(maxVecZ + pistonZ, pistonZ);
|
||||
}
|
||||
return VectorUtils.cutBoxToVector(targetVec, minVector, maxVector);
|
||||
return VectorUtils.cutBoxToVector(targetVec, minVecX, minVecY, minVecZ, maxVecX, maxVecY, maxVecZ);
|
||||
}
|
||||
|
||||
public void endOfTick(GrimPlayer player, double d) {
|
||||
@@ -815,15 +869,20 @@ public class PredictionEngine {
|
||||
for (int applyStuckSpeed = 1; applyStuckSpeed >= 0; applyStuckSpeed--) {
|
||||
if (applyStuckSpeed == 0 && player.isForceStuckSpeed()) break;
|
||||
|
||||
Vector3dm input = transformInputsToVector(player, new Vector3dm(strafe, 0, forward));
|
||||
VectorData result = new VectorData(possibleLastTickOutput.vector.clone()
|
||||
.add(getMovementResultFromInput(player, input, speed, player.yaw)),
|
||||
Vec2d input = transformInputsToVector(player, strafe, forward);
|
||||
Vector3dm movementResultFromInput = getMovementResultFromInput(player, input.x(), 0, input.z(), speed, player.yaw);
|
||||
VectorData result = new VectorData(possibleLastTickOutput.vectorX + movementResultFromInput.getX(),
|
||||
possibleLastTickOutput.vectorY + movementResultFromInput.getY(),
|
||||
possibleLastTickOutput.vectorZ + movementResultFromInput.getZ(),
|
||||
possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
result.input = input;
|
||||
if (applyStuckSpeed != 0) {
|
||||
result = result.returnNewModified(result.vector.clone().multiply(player.stuckSpeedMultiplier), VectorData.VectorType.StuckMultiplier);
|
||||
result = result.returnNewModified(result.vectorX * player.stuckSpeedMultiplier.getX(),
|
||||
result.vectorY * player.stuckSpeedMultiplier.getY(),
|
||||
result.vectorZ * player.stuckSpeedMultiplier.getZ(),
|
||||
VectorData.VectorType.StuckMultiplier);
|
||||
}
|
||||
result = result.returnNewModified(handleOnClimbable(result.vector.clone(), player), VectorData.VectorType.Climbable);
|
||||
result = result.returnNewModified(handleOnClimbable(result.vectorX, result.vectorY, result.vectorZ, player), VectorData.VectorType.Climbable);
|
||||
// Signal that we need to flip sneaking bounding box
|
||||
if (loopUsingItem == 1)
|
||||
result = result.returnNewModified(VectorData.VectorType.Flip_Use_Item);
|
||||
@@ -889,25 +948,23 @@ public class PredictionEngine {
|
||||
|
||||
// This is just the vanilla equation, which accepts invalid inputs greater than 1
|
||||
// We need it because of collision support when a player is using speed
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, Vector3dm inputVector, float f, float f2) {
|
||||
float f2InRadians = GrimMath.radians(f2);
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, double x, double y, double z, float speed, float yaw) {
|
||||
float f2InRadians = GrimMath.radians(yaw);
|
||||
float f3 = player.trigHandler.sin(f2InRadians);
|
||||
float f4 = player.trigHandler.cos(f2InRadians);
|
||||
|
||||
double xResult = inputVector.getX() * f4 - inputVector.getZ() * f3;
|
||||
double zResult = inputVector.getZ() * f4 + inputVector.getX() * f3;
|
||||
double xResult = x * f4 - z * f3;
|
||||
double zResult = z * f4 + x * f3;
|
||||
|
||||
return new Vector3dm(xResult * f, 0, zResult * f);
|
||||
return new Vector3dm(xResult * speed, 0, zResult * speed);
|
||||
}
|
||||
|
||||
public Vector3dm handleOnClimbable(Vector3dm vector, GrimPlayer player) {
|
||||
return vector;
|
||||
public Vector3dm handleOnClimbable(double x, double y, double z, GrimPlayer player) {
|
||||
return new Vector3dm(x, y, z);
|
||||
}
|
||||
|
||||
public void doJump(GrimPlayer player, Vector3dm vector) {
|
||||
if (!player.lastOnGround || player.onGround)
|
||||
return;
|
||||
|
||||
JumpPower.jumpFromGround(player, vector);
|
||||
public void doJump(GrimPlayer player, double[] vectorArr) {
|
||||
if (!player.lastOnGround || player.onGround) return;
|
||||
JumpPower.jumpFromGround(player, vectorArr);
|
||||
}
|
||||
}
|
||||
+31
-17
@@ -3,6 +3,7 @@ package ac.grim.grimac.predictionengine.predictions;
|
||||
import ac.grim.grimac.player.GrimPlayer;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vec2d;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.ReachUtils;
|
||||
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
|
||||
@@ -13,11 +14,13 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PredictionEngineElytra extends PredictionEngine {
|
||||
public static Vector3dm getElytraMovement(GrimPlayer player, Vector3dm vector, Vector3dm lookVector) {
|
||||
public static void applyElytraMovementInPlace(GrimPlayer player,
|
||||
double[] vectorArr,
|
||||
double lookX, double lookY, double lookZ) {
|
||||
float pitchRadians = GrimMath.radians(player.pitch);
|
||||
double horizontalSqrt = Math.sqrt(lookVector.getX() * lookVector.getX() + lookVector.getZ() * lookVector.getZ());
|
||||
double horizontalLength = vector.clone().setY(0).length();
|
||||
double length = lookVector.length();
|
||||
double horizontalSqrt = Math.sqrt(lookX * lookX + lookZ * lookZ);
|
||||
double horizontalLength = GrimMath.length(vectorArr[0], 0, vectorArr[2]);
|
||||
double length = GrimMath.length(lookX, lookY, lookZ);
|
||||
|
||||
// Mojang changed from using their math to using regular java math in 1.18.2 elytra movement
|
||||
double vertCosRotation = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_18_2) ? Math.cos(pitchRadians) : player.trigHandler.cos(pitchRadians);
|
||||
@@ -31,27 +34,30 @@ public class PredictionEngineElytra extends PredictionEngine {
|
||||
recalculatedGravity = player.getClientVersion().isOlderThan(ClientVersion.V_1_20_5) ? 0.01 : Math.min(recalculatedGravity, 0.01);
|
||||
}
|
||||
|
||||
vector.add(new Vector3dm(0.0D, recalculatedGravity * (-1.0D + vertCosRotation * 0.75D), 0.0D));
|
||||
vectorArr[1] += recalculatedGravity * (-1.0D + vertCosRotation * 0.75D);
|
||||
double d5;
|
||||
|
||||
// Handle slowing the player down when falling
|
||||
if (vector.getY() < 0.0D && horizontalSqrt > 0.0D) {
|
||||
d5 = vector.getY() * -0.1D * vertCosRotation;
|
||||
vector.add(new Vector3dm(lookVector.getX() * d5 / horizontalSqrt, d5, lookVector.getZ() * d5 / horizontalSqrt));
|
||||
if (vectorArr[1] < 0.0D && horizontalSqrt > 0.0D) {
|
||||
d5 = vectorArr[1] * -0.1D * vertCosRotation;
|
||||
vectorArr[0] += lookX * d5 / horizontalSqrt;
|
||||
vectorArr[1] += d5;
|
||||
vectorArr[2] += lookZ * d5 / horizontalSqrt;
|
||||
}
|
||||
|
||||
// Handle accelerating the player when they are looking down
|
||||
if (pitchRadians < 0.0F && horizontalSqrt > 0.0D) {
|
||||
d5 = horizontalLength * (double) (-player.trigHandler.sin(pitchRadians)) * 0.04D;
|
||||
vector.add(new Vector3dm(-lookVector.getX() * d5 / horizontalSqrt, d5 * 3.2D, -lookVector.getZ() * d5 / horizontalSqrt));
|
||||
vectorArr[0] += -lookX * d5 / horizontalSqrt;
|
||||
vectorArr[1] += d5 * 3.2D;
|
||||
vectorArr[2] += -lookZ * d5 / horizontalSqrt;
|
||||
}
|
||||
|
||||
// Handle accelerating the player sideways
|
||||
if (horizontalSqrt > 0) {
|
||||
vector.add(new Vector3dm((lookVector.getX() / horizontalSqrt * horizontalLength - vector.getX()) * 0.1D, 0.0D, (lookVector.getZ() / horizontalSqrt * horizontalLength - vector.getZ()) * 0.1D));
|
||||
vectorArr[0] += (lookX / horizontalSqrt * horizontalLength - vectorArr[0]) * 0.1D;
|
||||
vectorArr[2] += (lookZ / horizontalSqrt * horizontalLength - vectorArr[2]) * 0.1D;
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
// Inputs have no effect on movement
|
||||
@@ -65,11 +71,19 @@ public class PredictionEngineElytra extends PredictionEngine {
|
||||
for (int applyStuckSpeed = 1; applyStuckSpeed >= 0; applyStuckSpeed--) {
|
||||
if (applyStuckSpeed == 0 && player.isForceStuckSpeed()) break;
|
||||
for (VectorData data : possibleVectors) {
|
||||
Vector3dm elytraResult = getElytraMovement(player, data.vector.clone(), currentLook);
|
||||
if (applyStuckSpeed != 0) elytraResult.multiply(player.stuckSpeedMultiplier);
|
||||
elytraResult.multiply(new Vector3dm(0.99F, 0.98F, 0.99F));
|
||||
VectorData modified = data.returnNewModified(elytraResult, VectorData.VectorType.InputResult);
|
||||
modified.input = new Vector3dm(0, 0, 0);
|
||||
double[] vector = {data.vectorX, data.vectorY, data.vectorZ};
|
||||
applyElytraMovementInPlace(player, vector, currentLook.getX(), currentLook.getY(), currentLook.getZ());
|
||||
if (applyStuckSpeed != 0) {
|
||||
vector[0] *= player.stuckSpeedMultiplier.getX();
|
||||
vector[1] *= player.stuckSpeedMultiplier.getY();
|
||||
vector[2] *= player.stuckSpeedMultiplier.getZ();
|
||||
}
|
||||
vector[0] *= 0.99F;
|
||||
vector[1] *= 0.98F;
|
||||
vector[2] *= 0.99f;
|
||||
|
||||
VectorData modified = data.returnNewModified(vector[0], vector[1], vector[2], VectorData.VectorType.InputResult);
|
||||
modified.input = new Vec2d(0, 0);
|
||||
results.add(modified);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-7
@@ -11,18 +11,23 @@ import java.util.Set;
|
||||
public class PredictionEngineLava extends PredictionEngine {
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
final VectorData[] vectorsToProcess = existingVelocities.toArray(new VectorData[0]);
|
||||
for (VectorData vector : vectorsToProcess) {
|
||||
final double startX = vector.vectorX;
|
||||
final double startY = vector.vectorY;
|
||||
final double startZ = vector.vectorZ;
|
||||
|
||||
if (player.couldSkipTick && vector.isZeroPointZeroThree()) {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), startY, startY + 0.05f);
|
||||
existingVelocities.add(new VectorData(startX, extraVelFromVertTickSkipUpwards, startZ, vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(startX, startY + 0.04f, startZ, vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingLava && player.lastOnGround && !player.onGround) {
|
||||
Vector3dm withJump = vector.vector.clone();
|
||||
super.doJump(player, withJump);
|
||||
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
|
||||
double[] jumpValues = {startX, startY, startZ};
|
||||
super.doJump(player, jumpValues);
|
||||
existingVelocities.add(new VectorData(jumpValues[0], jumpValues[1], jumpValues[2], vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-25
@@ -19,20 +19,24 @@ import java.util.Set;
|
||||
|
||||
public class PredictionEngineNormal extends PredictionEngine {
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector3dm vector) {
|
||||
double adjustedY = vector.getY();
|
||||
public static double staticVectorEndOfTickY(GrimPlayer player, double y) {
|
||||
double adjustedY = y;
|
||||
final OptionalInt levitation = player.compensatedEntities.getPotionLevelForPlayer(PotionTypes.LEVITATION);
|
||||
if (levitation.isPresent()) {
|
||||
adjustedY += (0.05 * (levitation.getAsInt() + 1) - vector.getY()) * 0.2;
|
||||
adjustedY += (0.05 * (levitation.getAsInt() + 1) - y) * 0.2;
|
||||
// Reset fall distance with levitation
|
||||
player.fallDistance = 0;
|
||||
} else if (player.hasGravity) {
|
||||
adjustedY -= player.gravity;
|
||||
}
|
||||
|
||||
vector.setX(vector.getX() * player.friction);
|
||||
vector.setY(adjustedY * 0.98F);
|
||||
vector.setZ(vector.getZ() * player.friction);
|
||||
return adjustedY * 0.98F;
|
||||
}
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, VectorData vectorData) {
|
||||
vectorData.vectorX = vectorData.vectorX * player.friction;
|
||||
vectorData.vectorY = staticVectorEndOfTickY(player, vectorData.vectorY);
|
||||
vectorData.vectorZ = vectorData.vectorZ * player.friction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,8 +45,9 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
Vector3dm jump = vector.vector.clone();
|
||||
final VectorData[] vectorsToProcess = existingVelocities.toArray(new VectorData[0]);
|
||||
for (VectorData vector : vectorsToProcess) {
|
||||
double[] jumpValues = {vector.vectorX, vector.vectorY, vector.vectorZ};
|
||||
|
||||
if (!player.isFlying) {
|
||||
// Negative jump boost does not allow the player to leave the ground
|
||||
@@ -54,17 +59,17 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
if (((jumpBoost.isEmpty() || jumpBoost.getAsInt() >= 0) && player.onGround) || !player.lastOnGround)
|
||||
return;
|
||||
|
||||
JumpPower.jumpFromGround(player, jump);
|
||||
JumpPower.jumpFromGround(player, jumpValues);
|
||||
} else {
|
||||
jump.add(new Vector3dm(0, player.flySpeed * 3, 0));
|
||||
jumpValues[1] = player.flySpeed * 3;
|
||||
if (!player.wasFlying) {
|
||||
Vector3dm edgeCaseJump = jump.clone();
|
||||
JumpPower.jumpFromGround(player, edgeCaseJump);
|
||||
existingVelocities.add(vector.returnNewModified(edgeCaseJump, VectorData.VectorType.Jump));
|
||||
double[] edgeCaseJumpValues = {jumpValues[0], jumpValues[1], jumpValues[2]};
|
||||
JumpPower.jumpFromGround(player, edgeCaseJumpValues);
|
||||
existingVelocities.add(vector.returnNewModified(edgeCaseJumpValues[0], edgeCaseJumpValues[1], edgeCaseJumpValues[2], VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
|
||||
existingVelocities.add(vector.returnNewModified(jump, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(vector.returnNewModified(jumpValues[0], jumpValues[1], jumpValues[2], VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,32 +91,30 @@ public class PredictionEngineNormal extends PredictionEngine {
|
||||
if (player.lastWasClimbing == 0 && (player.pointThreeEstimator.isNearClimbable() || player.isClimbing) && (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14)
|
||||
|| !Collisions.isEmpty(player, player.boundingBox.copy().expand(
|
||||
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5))) || walkingOnPowderSnow) {
|
||||
Vector3dm ladderVelocity = player.clientVelocity.clone().setY(0.2);
|
||||
staticVectorEndOfTick(player, ladderVelocity);
|
||||
player.lastWasClimbing = ladderVelocity.getY();
|
||||
player.lastWasClimbing = staticVectorEndOfTickY(player, 0.2);
|
||||
}
|
||||
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
staticVectorEndOfTick(player, vector.vector);
|
||||
staticVectorEndOfTick(player, vector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3dm handleOnClimbable(Vector3dm vector, GrimPlayer player) {
|
||||
public Vector3dm handleOnClimbable(double x, double y, double z, GrimPlayer player) {
|
||||
if (player.isClimbing) {
|
||||
// Reset fall distance when climbing
|
||||
player.fallDistance = 0;
|
||||
|
||||
vector.setX(GrimMath.clamp(vector.getX(), -0.15F, 0.15F));
|
||||
vector.setZ(GrimMath.clamp(vector.getZ(), -0.15F, 0.15F));
|
||||
vector.setY(Math.max(vector.getY(), -0.15F));
|
||||
x = GrimMath.clamp(x, -0.15F, 0.15F);
|
||||
z = GrimMath.clamp(z, -0.15F, 0.15F);
|
||||
y = Math.max(y, -0.15F);
|
||||
|
||||
// Yes, this uses shifting not crouching
|
||||
if (vector.getY() < 0.0 && !(player.compensatedWorld.getBlockType(player.lastX, player.lastY, player.lastZ) == StateTypes.SCAFFOLDING) && player.isSneaking && !player.isFlying) {
|
||||
vector.setY(0.0);
|
||||
if (y < 0.0 && !(player.compensatedWorld.getBlockType(player.lastX, player.lastY, player.lastZ) == StateTypes.SCAFFOLDING) && player.isSneaking && !player.isFlying) {
|
||||
y = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
return vector;
|
||||
return new Vector3dm(x, y, z);
|
||||
}
|
||||
}
|
||||
+27
-16
@@ -19,12 +19,19 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
private double playerGravity;
|
||||
private float swimmingFriction;
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, Vector3dm vector, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vector.multiply(new Vector3dm(swimmingFriction, 0.8F, swimmingFriction));
|
||||
Vector3dm fluidVector = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, vector);
|
||||
vector.setX(fluidVector.getX());
|
||||
vector.setY(fluidVector.getY());
|
||||
vector.setZ(fluidVector.getZ());
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, double[] vector, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vector[0] *= swimmingFriction;
|
||||
vector[1] *= 0.8F;
|
||||
vector[2] *= swimmingFriction;
|
||||
double y = FluidFallingAdjustedMovement.getAdjustedY(player, playerGravity, isFalling, vector[1]);
|
||||
vector[1] = y;
|
||||
}
|
||||
|
||||
public static void staticVectorEndOfTick(GrimPlayer player, VectorData vectorData, float swimmingFriction, double playerGravity, boolean isFalling) {
|
||||
vectorData.vectorX *= swimmingFriction;
|
||||
vectorData.vectorY *= 0.8F;
|
||||
vectorData.vectorZ *= swimmingFriction;
|
||||
vectorData.vectorY = FluidFallingAdjustedMovement.getAdjustedY(player, playerGravity, isFalling, vectorData.vectorY);
|
||||
}
|
||||
|
||||
public static Set<VectorData> transformSwimmingVectors(GrimPlayer player, Set<VectorData> base) {
|
||||
@@ -45,7 +52,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
double scalar = lookYAmount < -0.2 ? 0.085 : 0.06;
|
||||
|
||||
// The player can always press jump and activate this
|
||||
swimmingVelocities.add(vector.returnNewModified(new Vector3dm(vector.vector.getX(), vector.vector.getY() + ((lookYAmount - vector.vector.getY()) * scalar), vector.vector.getZ()), VectorData.VectorType.SwimmingSpace));
|
||||
swimmingVelocities.add(vector.returnNewModified(new Vector3dm(vector.vectorX, vector.vectorY + ((lookYAmount - vector.vectorY) * scalar), vector.vectorZ), VectorData.VectorType.SwimmingSpace));
|
||||
|
||||
// This scenario will occur if the player does not press jump and the other conditions are met
|
||||
// Theoretically we should check this BEFORE allowing no look, but there isn't a cheat that takes advantage of this yet
|
||||
@@ -54,7 +61,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
// If you feel adventurous, re-add the following line to eliminate this unfair disadvantage
|
||||
|
||||
//if (d > 0.0 && player.world.getFluidLevelAt(player.lastX, player.lastY + 1.0 - 0.1, player.lastZ) == 0) {
|
||||
swimmingVelocities.add(vector.returnNewModified(vector.vector, VectorData.VectorType.SurfaceSwimming));
|
||||
swimmingVelocities.add(vector.returnNewModified(vector.vectorX, vector.vectorY, vector.vectorZ, VectorData.VectorType.SurfaceSwimming));
|
||||
|
||||
}
|
||||
return swimmingVelocities;
|
||||
@@ -72,17 +79,21 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
final double startX = vector.vectorX;
|
||||
final double startY = vector.vectorY;
|
||||
final double startZ = vector.vectorZ;
|
||||
|
||||
if (player.couldSkipTick && vector.isZeroPointZeroThree()) {
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), vector.vector.clone().getY(), vector.vector.clone().getY() + 0.05f);
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().setY(extraVelFromVertTickSkipUpwards), vector, VectorData.VectorType.Jump));
|
||||
double extraVelFromVertTickSkipUpwards = GrimMath.clamp(player.actualMovement.getY(), startY, startY + 0.05f);
|
||||
existingVelocities.add(new VectorData(startX, extraVelFromVertTickSkipUpwards, startZ, vector, VectorData.VectorType.Jump));
|
||||
} else {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(startX, startY + 0.04f, startZ, vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
|
||||
if (player.slightlyTouchingWater && player.lastOnGround && !player.onGround) {
|
||||
Vector3dm withJump = vector.vector.clone();
|
||||
super.doJump(player, withJump);
|
||||
existingVelocities.add(new VectorData(withJump, vector, VectorData.VectorType.Jump));
|
||||
double[] jumpValues = {startX, startY, startZ};
|
||||
super.doJump(player, jumpValues);
|
||||
existingVelocities.add(new VectorData(jumpValues[0], jumpValues[1], jumpValues[2], vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +103,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
super.endOfTick(player, playerGravity);
|
||||
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
staticVectorEndOfTick(player, vector.vector, swimmingFriction, playerGravity, isFalling);
|
||||
staticVectorEndOfTick(player, vector, swimmingFriction, playerGravity, isFalling);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +112,7 @@ public class PredictionEngineWater extends PredictionEngine {
|
||||
// "hacky" climbing where player enters ladder within 0.03 movement (WHY DOES 0.03 EXIST???)
|
||||
if (player.lastWasClimbing == 0 && player.pointThreeEstimator.isNearClimbable() && (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) || !Collisions.isEmpty(player, player.boundingBox.copy().expand(
|
||||
player.clientVelocity.getX(), 0, player.clientVelocity.getZ()).expand(0.5, -SimpleCollisionBox.COLLISION_EPSILON, 0.5)))) {
|
||||
player.lastWasClimbing = FluidFallingAdjustedMovement.getFluidFallingAdjustedMovement(player, playerGravity, isFalling, player.clientVelocity.clone().setY(0.2D * 0.8F)).getY();
|
||||
player.lastWasClimbing = FluidFallingAdjustedMovement.getAdjustedY(player, playerGravity, isFalling, 0.2D * 0.8F);
|
||||
}
|
||||
|
||||
Set<VectorData> baseVelocities = super.fetchPossibleStartTickVectors(player);
|
||||
|
||||
+15
-10
@@ -20,8 +20,8 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
|
||||
// This is just the vanilla equation for legacy water movement
|
||||
@Override
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, Vector3dm inputVector, float f, float f2) {
|
||||
float lengthSquared = (float) inputVector.lengthSquared();
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, double x, double y, double z, float f, float f2) {
|
||||
float lengthSquared = (float) GrimMath.lengthSquared(x, y, z);
|
||||
|
||||
if (lengthSquared >= 1.0E-4F) {
|
||||
lengthSquared = (float) Math.sqrt(lengthSquared);
|
||||
@@ -31,13 +31,15 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
}
|
||||
|
||||
lengthSquared = swimmingSpeed / lengthSquared;
|
||||
inputVector.multiply(lengthSquared);
|
||||
x *= lengthSquared;
|
||||
y *= lengthSquared;
|
||||
z *= lengthSquared;
|
||||
float yawRadians = GrimMath.radians(player.yaw);
|
||||
float sinResult = player.trigHandler.sin(yawRadians);
|
||||
float cosResult = player.trigHandler.cos(yawRadians);
|
||||
|
||||
return new Vector3dm(inputVector.getX() * cosResult - inputVector.getZ() * sinResult,
|
||||
inputVector.getY(), inputVector.getZ() * cosResult + inputVector.getX() * sinResult);
|
||||
return new Vector3dm(x * cosResult - z * sinResult,
|
||||
y, z * cosResult + x * sinResult);
|
||||
}
|
||||
|
||||
return new Vector3dm();
|
||||
@@ -46,11 +48,12 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
|
||||
@Override
|
||||
public void addJumpsToPossibilities(GrimPlayer player, Set<VectorData> existingVelocities) {
|
||||
for (VectorData vector : new HashSet<>(existingVelocities)) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.04f, 0)), vector, VectorData.VectorType.Jump));
|
||||
final VectorData[] vectorsToProcess = existingVelocities.toArray(new VectorData[0]);
|
||||
for (VectorData vector : vectorsToProcess) {
|
||||
existingVelocities.add(new VectorData(vector.vectorX, vector.vectorY + 0.04f, vector.vectorZ, vector, VectorData.VectorType.Jump));
|
||||
|
||||
if (player.skippedTickInActualMovement) {
|
||||
existingVelocities.add(new VectorData(vector.vector.clone().add(new Vector3dm(0, 0.02f, 0)), vector, VectorData.VectorType.Jump));
|
||||
existingVelocities.add(new VectorData(vector.vectorX,vector.vectorY + 0.02f, vector.vectorZ, vector, VectorData.VectorType.Jump));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,10 +63,12 @@ public class PredictionEngineWaterLegacy extends PredictionEngine {
|
||||
super.endOfTick(player, playerGravity);
|
||||
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
vector.vector.multiply(new Vector3dm(swimmingFriction, 0.8F, swimmingFriction));
|
||||
vector.vectorX *= swimmingFriction;
|
||||
vector.vectorY *= 0.8F;
|
||||
vector.vectorZ *= swimmingFriction;
|
||||
|
||||
// Gravity
|
||||
vector.vector.setY(vector.vector.getY() - 0.02D);
|
||||
vector.vectorY -= 0.02D;
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-32
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
|
||||
import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.enums.BoatEntityStatus;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vec2d;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockProperties;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
@@ -146,7 +147,7 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
|
||||
for (VectorData data : possibleVectors) {
|
||||
// TODO: is this correct?
|
||||
data.input = new Vector3dm(player.vehicleData.vehicleForward, 0, player.vehicleData.vehicleHorizontal);
|
||||
data.input = new Vec2d(player.vehicleData.vehicleForward, player.vehicleData.vehicleHorizontal);
|
||||
|
||||
for (int applyStuckSpeed = 1; applyStuckSpeed >= 0; applyStuckSpeed--) {
|
||||
if (applyStuckSpeed == 0 && player.isForceStuckSpeed()) break;
|
||||
@@ -155,14 +156,41 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
// so if a player tries to move in both directions, a packet will
|
||||
// show that the player is staying, but the boat will move anyway
|
||||
if (player.vehicleData.vehicleForward == 0) {
|
||||
Vector3dm vector = data.vector.clone();
|
||||
controlBoat(player, vector, true);
|
||||
if (applyStuckSpeed != 0) vector.multiply(player.stuckSpeedMultiplier);
|
||||
vectors.add(data.returnNewModified(vector, VectorData.VectorType.InputResult));
|
||||
float f = 0.035F;
|
||||
double x = data.vectorX + player.trigHandler.sin(GrimMath.radians(-player.yaw)) * f;
|
||||
double y = data.vectorY;
|
||||
double z = data.vectorZ + (double) (player.trigHandler.cos(GrimMath.radians(player.yaw)) * f);
|
||||
if (applyStuckSpeed != 0) {
|
||||
x *= player.stuckSpeedMultiplier.getX();
|
||||
y *= player.stuckSpeedMultiplier.getY();
|
||||
z *= player.stuckSpeedMultiplier.getZ();
|
||||
}
|
||||
vectors.add(data.returnNewModified(x, y, z, VectorData.VectorType.InputResult));
|
||||
}
|
||||
|
||||
controlBoat(player, data.vector, false);
|
||||
if (applyStuckSpeed != 0) data.vector.multiply(player.stuckSpeedMultiplier);
|
||||
float f;
|
||||
if (player.vehicleData.vehicleHorizontal != 0) {
|
||||
f = 0.005F;
|
||||
if (player.vehicleData.vehicleForward > 0.1) {
|
||||
f += 0.04F;
|
||||
} else if (player.vehicleData.vehicleForward < -0.01) {
|
||||
f -= 0.005F;
|
||||
}
|
||||
} else {
|
||||
f = 0.0F;
|
||||
}
|
||||
|
||||
// TODO (Mutation) (Confirm if Correct)
|
||||
// It seems possible for applyStuckSpeed = 0, and 1 to add the same vector to the set twice
|
||||
// since we're mutating data.vectorX, Y, Z which is very likely incorrect but I don't know enough to say for certain
|
||||
data.vectorX = data.vectorX + player.trigHandler.sin(GrimMath.radians(-player.yaw)) * f;
|
||||
data.vectorZ = data.vectorZ + (double) (player.trigHandler.cos(GrimMath.radians(player.yaw)) * f);
|
||||
|
||||
if (applyStuckSpeed != 0) {
|
||||
data.vectorX *= player.stuckSpeedMultiplier.getX();
|
||||
data.vectorY *= player.stuckSpeedMultiplier.getY();
|
||||
data.vectorZ *= player.stuckSpeedMultiplier.getZ();
|
||||
}
|
||||
vectors.add(data);
|
||||
}
|
||||
}
|
||||
@@ -176,7 +204,7 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
addFluidPushingToStartingVectors(player, vectors);
|
||||
|
||||
for (VectorData data : vectors) {
|
||||
floatBoat(player, data.vector);
|
||||
floatBoat(player, data);
|
||||
}
|
||||
|
||||
return vectors;
|
||||
@@ -186,6 +214,10 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
public void endOfTick(GrimPlayer player, double d) {
|
||||
super.endOfTick(player, d);
|
||||
Collisions.handleInsideBlocks(player);
|
||||
|
||||
// yes, this is how mojank does it
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
Collisions.applyEffectsFromBlocks(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,7 +225,7 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void floatBoat(GrimPlayer player, Vector3dm vector) {
|
||||
private void floatBoat(GrimPlayer player, VectorData vector) {
|
||||
double d1 = player.hasGravity ? -0.04f : 0;
|
||||
double d2 = 0.0D;
|
||||
float invFriction = 0.05F;
|
||||
@@ -204,7 +236,7 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
player.lastY = getWaterLevelAbove(player) - 0.5625F + 0.101D;
|
||||
player.boundingBox = GetBoundingBox.getCollisionBoxForPlayer(player, player.lastX, player.lastY, player.lastZ);
|
||||
player.actualMovement = new Vector3dm(player.x - player.lastX, player.y - player.lastY, player.z - player.lastZ);
|
||||
vector.setY(0);
|
||||
vector.vectorY = 0;
|
||||
|
||||
player.vehicleData.lastYd = 0.0D;
|
||||
player.vehicleData.status = BoatEntityStatus.IN_WATER;
|
||||
@@ -225,12 +257,12 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
player.vehicleData.landFriction /= 2.0F;
|
||||
}
|
||||
|
||||
vector.setX(vector.getX() * invFriction);
|
||||
vector.setY(vector.getY() + d1);
|
||||
vector.setZ(vector.getZ() * invFriction);
|
||||
vector.vectorX *= invFriction;
|
||||
vector.vectorY += d1;
|
||||
vector.vectorZ *= invFriction;
|
||||
|
||||
if (d2 > 0.0D) {
|
||||
double yVel = vector.getY();
|
||||
double yVel = vector.vectorY;
|
||||
vector.setY((yVel + d2 * 0.06153846016296973D) * 0.75D);
|
||||
}
|
||||
}
|
||||
@@ -268,22 +300,4 @@ public class PredictionEngineBoat extends PredictionEngine {
|
||||
|
||||
return (float) (l + 1);
|
||||
}
|
||||
|
||||
private void controlBoat(GrimPlayer player, Vector3dm vector, boolean intermediate) {
|
||||
float f = 0.0F;
|
||||
if (player.vehicleData.vehicleHorizontal != 0 && (!intermediate && player.vehicleData.vehicleForward == 0)) {
|
||||
f += 0.005F;
|
||||
}
|
||||
|
||||
//player.boatData.yRot += player.boatData.deltaRotation;
|
||||
if (intermediate || player.vehicleData.vehicleForward > 0.1) {
|
||||
f += 0.04F;
|
||||
}
|
||||
|
||||
if (intermediate || player.vehicleData.vehicleForward < -0.01) {
|
||||
f -= 0.005F;
|
||||
}
|
||||
|
||||
vector.add(new Vector3dm(player.trigHandler.sin(GrimMath.radians(-player.yaw)) * f, 0, (double) (player.trigHandler.cos(GrimMath.radians(player.yaw)) * f)));
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -18,9 +18,9 @@ public class PredictionEngineHappyGhast extends PredictionEngineNormal {
|
||||
@Override
|
||||
public void endOfTick(GrimPlayer player, double delta) {
|
||||
for (VectorData vector : player.getPossibleVelocitiesMinusKnockback()) {
|
||||
vector.vector.setX(vector.vector.getX() * multiplier);
|
||||
vector.vector.setY(vector.vector.getY() * multiplier);
|
||||
vector.vector.setZ(vector.vector.getZ() * multiplier);
|
||||
vector.vectorX *= multiplier;
|
||||
vector.vectorY *= multiplier;
|
||||
vector.vectorZ *= multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,15 @@ public class PredictionEngineHappyGhast extends PredictionEngineNormal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, Vector3dm inputVector, float flyingSpeed, float yRot) {
|
||||
public Vector3dm getMovementResultFromInput(GrimPlayer player, double x, double y, double z, float flyingSpeed, float yRot) {
|
||||
float yRotRadians = GrimMath.radians(yRot);
|
||||
float sin = player.trigHandler.sin(yRotRadians);
|
||||
float cos = player.trigHandler.cos(yRotRadians);
|
||||
|
||||
double xResult = inputVector.getX() * cos - inputVector.getZ() * sin;
|
||||
double zResult = inputVector.getZ() * cos + inputVector.getX() * sin;
|
||||
double xResult = x * cos - z * sin;
|
||||
double zResult = z * cos + x * sin;
|
||||
|
||||
return new Vector3dm(xResult * flyingSpeed, inputVector.getY() * flyingSpeed, zResult * flyingSpeed);
|
||||
return new Vector3dm(xResult * flyingSpeed, y * flyingSpeed, zResult * flyingSpeed);
|
||||
}
|
||||
|
||||
}
|
||||
+18
-12
@@ -7,6 +7,7 @@ import ac.grim.grimac.utils.data.VectorData;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityCamel;
|
||||
import ac.grim.grimac.utils.data.packetentity.PacketEntityHorse;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Vec2d;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.BlockProperties;
|
||||
import ac.grim.grimac.utils.nmsutil.JumpPower;
|
||||
@@ -59,10 +60,12 @@ public final class PredictionEngineRideableUtils {
|
||||
}
|
||||
|
||||
final double multiplier = (double) (22.2222F * player.vehicleData.horseJump) * camel.getAttributeValue(Attributes.MOVEMENT_SPEED) * (double) BlockProperties.getBlockSpeedFactor(player, player.mainSupportingBlockData, new Vector3d(player.lastX, player.lastY, player.lastZ));
|
||||
Vector3dm jumpVelocity = ReachUtils.getLook(player, player.yaw, player.pitch).multiply(new Vector3dm(1.0, 0.0, 1.0)).normalize().multiply(multiplier).add(new Vector3dm(0, (double) (1.4285F * player.vehicleData.horseJump) * jumpYVelocity, 0));
|
||||
Vector3dm jumpVelocity = ReachUtils.getLook(player, player.yaw, player.pitch).multiply(1.0, 0.0, 1.0).normalize().multiply(multiplier).add(0, (double) (1.4285F * player.vehicleData.horseJump) * jumpYVelocity, 0);
|
||||
|
||||
for (VectorData vectorData : possibleVectors) {
|
||||
vectorData.vector.add(jumpVelocity);
|
||||
vectorData.vectorX += jumpVelocity.getX();
|
||||
vectorData.vectorY += jumpVelocity.getY();
|
||||
vectorData.vectorZ += jumpVelocity.getZ();
|
||||
}
|
||||
|
||||
player.vehicleData.horseJumping = true;
|
||||
@@ -102,9 +105,10 @@ public final class PredictionEngineRideableUtils {
|
||||
float f3 = player.trigHandler.cos(yawRadians);
|
||||
|
||||
for (VectorData vectorData : possibleVectors) {
|
||||
vectorData.vector.setY(jumpVelocity);
|
||||
vectorData.vectorY = jumpVelocity;
|
||||
if (forwardInput > 0.0F) {
|
||||
vectorData.vector.add(new Vector3dm(-0.4F * f2 * player.vehicleData.horseJump, 0.0D, 0.4F * f3 * player.vehicleData.horseJump));
|
||||
vectorData.vectorX += -0.4F * f2 * player.vehicleData.horseJump;
|
||||
vectorData.vectorZ += 0.4F * f3 * player.vehicleData.horseJump;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,22 +126,24 @@ public final class PredictionEngineRideableUtils {
|
||||
for (int applyStuckSpeed = 1; applyStuckSpeed >= 0; applyStuckSpeed--) {
|
||||
if (applyStuckSpeed == 0 && player.isForceStuckSpeed()) break;
|
||||
|
||||
VectorData result = new VectorData(possibleLastTickOutput.vector.clone().add(predictionEngine.getMovementResultFromInput(player, movementVector, speed, player.yaw)), possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
result.input = new Vector3dm(player.vehicleData.vehicleForward, 0, player.vehicleData.vehicleHorizontal);
|
||||
Vector3dm vector = result.vector.clone();
|
||||
Vector3dm vector3dm = predictionEngine.getMovementResultFromInput(player, movementVector.getX(), movementVector.getY(), movementVector.getZ(), speed, player.yaw).add(possibleLastTickOutput.vectorX, possibleLastTickOutput.vectorY, possibleLastTickOutput.vectorZ);
|
||||
VectorData result = new VectorData(vector3dm, possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
result.input = new Vec2d(player.vehicleData.vehicleForward, player.vehicleData.vehicleHorizontal);
|
||||
|
||||
Vector3dm vector = new Vector3dm(result.vectorX, result.vectorY, result.vectorZ);
|
||||
if (applyStuckSpeed != 0) vector.multiply(player.stuckSpeedMultiplier);
|
||||
result = result.returnNewModified(vector, VectorData.VectorType.StuckMultiplier);
|
||||
result = result.returnNewModified(new PredictionEngineNormal().handleOnClimbable(result.vector.clone(), player), VectorData.VectorType.Climbable);
|
||||
result = result.returnNewModified(new PredictionEngineNormal().handleOnClimbable(result.vectorX, result.vectorY, result.vectorZ, player), VectorData.VectorType.Climbable);
|
||||
returnVectors.add(result);
|
||||
|
||||
// This is the laziest way to reduce false positives such as horse rearing
|
||||
// No bypasses can ever be derived from this, so why not?
|
||||
result = new VectorData(possibleLastTickOutput.vector.clone(), possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
result.input = new Vector3dm(player.vehicleData.vehicleForward, 0, player.vehicleData.vehicleHorizontal);
|
||||
vector = result.vector.clone();
|
||||
result = new VectorData(possibleLastTickOutput.vectorX, possibleLastTickOutput.vectorY, possibleLastTickOutput.vectorZ, possibleLastTickOutput, VectorData.VectorType.InputResult);
|
||||
result.input = new Vec2d(player.vehicleData.vehicleForward, player.vehicleData.vehicleHorizontal);
|
||||
vector = new Vector3dm(result.vectorX, result.vectorY, result.vectorZ);
|
||||
if (applyStuckSpeed != 0) vector.multiply(player.stuckSpeedMultiplier);
|
||||
result = result.returnNewModified(vector, VectorData.VectorType.StuckMultiplier);
|
||||
result = result.returnNewModified(new PredictionEngineNormal().handleOnClimbable(result.vector.clone(), player), VectorData.VectorType.Climbable);
|
||||
result = result.returnNewModified(new PredictionEngineNormal().handleOnClimbable(result.vectorX, result.vectorY, result.vectorZ, player), VectorData.VectorType.Climbable);
|
||||
returnVectors.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,40 +30,117 @@ public class MessageUtil {
|
||||
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
|
||||
}
|
||||
|
||||
public @NotNull String toUnlabeledString(int x, int y, int z) {
|
||||
return x + ", " + y + ", " + z;
|
||||
}
|
||||
|
||||
public @NotNull String toUnlabeledString(double x, double y, double z) {
|
||||
return x + ", " + y + ", " + z;
|
||||
}
|
||||
|
||||
public @NotNull String toUnlabledString(@Nullable Vector3f vec) {
|
||||
return vec == null ? "null" : vec.x + ", " + vec.y + ", " + vec.z;
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable GrimPlayer player, @NotNull String string, boolean removeFormatting) {
|
||||
@Contract("_, null, _ -> null; _, !null, _ -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable GrimPlayer player, @Nullable String string, boolean removeFormatting) {
|
||||
return replacePlaceholders(player, player == null ? null : player.platformPlayer, string, removeFormatting);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable GrimPlayer player, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable GrimPlayer player, @Nullable String string) {
|
||||
return replacePlaceholders(player, player == null ? null : player.platformPlayer, string, false);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable Sender sender, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable Sender sender, @Nullable String string) {
|
||||
return replacePlaceholders(sender != null ? sender.getPlatformPlayer() : null, string);
|
||||
}
|
||||
|
||||
public @NotNull String replacePlaceholders(@Nullable PlatformPlayer player, @NotNull String string) {
|
||||
@Contract("_, null -> null; _, !null -> !null")
|
||||
public @Nullable String replacePlaceholders(@Nullable PlatformPlayer player, @Nullable String string) {
|
||||
return replacePlaceholders(player == null ? null : GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(player.getUniqueId()), player, string, false);
|
||||
}
|
||||
|
||||
private @NotNull String replacePlaceholders(@Nullable GrimPlayer grimPlayer, @Nullable PlatformPlayer platformPlayer, @NotNull String string, boolean removeFormatting) {
|
||||
for (Map.Entry<String, String> entry : GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements().entrySet()) {
|
||||
string = string.replace(entry.getKey(), entry.getValue());
|
||||
private static final Pattern UNIFIED_PLACEHOLDER_PATTERN = Pattern.compile("%([a-zA-Z0-9_]+)%");
|
||||
|
||||
@Contract("_, _, null, _ -> null; _, _, !null, _ -> !null")
|
||||
private @Nullable String replacePlaceholders(@Nullable GrimPlayer grimPlayer, @Nullable PlatformPlayer platformPlayer, @Nullable String string, boolean removeFormatting) {
|
||||
if (string == null) return null;
|
||||
|
||||
// --- OPTIMIZATION 1: THE FAST PATH ---
|
||||
// If the string contains no '%' characters, it's impossible for it to have placeholders.
|
||||
// indexOf() is a JVM intrinsic and is magnitudes faster than even creating a Matcher.
|
||||
if (string.indexOf('%') == -1) {
|
||||
// Since there are no % signs we can skip calling papi or our own replacement code
|
||||
return string;
|
||||
}
|
||||
|
||||
if (grimPlayer != null) {
|
||||
for (Map.Entry<String, Function<GrimUser, String>> entry : GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements().entrySet()) {
|
||||
String value = entry.getValue().apply(grimPlayer).replace('%', PLACEHOLDER_ESCAPE_CHAR);
|
||||
if (removeFormatting) value = filterDiscordText(value);
|
||||
string = string.replace(entry.getKey(), value);
|
||||
final Matcher matcher = UNIFIED_PLACEHOLDER_PATTERN.matcher(string);
|
||||
|
||||
// If matcher.find() is false, it means '%' existed but not in a valid %...% pattern.
|
||||
// This avoids allocating a StringBuilder unless absolutely necessary.
|
||||
if (!matcher.find()) {
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, string);
|
||||
}
|
||||
|
||||
// Get references to the maps once, outside the loop.
|
||||
final Map<String, String> staticReplacements = GrimAPI.INSTANCE.getExternalAPI().getStaticReplacements();
|
||||
final Map<String, Function<GrimUser, String>> variableReplacements = GrimAPI.INSTANCE.getExternalAPI().getVariableReplacements();
|
||||
final StringBuilder sb = new StringBuilder(string.length() + 32); // Pre-size with a little extra room
|
||||
|
||||
// --- OPTIMIZATION 2: THE UNIFIED SINGLE-PASS LOOP ---
|
||||
// We use a do-while loop because we already performed the first matcher.find().
|
||||
do {
|
||||
// The full placeholder, e.g., "%tps%" or "%prefix%"
|
||||
final String keyWithPercent = matcher.group(0);
|
||||
String value = null;
|
||||
|
||||
// --- OPTIMIZATION 3: UNIFIED LOOKUP ---
|
||||
// We check the static map first. This is a single, O(1) hash map lookup.
|
||||
String staticValue = staticReplacements.get(keyWithPercent);
|
||||
|
||||
if (staticValue != null) {
|
||||
value = staticValue;
|
||||
} else if (grimPlayer != null) {
|
||||
// If it's not a static placeholder, check if it's a dynamic one.
|
||||
// This is a second, O(1) hash map lookup.
|
||||
final Function<GrimUser, String> func = variableReplacements.get(keyWithPercent);
|
||||
if (func != null) {
|
||||
// LAZY EVALUATION: We only call the expensive function (like getTPS)
|
||||
// if we actually found its placeholder in the string.
|
||||
value = func.apply(grimPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, string).replace(PLACEHOLDER_ESCAPE_CHAR, '%');
|
||||
// If we found no replacement, `value` will be null.
|
||||
// In that case, we treat the placeholder as literal text by appending the original key.
|
||||
if (value == null) {
|
||||
value = keyWithPercent;
|
||||
}
|
||||
|
||||
// --- OPTIMIZATION 4: CONDITIONAL FORMATTING ---
|
||||
// The check for `removeFormatting` is inside the loop, but it's a simple boolean
|
||||
// check and the cost is negligible compared to the string operations.
|
||||
if (removeFormatting) {
|
||||
// Note: This assumes `filterDiscordText` is reasonably fast.
|
||||
// If it's slow, there are further micro-optimizations, but this is the right place for it.
|
||||
value = filterDiscordText(value);
|
||||
}
|
||||
|
||||
// `appendReplacement` efficiently appends the text between matches and our replacement value.
|
||||
// `Matcher.quoteReplacement` should handle any '$' or '\' in the replacement value.
|
||||
matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
|
||||
|
||||
} while (matcher.find());
|
||||
|
||||
// Append the final part of the string after the last found placeholder.
|
||||
matcher.appendTail(sb);
|
||||
|
||||
// Create the final string from our builder.
|
||||
String grimReplaced = sb.toString();
|
||||
|
||||
return GrimAPI.INSTANCE.getMessagePlaceHolderManager().replacePlaceholders(platformPlayer, grimReplaced).replace(PLACEHOLDER_ESCAPE_CHAR, '%');
|
||||
}
|
||||
|
||||
public static String filterDiscordText(String message) {
|
||||
|
||||
@@ -14,7 +14,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class BlockBreak {
|
||||
public final Vector3i position;
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
public final BlockFace face;
|
||||
public final int faceId;
|
||||
public final DiggingAction action;
|
||||
@@ -24,9 +26,11 @@ public final class BlockBreak {
|
||||
@Getter
|
||||
private boolean cancelled;
|
||||
|
||||
public BlockBreak(GrimPlayer player, Vector3i position, BlockFace face, int faceId, DiggingAction action, int sequence, WrappedBlockState block) {
|
||||
public BlockBreak(GrimPlayer player, final int x, final int y, final int z, BlockFace face, int faceId, DiggingAction action, int sequence, WrappedBlockState block) {
|
||||
this.player = player;
|
||||
this.position = position;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.face = face;
|
||||
this.faceId = faceId;
|
||||
this.action = action;
|
||||
@@ -39,12 +43,12 @@ public final class BlockBreak {
|
||||
}
|
||||
|
||||
public SimpleCollisionBox getCombinedBox() {
|
||||
CollisionBox placedOn = HitboxData.getBlockHitbox(player, player.inventory.getHeldItem().getType().getPlacedType(), player.getClientVersion(), block, true, position.x, position.y, position.z);
|
||||
CollisionBox placedOn = HitboxData.getBlockHitbox(player, player.inventory.getHeldItem().getType().getPlacedType(), player.getClientVersion(), block, true, x, y, z);
|
||||
|
||||
List<SimpleCollisionBox> boxes = new ArrayList<>();
|
||||
placedOn.downCast(boxes);
|
||||
|
||||
SimpleCollisionBox combined = new SimpleCollisionBox(position.x, position.y, position.z);
|
||||
SimpleCollisionBox combined = new SimpleCollisionBox(x, y, z);
|
||||
for (SimpleCollisionBox box : boxes) {
|
||||
double minX = Math.max(box.minX, combined.minX);
|
||||
double minY = Math.max(box.minY, combined.minY);
|
||||
|
||||
@@ -555,6 +555,7 @@ public class BlockPlace {
|
||||
// 1.8 clients will simply not send the place when it fails, thanks mojang.
|
||||
if (player.getClientVersion().isNewerThan(ClientVersion.V_1_8)) {
|
||||
for (PacketEntity entity : player.compensatedEntities.entityMap.values()) {
|
||||
if (!entity.canHit()) continue;
|
||||
SimpleCollisionBox interpBox = entity.getPossibleCollisionBoxes();
|
||||
|
||||
final double scale = entity.getAttributeValue(Attributes.SCALE);
|
||||
@@ -567,8 +568,10 @@ public class BlockPlace {
|
||||
// This happens due to the lack of an idle packet on 1.9+ clients
|
||||
// On 1.8 clients this should practically never happen
|
||||
if (interpWidth - width > 0.05 || interpHeight - height > 0.05) {
|
||||
Vector3d entityPos = entity.trackedServerPosition.getPos();
|
||||
interpBox = GetBoundingBox.getPacketEntityBoundingBox(player, entityPos.getX(), entityPos.getY(), entityPos.getZ(), entity);
|
||||
final double x = entity.trackedServerPosition.getX();
|
||||
final double y = entity.trackedServerPosition.getX();
|
||||
final double z = entity.trackedServerPosition.getX();
|
||||
interpBox = GetBoundingBox.getPacketEntityBoundingBox(player, x, y, z, entity);
|
||||
}
|
||||
|
||||
if (box.isIntersected(interpBox)) {
|
||||
@@ -591,7 +594,7 @@ public class BlockPlace {
|
||||
|
||||
// Check for waterlogged
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
|
||||
if (state.getInternalData().containsKey(StateValue.WATERLOGGED)) { // waterloggable
|
||||
if (state.hasProperty(StateValue.WATERLOGGED)) { // waterloggable
|
||||
state.setWaterlogged(existingState.getType() == StateTypes.WATER && existingState.getLevel() == 0);
|
||||
}
|
||||
}
|
||||
@@ -640,9 +643,13 @@ public class BlockPlace {
|
||||
Vector3dm look = ReachUtils.getLook(player, player.yaw, player.pitch);
|
||||
|
||||
final double distance = player.compensatedEntities.self.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE) + 3;
|
||||
Vector3dm eyePos = new Vector3dm(player.x, player.y + player.getEyeHeight(), player.z);
|
||||
Vector3dm endReachPos = eyePos.clone().add(new Vector3dm(look.getX() * distance, look.getY() * distance, look.getZ() * distance));
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(box, eyePos, endReachPos).first();
|
||||
final double eyeX = player.x;
|
||||
final double eyeY = player.y + player.getEyeHeight();
|
||||
final double eyeZ = player.z;
|
||||
final double endX = eyeX + look.getX() * distance;
|
||||
final double endY = eyeY + look.getY() * distance;
|
||||
final double endZ = eyeZ + look.getZ() * distance;
|
||||
Vector3dm intercept = ReachUtils.calculateIntercept(box, eyeX, eyeY, eyeZ, endX, endY, endZ).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!)
|
||||
|
||||
@@ -11,7 +11,12 @@ import lombok.Setter;
|
||||
@Getter
|
||||
@Setter
|
||||
public final class PositionUpdate {
|
||||
private final Vector3d from, to;
|
||||
private final double fromX;
|
||||
private final double fromY;
|
||||
private final double fromZ;
|
||||
private final double toX;
|
||||
private final double toY;
|
||||
private final double toZ;
|
||||
private final boolean onGround;
|
||||
private final SetBackData setback;
|
||||
private final TeleportData teleportData;
|
||||
|
||||
+1
-3
@@ -1,5 +1,3 @@
|
||||
package ac.grim.grimac.utils.anticheat.update;
|
||||
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
|
||||
public record VehiclePositionUpdate(Vector3d from, Vector3d to, float xRot, float yRot, boolean isTeleport) {}
|
||||
public record VehiclePositionUpdate(double fromX, double fromY, double fromZ, double toX, double toY, double toZ, float xRot, float yRot, boolean isTeleport) {}
|
||||
@@ -318,7 +318,7 @@ public enum BlockPlaceResult {
|
||||
WrappedBlockState typePlacingOn = place.getDirectionalState(primaryDir.getOppositeFace()); // Block we are placing on
|
||||
|
||||
// Check to see if we can place on the block or there is dripstone on the block that we are placing on also pointing upwards
|
||||
boolean primarySameType = typePlacingOn.getInternalData().containsKey(StateValue.VERTICAL_DIRECTION) && typePlacingOn.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean primarySameType = typePlacingOn.hasProperty(StateValue.VERTICAL_DIRECTION) && typePlacingOn.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean primaryValid = place.isFullFace(primaryDir.getOppositeFace()) || primarySameType;
|
||||
|
||||
// Try to use the opposite direction, just to see if switching directions makes it valid.
|
||||
@@ -326,7 +326,7 @@ public enum BlockPlaceResult {
|
||||
BlockFace secondaryDirection = primaryDir.getOppositeFace(); // See if placing it DOWNWARDS is valid
|
||||
WrappedBlockState secondaryType = place.getDirectionalState(secondaryDirection.getOppositeFace()); // Get the block above us
|
||||
// Check if the dripstone above us is also facing downwards
|
||||
boolean secondarySameType = secondaryType.getInternalData().containsKey(StateValue.VERTICAL_DIRECTION) && secondaryType.getVerticalDirection().name().equals(primaryDir.name());
|
||||
boolean secondarySameType = secondaryType.hasProperty(StateValue.VERTICAL_DIRECTION) && secondaryType.getVerticalDirection().name().equals(primaryDir.name());
|
||||
|
||||
primaryDir = secondaryDirection;
|
||||
// Update block survivability
|
||||
|
||||
@@ -7,15 +7,17 @@ import org.jetbrains.annotations.NotNull;
|
||||
public record BlockModification(
|
||||
WrappedBlockState oldBlockContents,
|
||||
WrappedBlockState newBlockContents,
|
||||
Vector3i location,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
int tick,
|
||||
Cause cause
|
||||
) {
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return String.format(
|
||||
"BlockModification{location=%s, old=%s, new=%s, tick=%d, cause=%s}",
|
||||
location, oldBlockContents, newBlockContents, tick, cause
|
||||
"BlockModification{x=%d, y=%d, z=%d, old=%s, new=%s, tick=%d, cause=%s}",
|
||||
x, y, z, oldBlockContents, newBlockContents, tick, cause
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
new HexCollisionBox(5.0D, 0.0D, 5.0D, 11.0D, 7.0D, 11.0D),
|
||||
new HexCollisionBox(6.0D, 7.0D, 6.0D, 10.0D, 9.0D, 10.0D));
|
||||
|
||||
}, StateTypes.LANTERN, StateTypes.SOUL_LANTERN),
|
||||
}, BlockTags.LANTERNS.getStates().toArray(new StateType[0])),
|
||||
|
||||
|
||||
LECTERN((player, version, data, x, y, z) -> {
|
||||
@@ -516,7 +516,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
}
|
||||
|
||||
return new HexCollisionBox(6.5D, 6.5D, 0.0D, 9.5D, 9.5D, 16.0D);
|
||||
}, StateTypes.CHAIN),
|
||||
}, Materials.getChains().toArray(new StateType[0])),
|
||||
|
||||
CHORUS_PLANT(new DynamicChorusPlant(), StateTypes.CHORUS_PLANT),
|
||||
|
||||
@@ -552,7 +552,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
|
||||
STAIR(new DynamicStair(), BlockTags.STAIRS.getStates().toArray(new StateType[0])),
|
||||
|
||||
CHEST(new DynamicChest(), StateTypes.CHEST, StateTypes.TRAPPED_CHEST),
|
||||
CHEST(new DynamicChest(), Materials.getChests().toArray(new StateType[0])),
|
||||
|
||||
ENDER_CHEST(new SimpleCollisionBox(0.0625F, 0.0F, 0.0625F,
|
||||
0.9375F, 0.875F, 0.9375F, false),
|
||||
@@ -675,7 +675,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
0.625, 0.625, 0.625, false),
|
||||
StateTypes.STRUCTURE_VOID),
|
||||
|
||||
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), StateTypes.END_ROD, StateTypes.LIGHTNING_ROD),
|
||||
END_ROD((player, version, data, x, y, z) -> getEndRod(version, data.getFacing()), Materials.getRods().toArray(new StateType[0])),
|
||||
|
||||
CAULDRON((player, version, data, x, y, z) -> {
|
||||
if (version.isNewerThan(ClientVersion.V_1_13_2)) { // changed in 19w13a, 1.14 Snapshot
|
||||
@@ -813,7 +813,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
}, StateTypes.TRIPWIRE_HOOK),
|
||||
|
||||
TORCH(new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D),
|
||||
StateTypes.TORCH, StateTypes.REDSTONE_TORCH),
|
||||
StateTypes.TORCH, StateTypes.REDSTONE_TORCH, StateTypes.COPPER_TORCH),
|
||||
|
||||
WALL_TORCH((player, version, data, x, y, z) -> switch (data.getFacing()) {
|
||||
case NORTH -> new HexCollisionBox(5.5D, 3.0D, 11.0D, 10.5D, 13.0D, 16.0D);
|
||||
@@ -822,7 +822,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
case EAST -> new HexCollisionBox(0.0D, 3.0D, 5.5D, 5.0D, 13.0D, 10.5D);
|
||||
// 1.13 separates wall and normal torches, 1.12 does not
|
||||
default -> new HexCollisionBox(6.0D, 0.0D, 6.0D, 10.0D, 10.0D, 10.0D);
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH),
|
||||
}, StateTypes.WALL_TORCH, StateTypes.REDSTONE_WALL_TORCH, StateTypes.COPPER_WALL_TORCH),
|
||||
|
||||
// 1.17 blocks
|
||||
CANDLE((player, version, data, x, y, z) -> {
|
||||
@@ -1018,6 +1018,30 @@ public enum CollisionData implements CollisionFactory {
|
||||
}
|
||||
}, StateTypes.DRIED_GHAST),
|
||||
|
||||
SHELF((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_21_9)) {
|
||||
// ViaVersion replacement block (planks)
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
|
||||
return switch (data.getFacing()) {
|
||||
case NORTH -> new ComplexCollisionBox(3, new HexCollisionBox(0, 12, 11, 16, 16, 13), new HexCollisionBox(0, 0, 13, 16, 16, 16), new HexCollisionBox(0, 0, 11, 16, 4, 13));
|
||||
case SOUTH -> new ComplexCollisionBox(3, new HexCollisionBox(0, 12, 3, 16, 16, 5), new HexCollisionBox(0, 0, 0, 16, 16, 3), new HexCollisionBox(0, 0, 3, 16, 4, 5));
|
||||
case WEST -> new ComplexCollisionBox(3, new HexCollisionBox(11, 12, 0, 13, 16, 16), new HexCollisionBox(13, 0, 0, 16, 16, 16), new HexCollisionBox(11, 0, 0, 13, 4, 16));
|
||||
case EAST -> new ComplexCollisionBox(3, new HexCollisionBox(3, 12, 0, 5, 16, 16), new HexCollisionBox(0, 0, 0, 3, 16, 16), new HexCollisionBox(3, 0, 0, 5, 4, 16));
|
||||
default -> throw new IllegalStateException("Unexpected value: " + data.getFacing());
|
||||
};
|
||||
}, BlockTags.WOODEN_SHELVES.getStates().toArray(new StateType[0])),
|
||||
|
||||
COPPER_GOLEM_STATUE((player, version, data, x, y, z) -> {
|
||||
if (version.isOlderThan(ClientVersion.V_1_21_9)) {
|
||||
// ViaVersion replacement block (copper block)
|
||||
return new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true);
|
||||
}
|
||||
|
||||
return new HexCollisionBox(3, 0, 3, 13, 14, 13);
|
||||
}, BlockTags.COPPER_GOLEM_STATUES.getStates().toArray(new StateType[0])),
|
||||
|
||||
DEFAULT(new SimpleCollisionBox(0, 0, 0, 1, 1, 1, true), StateTypes.STONE);
|
||||
|
||||
// This should be an array... but a hashmap will do for now...
|
||||
@@ -1049,7 +1073,7 @@ public enum CollisionData implements CollisionFactory {
|
||||
this.materials = mList.toArray(new StateType[0]);
|
||||
}
|
||||
|
||||
private static CollisionBox getAmethystBox(ClientVersion version, com.github.retrooper.packetevents.protocol.world.BlockFace facing, int param_0, int param_1) {
|
||||
private static CollisionBox getAmethystBox(ClientVersion version, BlockFace facing, int param_0, int param_1) {
|
||||
if (version.isOlderThanOrEquals(ClientVersion.V_1_16_4))
|
||||
return NoCollisionBox.INSTANCE;
|
||||
|
||||
@@ -1146,7 +1170,11 @@ public enum CollisionData implements CollisionFactory {
|
||||
// Would pre-computing all states be worth the memory cost? I doubt it
|
||||
public static CollisionData getData(StateType state) { // TODO: Find a better hack for lava and scaffolding
|
||||
// What the fuck mojang, why put noCollision() and then give PITCHER_CROP collision?
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING || state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE || state == StateTypes.PALE_MOSS_CARPET || BlockTags.WALL_HANGING_SIGNS.contains(state) ? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
|
||||
return state.isSolid() || state == StateTypes.LAVA || state == StateTypes.SCAFFOLDING
|
||||
|| state == StateTypes.PITCHER_CROP || state == StateTypes.HEAVY_CORE
|
||||
|| state == StateTypes.PALE_MOSS_CARPET || BlockTags.WALL_HANGING_SIGNS.contains(state)
|
||||
|| BlockTags.COPPER_GOLEM_STATUES.contains(state)
|
||||
? rawLookupMap.getOrDefault(state, DEFAULT) : NO_COLLISION;
|
||||
}
|
||||
|
||||
// TODO: This is wrong if a block doesn't have any hitbox and isn't specified, light block?
|
||||
|
||||
@@ -331,7 +331,7 @@ public enum HitboxData implements HitBoxFactory {
|
||||
if (data.getSouth() == South.TRUE) boxes.add(new HexCollisionBox(0, 0, 15, 16, 16, 16));
|
||||
|
||||
// TODO: when was isUp() added?
|
||||
if (data.getInternalData().containsKey(StateValue.UP) && data.isUp()) {
|
||||
if (data.hasProperty(StateValue.UP) && data.isUp()) {
|
||||
boxes.add(new HexCollisionBox(0, 15, 0, 16, 16, 16));
|
||||
}
|
||||
|
||||
|
||||
+135
-8
@@ -1,10 +1,13 @@
|
||||
package ac.grim.grimac.utils.collisions.datatypes;
|
||||
|
||||
import ac.grim.grimac.predictionengine.blockeffects.BlockCollisions;
|
||||
import ac.grim.grimac.utils.math.GrimMath;
|
||||
import ac.grim.grimac.utils.math.Location;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import ac.grim.grimac.utils.nmsutil.Collisions;
|
||||
import ac.grim.grimac.utils.nmsutil.Ray;
|
||||
import com.github.retrooper.packetevents.protocol.world.BlockFace;
|
||||
import com.github.retrooper.packetevents.protocol.world.Direction;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.util.Vector3i;
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
@@ -53,7 +56,7 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
}
|
||||
|
||||
public SimpleCollisionBox(Vector3i pos) {
|
||||
this(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
||||
this(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
// If you want to set a full block from a point
|
||||
@@ -409,16 +412,16 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
// Copied from hawk lol
|
||||
// I would like to point out that this is magic to me and I have not attempted to understand this code
|
||||
public Vector3dm intersectsRay(Ray ray, float minDist, float maxDist) {
|
||||
Vector3dm invDir = new Vector3dm(1f / ray.getDirection().getX(), 1f / ray.getDirection().getY(), 1f / ray.getDirection().getZ());
|
||||
Vector3dm invDir = new Vector3dm(1f / ray.getDirX(), 1f / ray.getDirY(), 1f / ray.getDirZ());
|
||||
|
||||
boolean signDirX = invDir.getX() < 0;
|
||||
boolean signDirY = invDir.getY() < 0;
|
||||
boolean signDirZ = invDir.getZ() < 0;
|
||||
|
||||
double tmin = ((signDirX ? maxX : minX) - ray.getOrigin().getX()) * invDir.getX();
|
||||
double tmax = ((signDirX ? minX : maxX) - ray.getOrigin().getX()) * invDir.getX();
|
||||
double tymin = ((signDirY ? maxY : minY) - ray.getOrigin().getY()) * invDir.getY();
|
||||
double tymax = ((signDirY ? minY : maxY) - ray.getOrigin().getY()) * invDir.getY();
|
||||
double tmin = ((signDirX ? maxX : minX) - ray.getOriginX()) * invDir.getX();
|
||||
double tmax = ((signDirX ? minX : maxX) - ray.getOriginX()) * invDir.getX();
|
||||
double tymin = ((signDirY ? maxY : minY) - ray.getOriginY()) * invDir.getY();
|
||||
double tymax = ((signDirY ? minY : maxY) - ray.getOriginY()) * invDir.getY();
|
||||
|
||||
if (tmin > tymax || tymin > tmax) {
|
||||
return null;
|
||||
@@ -427,8 +430,8 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
if (tymin > tmin) tmin = tymin;
|
||||
if (tymax < tmax) tmax = tymax;
|
||||
|
||||
double tzmin = ((signDirZ ? maxZ : minZ) - ray.getOrigin().getZ()) * invDir.getZ();
|
||||
double tzmax = ((signDirZ ? minZ : maxZ) - ray.getOrigin().getZ()) * invDir.getZ();
|
||||
double tzmin = ((signDirZ ? maxZ : minZ) - ray.getOriginZ()) * invDir.getZ();
|
||||
double tzmax = ((signDirZ ? minZ : maxZ) - ray.getOriginZ()) * invDir.getZ();
|
||||
|
||||
if ((tmin > tzmax) || (tzmin > tmax)) {
|
||||
return null;
|
||||
@@ -448,6 +451,10 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
return new Vector3dm(minX, minY, minZ);
|
||||
}
|
||||
|
||||
public Vector3d getCenter() {
|
||||
return new Vector3d(GrimMath.lerp(0.5, this.minX, this.maxX), GrimMath.lerp(0.5, this.minY, this.maxY), GrimMath.lerp(0.5, this.minZ, this.maxZ));
|
||||
}
|
||||
|
||||
public DoubleList getYPointPositions() {
|
||||
return create(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
@@ -509,6 +516,37 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
return maxZ - minZ;
|
||||
}
|
||||
|
||||
public SimpleCollisionBox move(Vector3d vector) {
|
||||
return this.move(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
public SimpleCollisionBox move(double x, double y, double z) {
|
||||
return new SimpleCollisionBox(this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z);
|
||||
}
|
||||
|
||||
public boolean intersects(SimpleCollisionBox collisionBox) {
|
||||
return this.intersects(collisionBox.minX, collisionBox.minY, collisionBox.minZ, collisionBox.maxX, collisionBox.maxY, collisionBox.maxZ);
|
||||
}
|
||||
|
||||
public boolean intersects(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
||||
}
|
||||
|
||||
public boolean intersects(Vector3d min, Vector3d max) {
|
||||
return this.intersects(
|
||||
Math.min(min.x, max.x),
|
||||
Math.min(min.y, max.y),
|
||||
Math.min(min.z, max.z),
|
||||
Math.max(min.x, max.x),
|
||||
Math.max(min.y, max.y),
|
||||
Math.max(min.z, max.z)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean intersects(Vector3i blockPos) {
|
||||
return this.intersects(blockPos.getX(), blockPos.getY(), blockPos.getZ(), blockPos.getX() + 1, blockPos.getY() + 1, blockPos.getZ() + 1);
|
||||
}
|
||||
|
||||
public static Iterable<Vector3i> betweenClosed(SimpleCollisionBox box) {
|
||||
Vector3i startBlockPos = containing(box.minX, box.minY, box.minZ);
|
||||
Vector3i endBlockPos = containing(box.maxX, box.maxY, box.maxZ);
|
||||
@@ -554,6 +592,95 @@ public class SimpleCollisionBox implements CollisionBox {
|
||||
};
|
||||
}
|
||||
|
||||
public static Iterable<Vector3i> betweenCornersInDirection(SimpleCollisionBox boundingBox, Vector3d directionVector) {
|
||||
Vector3d min = boundingBox.min().toVector3d();
|
||||
int minX = GrimMath.floor(min.x);
|
||||
int minY = GrimMath.floor(min.y);
|
||||
int minZ = GrimMath.floor(min.z);
|
||||
Vector3d max = boundingBox.max().toVector3d();
|
||||
int maxX = GrimMath.floor(max.x);
|
||||
int maxY = GrimMath.floor(max.y);
|
||||
int maxZ = GrimMath.floor(max.z);
|
||||
return betweenCornersInDirection(minX, minY, minZ, maxX, maxY, maxZ, directionVector);
|
||||
}
|
||||
|
||||
public static Iterable<Vector3i> betweenCornersInDirection(Vector3i min, Vector3i max, Vector3d directionVector) {
|
||||
return betweenCornersInDirection(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ(), directionVector);
|
||||
}
|
||||
|
||||
public static Iterable<Vector3i> betweenCornersInDirection(int x1, int y1, int z1, int x2, int y2, int z2, Vector3d directionVector) {
|
||||
int minX = Math.min(x1, x2);
|
||||
int minY = Math.min(y1, y2);
|
||||
int minZ = Math.min(z1, z2);
|
||||
int maxX = Math.max(x1, x2);
|
||||
int maxY = Math.max(y1, y2);
|
||||
int maxZ = Math.max(z1, z2);
|
||||
|
||||
int sizeX = maxX - minX;
|
||||
int sizeY = maxY - minY;
|
||||
int sizeZ = maxZ - minZ;
|
||||
|
||||
int startX = directionVector.x >= 0.0 ? minX : maxX;
|
||||
int startY = directionVector.y >= 0.0 ? minY : maxY;
|
||||
int startZ = directionVector.z >= 0.0 ? minZ : maxZ;
|
||||
|
||||
List<Collisions.Axis> axisOrder = BlockCollisions.axisStepOrder(directionVector);
|
||||
Collisions.Axis primaryAxis = axisOrder.get(0);
|
||||
Collisions.Axis secondaryAxis = axisOrder.get(1);
|
||||
Collisions.Axis tertiaryAxis = axisOrder.get(2);
|
||||
|
||||
Direction primaryDirection = primaryAxis.get(directionVector) >= 0.0 ? primaryAxis.getPositive() : primaryAxis.getNegative();
|
||||
Direction secondaryDirection = secondaryAxis.get(directionVector) >= 0.0 ? secondaryAxis.getPositive() : secondaryAxis.getNegative();
|
||||
Direction tertiaryDirection = tertiaryAxis.get(directionVector) >= 0.0 ? tertiaryAxis.getPositive() : tertiaryAxis.getNegative();
|
||||
|
||||
int primaryCount = primaryAxis.choose(sizeX, sizeY, sizeZ);
|
||||
int secondaryCount = secondaryAxis.choose(sizeX, sizeY, sizeZ);
|
||||
int tertiaryCount = tertiaryAxis.choose(sizeX, sizeY, sizeZ);
|
||||
|
||||
return () -> new AbstractIterator<>() {
|
||||
private int firstIndex;
|
||||
private int secondIndex;
|
||||
private int thirdIndex;
|
||||
private boolean end;
|
||||
private final int firstDirX = primaryDirection.getVector().getX();
|
||||
private final int firstDirY = primaryDirection.getVector().getY();
|
||||
private final int firstDirZ = primaryDirection.getVector().getZ();
|
||||
private final int secondDirX = secondaryDirection.getVector().getX();
|
||||
private final int secondDirY = secondaryDirection.getVector().getY();
|
||||
private final int secondDirZ = secondaryDirection.getVector().getZ();
|
||||
private final int thirdDirX = tertiaryDirection.getVector().getX();
|
||||
private final int thirdDirY = tertiaryDirection.getVector().getY();
|
||||
private final int thirdDirZ = tertiaryDirection.getVector().getZ();
|
||||
|
||||
protected Vector3i computeNext() {
|
||||
if (this.end) {
|
||||
return this.endOfData();
|
||||
} else {
|
||||
Vector3i cursor = new Vector3i(
|
||||
startX + this.firstDirX * this.firstIndex + this.secondDirX * this.secondIndex + this.thirdDirX * this.thirdIndex,
|
||||
startY + this.firstDirY * this.firstIndex + this.secondDirY * this.secondIndex + this.thirdDirY * this.thirdIndex,
|
||||
startZ + this.firstDirZ * this.firstIndex + this.secondDirZ * this.secondIndex + this.thirdDirZ * this.thirdIndex
|
||||
);
|
||||
|
||||
if (this.thirdIndex < tertiaryCount) {
|
||||
this.thirdIndex++;
|
||||
} else if (this.secondIndex < secondaryCount) {
|
||||
this.secondIndex++;
|
||||
this.thirdIndex = 0;
|
||||
} else if (this.firstIndex < primaryCount) {
|
||||
this.firstIndex++;
|
||||
this.thirdIndex = 0;
|
||||
this.secondIndex = 0;
|
||||
} else {
|
||||
this.end = true;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleCollisionBox{" +
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package ac.grim.grimac.utils.common;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class GrimArguments {
|
||||
public static final boolean TRANSACTION_KICKS = !Boolean.getBoolean("grim.disable-transaction-kick");
|
||||
public static final String API_URL = System.getProperty("grim.api-url", "https://api.grim.ac/v1/server/");
|
||||
public static final String PASTE_URL = System.getProperty("grim.paste-url", "https://paste.grim.ac/");
|
||||
public static final String PLATFORM_OVERRIDE = System.getProperty("grim.platform-override", "");
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ac.grim.grimac.utils.common.arguments;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Getter
|
||||
public class ArgumentOptions<T> {
|
||||
|
||||
private ArgumentOptions(Class<T> clazz, String key, Supplier<T> defaultSupplier) {
|
||||
this.clazz = clazz;
|
||||
this.key = key;
|
||||
this.defaultSupplier = defaultSupplier;
|
||||
}
|
||||
|
||||
private final Class<T> clazz;
|
||||
private String key;
|
||||
private Supplier<T> defaultSupplier;
|
||||
private Predicate<T> verifier = t -> true;
|
||||
private Function<T, T> modifier = t -> t;
|
||||
private SystemArgument.Visibility visibility = SystemArgument.Visibility.VISIBLE;
|
||||
private boolean nullable = false;
|
||||
|
||||
public static <T> Builder<T> from(Class<T> clazz, String key, @NotNull Supplier<@NotNull T> defaultValue) {
|
||||
return new Builder<>(new ArgumentOptions<>(clazz, key, defaultValue));
|
||||
}
|
||||
|
||||
public static <T> Builder<T> from(Class<T> clazz, String key, T defaultValue) {
|
||||
return new Builder<>(new ArgumentOptions<>(clazz, key, () -> defaultValue)).nullable(defaultValue == null);
|
||||
}
|
||||
|
||||
public static <T> Builder<T> from(Class<T> clazz, String key) {
|
||||
return new Builder<>(new ArgumentOptions<>(clazz, key, () -> null)).nullable(true);
|
||||
}
|
||||
|
||||
public record Builder<T>(ArgumentOptions<T> options) {
|
||||
|
||||
public Builder<T> key(String key) {
|
||||
options.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> verifier(Predicate<T> predicate) {
|
||||
options.verifier = predicate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> modifier(Function<T, T> modifier) {
|
||||
options.modifier = modifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> defaultSupplier(Supplier<T> supplier) {
|
||||
options.defaultSupplier = supplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> visibility(SystemArgument.Visibility visibility) {
|
||||
options.visibility = visibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder<T> nullable(boolean nullable) {
|
||||
options.nullable = nullable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ArgumentOptions<T> build() {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package ac.grim.grimac.utils.common.arguments;
|
||||
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ArgumentUtils {
|
||||
|
||||
private static final Pattern WEBSITE_URL_PATTERN = Pattern.compile("^(https?://)?(localhost:[0-9]{1,4}|(([a-zA-Z0-9_-]+\\.)?[a-zA-Z0-9_-]+\\.[a-zA-Z]{2,3}))(/[a-zA-Z0-9_/-?=]*)?$");
|
||||
|
||||
public static <T extends Number> Predicate<T> validRange(T min, T max) {
|
||||
return number -> number.doubleValue() >= min.doubleValue() && number.doubleValue() <= max.doubleValue();
|
||||
}
|
||||
|
||||
public static Predicate<String> validURL() {
|
||||
return string -> WEBSITE_URL_PATTERN.matcher(string).matches();
|
||||
}
|
||||
|
||||
public static Function<String, String> modifyURL(Supplier<String> defaultValue) {
|
||||
return string -> {
|
||||
if (!string.endsWith("/")) string += "/";
|
||||
if (WEBSITE_URL_PATTERN.matcher(string).matches()) return string;
|
||||
//LogUtil.warn("Invalid URL: " + string);
|
||||
return defaultValue.get();
|
||||
};
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<Long> range(String key, long defaultValue, long min, long max) {
|
||||
return ArgumentOptions.from(Long.class, key, defaultValue)
|
||||
.verifier(validRange(min, max))
|
||||
.modifier(aLong -> Math.min(Math.max(aLong, min), max));
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<Integer> range(String key, int defaultValue, int min, int max) {
|
||||
return ArgumentOptions.from(Integer.class, key, defaultValue)
|
||||
.verifier(validRange(min, max))
|
||||
.modifier(integer -> Math.min(Math.max(integer, min), max));
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<String> url(String key, String defaultUrl) {
|
||||
return ArgumentOptions.from(String.class, key, defaultUrl)
|
||||
.verifier(ArgumentUtils.validURL())
|
||||
.modifier(modifyURL(() -> defaultUrl));
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<String> string(String key, String defaultValue) {
|
||||
return ArgumentOptions.from(String.class, key, defaultValue);
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<Boolean> string(String key, boolean defaultValue) {
|
||||
return ArgumentOptions.from(Boolean.class, key, defaultValue);
|
||||
}
|
||||
|
||||
public static ArgumentOptions.Builder<Platform> platform(String key) {
|
||||
return ArgumentOptions.from(Platform.class, key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ac.grim.grimac.utils.common.arguments;
|
||||
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
|
||||
import static ac.grim.grimac.utils.common.arguments.ArgumentUtils.platform;
|
||||
import static ac.grim.grimac.utils.common.arguments.ArgumentUtils.string;
|
||||
|
||||
public class CommonGrimArguments {
|
||||
|
||||
private final static SystemArgumentFactory FACTORY = SystemArgumentFactory.Builder.of("Grim")
|
||||
.optionModifier(builder -> builder.key("Grim" + builder.options().getKey()))
|
||||
.supportEnv()
|
||||
.build();
|
||||
|
||||
public final static SystemArgument<Boolean> KICK_ON_TRANSACTION_ERRORS = FACTORY.create(string("KickOnTransactionTaskErrors", false));
|
||||
public final static SystemArgument<String> API_URL = FACTORY.create(string("APIUrl", "https://api.grim.ac/v1/server/"));
|
||||
public final static SystemArgument<String> PASTE_URL = FACTORY.create(string("PasteUrl", "https://paste.grim.ac/"));
|
||||
public final static SystemArgument<Platform> PLATFORM_OVERRIDE = FACTORY.create(platform("PlatformOverride"));
|
||||
|
||||
/**
|
||||
* Enables "Fast Bypass" mode for chat messages sent by GrimAC.
|
||||
* <p>
|
||||
* <b>BENEFIT:</b> Messages are sent directly as packets, significantly improving
|
||||
* performance and reducing server overhead especially when lots of alerts are being sent.
|
||||
* <p>
|
||||
* <b>TRADE-OFF:</b> This completely bypasses the platform's event system (e.g., Bukkit's chat events).
|
||||
* Other plugins will NOT be able to see, format, or cancel these messages.
|
||||
* <p>
|
||||
* This setting is opt-in (default: false) and requires a server restart to change.
|
||||
*/
|
||||
public final static SystemArgument<Boolean> USE_CHAT_FAST_BYPASS = FACTORY.create(string("ChatFastBypass", true));
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ac.grim.grimac.utils.common.arguments;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public record SystemArgument<T>(String key, Class<T> clazz, T value, boolean set,
|
||||
Visibility visibility) {
|
||||
|
||||
public boolean matches(Predicate<T> predicate) {
|
||||
return predicate.test(value);
|
||||
}
|
||||
|
||||
public <K> K mapValue(Function<T, K> mapper, K otherwise) {
|
||||
try {
|
||||
return value == null ? otherwise : mapper.apply(value);
|
||||
} catch (Exception e) {
|
||||
//TODO: add back logging once LogUtil has been refactored
|
||||
//LogUtil.exception("Failed to map value for argument " + key, e);
|
||||
}
|
||||
return otherwise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SystemArgument<?> that = (SystemArgument<?>) o;
|
||||
return key.equals(that.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode();
|
||||
}
|
||||
|
||||
public enum Visibility {
|
||||
VISIBLE, // visible from commands
|
||||
HIDDEN, // only visible in console
|
||||
SECRET // only visible in console if set
|
||||
}
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package ac.grim.grimac.utils.common.arguments;
|
||||
|
||||
import ac.grim.grimac.platform.api.Platform;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public record SystemArgumentFactory(Map<String, String> arguments,
|
||||
Map<Class<?>, Function<String, ?>> parsers,
|
||||
Consumer<SystemArgument<?>> creationListener,
|
||||
Consumer<ArgumentOptions.Builder<?>> optionModifier) {
|
||||
|
||||
public static class Builder {
|
||||
|
||||
public static Builder of(final String prefix) {
|
||||
return new Builder(prefix);
|
||||
}
|
||||
|
||||
public Builder onRegister(Consumer<SystemArgument<?>> listener) {
|
||||
this.registerListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder optionModifier(Consumer<ArgumentOptions.Builder<?>> modifier) {
|
||||
this.optionModifier = modifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder supportEnv() {
|
||||
this.envSupport = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private final String prefix;
|
||||
private boolean envSupport = false;
|
||||
private Consumer<SystemArgument<?>> registerListener = argument -> {
|
||||
};
|
||||
private final Map<Class<?>, Function<String, ?>> parseBuilder;
|
||||
private Consumer<ArgumentOptions.Builder<?>> optionModifier = null;
|
||||
|
||||
private Builder(final String prefix) {
|
||||
this.prefix = prefix;
|
||||
this.parseBuilder = new HashMap<>();
|
||||
// Register default parsers
|
||||
registerDefaultParsers();
|
||||
}
|
||||
|
||||
protected void registerDefaultParsers() {
|
||||
registerParser(Boolean.class, Boolean::parseBoolean)
|
||||
.registerParser(Byte.class, Byte::parseByte)
|
||||
.registerParser(Short.class, Short::parseShort)
|
||||
.registerParser(Integer.class, Integer::parseInt)
|
||||
.registerParser(Float.class, Float::parseFloat)
|
||||
.registerParser(Double.class, Double::parseDouble)
|
||||
.registerParser(Long.class, Long::parseLong)
|
||||
.registerParser(Character.class, s -> !s.isEmpty() ? s.charAt(0) : '\0')
|
||||
.registerParser(char[].class, String::toCharArray)
|
||||
.registerParser(String.class, String::valueOf)
|
||||
.registerParser(Charset.class, Charset::forName)
|
||||
.registerParser(Platform.class, Platform::getByName);
|
||||
}
|
||||
|
||||
public <T> Builder registerParser(Class<T> type, Function<String, T> parser) {
|
||||
parseBuilder.put(type, parser);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void updateFromEnv(Map<String, String> builder) {
|
||||
for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
|
||||
if (entry.getKey().startsWith(prefix.toUpperCase())) {
|
||||
if (builder.put(entry.getKey().toLowerCase(), entry.getValue()) != null) {
|
||||
warn("Env variable overwriting system variable: " + entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SystemArgumentFactory build() {
|
||||
// Parse arguments from JVM arguments
|
||||
final String findPrefix = "-d" + prefix.toLowerCase();
|
||||
Map<String, String> builder = getSystemPropertiesMap(findPrefix);
|
||||
if (envSupport) {
|
||||
try {
|
||||
updateFromEnv(builder);
|
||||
} catch (Exception e) {
|
||||
exception("Failed to read environment variables", e);
|
||||
}
|
||||
}
|
||||
//
|
||||
return new SystemArgumentFactory(Map.copyOf(builder), Map.copyOf(parseBuilder), registerListener, optionModifier);
|
||||
}
|
||||
|
||||
protected @NotNull Map<String, String> getSystemPropertiesMap(String findPrefix) {
|
||||
Map<String, String> builder = new HashMap<>();
|
||||
for (String line : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
|
||||
if (line.toLowerCase().startsWith(findPrefix)) {
|
||||
int index = line.indexOf('=');
|
||||
if (index > 0 && index < line.length() - 1) {
|
||||
String key = line.substring(2, index); // remove -D
|
||||
String value = line.substring(index + 1);
|
||||
builder.put(key.toLowerCase(), value);
|
||||
} else {
|
||||
warn("Invalid startup argument: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> SystemArgument<T> createDefaultSupplier(ArgumentOptions<T> options) {
|
||||
T value = options.getModifier().apply(options.getDefaultSupplier().get());
|
||||
if (value == null && !options.isNullable())
|
||||
throw new IllegalArgumentException("Default value cannot be null for startup argument \"" + options.getKey() + "\"");
|
||||
if ((value != null && !options.getVerifier().test(value)))
|
||||
throw new IllegalArgumentException("Invalid default value for startup argument \"" + options.getKey() + "\"");
|
||||
SystemArgument<T> argument = new SystemArgument<>(options.getKey(), options.getClazz(), value, false, options.getVisibility());
|
||||
creationListener.accept(argument);
|
||||
return argument;
|
||||
}
|
||||
|
||||
public <T> SystemArgument<T> create(ArgumentOptions.Builder<T> builder) {
|
||||
if (optionModifier != null) optionModifier.accept(builder);
|
||||
final ArgumentOptions<T> options = builder.build();
|
||||
//
|
||||
final String value = arguments.get(options.getKey().toLowerCase());
|
||||
if (value == null) return createDefaultSupplier(options);
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<String, T> parser = (Function<String, T>) parsers.get(options.getClazz());
|
||||
if (parser == null) return createDefaultSupplier(options);
|
||||
T parsed = options.getModifier().apply(parser.apply(value));
|
||||
if (parsed == null || !options.getVerifier().test(parsed))
|
||||
return createDefaultSupplier(options);
|
||||
SystemArgument<T> newArgument = new SystemArgument<>(options.getKey(), options.getClazz(), parsed, true, options.getVisibility());
|
||||
creationListener.accept(newArgument);
|
||||
return newArgument;
|
||||
} catch (Exception e) {
|
||||
exception("Failed to parse value for startup argument \"" + options.getKey() + "\"", e);
|
||||
return createDefaultSupplier(options);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getFoundArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
//TODO: add back logging once LogUtil has been refactored
|
||||
|
||||
private static void exception(String message, Exception e) {
|
||||
//LogUtil.exception(message, e);
|
||||
}
|
||||
|
||||
private static void warn(String message) {
|
||||
//LogUtil.warn(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public class PacketStateData {
|
||||
public boolean lastServerTransWasValid = false;
|
||||
@Getter
|
||||
private int slowedByUsingItemSlot = Integer.MIN_VALUE;
|
||||
public boolean sendingBundlePacket;
|
||||
|
||||
// If true, the player's rotation was forced to the horse's rotation only on 1.13-
|
||||
public boolean horseInteractCausedForcedRotation = false;
|
||||
|
||||
@@ -39,18 +39,17 @@ public class ReachInterpolationData {
|
||||
private boolean expandNonRelative = false;
|
||||
private int cancelledLerpInterpolationStepsLowBound = Integer.MAX_VALUE;
|
||||
|
||||
public ReachInterpolationData(GrimPlayer player, SimpleCollisionBox startingLocation, TrackedPosition position, PacketEntity entity) {
|
||||
final boolean isPointNine = !player.inVehicle() && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9);
|
||||
public ReachInterpolationData(GrimPlayer player, SimpleCollisionBox startingLocation, final double x, final double y, final double z, PacketEntity entity) {
|
||||
final boolean unreliableTicking = !player.inVehicle() && player.canSkipTicks();
|
||||
|
||||
this.startingLocation = startingLocation;
|
||||
final Vector3d pos = position.getPos();
|
||||
this.targetLocation = new SimpleCollisionBox(pos.x, pos.y, pos.z, pos.x, pos.y, pos.z, false);
|
||||
this.targetLocation = new SimpleCollisionBox(x, y, z, x, y, z, false);
|
||||
this.player = player;
|
||||
this.entity = entity;
|
||||
|
||||
// 1.9 -> 1.8 precision loss in packets
|
||||
// (ViaVersion is doing some stuff that makes this code difficult)
|
||||
if (!isPointNine && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9) && PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
|
||||
targetLocation.expand(0.03125);
|
||||
}
|
||||
|
||||
@@ -66,7 +65,8 @@ public class ReachInterpolationData {
|
||||
interpolationSteps = 1;
|
||||
}
|
||||
|
||||
if (isPointNine) interpolationStepsHighBound = getInterpolationSteps();
|
||||
// If the player doesn't tick reliably, their interpolation is anywhere between min and max steps.
|
||||
if (unreliableTicking) interpolationStepsHighBound = getInterpolationSteps();
|
||||
}
|
||||
|
||||
// While riding entities, there is no interpolation.
|
||||
|
||||
@@ -11,22 +11,34 @@ import lombok.ToString;
|
||||
public class SetBackData {
|
||||
private final TeleportData teleportData;
|
||||
private final float xRot, yRot;
|
||||
private final Vector3dm velocity;
|
||||
private final double velocityX, velocityY, velocityZ;
|
||||
private boolean hasVelocity;
|
||||
private final boolean vehicle;
|
||||
private boolean isComplete = false;
|
||||
// TODO: Rethink when we block movements for teleports, perhaps after 10 ticks or 5 blocks?
|
||||
private boolean isPlugin;
|
||||
private int ticksComplete = 0;
|
||||
|
||||
public SetBackData(TeleportData teleportData, float xRot, float yRot, Vector3dm velocity, boolean vehicle, boolean isPlugin) {
|
||||
public SetBackData(TeleportData teleportData, float xRot, float yRot, double velocityX, double velocityY, double velocityZ, boolean hasVelocity, boolean vehicle, boolean isPlugin) {
|
||||
this.teleportData = teleportData;
|
||||
this.xRot = xRot;
|
||||
this.yRot = yRot;
|
||||
this.velocity = velocity;
|
||||
this.velocityX = velocityX;
|
||||
this.velocityY = velocityY;
|
||||
this.velocityZ = velocityZ;
|
||||
this.hasVelocity = hasVelocity;
|
||||
this.vehicle = vehicle;
|
||||
this.isPlugin = isPlugin;
|
||||
}
|
||||
|
||||
public SetBackData(TeleportData teleportData, float xRot, float yRot, double velocityX, double velocityY, double velocityZ, boolean vehicle, boolean isPlugin) {
|
||||
this(teleportData, xRot, yRot, velocityX, velocityY, velocityZ, true, vehicle, isPlugin);
|
||||
}
|
||||
|
||||
public SetBackData(TeleportData teleportData, float xRot, float yRot, boolean vehicle, boolean isPlugin) {
|
||||
this(teleportData, xRot, yRot, 0, 0, 0, false, vehicle, isPlugin);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (isComplete) ticksComplete++;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class TeleportData {
|
||||
private final Vector3d location;
|
||||
private final Vector3d velocity;
|
||||
private final RelativeFlag flags;
|
||||
private final double locationX;
|
||||
private final double locationY;
|
||||
private final double locationZ;
|
||||
private final double velocityX;
|
||||
private final double velocityY;
|
||||
private final double velocityZ;
|
||||
private final @NotNull RelativeFlag flags;
|
||||
@Setter
|
||||
private int transaction;
|
||||
@Setter
|
||||
@@ -36,29 +40,27 @@ public class TeleportData {
|
||||
if (!isRelativeZ()) {
|
||||
vector.setZ(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (velocity != null && isStupidTeleportSystem) {
|
||||
} else {
|
||||
// WHAT WAS MOJANG THINKING MAKING TELEPORTS A REPLACEMENT PACKET FOR EXPLOSION VELOCITY /s
|
||||
if (isRelativeDeltaX()) {
|
||||
vector.setX(vector.getX() + velocity.getX());
|
||||
vector.setX(vector.getX() + velocityX);
|
||||
} else {
|
||||
vector.setX(velocity.getX());
|
||||
vector.setX(velocityX);
|
||||
}
|
||||
|
||||
if (isRelativeDeltaY()) {
|
||||
vector.setY(vector.getY() + velocity.getY());
|
||||
vector.setY(vector.getY() + velocityY);
|
||||
} else {
|
||||
vector.setY(velocity.getY());
|
||||
vector.setY(velocityY);
|
||||
// Is this correct? Don't know don't care.
|
||||
player.lastWasClimbing = 0; // Vertical movement reset
|
||||
player.canSwimHop = false; // Vertical movement reset
|
||||
}
|
||||
|
||||
if (isRelativeDeltaZ()) {
|
||||
vector.setZ(vector.getZ() + velocity.getZ());
|
||||
vector.setZ(vector.getZ() + velocityZ);
|
||||
} else {
|
||||
vector.setZ(velocity.getZ());
|
||||
vector.setZ(velocityZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ public final class TrackedPosition {
|
||||
private static final double LEGACY_COORDINATE_SCALE = 32.0;
|
||||
|
||||
private final double scale;
|
||||
@Setter
|
||||
private Vector3d pos = new Vector3d();
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
|
||||
public TrackedPosition() {
|
||||
// this.scale = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? MODERN_COORDINATE_SCALE : LEGACY_COORDINATE_SCALE;
|
||||
@@ -35,23 +36,38 @@ public final class TrackedPosition {
|
||||
return value / scale;
|
||||
}
|
||||
|
||||
// Method since 1.16.
|
||||
public Vector3d withDelta(long x, long y, long z) {
|
||||
if (x == 0L && y == 0L && z == 0L) {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
double d = x == 0L ? this.pos.x : unpack(pack(this.pos.x, scale) + x);
|
||||
double e = y == 0L ? this.pos.y : unpack(pack(this.pos.y, scale) + y);
|
||||
double f = z == 0L ? this.pos.z : unpack(pack(this.pos.z, scale) + z);
|
||||
return new Vector3d(d, e, f);
|
||||
// Methods since 1.16.
|
||||
public double withDeltaX(long x) {
|
||||
if (x == 0L) return this.x;
|
||||
return unpack(pack(this.x, scale) + x);
|
||||
}
|
||||
|
||||
// In 1.16-, this was different.
|
||||
public Vector3d withDeltaLegacy(double x, double y, double z) {
|
||||
double d = unpackLegacy(packLegacy(this.pos.x, scale) + x);
|
||||
double e = unpackLegacy(packLegacy(this.pos.y, scale) + y);
|
||||
double f = unpackLegacy(packLegacy(this.pos.z, scale) + z);
|
||||
return new Vector3d(d, e, f);
|
||||
public double withDeltaY(long y) {
|
||||
if (y == 0L) return this.y;
|
||||
return unpack(pack(this.y, scale) + y);
|
||||
}
|
||||
|
||||
public double withDeltaZ(long z) {
|
||||
if (z == 0L) return this.z;
|
||||
return unpack(pack(this.z, scale) + z);
|
||||
}
|
||||
|
||||
// In 1.16-, these were different.
|
||||
public double withDeltaLegacyX(double x) {
|
||||
return unpackLegacy(packLegacy(this.x, scale) + x);
|
||||
}
|
||||
|
||||
public double withDeltaLegacyY(double y) {
|
||||
return unpackLegacy(packLegacy(this.y, scale) + y);
|
||||
}
|
||||
|
||||
public double withDeltaLegacyZ(double z) {
|
||||
return unpackLegacy(packLegacy(this.z, scale) + z);
|
||||
}
|
||||
|
||||
public void setPos(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package ac.grim.grimac.utils.data;
|
||||
|
||||
import ac.grim.grimac.utils.anticheat.MessageUtil;
|
||||
import ac.grim.grimac.utils.math.Vec2d;
|
||||
import ac.grim.grimac.utils.math.Vector3dm;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
@@ -10,15 +12,33 @@ public class VectorData {
|
||||
public final VectorType vectorType;
|
||||
public VectorData lastVector;
|
||||
public VectorData preUncertainty;
|
||||
public Vector3dm vector;
|
||||
public @MonotonicNonNull Vector3dm input;
|
||||
public double vectorX, vectorY, vectorZ;
|
||||
public @MonotonicNonNull Vec2d input;
|
||||
|
||||
@Getter
|
||||
private boolean isKnockback, firstBreadKb, isExplosion, firstBreadExplosion, isTrident, isZeroPointZeroThree, isSwimHop, isFlipSneaking, isFlipItem, isJump, isAttackSlow = false;
|
||||
|
||||
// For handling replacing the type of vector it is while keeping data
|
||||
public VectorData(Vector3dm vector, VectorData lastVector, VectorType vectorType) {
|
||||
this.vector = vector;
|
||||
this(vector.getX(), vector.getY(), vector.getZ(), lastVector, vectorType);
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.vectorX = x;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.vectorY = y;
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
this.vectorZ = z;
|
||||
}
|
||||
|
||||
public VectorData(final double x, final double y, final double z, VectorData lastVector, VectorType vectorType) {
|
||||
this.vectorX = x;
|
||||
this.vectorY = y;
|
||||
this.vectorZ = z;
|
||||
this.lastVector = lastVector;
|
||||
this.vectorType = vectorType;
|
||||
|
||||
@@ -42,30 +62,40 @@ public class VectorData {
|
||||
}
|
||||
|
||||
public VectorData(Vector3dm vector, VectorType vectorType) {
|
||||
this.vector = vector;
|
||||
this(vector.getX(), vector.getY(), vector.getZ(), vectorType);
|
||||
}
|
||||
|
||||
public VectorData(final double x, final double y, final double z, VectorType vectorType) {
|
||||
this.vectorX = x;
|
||||
this.vectorY = y;
|
||||
this.vectorZ = z;
|
||||
this.vectorType = vectorType;
|
||||
addVectorType(vectorType);
|
||||
}
|
||||
|
||||
public VectorData returnNewModified(VectorType type) {
|
||||
return new VectorData(vector, this, type);
|
||||
return new VectorData(this.vectorX, this.vectorY, this.vectorZ, this, type);
|
||||
}
|
||||
|
||||
public VectorData returnNewModified(Vector3dm newVec, VectorType type) {
|
||||
return new VectorData(newVec, this, type);
|
||||
}
|
||||
|
||||
public VectorData returnNewModified(final double x, final double y, final double z, VectorType type) {
|
||||
return new VectorData(x, y, z, this, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VectorData that = (VectorData) o;
|
||||
return isKnockback == that.isKnockback && firstBreadKb == that.firstBreadKb && isExplosion == that.isExplosion && firstBreadExplosion == that.firstBreadExplosion && isTrident == that.isTrident && isZeroPointZeroThree == that.isZeroPointZeroThree && isSwimHop == that.isSwimHop && isFlipSneaking == that.isFlipSneaking && isFlipItem == that.isFlipItem && isJump == that.isJump && isAttackSlow == that.isAttackSlow && vectorType == that.vectorType && Objects.equals(lastVector, that.lastVector) && Objects.equals(preUncertainty, that.preUncertainty) && Objects.equals(vector, that.vector);
|
||||
return isKnockback == that.isKnockback && firstBreadKb == that.firstBreadKb && isExplosion == that.isExplosion && firstBreadExplosion == that.firstBreadExplosion && isTrident == that.isTrident && isZeroPointZeroThree == that.isZeroPointZeroThree && isSwimHop == that.isSwimHop && isFlipSneaking == that.isFlipSneaking && isFlipItem == that.isFlipItem && isJump == that.isJump && isAttackSlow == that.isAttackSlow && vectorType == that.vectorType && Objects.equals(lastVector, that.lastVector) && Objects.equals(preUncertainty, that.preUncertainty) && vectorX == that.vectorX && vectorY == that.vectorY && vectorZ == that.vectorZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(vectorType, lastVector, preUncertainty, vector, isKnockback, firstBreadKb, isExplosion, firstBreadExplosion, isTrident, isZeroPointZeroThree, isSwimHop, isFlipSneaking, isFlipItem, isJump, isAttackSlow);
|
||||
return Objects.hash(vectorType, lastVector, preUncertainty, vectorX, vectorY, vectorZ, isKnockback, firstBreadKb, isExplosion, firstBreadExplosion, isTrident, isZeroPointZeroThree, isSwimHop, isFlipSneaking, isFlipItem, isJump, isAttackSlow);
|
||||
}
|
||||
|
||||
public void addVectorType(VectorType type) {
|
||||
@@ -88,7 +118,7 @@ public class VectorData {
|
||||
public String toString() {
|
||||
return "VectorData{" +
|
||||
"pointThree=" + isZeroPointZeroThree +
|
||||
", vector=" + vector +
|
||||
", vector=" + MessageUtil.toUnlabeledString(vectorX, vectorY, vectorZ) +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ public class VehicleData {
|
||||
public float vehicleForward = 0f;
|
||||
public boolean lastDummy = false;
|
||||
public boolean wasVehicleSwitch = false;
|
||||
public final ConcurrentLinkedQueue<Pair<Integer, Vector3d>> vehicleTeleports = new ConcurrentLinkedQueue<>();
|
||||
public final ConcurrentLinkedQueue<VehicleTeleport> vehicleTeleports = new ConcurrentLinkedQueue<>();
|
||||
public float nextHorseJump = 0;
|
||||
public float horseJump = 0;
|
||||
public boolean horseJumping = false;
|
||||
public int camelDashCooldown;
|
||||
|
||||
public record VehicleTeleport(int teleportId, double x, double y, double z) {}
|
||||
}
|
||||
@@ -73,12 +73,14 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
this.uuid = uuid;
|
||||
initAttributes(player);
|
||||
this.trackedServerPosition = new TrackedPosition();
|
||||
this.trackedServerPosition.setPos(new Vector3d(x, y, z));
|
||||
this.trackedServerPosition.setPos(x, y, z);
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) { // Thanks ViaVersion
|
||||
trackedServerPosition.setPos(new Vector3d(((int) (x * 32)) / 32d, ((int) (y * 32)) / 32d, ((int) (z * 32)) / 32d));
|
||||
trackedServerPosition.setPos(((int) (x * 32)) / 32d, ((int) (y * 32)) / 32d, ((int) (z * 32)) / 32d);
|
||||
}
|
||||
final Vector3d pos = trackedServerPosition.getPos();
|
||||
this.newPacketLocation = new ReachInterpolationData(player, new SimpleCollisionBox(pos.x, pos.y, pos.z, pos.x, pos.y, pos.z, false), trackedServerPosition, this);
|
||||
final double xPos = trackedServerPosition.getX();
|
||||
final double yPos = trackedServerPosition.getY();
|
||||
final double zPos = trackedServerPosition.getZ();
|
||||
this.newPacketLocation = new ReachInterpolationData(player, new SimpleCollisionBox(xPos, yPos, zPos, xPos, yPos, zPos, false), xPos, yPos, zPos, this);
|
||||
}
|
||||
|
||||
protected void trackAttribute(ValuedAttribute valuedAttribute) {
|
||||
@@ -129,31 +131,38 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
if (relative) {
|
||||
// This only matters for 1.9+ clients, but it won't hurt 1.8 clients either... align for imprecision
|
||||
final double scale = trackedServerPosition.getScale();
|
||||
Vector3d vec3d;
|
||||
final double x;
|
||||
final double y;
|
||||
final double z;
|
||||
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16)) {
|
||||
vec3d = trackedServerPosition.withDelta(TrackedPosition.pack(relX, scale), TrackedPosition.pack(relY, scale), TrackedPosition.pack(relZ, scale));
|
||||
x = trackedServerPosition.withDeltaX(TrackedPosition.pack(relX, scale));
|
||||
y = trackedServerPosition.withDeltaY(TrackedPosition.pack(relY, scale));
|
||||
z = trackedServerPosition.withDeltaZ(TrackedPosition.pack(relZ, scale));
|
||||
} else {
|
||||
vec3d = trackedServerPosition.withDeltaLegacy(TrackedPosition.packLegacy(relX, scale), TrackedPosition.packLegacy(relY, scale), TrackedPosition.packLegacy(relZ, scale));
|
||||
x = trackedServerPosition.withDeltaLegacyX(TrackedPosition.packLegacy(relX, scale));
|
||||
y = trackedServerPosition.withDeltaLegacyY(TrackedPosition.packLegacy(relY, scale));
|
||||
z = trackedServerPosition.withDeltaLegacyZ(TrackedPosition.packLegacy(relZ, scale));
|
||||
}
|
||||
trackedServerPosition.setPos(vec3d);
|
||||
trackedServerPosition.setPos(x, y, z);
|
||||
} else {
|
||||
trackedServerPosition.setPos(new Vector3d(relX, relY, relZ));
|
||||
trackedServerPosition.setPos(relX, relY, relZ);
|
||||
// ViaVersion desync's here for teleports
|
||||
// It simply teleports the entity with its position divided by 32... ignoring the offset this causes.
|
||||
// Thanks a lot ViaVersion! Please don't fix this, or it will be a pain to support.
|
||||
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
|
||||
trackedServerPosition.setPos(new Vector3d(((int) (relX * 32)) / 32d, ((int) (relY * 32)) / 32d, ((int) (relZ * 32)) / 32d));
|
||||
trackedServerPosition.setPos(((int) (relX * 32)) / 32d, ((int) (relY * 32)) / 32d, ((int) (relZ * 32)) / 32d);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.oldPacketLocation = newPacketLocation;
|
||||
this.newPacketLocation = new ReachInterpolationData(player, oldPacketLocation.getPossibleLocationCombined(), trackedServerPosition, this);
|
||||
this.newPacketLocation = new ReachInterpolationData(player, oldPacketLocation.getPossibleLocationCombined(), trackedServerPosition.getX(), trackedServerPosition.getY(), trackedServerPosition.getZ(), this);
|
||||
|
||||
// TODO make config option to rewrite Rots to PosRots instead of expanding to handle this false
|
||||
// https://bugs.mojang.com/browse/MC-255263
|
||||
if (!hasPos &&
|
||||
(player.getClientVersion().isNewerThan(ClientVersion.V_1_21_4) ||
|
||||
(player.getClientVersion().isOlderThan(ClientVersion.V_1_20_2)) && player.getClientVersion().isNewerThan(ClientVersion.V_1_14_4))
|
||||
// Fixed in 1.21.9 again
|
||||
(player.getClientVersion().isOlderThan(ClientVersion.V_1_21_9) && player.getClientVersion().isNewerThan(ClientVersion.V_1_21_4)) ||
|
||||
(player.getClientVersion().isOlderThan(ClientVersion.V_1_20_2) && player.getClientVersion().isNewerThan(ClientVersion.V_1_14_4))
|
||||
) {
|
||||
newPacketLocation.cancelLerp();
|
||||
}
|
||||
@@ -207,7 +216,7 @@ public class PacketEntity extends TypedPacketEntity {
|
||||
public void setPositionRaw(GrimPlayer player, SimpleCollisionBox box) {
|
||||
// I'm disappointed in you mojang. Please don't set the packet position as it desyncs it...
|
||||
// But let's follow this flawed client-sided logic!
|
||||
this.trackedServerPosition.setPos(new Vector3d((box.maxX - box.minX) / 2 + box.minX, box.minY, (box.maxZ - box.minZ) / 2 + box.minZ));
|
||||
this.trackedServerPosition.setPos((box.maxX - box.minX) / 2 + box.minX, box.minY, (box.maxZ - box.minZ) / 2 + box.minZ);
|
||||
// This disables interpolation
|
||||
this.newPacketLocation = new ReachInterpolationData(player, box, this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package ac.grim.grimac.utils.function;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BlockStateAtPosPredicate {
|
||||
boolean test(WrappedBlockState state, int x, int y, int z);
|
||||
}
|
||||
Loaded 100 of 138 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user