Skip to content

Commit

Permalink
Increase packet limits for multiple connected clients playing from on…
Browse files Browse the repository at this point in the history
…e IP address (GeyserMC#5351)

* Fix GeyserMC#4926

* Extend the RakServerRateLimiter, now that it is possible

* Update core/src/main/java/org/geysermc/geyser/network/netty/handler/RakGeyserRateLimiter.java

* cast to int
  • Loading branch information
onebeastchris authored Feb 23, 2025
1 parent 07be402 commit 87d9907
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.network.GeyserServerInitializer;
import org.geysermc.geyser.network.netty.handler.RakConnectionRequestHandler;
import org.geysermc.geyser.network.netty.handler.RakGeyserRateLimiter;
import org.geysermc.geyser.network.netty.handler.RakPingHandler;
import org.geysermc.geyser.network.netty.proxy.ProxyServerHandler;
import org.geysermc.geyser.ping.GeyserPingInfo;
Expand Down Expand Up @@ -175,6 +176,9 @@ private void modifyHandlers(ChannelFuture future) {
if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) {
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
channel.pipeline().remove(RakServerRateLimiter.NAME);
} else {
// Use our own rate limiter to allow multiple players from the same IP
channel.pipeline().replace(RakServerRateLimiter.NAME, RakGeyserRateLimiter.NAME, new RakGeyserRateLimiter(channel));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/

package org.geysermc.geyser.network.netty.handler;

import io.netty.channel.Channel;
import org.cloudburstmc.netty.channel.raknet.RakServerChannel;
import org.cloudburstmc.netty.handler.codec.raknet.server.RakServerRateLimiter;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.SessionManager;

import java.net.InetAddress;

public class RakGeyserRateLimiter extends RakServerRateLimiter {
public static final String NAME = "rak-geyser-rate-limiter";
private final SessionManager sessionManager;

public RakGeyserRateLimiter(Channel channel) {
super((RakServerChannel) channel);
this.sessionManager = GeyserImpl.getInstance().getSessionManager();
}

@Override
protected int getAddressMaxPacketCount(InetAddress address) {
// Using a factor of 0.8 for now, as the default packet count is already padded for multiple
return (int) (super.getAddressMaxPacketCount(address) * sessionManager.getAddressMultiplier(address) * 0.8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.text.GeyserLocale;

import java.util.*;
import java.net.InetAddress;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public final class SessionManager {
/**
Expand All @@ -47,6 +54,13 @@ public final class SessionManager {
@Getter
private final Map<UUID, GeyserSession> sessions = new ConcurrentHashMap<>();

/**
* Stores the number of connected sessions per address they're connected from.
* Used to raise per-IP connection limits.
*/
@Getter(AccessLevel.PACKAGE)
private final Map<InetAddress, AtomicInteger> connectedClients = new ConcurrentHashMap<>();

/**
* Called once the player has successfully authenticated to the Geyser server.
*/
Expand All @@ -60,6 +74,14 @@ public void addPendingSession(GeyserSession session) {
public void addSession(UUID uuid, GeyserSession session) {
pendingSessions.remove(session);
sessions.put(uuid, session);
connectedClients.compute(session.getSocketAddress().getAddress(), (key, count) -> {
if (count == null) {
return new AtomicInteger(0);
}

count.getAndIncrement();
return count;
});
}

public void removeSession(GeyserSession session) {
Expand All @@ -68,6 +90,17 @@ public void removeSession(GeyserSession session) {
// Connection was likely pending
pendingSessions.remove(session);
}
connectedClients.computeIfPresent(session.getSocketAddress().getAddress(), (key, count) -> {
if (count.decrementAndGet() <= 0) {
return null;
}
return count;
});
}

public int getAddressMultiplier(InetAddress ip) {
AtomicInteger atomicInteger = connectedClients.get(ip);
return atomicInteger == null ? 1 : atomicInteger.get();
}

public @Nullable GeyserSession sessionByXuid(@NonNull String xuid) {
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ websocket = "1.5.1"
protocol-connection = "3.0.0.Beta6-20250212.131009-3"
protocol-common = "3.0.0.Beta6-20250212.131009-3"
protocol-codec = "3.0.0.Beta6-20250212.131009-3"
raknet = "1.0.0.CR3-20250128.101054-17"
raknet = "1.0.0.CR3-20250218.160705-18"
minecraftauth = "4.1.1"
mcprotocollib = "1.21.4-20250218.175633-22"
adventure = "4.14.0"
Expand Down

0 comments on commit 87d9907

Please sign in to comment.