mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-05 09:20:15 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e33aac29c5 | ||
|
|
42dc3af6c9 | ||
|
|
ff402d5096 | ||
|
|
06a72bf3bd | ||
|
|
3fb4291163 | ||
|
|
cc57b1d978 | ||
|
|
750b42b5ab | ||
|
|
ad017024ae | ||
|
|
5887d82c19 | ||
|
|
b1e5d01062 | ||
|
|
a50bf45176 | ||
|
|
e0b5ad2a33 | ||
|
|
e5ee94362c | ||
|
|
d877568b38 | ||
|
|
0edec77135 | ||
|
|
54445cdfdb | ||
|
|
8545d60791 | ||
|
|
11327e2890 | ||
|
|
ad511836f3 | ||
|
|
5cb79d3acf | ||
|
|
032dd4f94e | ||
|
|
e831c1a646 | ||
|
|
f50c90a690 | ||
|
|
1e7e4ba5f1 |
No files matched your search
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
contact@jonesdev.xyz.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
+7
-9
@@ -12,12 +12,9 @@ plugins {
|
||||
|
||||
apply(plugin = "io.toolebox.git-versioner")
|
||||
|
||||
rootProject.group = "xyz.jonesdev.sonar"
|
||||
val semanticVersion = "2.0.0"
|
||||
|
||||
versioner {
|
||||
pattern {
|
||||
pattern = "$semanticVersion-%h(-%c)"
|
||||
pattern = "$version-%h-%c-%b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +32,13 @@ subprojects {
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.projectlombok:lombok:1.18.28")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.28")
|
||||
compileOnly("org.projectlombok:lombok:1.18.30")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
|
||||
testCompileOnly("org.projectlombok:lombok:1.18.28")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.28")
|
||||
testCompileOnly("org.projectlombok:lombok:1.18.30")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
|
||||
compileOnly("io.netty:netty-all:4.1.97.Final") // netty
|
||||
compileOnly("io.netty:netty-all:4.1.98.Final") // netty
|
||||
|
||||
implementation("net.kyori:adventure-nbt:4.14.0") // nbt
|
||||
implementation("com.j256.ormlite:ormlite-jdbc:6.1") // ORM
|
||||
@@ -62,6 +59,7 @@ tasks {
|
||||
// Set the implementation version, so we can create exact version
|
||||
// information in-game and make it accessible to the user.
|
||||
attributes["Implementation-Version"] = version
|
||||
attributes["Implementation-Vendor"] = "Jones Development, Sonar Contributors"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
group=xyz.jonesdev.sonar
|
||||
version=2.0.1
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandRegistry;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.controller.VerifiedPlayerController;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEventManager;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.api.server.ServerWrapper;
|
||||
@@ -58,6 +59,11 @@ public interface Sonar {
|
||||
*/
|
||||
void reload();
|
||||
|
||||
@NotNull
|
||||
default SonarEventManager getEventManager() {
|
||||
return SonarEventManager.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default SonarVersion getVersion() {
|
||||
return SonarVersion.GET;
|
||||
|
||||
@@ -24,6 +24,7 @@ public final class SonarVersion {
|
||||
static final SonarVersion GET = new SonarVersion();
|
||||
private final String semanticVersion, full, formatted, commitSHA;
|
||||
private final int build;
|
||||
private final boolean isOnMainBranch;
|
||||
|
||||
SonarVersion() {
|
||||
final Package manifest = Sonar.class.getPackage();
|
||||
@@ -31,11 +32,12 @@ public final class SonarVersion {
|
||||
|
||||
if (versionString == null) {
|
||||
this.full = "<could not retrieve version>";
|
||||
this.semanticVersion = "<unknown>";
|
||||
this.commitSHA = "<unknown>";
|
||||
this.build = 0;
|
||||
this.semanticVersion = "<unknown version>";
|
||||
this.commitSHA = "<unknown commit>";
|
||||
this.build = -1;
|
||||
this.isOnMainBranch = false;
|
||||
this.formatted = full;
|
||||
Sonar.get().getLogger().error("Could not find version information (Is the manifest missing?)");
|
||||
Sonar.get().getLogger().warn("Could not find version information. Is the manifest missing?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,6 +45,7 @@ public final class SonarVersion {
|
||||
this.semanticVersion = versionString.split("-")[0];
|
||||
this.commitSHA = versionString.split("-")[1];
|
||||
this.build = Integer.parseInt(versionString.split("-")[2]);
|
||||
this.isOnMainBranch = versionString.split("-")[3].equals("main");
|
||||
this.formatted = semanticVersion + " build " + build + " (" + commitSHA + ")";
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,21 @@ package xyz.jonesdev.sonar.api.config;
|
||||
import lombok.Getter;
|
||||
import org.simpleyaml.configuration.comments.format.YamlCommentFormat;
|
||||
import org.simpleyaml.configuration.file.YamlFile;
|
||||
import org.simpleyaml.exceptions.InvalidConfigurationException;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.Sonar.LINE_SEPARATOR;
|
||||
|
||||
@Getter
|
||||
public final class SimpleYamlConfig {
|
||||
private final File file;
|
||||
private final YamlFile yaml;
|
||||
|
||||
private static final List<String> HEADER = Arrays.asList(
|
||||
@@ -48,6 +54,7 @@ public final class SimpleYamlConfig {
|
||||
throw new IllegalStateException("Could not create folder?!");
|
||||
}
|
||||
|
||||
this.file = file;
|
||||
this.yaml = new YamlFile(file.getPath());
|
||||
}
|
||||
|
||||
@@ -60,14 +67,42 @@ public final class SimpleYamlConfig {
|
||||
}
|
||||
|
||||
public void load() {
|
||||
String backupContent = null;
|
||||
try {
|
||||
// https://github.com/jonesdevelopment/sonar/issues/26
|
||||
// Only load the configuration if the file already exists
|
||||
if (yaml.exists()) {
|
||||
try {
|
||||
// Load all lines from the existing files before trying to load/overwrite it
|
||||
backupContent = String.join(LINE_SEPARATOR, Files.readAllLines(file.toPath()));
|
||||
} catch (IOException exception) {
|
||||
Sonar.get().getLogger().error("Error while creating backup configuration: {}", exception);
|
||||
}
|
||||
yaml.loadWithComments();
|
||||
} else {
|
||||
yaml.createOrLoadWithComments();
|
||||
}
|
||||
|
||||
// Always load the comment format and options
|
||||
yaml.setCommentFormat(YamlCommentFormat.PRETTY);
|
||||
yaml.createOrLoadWithComments();
|
||||
|
||||
yaml.options().headerFormatter().commentPrefix("# ");
|
||||
yaml.setHeader(String.join(Sonar.LINE_SEPARATOR, HEADER));
|
||||
yaml.setHeader(String.join(LINE_SEPARATOR, HEADER));
|
||||
} catch (InvalidConfigurationException exception) {
|
||||
Sonar.get().getLogger().error("Invalid configuration: {}", exception);
|
||||
|
||||
// Try to save a backup configuration to prevent data loss
|
||||
if (backupContent != null) {
|
||||
final File backupFile = new File(file.getParentFile(), file.getName() + ".broken");
|
||||
try (final FileWriter fileWriter = new FileWriter(backupFile, false)) {
|
||||
fileWriter.write(backupContent);
|
||||
Sonar.get().getLogger().info("Saved backup configuration to {}", backupFile.getName());
|
||||
} catch (Throwable throwable) {
|
||||
Sonar.get().getLogger().warn("Could not load from backup configuration: {}", throwable);
|
||||
}
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
Sonar.get().getLogger().error("Error while loading configuration: {}", exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,11 @@ public final class SonarConfiguration {
|
||||
DatabaseType.valueOf(generalConfig.getString("general.database.type", DatabaseType.NONE.name()).toUpperCase());
|
||||
|
||||
// Message settings
|
||||
messagesConfig = new SimpleYamlConfig(pluginFolder, "lang/" + LANGUAGE);
|
||||
// Only create a new messages configuration object if the preferred language changed
|
||||
// https://github.com/jonesdevelopment/sonar/issues/26
|
||||
if (messagesConfig == null || !messagesConfig.getFile().getName().equals(LANGUAGE + ".yml")) {
|
||||
messagesConfig = new SimpleYamlConfig(pluginFolder, "lang/" + LANGUAGE);
|
||||
}
|
||||
messagesConfig.load();
|
||||
|
||||
messagesConfig.getYaml().setComment("messages.prefix",
|
||||
|
||||
@@ -28,7 +28,8 @@ import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
|
||||
// Mostly taken from
|
||||
// https://github.com/Elytrium/LimboAuth/blob/master/src/main/java/net/elytrium/limboauth/dependencies/BaseLibrary.java
|
||||
@@ -77,13 +78,12 @@ public enum Dependency {
|
||||
|
||||
try (final InputStream inputStream = mvnRepoURL.openStream()) {
|
||||
Files.createDirectories(tempFilePath.getParent());
|
||||
Files.copy(inputStream, Files.createFile(tempFilePath), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(inputStream, Files.createFile(tempFilePath), REPLACE_EXISTING);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException(exception);
|
||||
}
|
||||
|
||||
Sonar.get().getLogger().info("Finished downloading {} ({}s)!",
|
||||
tempFilePath.getFileName(), timer.formattedDelay());
|
||||
Sonar.get().getLogger().info("Finished downloading {} ({}s)!", tempFilePath.getFileName(), timer);
|
||||
}
|
||||
|
||||
return tempFilePath.toUri().toURL();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event;
|
||||
|
||||
public interface SonarEvent {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SonarEventListener {
|
||||
|
||||
/**
|
||||
* Handles the publishing of an event
|
||||
*
|
||||
* @param event [Cancellable] Sonar event
|
||||
*/
|
||||
void handle(final SonarEvent event);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Vector;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class SonarEventManager {
|
||||
public static final SonarEventManager INSTANCE = new SonarEventManager();
|
||||
private static final Collection<SonarEventListener> EVENT_LISTENERS = new Vector<>(0);
|
||||
|
||||
/**
|
||||
* Publishes the given event to all listeners
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public void publish(final @NotNull SonarEvent event) {
|
||||
for (final SonarEventListener eventListener : EVENT_LISTENERS) {
|
||||
eventListener.handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers one (or more) event listeners
|
||||
*
|
||||
* @param listeners One (or more) listeners to register
|
||||
* @see #unregisterListener(SonarEventListener...) Unregister an event listener
|
||||
*/
|
||||
public synchronized void registerListener(final @NotNull SonarEventListener... listeners) {
|
||||
EVENT_LISTENERS.addAll(Arrays.asList(listeners));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters one (or more) event listeners
|
||||
*
|
||||
* @param listeners One (or more) listeners to unregister
|
||||
* @apiNote This does not have any effect if the given listeners are not registered
|
||||
* @see #registerListener(SonarEventListener...) Register an event listener
|
||||
*/
|
||||
public synchronized void unregisterListener(final @NotNull SonarEventListener... listeners) {
|
||||
EVENT_LISTENERS.removeAll(Arrays.asList(listeners));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event.impl;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class UserBlacklistedEvent implements SonarEvent {
|
||||
@ApiStatus.Experimental
|
||||
private final FallbackUser<?, ?> user;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event.impl;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifyFailedEvent implements SonarEvent {
|
||||
private final FallbackUser<?, ?> user;
|
||||
private final String reason;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event.impl;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifyJoinEvent implements SonarEvent {
|
||||
private final String username;
|
||||
private final FallbackUser<?, ?> user;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.event.impl;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifySuccessEvent implements SonarEvent {
|
||||
private final String username;
|
||||
private final UUID offlineUuid;
|
||||
private final FallbackUser<?, ?> user;
|
||||
private final long timeTakenToVerify;
|
||||
}
|
||||
+9
-1
@@ -24,13 +24,15 @@ import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.cappuccino.Cappuccino;
|
||||
import xyz.jonesdev.cappuccino.ExpiringCache;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserBlacklistedEvent;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifyFailedEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.statistics.Statistics;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface FallbackPlayer<X, Y> {
|
||||
public interface FallbackUser<X, Y> {
|
||||
@NotNull Fallback getFallback();
|
||||
|
||||
@NotNull X getPlayer();
|
||||
@@ -72,6 +74,9 @@ public interface FallbackPlayer<X, Y> {
|
||||
*/
|
||||
default void fail(final @Nullable String reason) {
|
||||
if (getChannel().isActive()) {
|
||||
// Call the VerifyFailedEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifyFailedEvent(this, reason));
|
||||
|
||||
disconnect(Sonar.get().getConfig().VERIFICATION_FAILED);
|
||||
|
||||
if (reason != null) {
|
||||
@@ -89,6 +94,9 @@ public interface FallbackPlayer<X, Y> {
|
||||
|
||||
// Check if the player has too many failed attempts
|
||||
if (PREVIOUS_FAILS.has(getInetAddress().toString())) {
|
||||
// Call the BotBlacklistedEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserBlacklistedEvent(this));
|
||||
|
||||
getFallback().getBlacklisted().put(getInetAddress().toString());
|
||||
getFallback().getLogger().info(Sonar.get().getConfig().VERIFICATION_BLACKLIST_LOG
|
||||
.replace("%ip%", Sonar.get().getConfig().formatAddress(getInetAddress()))
|
||||
@@ -35,7 +35,8 @@ public final class SystemTimer {
|
||||
return delay() >= amount;
|
||||
}
|
||||
|
||||
public String formattedDelay() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%.3f", delay() / 1000D);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public abstract class Verbose implements JVMProfiler {
|
||||
totalJoins = Statistics.TOTAL_TRAFFIC.get();
|
||||
|
||||
// Statistically determine the joins per second without any caches
|
||||
if (totalJoins > 0 && secondTimer.delay() >= 1000L) {
|
||||
if (totalJoins > 0 && secondTimer.elapsed(1000L)) {
|
||||
secondTimer.reset();
|
||||
joinsPerSecond = totalJoins - lastTotalJoins;
|
||||
lastTotalJoins = totalJoins;
|
||||
|
||||
@@ -8,7 +8,7 @@ apply(plugin = "net.minecrell.plugin-yml.bukkit")
|
||||
|
||||
bukkit {
|
||||
name = rootProject.name
|
||||
version = rootProject.version.toString()
|
||||
version = rootProject.version.toString().split("-")[0]
|
||||
main = "xyz.jonesdev.sonar.bukkit.SonarBukkitPlugin"
|
||||
authors = listOf("Jones Development", "Sonar Contributors")
|
||||
website = "https://jonesdev.xyz/discord/"
|
||||
|
||||
@@ -6,7 +6,7 @@ apply(plugin = "net.minecrell.plugin-yml.bungee")
|
||||
|
||||
bungee {
|
||||
name = rootProject.name
|
||||
version = rootProject.version.toString()
|
||||
version = rootProject.version.toString().split("-")[0]
|
||||
main = "xyz.jonesdev.sonar.bukkit.SonarBukkitPlugin"
|
||||
author = "Jones Development, Sonar Contributors"
|
||||
description = "Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)"
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackPlayer;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.handler.FallbackInitialHandler;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.Disconnect;
|
||||
@@ -35,7 +35,7 @@ import java.net.InetAddress;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPlayerWrapper implements FallbackPlayer<ChannelWrapper, FallbackInitialHandler> {
|
||||
public final class FallbackUserWrapper implements FallbackUser<ChannelWrapper, FallbackInitialHandler> {
|
||||
private final Fallback fallback;
|
||||
private final ChannelWrapper player;
|
||||
private final FallbackInitialHandler connection;
|
||||
+3
-3
@@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.FallbackPlayerWrapper;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.FallbackUserWrapper;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Objects;
|
||||
@@ -43,7 +43,7 @@ public final class FallbackInitialHandler extends InitialHandler {
|
||||
private static final @NotNull Fallback FALLBACK = Objects.requireNonNull(Sonar.get().getFallback());
|
||||
private ChannelWrapper channelWrapper;
|
||||
@SuppressWarnings("unused") // TODO: remove later
|
||||
private @Nullable FallbackPlayerWrapper player;
|
||||
private @Nullable FallbackUserWrapper player;
|
||||
|
||||
@Override
|
||||
public void connected(final ChannelWrapper channelWrapper) throws Exception {
|
||||
@@ -58,7 +58,7 @@ public final class FallbackInitialHandler extends InitialHandler {
|
||||
if (player == null && !Sonar.get().getVerifiedPlayerController().has(inetAddress)) {
|
||||
final Channel channel = channelWrapper.getHandle();
|
||||
|
||||
player = new FallbackPlayerWrapper(
|
||||
player = new FallbackUserWrapper(
|
||||
FALLBACK, channelWrapper, this,
|
||||
channel, channel.pipeline(), inetAddress,
|
||||
ProtocolVersion.fromId(getHandshake().getProtocolVersion())
|
||||
|
||||
@@ -63,13 +63,20 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
}
|
||||
|
||||
public final void initialize() {
|
||||
getLogger().info("Successfully booted in {}s!", launchTimer.formattedDelay());
|
||||
// Check if the branch is not the main branch to warn about unstable versions
|
||||
if (!getVersion().isOnMainBranch()) {
|
||||
getLogger().warn("You are currently using an unofficial experimental branch.");
|
||||
getLogger().warn("It is highly recommended to use the latest stable release of Sonar:");
|
||||
getLogger().warn("https://github.com/jonesdevelopment/sonar/releases");
|
||||
}
|
||||
|
||||
getLogger().info("Successfully booted in {}s!", launchTimer);
|
||||
getLogger().info("Initializing shared components...");
|
||||
|
||||
// Reload configuration
|
||||
reload();
|
||||
|
||||
getLogger().info("Successfully initialized components in {}s!", launchTimer.formattedDelay());
|
||||
getLogger().info("Successfully initialized components in {}s!", launchTimer);
|
||||
getLogger().info("Enabling all tasks and features...");
|
||||
|
||||
try {
|
||||
@@ -77,7 +84,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
enable();
|
||||
|
||||
// Done
|
||||
getLogger().info("Done ({}s)!", launchTimer.formattedDelay());
|
||||
getLogger().info("Done ({}s)!", launchTimer);
|
||||
} catch (Throwable throwable) {
|
||||
// An error has occurred
|
||||
getLogger().error("An error has occurred while launching Sonar: {}", throwable);
|
||||
|
||||
+49
-44
@@ -24,7 +24,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackPlayer;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifySuccessEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.model.VerifiedPlayer;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@@ -41,7 +42,7 @@ import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.*;
|
||||
|
||||
public final class FallbackVerificationHandler implements FallbackPacketListener {
|
||||
private final @NotNull FallbackPlayer<?, ?> player;
|
||||
private final @NotNull FallbackUser<?, ?> user;
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
private final short transactionId;
|
||||
@@ -72,10 +73,10 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
private final boolean canMove;
|
||||
}
|
||||
|
||||
public FallbackVerificationHandler(final @NotNull FallbackPlayer<?, ?> player,
|
||||
public FallbackVerificationHandler(final @NotNull FallbackUser<?, ?> user,
|
||||
final @NotNull String username,
|
||||
final @NotNull UUID uuid) {
|
||||
this.player = player;
|
||||
this.user = user;
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.transactionId = (short) RANDOM.nextInt();
|
||||
@@ -83,14 +84,14 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
this.state = State.KEEP_ALIVE;
|
||||
|
||||
// Send LoginSuccess packet to make the client think they are joining the server
|
||||
player.write(new ServerLoginSuccess(username, uuid));
|
||||
user.write(new ServerLoginSuccess(username, uuid));
|
||||
|
||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
// 1.7 players don't have KeepAlive packets in the login process
|
||||
sendJoinGamePacket();
|
||||
} else {
|
||||
// Send first KeepAlive to check if the connection is somewhat responsive
|
||||
player.write(new KeepAlive(verifyKeepAliveId));
|
||||
user.write(new KeepAlive(verifyKeepAliveId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// and go on with the flow of the verification.
|
||||
state = State.TRANSACTION;
|
||||
// Send a transaction with a
|
||||
player.write(new Transaction(
|
||||
user.write(new Transaction(
|
||||
0, transactionId, false
|
||||
));
|
||||
}
|
||||
@@ -109,7 +110,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// and go on with the flow of the verification.
|
||||
state = State.CLIENT_SETTINGS;
|
||||
// Select the JoinGame packet for the respective protocol version
|
||||
player.write(getJoinPacketForVersion(player.getProtocolVersion()));
|
||||
user.write(getJoinPacketForVersion(user.getProtocolVersion()));
|
||||
}
|
||||
|
||||
private void sendChunkData() {
|
||||
@@ -117,38 +118,36 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// and go on with the flow of the verification.
|
||||
state = State.POSITION;
|
||||
// Teleport player into the fake lobby by sending an empty chunk
|
||||
player.write(EMPTY_CHUNK_DATA);
|
||||
user.write(EMPTY_CHUNK_DATA);
|
||||
// Send an UpdateSectionBlocks packet with a platform of blocks
|
||||
// to check if the player collides with the solid platform.
|
||||
player.write(UPDATE_SECTION_BLOCKS);
|
||||
user.write(UPDATE_SECTION_BLOCKS);
|
||||
}
|
||||
|
||||
private static boolean validateClientLocale(final @NotNull FallbackPlayer<?, ?> player, final String locale) {
|
||||
private static boolean validateClientLocale(final @SuppressWarnings("unused") @NotNull FallbackUser<?, ?> user,
|
||||
final String locale) {
|
||||
// Check the client locale by performing a simple regex check on it
|
||||
final Pattern pattern = Sonar.get().getConfig().VALID_LOCALE_REGEX;
|
||||
return pattern.matcher(locale).matches(); // Disallow non-ascii characters (by default)
|
||||
}
|
||||
|
||||
private static boolean validateClientBrand(final @NotNull FallbackPlayer<?, ?> player, final ByteBuf content) {
|
||||
private static boolean validateClientBrand(final @NotNull FallbackUser<?, ?> user, final ByteBuf content) {
|
||||
// We have to catch every DecoderException, so we can fail and punish
|
||||
// the player instead of only disconnecting them due to an exception.
|
||||
try {
|
||||
// Regex pattern for validating client brands
|
||||
final Pattern pattern = Sonar.get().getConfig().VALID_BRAND_REGEX;
|
||||
// 1.7 has some very weird issues when trying to decode the client brand
|
||||
final boolean legacy = player.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0;
|
||||
final int cap = Sonar.get().getConfig().MAXIMUM_BRAND_LENGTH;
|
||||
// Read the client brand using our custom readString method that supports 1.7.
|
||||
// The legacy version of readString does not compare the string length
|
||||
// with the VarInt sent by the client.
|
||||
final String read = ProtocolUtil.readString(content, cap, legacy);
|
||||
// TODO: fix this?
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) return true;
|
||||
// No need to check for empty or too long client brands since
|
||||
// ProtocolUtil#readString already does exactly that.
|
||||
final String read = ProtocolUtil.readString(content, Sonar.get().getConfig().MAXIMUM_BRAND_LENGTH);
|
||||
// Regex pattern for validating client brands
|
||||
final Pattern pattern = Sonar.get().getConfig().VALID_BRAND_REGEX;
|
||||
return !read.equals("Vanilla") // The normal brand is always lowercase
|
||||
&& pattern.matcher(read).matches(); // Disallow non-ascii characters (by default)
|
||||
} catch (DecoderException exception) {
|
||||
// Fail if the string (client brand) could not be decoded properly
|
||||
player.fail("could not decode string");
|
||||
user.fail("could not decode string");
|
||||
// Throw the exception so we don't continue checking
|
||||
throw exception;
|
||||
}
|
||||
@@ -164,17 +163,20 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// Check for timeout since the player could be sending packets but not important ones
|
||||
final long timeout = Sonar.get().getConfig().VERIFICATION_TIMEOUT;
|
||||
// Check if the time limit has exceeded
|
||||
if (login.delay() > timeout) {
|
||||
player.getChannel().close();
|
||||
if (login.elapsed(timeout)) {
|
||||
user.getChannel().close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet instanceof KeepAlive) {
|
||||
final KeepAlive keepAlive = (KeepAlive) packet;
|
||||
|
||||
// 1.7-1.8.9 are sending a KeepAlive packet with the ID 0 every 20 ticks
|
||||
if (keepAlive.getId() == 0 && user.getProtocolVersion().compareTo(MINECRAFT_1_8) <= 0) return;
|
||||
|
||||
// Check if we are currently expecting a KeepAlive packet
|
||||
assertState(State.KEEP_ALIVE);
|
||||
|
||||
final KeepAlive keepAlive = (KeepAlive) packet;
|
||||
|
||||
checkFrame(keepAlive.getId() == verifyKeepAliveId, "invalid KeepAlive ID");
|
||||
|
||||
// The correct KeepAlive packet has been received
|
||||
@@ -185,7 +187,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
final ClientSettings clientSettings = (ClientSettings) packet;
|
||||
|
||||
// Validate the locale using a regex to filter unwanted characters.
|
||||
checkFrame(validateClientLocale(player, clientSettings.getLocale()), "invalid locale");
|
||||
checkFrame(validateClientLocale(user, clientSettings.getLocale()), "invalid locale");
|
||||
|
||||
// Clients sometimes mess up the ClientSettings or PluginMessage packet.
|
||||
if (state == State.CLIENT_SETTINGS) {
|
||||
@@ -203,11 +205,11 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// Check if the channel is correct - 1.13 uses the new namespace
|
||||
// system ('minecraft:' + channel) and anything below 1.13 uses
|
||||
// the legacy namespace system ('MC|' + channel).
|
||||
final boolean v1_13 = player.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0;
|
||||
final boolean v1_13 = user.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0;
|
||||
checkFrame(pluginMessage.getChannel().equals("MC|Brand") || v1_13, "invalid channel");
|
||||
|
||||
// Validate the client branding using a regex to filter unwanted characters.
|
||||
checkFrame(validateClientBrand(player, pluginMessage.content()), "invalid client brand");
|
||||
checkFrame(validateClientBrand(user, pluginMessage.content()), "invalid client brand");
|
||||
|
||||
// Clients sometimes mess up the ClientSettings or PluginMessage packet.
|
||||
if (state == State.PLUGIN_MESSAGE) {
|
||||
@@ -235,12 +237,12 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
}
|
||||
|
||||
// Make sure the player is unable to fly (the player is in spectator mode)
|
||||
player.write(DEFAULT_ABILITIES);
|
||||
user.write(DEFAULT_ABILITIES);
|
||||
// Teleport the player to the spawn position
|
||||
player.write(SPAWN_TELEPORT);
|
||||
user.write(SPAWN_TELEPORT);
|
||||
|
||||
// 1.7-1.8 clients do not have a TeleportConfirm packet
|
||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) <= 0) {
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) <= 0) {
|
||||
// Immediately send the chunk data as 1.7-1.8 can't confirm teleports
|
||||
sendChunkData();
|
||||
} else {
|
||||
@@ -304,9 +306,8 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// The onGround property can never be true when we aren't checking for collisions
|
||||
checkFrame(!ground || state == State.COLLISIONS, "invalid ground state");
|
||||
|
||||
// Skip teleports using this small check
|
||||
// Check if the chunk might be unloaded
|
||||
if (deltaY > 0.07) {
|
||||
|
||||
// Verify the player if they sent correct movement packets
|
||||
if (movementTick++ >= MAX_MOVEMENT_TICK) {
|
||||
if (Sonar.get().getConfig().CHECK_COLLISIONS) {
|
||||
@@ -334,26 +335,27 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// Checking collisions is disabled, just finish verification
|
||||
finish();
|
||||
}
|
||||
} else if (y >= DEFAULT_Y_COLLIDE_POSITION && y <= DYNAMIC_SPAWN_Y_POSITION) {
|
||||
} else if (y <= DYNAMIC_SPAWN_Y_POSITION && y >= DEFAULT_Y_COLLIDE_POSITION) {
|
||||
// This is a basic gravity check that predicts the next y position
|
||||
final double predictedY = PREPARED_MOVEMENT_PACKETS[movementTick];
|
||||
final double offsetY = Math.abs(deltaY - predictedY);
|
||||
|
||||
// Check if the y motion is roughly equal to the predicted value
|
||||
checkFrame(offsetY < 0.01, "invalid offset: " + y + ", " + offsetY);
|
||||
final String verbose = String.format("%d: %.7f %.10f %.10f!%.10f", movementTick, y, offsetY, deltaY, predictedY);
|
||||
checkFrame(offsetY < 0.01, verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertState(final @NotNull State expectedState) {
|
||||
if (expectedState != state) {
|
||||
player.fail("expected " + expectedState + ", got " + state);
|
||||
user.fail("expected " + expectedState + ", got " + state);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFrame(final boolean condition, final String message) {
|
||||
if (!condition) {
|
||||
player.fail(message);
|
||||
user.fail(message);
|
||||
throw new CorruptedFrameException(message);
|
||||
}
|
||||
}
|
||||
@@ -361,21 +363,24 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
private void finish() {
|
||||
state = State.SUCCESS;
|
||||
|
||||
player.getFallback().getConnected().remove(username);
|
||||
user.getFallback().getConnected().remove(username);
|
||||
|
||||
// Add verified player to the database
|
||||
final VerifiedPlayer verifiedPlayer = new VerifiedPlayer(
|
||||
player.getInetAddress().toString(), uuid, login.getStart()
|
||||
user.getInetAddress().toString(), uuid, login.getStart()
|
||||
);
|
||||
Sonar.get().getVerifiedPlayerController().add(verifiedPlayer);
|
||||
|
||||
// Disconnect player with the verification success message
|
||||
player.disconnect(Sonar.get().getConfig().VERIFICATION_SUCCESS);
|
||||
// Call the VerifySuccessEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifySuccessEvent(username, uuid, user, login.delay()));
|
||||
|
||||
player.getFallback().getLogger().info(
|
||||
// Disconnect player with the verification success message
|
||||
user.disconnect(Sonar.get().getConfig().VERIFICATION_SUCCESS);
|
||||
|
||||
user.getFallback().getLogger().info(
|
||||
Sonar.get().getConfig().VERIFICATION_SUCCESSFUL_LOG
|
||||
.replace("%name%", username)
|
||||
.replace("%time%", login.formattedDelay())
|
||||
.replace("%time%", login.toString())
|
||||
);
|
||||
}
|
||||
}
|
||||
+12
-12
@@ -23,21 +23,21 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackPlayer;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
private final FallbackPlayer<?, ?> connection;
|
||||
private final FallbackUser<?, ?> user;
|
||||
private final FallbackPacketRegistry.ProtocolRegistry registry;
|
||||
private final FallbackPacketListener listener;
|
||||
|
||||
public FallbackPacketDecoder(final @NotNull FallbackPlayer<?, ?> connection,
|
||||
public FallbackPacketDecoder(final @NotNull FallbackUser<?, ?> user,
|
||||
final @NotNull FallbackPacketListener listener) {
|
||||
this.connection = connection;
|
||||
this.user = user;
|
||||
this.registry = FallbackPacketRegistry.SONAR.getProtocolRegistry(
|
||||
FallbackPacketRegistry.Direction.SERVERBOUND, connection.getProtocolVersion()
|
||||
FallbackPacketRegistry.Direction.SERVERBOUND, user.getProtocolVersion()
|
||||
);
|
||||
this.listener = listener;
|
||||
}
|
||||
@@ -64,14 +64,14 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
doLengthSanityChecks(byteBuf, packet);
|
||||
|
||||
try {
|
||||
packet.decode(byteBuf, connection.getProtocolVersion());
|
||||
packet.decode(byteBuf, user.getProtocolVersion());
|
||||
} catch (Throwable throwable) {
|
||||
connection.fail("could not decode packet");
|
||||
user.fail("could not decode packet");
|
||||
throw new CorruptedFrameException("Failed to decode packet");
|
||||
}
|
||||
|
||||
if (byteBuf.isReadable()) {
|
||||
connection.fail("could not read packet to end");
|
||||
user.fail("could not read packet to end (" + byteBuf.readableBytes() + " bytes left)");
|
||||
throw new CorruptedFrameException("Could not read packet to end");
|
||||
}
|
||||
|
||||
@@ -85,15 +85,15 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private void doLengthSanityChecks(final ByteBuf byteBuf,
|
||||
final @NotNull FallbackPacket packet) throws Exception {
|
||||
final int expectedMaxLen = packet.expectedMaxLength(byteBuf, connection.getProtocolVersion());
|
||||
final int expectedMaxLen = packet.expectedMaxLength(byteBuf, user.getProtocolVersion());
|
||||
if (expectedMaxLen != -1 && byteBuf.readableBytes() > expectedMaxLen) {
|
||||
connection.fail("packet too large");
|
||||
user.fail("packet too large");
|
||||
throw new CorruptedFrameException("Packet too large");
|
||||
}
|
||||
|
||||
final int expectedMinLen = packet.expectedMinLength(byteBuf, connection.getProtocolVersion());
|
||||
final int expectedMinLen = packet.expectedMinLength(byteBuf, user.getProtocolVersion());
|
||||
if (byteBuf.readableBytes() < expectedMinLen) {
|
||||
connection.fail("packet too small");
|
||||
user.fail("packet too small");
|
||||
throw new CorruptedFrameException("Packet too small");
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ public enum FallbackPacketRegistry {
|
||||
map(0x20, MINECRAFT_1_19_3, true),
|
||||
map(0x24, MINECRAFT_1_19_4, true));
|
||||
clientbound.register(UpdateSectionBlocks.class, UpdateSectionBlocks::new,
|
||||
map(34, MINECRAFT_1_7_2, true),
|
||||
map(0x22, MINECRAFT_1_7_2, true),
|
||||
map(16, MINECRAFT_1_9, true),
|
||||
map(15, MINECRAFT_1_13, true),
|
||||
map(16, MINECRAFT_1_15, true),
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ public class FallbackPreparer {
|
||||
8, DYNAMIC_SPAWN_Y_POSITION, 8,
|
||||
0f, 0f,
|
||||
RANDOM.nextInt(Short.MAX_VALUE),
|
||||
true
|
||||
false // 1.7 support
|
||||
);
|
||||
|
||||
// Prepare collision platform positions
|
||||
|
||||
+12
-6
@@ -38,14 +38,20 @@ public final class DefaultSpawnPosition implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
final long encoded = protocolVersion.compareTo(MINECRAFT_1_14) < 0
|
||||
? ((x & 0x3FFFFFFL) << 38) | ((y & 0xFFFL) << 26) | (z & 0x3FFFFFFL)
|
||||
: ((x & 0x3FFFFFFL) << 38) | ((y & 0x3FFFFFFL) << 12) | (z & 0xFFFL);
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
byteBuf.writeInt(x);
|
||||
byteBuf.writeInt(y);
|
||||
byteBuf.writeInt(z);
|
||||
} else {
|
||||
final long encoded = protocolVersion.compareTo(MINECRAFT_1_14) < 0
|
||||
? ((x & 0x3FFFFFFL) << 38) | ((y & 0xFFFL) << 26) | (z & 0x3FFFFFFL)
|
||||
: ((x & 0x3FFFFFFL) << 38) | ((y & 0x3FFFFFFL) << 12) | (z & 0xFFFL);
|
||||
|
||||
byteBuf.writeLong(encoded);
|
||||
byteBuf.writeLong(encoded);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0) {
|
||||
byteBuf.writeFloat(angle);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0) {
|
||||
byteBuf.writeFloat(angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-24
@@ -48,10 +48,20 @@ public final class EmptyChunkData implements FallbackPacket {
|
||||
private static final byte[] LEGACY_FILLER_BYTES = new byte[256];
|
||||
private static final byte[] MODERN_FILLER_BYTES = new byte[1024];
|
||||
|
||||
private static final long[] MASK;
|
||||
|
||||
// Prepare nbt for 1.18 and pre-1.18
|
||||
private static final CompoundBinaryTag MODERN_TAG, LEGACY_TAG;
|
||||
|
||||
static {
|
||||
final BitSet bitSet = new BitSet();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
bitSet.set(i, false);
|
||||
}
|
||||
|
||||
MASK = bitSet.toLongArray();
|
||||
|
||||
MODERN_TAG = prepareNBT(false);
|
||||
LEGACY_TAG = prepareNBT(true);
|
||||
}
|
||||
@@ -73,32 +83,26 @@ public final class EmptyChunkData implements FallbackPacket {
|
||||
byteBuf.writeInt(x);
|
||||
byteBuf.writeInt(z);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
byteBuf.writeBoolean(true);
|
||||
}
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17_1) <= 0) {
|
||||
writeVarInt(byteBuf, MASK.length);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16) >= 0 && protocolVersion.compareTo(MINECRAFT_1_16_2) < 0) {
|
||||
byteBuf.writeBoolean(true);
|
||||
}
|
||||
for (final long l : MASK) {
|
||||
byteBuf.writeLong(l);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
byteBuf.writeBoolean(true); // Full chunk
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) == 0) {
|
||||
byteBuf.writeShort(1);
|
||||
} else {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0
|
||||
&& protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_16_2) < 0) {
|
||||
byteBuf.writeBoolean(true); // Ignore old data
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) > 0) {
|
||||
writeVarInt(byteBuf, 0);
|
||||
}
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_18) < 0) {
|
||||
final BitSet bitSet = new BitSet();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
bitSet.set(i, false);
|
||||
}
|
||||
|
||||
long[] mask = bitSet.toLongArray();
|
||||
writeVarInt(byteBuf, mask.length);
|
||||
|
||||
for (long l : mask) {
|
||||
byteBuf.writeLong(l);
|
||||
} else {
|
||||
byteBuf.writeShort(1); // Fix void chunk
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +125,12 @@ public final class EmptyChunkData implements FallbackPacket {
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13) < 0) {
|
||||
writeArray(byteBuf, LEGACY_FILLER_BYTES); // 1.8 - 1.12.2
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
writeArray(byteBuf, LEGACY_FILLER_BYTES); // 1.8 - 1.12.2
|
||||
} else {
|
||||
byteBuf.writeInt(0); // Compressed size.
|
||||
byteBuf.writeBytes(new byte[2]); // 1.7
|
||||
}
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_15) < 0) {
|
||||
writeArray(byteBuf, MODERN_FILLER_BYTES); // 1.13 - 1.14.4
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_18) < 0) {
|
||||
|
||||
+14
@@ -36,6 +36,10 @@ public final class Position implements FallbackPacket {
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
x = byteBuf.readDouble();
|
||||
// https://github.com/jonesdevelopment/sonar/issues/20
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
byteBuf.skipBytes(8);
|
||||
}
|
||||
y = byteBuf.readDouble();
|
||||
z = byteBuf.readDouble();
|
||||
onGround = byteBuf.readBoolean();
|
||||
@@ -45,4 +49,14 @@ public final class Position implements FallbackPacket {
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0 ? 33 : 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
+26
-6
@@ -41,6 +41,10 @@ public final class PositionLook implements FallbackPacket {
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
x = byteBuf.readDouble();
|
||||
// https://github.com/jonesdevelopment/sonar/issues/20
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
byteBuf.skipBytes(8);
|
||||
}
|
||||
y = byteBuf.readDouble();
|
||||
z = byteBuf.readDouble();
|
||||
yaw = byteBuf.readFloat();
|
||||
@@ -55,14 +59,30 @@ public final class PositionLook implements FallbackPacket {
|
||||
byteBuf.writeDouble(z);
|
||||
byteBuf.writeFloat(yaw);
|
||||
byteBuf.writeFloat(pitch);
|
||||
byteBuf.writeByte(0x00);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
writeVarInt(byteBuf, teleportId);
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
byteBuf.writeBoolean(onGround);
|
||||
} else {
|
||||
byteBuf.writeByte(0x00);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0 && protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
byteBuf.writeBoolean(true); // Dismount vehicle
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
writeVarInt(byteBuf, teleportId);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0
|
||||
&& protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
byteBuf.writeBoolean(true); // Always dismount vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0 ? 41 : 33;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
return 33;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -41,7 +41,7 @@ public final class Transaction implements FallbackPacket {
|
||||
byteBuf.writeByte(windowId);
|
||||
byteBuf.writeShort((short) id);
|
||||
// The "accepted" field is actually really unnecessary since
|
||||
// the client must accept the transaction either way.
|
||||
// it's never even used in the client.
|
||||
byteBuf.writeBoolean(accepted);
|
||||
} else {
|
||||
byteBuf.writeInt(id);
|
||||
|
||||
+17
-7
@@ -26,15 +26,14 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.ChangedBlock;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_16_2;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_20;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarLong;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(exclude = {"changedBlocks"})
|
||||
public final class UpdateSectionBlocks implements FallbackPacket {
|
||||
private int sectionX, sectionZ;
|
||||
private ChangedBlock[] changedBlocks;
|
||||
@@ -44,11 +43,22 @@ public final class UpdateSectionBlocks implements FallbackPacket {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_2) < 0) {
|
||||
byteBuf.writeInt(sectionX);
|
||||
byteBuf.writeInt(sectionZ);
|
||||
writeVarInt(byteBuf, changedBlocks.length);
|
||||
|
||||
for (final ChangedBlock block : changedBlocks) {
|
||||
byteBuf.writeShort(block.getLegacyChunkPosCrammed());
|
||||
writeVarInt(byteBuf, block.getType().getId(protocolVersion));
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
byteBuf.writeShort((short) changedBlocks.length);
|
||||
byteBuf.writeInt(4 * changedBlocks.length);
|
||||
|
||||
for (final ChangedBlock block : changedBlocks) {
|
||||
byteBuf.writeShort(block.getLegacyChunkPosCrammed());
|
||||
byteBuf.writeShort(block.getType().getId(protocolVersion));
|
||||
}
|
||||
} else {
|
||||
writeVarInt(byteBuf, changedBlocks.length);
|
||||
|
||||
for (final ChangedBlock block : changedBlocks) {
|
||||
byteBuf.writeShort(block.getLegacyChunkPosCrammed());
|
||||
writeVarInt(byteBuf, block.getType().getId(protocolVersion));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final ChangedBlock lastChangedBlock = changedBlocks[changedBlocks.length - 1];
|
||||
|
||||
+6
-19
@@ -53,34 +53,21 @@ public class ProtocolUtil {
|
||||
|
||||
public static @NotNull String readString(final ByteBuf buf,
|
||||
final int cap) throws CorruptedFrameException {
|
||||
return readString(buf, cap, false);
|
||||
}
|
||||
|
||||
public static @NotNull String readString(final ByteBuf buf,
|
||||
final int cap,
|
||||
final boolean legacy) throws CorruptedFrameException {
|
||||
final int length = VarIntUtil.readVarInt(buf);
|
||||
|
||||
checkFrame(length >= 0, "Got a negative-length string");
|
||||
checkFrame(length <= cap * 3, "Bad string size");
|
||||
String str;
|
||||
if (legacy) {
|
||||
// TODO: length checking?
|
||||
str = buf.toString(StandardCharsets.UTF_8);
|
||||
buf.skipBytes(buf.readableBytes());
|
||||
} else {
|
||||
checkFrame(buf.isReadable(length), "Got an invalid string length");
|
||||
str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
||||
buf.skipBytes(length);
|
||||
}
|
||||
checkFrame(buf.isReadable(length), "Got an invalid string length");
|
||||
final String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
||||
buf.skipBytes(length);
|
||||
checkFrame(str.length() <= cap, "Got a too-long string");
|
||||
return str;
|
||||
}
|
||||
|
||||
public static void writeString(final ByteBuf buf, final CharSequence str) {
|
||||
public static void writeString(final ByteBuf byteBuf, final CharSequence str) {
|
||||
final int size = ByteBufUtil.utf8Bytes(str);
|
||||
VarIntUtil.writeVarInt(buf, size);
|
||||
buf.writeCharSequence(str, StandardCharsets.UTF_8);
|
||||
VarIntUtil.writeVarInt(byteBuf, size);
|
||||
byteBuf.writeCharSequence(str, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static void writeUUID(final @NotNull ByteBuf byteBuf, final @NotNull UUID uuid) {
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.nio.file.Path;
|
||||
@Getter
|
||||
@Plugin(id = "sonar",
|
||||
name = "Sonar",
|
||||
version = "2.0.0",
|
||||
version = "2.0.1",
|
||||
authors = {"Jones Development", "Sonar Contributors"},
|
||||
url = "https://jonesdev.xyz/discord/",
|
||||
description = "Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)"
|
||||
|
||||
+5
-1
@@ -29,6 +29,7 @@ import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifyJoinEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.statistics.Statistics;
|
||||
@@ -106,7 +107,7 @@ public final class FallbackSessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
// Create an instance for the Fallback connection
|
||||
final FallbackPlayerWrapper fallbackPlayer = new FallbackPlayerWrapper(
|
||||
final FallbackUserWrapper fallbackPlayer = new FallbackUserWrapper(
|
||||
fallback, connectedPlayer, mcConnection, mcConnection.getChannel(),
|
||||
mcConnection.getChannel().pipeline(), inetAddress,
|
||||
ProtocolVersion.fromId(connectedPlayer.getProtocolVersion().getProtocol())
|
||||
@@ -132,6 +133,9 @@ public final class FallbackSessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Call the VerifyJoinEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifyJoinEvent(gameProfile.getName(), fallbackPlayer));
|
||||
|
||||
// Mark the player as connected → verifying players
|
||||
fallback.getConnected().put(gameProfile.getName(), inetAddress);
|
||||
|
||||
|
||||
+4
-2
@@ -25,19 +25,21 @@ import io.netty.channel.ChannelPipeline;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackPlayer;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.Disconnect;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersion"})
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPlayerWrapper implements FallbackPlayer<ConnectedPlayer, MinecraftConnection> {
|
||||
public final class FallbackUserWrapper implements FallbackUser<ConnectedPlayer, MinecraftConnection> {
|
||||
private final Fallback fallback;
|
||||
private final ConnectedPlayer player;
|
||||
private final MinecraftConnection connection;
|
||||
Reference in new issue
Block a user