Skip to content

Commit

Permalink
Implement BungeeCord support (#76)
Browse files Browse the repository at this point in the history
* feat: remove Velocity's (de-)compression dependency

* feat: [wip] implement Fallback for BungeeCord

* feat: disable legacy pipelines by default (can be enabled again)

* fix(bungee): NPE on handshake

* fix: use correct protocol version for channel initialization

* feat: implement login packet spam fix

* feat(bungee): implement duplicate login/status packet spam fix

* feat: implement cleaner FallbackHandlerBoss

* fix(bungee): traffic handler not being injected properly

* fix(bungee): run verification check after running everything in the event loop

* fix(bungee): only throw exceptions for up- and downstream bridge

* fix(bungee): total traffic statistic not working

* fix(bukkit, bungee): logger wrapper not supporting arguments

* fix(bungee): perform the already-online check by the username, not the uuid

* style: fix FallbackChannelHandler comments

* style: don't annotate FallbackUserWrapper as EqualsAndHashCode

* style: fix ToString annotation in EmptyChunkData

* feat(fallback): remove 1.19/1.19.1 chat nbt mappings

* feat: use expiring cache for joins per second counter

* fix(bungee): don't log the join attempt if the login is canceled

* fix(all): lockdown mode not being saved correctly

* fix(bungee): closeWith method only working during verification

* fix(bungee): use Velocity's server write mark

* fix(bungee): don't set BB allocator if it's already set
  • Loading branch information
jonesdevelopment authored Oct 22, 2023
1 parent bf9e77a commit 8361f19
Show file tree
Hide file tree
Showing 30 changed files with 569 additions and 462 deletions.
6 changes: 3 additions & 3 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
- [Fallback](https://github.com/jonesdevelopment/sonar#fallback) is Sonar's main component designed to prevent
all types of bots.
- Spigot & BungeeCord
- The compression method is changed
to [Velocity's libdeflate](https://github.com/PaperMC/Velocity/tree/dev/3.0.0/native).
- Duplicate login/status packet spam fix
- Run server ping handling asynchronously
- The Varint decoder is updated to Velocity's
improved [MinecraftVarintFrameDecoder](https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java).

Expand Down Expand Up @@ -91,4 +91,4 @@ Sonar is licensed under the [GNU General Public License 3.0](https://www.gnu.org

- Special thanks to the [contributors of Sonar](https://github.com/jonesdevelopment/sonar/graphs/contributors).
- The nbt mappings were taken from [LimboAPI](https://github.com/Elytrium/LimboAPI).
- The compression and Varint decoding was taken from [Velocity](https://github.com/PaperMC/Velocity).
- The Varint decoding was taken from [Velocity](https://github.com/PaperMC/Velocity).
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ allprojects {
repositories {
mavenCentral() // Lombok
maven(url = "https://jitpack.io") // simple-yaml
maven(url = "https://repo.papermc.io/repository/maven-public") // Velocity natives
maven(url = "https://repo.papermc.io/repository/maven-public") // Velocity API
maven(url = "https://repo.jonesdev.xyz/releases/") // Bungee & Velocity proxy module
}
}
Expand Down Expand Up @@ -83,7 +83,6 @@ tasks {
relocate("net.kyori.adventure.nbt", "xyz.jonesdev.sonar.libs.nbt")
relocate("com.google.gson", "xyz.jonesdev.sonar.libs.gson")
relocate("com.j256.ormlite", "xyz.jonesdev.sonar.libs.ormlite")
relocate("com.velocitypowered.natives", "xyz.jonesdev.sonar.libs.natives")
}

compileJava {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public final class Fallback {
private final Map<String, InetAddress> connected = new ConcurrentHashMap<>();
// Only block the player for a few minutes to avoid issues
private final ExpiringCache<String> blacklisted = Cappuccino.buildExpiring(
10L, TimeUnit.MINUTES, 2500L
10L, TimeUnit.MINUTES, 5000L
);
private final @NotNull FallbackQueue queue = FallbackQueue.INSTANCE;
private final @NotNull FallbackRatelimiter ratelimiter = FallbackRatelimiter.INSTANCE;
Expand All @@ -66,6 +66,6 @@ public void error(final String message, final Object... args) {

public boolean isPotentiallyUnderAttack() {
final int min = SONAR.getConfig().getMinPlayersForAttack();
return connected.size() > min || queue.getQueuedPlayers().size() > min;
return Sonar.get().getVerboseHandler().getJoinsPerSecond().estimatedSize() > min;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;

@RequiredArgsConstructor
public enum ProtocolVersion {
Expand Down Expand Up @@ -73,6 +70,7 @@ public enum ProtocolVersion {
private final int protocol;

public static final ProtocolVersion MINIMUM_VERSION = MINECRAFT_1_7_2;
public static final ProtocolVersion LATEST_VERSION;
public static final Map<Integer, ProtocolVersion> ID_TO_PROTOCOL_CONSTANT;
public static final Set<ProtocolVersion> SUPPORTED_VERSIONS;

Expand All @@ -96,7 +94,8 @@ public enum ProtocolVersion {
}
}

SUPPORTED_VERSIONS = versions;
SUPPORTED_VERSIONS = Collections.unmodifiableSet(versions);
LATEST_VERSION = new ArrayList<>(versions).get(versions.size() - 1);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

package xyz.jonesdev.sonar.api.logger;

import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Array;

/**
* The whole purpose of doing this is, so we can have one logger for every module.
* We wouldn't need to get the plugin logger every time we try to use it.
Expand All @@ -28,4 +32,62 @@ public interface LoggerWrapper {
void warn(final String message, final Object... args);

void error(final String message, final Object... args);

// Taken from
// https://github.com/j256/ormlite-core/blob/master/src/main/java/com/j256/ormlite/logger/Logger.java
String ARG_STRING = "{}";
int ARG_STRING_LENGTH = ARG_STRING.length();
Object UNKNOWN_ARG = new Object();

default String buildFullMessage(final @NotNull String msg, final Object... args) {
StringBuilder sb = null;
int lastIndex = 0;
int argC = 0;
while (true) {
int argIndex = msg.indexOf(ARG_STRING, lastIndex);
// no more {} arguments?
if (argIndex == -1) {
break;
}
if (sb == null) {
// we build this lazily in case there is no {} in the msg
sb = new StringBuilder(128);
}
// add the string before the arg-string
sb.append(msg, lastIndex, argIndex);
// shift our last-index past the arg-string
lastIndex = argIndex + ARG_STRING_LENGTH;
// add the arguments
if (argC < args.length) {
appendArg(sb, args[argC]);
}
argC++;
}
if (sb == null) {
return msg;
} else {
sb.append(msg, lastIndex, msg.length());
return sb.toString();
}
}

default void appendArg(final StringBuilder stringBuilder, final Object arg) {
if (arg == UNKNOWN_ARG) {
// ignore it
} else if (arg == null) {
stringBuilder.append("null");
} else if (arg.getClass().isArray()) {
stringBuilder.append('[');
int length = Array.getLength(arg);
for (int i = 0; i < length; i++) {
if (i > 0) {
stringBuilder.append(", ");
}
appendArg(stringBuilder, Array.get(arg, i));
}
stringBuilder.append(']');
} else {
stringBuilder.append(arg);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.jetbrains.annotations.NotNull;
import xyz.jonesdev.cappuccino.Cappuccino;
import xyz.jonesdev.cappuccino.ExpiringCache;
import xyz.jonesdev.sonar.api.Sonar;
import xyz.jonesdev.sonar.api.profiler.JVMProfiler;
import xyz.jonesdev.sonar.api.statistics.Statistics;
import xyz.jonesdev.sonar.api.timer.SystemTimer;

import java.util.Collection;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static xyz.jonesdev.sonar.api.Sonar.DECIMAL_FORMAT;
import static xyz.jonesdev.sonar.api.fallback.traffic.TrafficCounter.INCOMING;
Expand All @@ -41,23 +43,14 @@
public final class Verbose implements JVMProfiler {
private final @NotNull Collection<String> subscribers = new Vector<>(0);
private final @NotNull Map<String, Audience> audiences = new ConcurrentHashMap<>();
private final SystemTimer secondTimer = new SystemTimer();
private int joinsPerSecond, totalJoins;
private int lastTotalJoins, animationIndex;
private int animationIndex;
private final ExpiringCache<Long> joinsPerSecond = Cappuccino.buildExpiring(1L, TimeUnit.SECONDS);

// Run action bar verbose
public void update() {
// Clean up all blacklisted IPs
Sonar.get().getFallback().getBlacklisted().cleanUp(false);

totalJoins = Statistics.TOTAL_TRAFFIC.get();

// Statistically determine the joins per second without any caches
if (totalJoins > 0 && secondTimer.elapsed(1000L)) {
secondTimer.reset();
joinsPerSecond = totalJoins - lastTotalJoins;
lastTotalJoins = totalJoins;
}
joinsPerSecond.cleanUp(false);

// Don't prepare component if there are no subscribers
if (subscribers.isEmpty()) return;
Expand All @@ -78,8 +71,8 @@ public void update() {
.replace("%verifying%", DECIMAL_FORMAT.format(Sonar.get().getFallback().getConnected().size()))
.replace("%blacklisted%",
DECIMAL_FORMAT.format(Sonar.get().getFallback().getBlacklisted().estimatedSize()))
.replace("%total-joins%", DECIMAL_FORMAT.format(totalJoins))
.replace("%per-second-joins%", DECIMAL_FORMAT.format(joinsPerSecond))
.replace("%total-joins%", DECIMAL_FORMAT.format(Statistics.TOTAL_TRAFFIC.get()))
.replace("%per-second-joins%", DECIMAL_FORMAT.format(joinsPerSecond.estimatedSize()))
.replace("%verify-total%", DECIMAL_FORMAT.format(Statistics.REAL_TRAFFIC.get()))
.replace("%verify-success%",
DECIMAL_FORMAT.format(Sonar.get().getVerifiedPlayerController().estimatedSize()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;

import java.util.Objects;
import java.util.logging.Level;

@Getter
public final class SonarBukkit extends SonarBootstrap<SonarBukkitPlugin> {
Expand All @@ -55,17 +54,17 @@ public SonarBukkit(final @NotNull SonarBukkitPlugin plugin) {

@Override
public void info(final String message, final Object... args) {
getPlugin().getLogger().log(Level.INFO, message, args);
getPlugin().getLogger().info(buildFullMessage(message, args));
}

@Override
public void warn(final String message, final Object... args) {
getPlugin().getLogger().log(Level.WARNING, message, args);
getPlugin().getLogger().warning(buildFullMessage(message, args));
}

@Override
public void error(final String message, final Object... args) {
getPlugin().getLogger().log(Level.SEVERE, message, args);
getPlugin().getLogger().severe(buildFullMessage(message, args));
}
};

Expand Down
6 changes: 0 additions & 6 deletions sonar-bungee/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ dependencies {
compileOnly(project(":api"))
compileOnly(project(":common"))

implementation("com.velocitypowered:velocity-native:1.1.9") {
exclude(group = "com.google.guava")
exclude(group = "io.netty")
exclude(group = "org.checkerframework")
}

compileOnly("net.md_5:bungeecord:1.20.2-rc2-SNAPSHOT")

// MiniMessage platform support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

@Getter
public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
Expand All @@ -58,17 +57,17 @@ public SonarBungee(final @NotNull SonarBungeePlugin plugin) {

@Override
public void info(final String message, final Object... args) {
getPlugin().getLogger().log(Level.INFO, message, args);
getPlugin().getLogger().info(buildFullMessage(message, args));
}

@Override
public void warn(final String message, final Object... args) {
getPlugin().getLogger().log(Level.WARNING, message, args);
getPlugin().getLogger().warning(buildFullMessage(message, args));
}

@Override
public void error(final String message, final Object... args) {
getPlugin().getLogger().log(Level.SEVERE, message, args);
getPlugin().getLogger().severe(buildFullMessage(message, args));
}
};

Expand All @@ -82,7 +81,7 @@ public void enable() {
getPlugin().getServer().getPluginManager().registerCommand(getPlugin(), new BungeeSonarCommand());

// Register Fallback listener
getPlugin().getServer().getPluginManager().registerListener(getPlugin(), new FallbackListener(getFallback()));
getPlugin().getServer().getPluginManager().registerListener(getPlugin(), new FallbackListener());

// Register audience register listener
getPlugin().getServer().getPluginManager().registerListener(getPlugin(), new AudienceListener());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.bungee.fallback;

import io.netty.handler.codec.DecoderException;

final class ConditionFailedException extends DecoderException {
public ConditionFailedException(final String message) {
super(message);
}
}
Loading

0 comments on commit 8361f19

Please sign in to comment.