Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ java {
}

dependencies {
compileOnlyApi(libs.jspecify)
api(libs.jspecify)

api(libs.gson)
api(libs.guava)
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
bstats = "org.bstats:bstats-base:3.2.1"
caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.4"
checker-qual = "org.checkerframework:checker-qual:4.2.2"
checkstyle = "com.puppycrawl.tools:checkstyle:13.9.0"
checkstyle = "com.puppycrawl.tools:checkstyle:13.10.0"
completablefutures = "com.spotify:completable-futures:0.3.6"
component = "gg.gemstone:component:1.0.2"
configurate3-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate3" }
Expand Down
58 changes: 0 additions & 58 deletions proxy/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -107,64 +107,6 @@ tasks {

transform(Log4j2PluginsCacheFileTransformer::class.java)

// Exclude all the collection types we don't intend to use
exclude("it/unimi/dsi/fastutil/booleans/**")
exclude("it/unimi/dsi/fastutil/bytes/**")
exclude("it/unimi/dsi/fastutil/chars/**")
exclude("it/unimi/dsi/fastutil/doubles/**")
exclude("it/unimi/dsi/fastutil/floats/**")
exclude("it/unimi/dsi/fastutil/longs/**")
exclude("it/unimi/dsi/fastutil/shorts/**")

// Exclude the fastutil IO utilities - we don't use them.
exclude("it/unimi/dsi/fastutil/io/**")

// Exclude most of the int types - Object2IntMap have a values() method that returns an
// IntCollection, and we need Int2ObjectMap
exclude("it/unimi/dsi/fastutil/ints/*Int2Boolean*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Byte*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Char*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Double*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Float*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Int*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Long*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Short*")
exclude("it/unimi/dsi/fastutil/ints/*Int2Reference*")
exclude("it/unimi/dsi/fastutil/ints/IntAVL*")
exclude("it/unimi/dsi/fastutil/ints/IntArrayF*")
exclude("it/unimi/dsi/fastutil/ints/IntArrayI*")
exclude("it/unimi/dsi/fastutil/ints/IntArrayL*")
exclude("it/unimi/dsi/fastutil/ints/IntArrayP*")
exclude("it/unimi/dsi/fastutil/ints/IntArraySet*")
exclude("it/unimi/dsi/fastutil/ints/*IntBi*")
exclude("it/unimi/dsi/fastutil/ints/Int*Pair")
exclude("it/unimi/dsi/fastutil/ints/IntLinked*")
exclude("it/unimi/dsi/fastutil/ints/IntList*")
exclude("it/unimi/dsi/fastutil/ints/IntHeap*")
exclude("it/unimi/dsi/fastutil/ints/IntOpen*")
exclude("it/unimi/dsi/fastutil/ints/IntRB*")
exclude("it/unimi/dsi/fastutil/ints/IntSorted*")
exclude("it/unimi/dsi/fastutil/ints/*Priority*")
exclude("it/unimi/dsi/fastutil/ints/*BigList*")

// Try to exclude everything BUT Object2Int{LinkedOpen,Open,CustomOpen}HashMap
exclude("it/unimi/dsi/fastutil/objects/*ObjectAVL*")
exclude("it/unimi/dsi/fastutil/objects/*Object*Big*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Boolean*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Byte*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Char*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Double*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Float*")
exclude("it/unimi/dsi/fastutil/objects/*Object2IntArray*")
exclude("it/unimi/dsi/fastutil/objects/*Object2IntAVL*")
exclude("it/unimi/dsi/fastutil/objects/*Object2IntRB*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Long*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Object*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Reference*")
exclude("it/unimi/dsi/fastutil/objects/*Object2Short*")
exclude("it/unimi/dsi/fastutil/objects/*ObjectRB*")
exclude("it/unimi/dsi/fastutil/objects/*Reference*")

// Exclude Checker Framework annotations
exclude("org/checkerframework/checker/**")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -251,8 +252,9 @@ public boolean handle(ServerboundCookieResponsePacket packet) {

@Override
public boolean handle(ServerboundCustomClickActionPacket packet) {
if (player.getConnectionInFlight() != null) {
player.getConnectionInFlight().ensureConnected().write(packet.retain());
VelocityServerConnection serverConnection = player.getConnectionInFlightOrConnectedServer();
if (serverConnection != null) {
serverConnection.ensureConnected().write(packet.retain());
return true;
}

Expand All @@ -279,8 +281,8 @@ public void handleGeneric(MinecraftPacket packet) {

MinecraftConnection smc = serverConnection.getConnection();
if (smc != null && serverConnection.getPhase().consideredComplete()) {
if (packet instanceof PluginMessagePacket) {
((PluginMessagePacket) packet).retain();
if (packet instanceof ByteBufHolder bufHolder) {
bufHolder.retain();
}

smc.write(packet);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2018-2026 Velocity 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 com.velocitypowered.proxy.connection.client;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class ClientConfigSessionHandlerTest {

private VelocityServer server;
private ConnectedPlayer player;
private ClientConfigSessionHandler handler;

@BeforeEach
void setUp() {
server = mock(VelocityServer.class);
player = mock(ConnectedPlayer.class);
handler = new ClientConfigSessionHandler(server, player);
}

@AfterEach
void tearDown() {
// nothing to clean up; each test manages its own ByteBufs
}

private ServerboundCustomClickActionPacket makePacket() {
ByteBuf frame = Unpooled.buffer().writeByte(0);
ServerboundCustomClickActionPacket pkt = new ServerboundCustomClickActionPacket();
pkt.replace(frame.readRetainedSlice(frame.readableBytes()));
return pkt;
}

@Test
void handleForwardsToInFlightServer() {
VelocityServerConnection inFlight = mock(VelocityServerConnection.class);
MinecraftConnection backend = mock(MinecraftConnection.class);
when(player.getConnectionInFlightOrConnectedServer()).thenReturn(inFlight);
when(inFlight.ensureConnected()).thenReturn(backend);

ServerboundCustomClickActionPacket pkt = makePacket();
assertTrue(handler.handle(pkt));
verify(backend).write(pkt);
ReferenceCountUtil.release(pkt);
}

@Test
void handleForwardsToConnectedServerWhenInFlightIsNull() {
VelocityServerConnection connected = mock(VelocityServerConnection.class);
MinecraftConnection backend = mock(MinecraftConnection.class);
when(player.getConnectionInFlightOrConnectedServer()).thenReturn(connected);
when(connected.ensureConnected()).thenReturn(backend);

ServerboundCustomClickActionPacket pkt = makePacket();
assertTrue(handler.handle(pkt));
verify(backend).write(pkt);
ReferenceCountUtil.release(pkt);
}

@Test
void handleReturnsFalseWhenNoServer() {
when(player.getConnectionInFlightOrConnectedServer()).thenReturn(null);

ServerboundCustomClickActionPacket pkt = makePacket();
assertFalse(handler.handle(pkt));
ReferenceCountUtil.release(pkt);
}

@Test
void handleGenericRetainsAndForwards() {
VelocityServerConnection connected = mock(VelocityServerConnection.class);
MinecraftConnection backend = mock(MinecraftConnection.class);
BackendConnectionPhase phase = mock(BackendConnectionPhase.class);
when(player.getConnectedServer()).thenReturn(connected);
when(connected.getConnection()).thenReturn(backend);
when(connected.getPhase()).thenReturn(phase);
when(phase.consideredComplete()).thenReturn(true);

ServerboundCustomClickActionPacket pkt = makePacket();
int refBefore = pkt.refCnt();

handler.handleGeneric(pkt);

// retain() was called (+1) before write
assertEquals(refBefore + 1, pkt.refCnt());
verify(backend).write(pkt);
ReferenceCountUtil.release(pkt);
}
}
Loading