GeyserConnectCustom/src/main/java/org/geysermc/extension/connect/PacketHandler.java
Konicai 59bfd77e8d
Ensure sessions are removed when a transfer packet is sent or they exit without the form (#86)
* Remove session usage in storage and server managers

* Don't allow GC to start when Geyser auth-type is offline

* Fix sessions still being held by Geyser when they are TransferPacket'ed to a bedrock server
2024-08-06 09:22:41 +01:00

166 lines
7.3 KiB
Java

/*
* Copyright (c) 2019-2024 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/GeyserConnect
*/
package org.geysermc.extension.connect;
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler;
import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.cloudburstmc.protocol.common.PacketSignal;
import org.geysermc.extension.connect.config.VirtualHostSection;
import org.geysermc.extension.connect.ui.UIHandler;
import org.geysermc.extension.connect.utils.Server;
import org.geysermc.extension.connect.utils.ServerManager;
import org.geysermc.extension.connect.utils.Utils;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.network.UpstreamPacketHandler;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.DimensionUtils;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class PacketHandler extends UpstreamPacketHandler {
private final GeyserSession session;
private final GeyserConnect geyserConnect;
private final BedrockPacketHandler originalPacketHandler;
public PacketHandler(GeyserConnect geyserConnect, GeyserSession session, BedrockPacketHandler packetHandler) {
super(session.getGeyser(), session);
this.session = session;
this.geyserConnect = geyserConnect;
this.originalPacketHandler = packetHandler;
// Spawn the player in the end (it just looks better)
DimensionUtils.setBedrockDimension(session, 2);
}
@Override
public void onDisconnect(String reason) {
// The user has disconnected without having connected to an actual server. If they have connected to
// a server (transfer packet or geyser proxy), then the original packet handler has been restored.
ServerManager.unloadServers(session);
originalPacketHandler.onDisconnect(reason);
}
@Override
public PacketSignal handle(SetLocalPlayerAsInitializedPacket packet) {
geyserConnect.logger().debug("Player initialized: " + Utils.displayName(session));
// Handle the virtual host if specified
VirtualHostSection vhost = geyserConnect.config().vhost();
if (vhost.enabled()) {
String domain = session.getClientData().getServerAddress();
// Build the regex matcher for the vhosts
Pattern regex = Pattern.compile("\\.?(" + vhost.domains().stream().map(Pattern::quote).collect(Collectors.joining("|")) + ")(:[0-9]+)?$");
if (regex.matcher(domain).find()) {
String target = domain.replaceAll(regex.pattern(), "").strip();
if (!target.isEmpty()) {
String address = "";
int port = 25565;
boolean online = true;
// Parse the address used
String[] domainParts = target.split("\\._");
for (int i = 0; i < domainParts.length; i++) {
String part = domainParts[i];
if (i == 0) {
address = part;
} else if (part.startsWith("p")) {
port = Integer.parseInt(part.substring(1));
} else if (part.startsWith("o")) {
online = false;
}
}
// They didn't specify an address so disconnect them
if (address.startsWith("_")) {
session.disconnect("disconnectionScreen.invalidIP");
return PacketSignal.HANDLED;
}
// Log the virtual host usage
geyserConnect.logger().info(Utils.displayName(session) + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : ""));
// Send the player to the wanted server
Utils.sendToServer(session, originalPacketHandler, new Server(address, port, online, false, null, null, null));
return PacketSignal.HANDLED;
}
}
}
// Handle normal connections
if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
if (!session.getUpstream().isInitialized()) {
session.getUpstream().setInitialized(true);
// Load the players servers
ServerManager.loadServers(session);
UIHandler uiHandler = new UIHandler(session, originalPacketHandler);
uiHandler.initialiseSession();
}
}
return PacketSignal.HANDLED;
}
@Override
public PacketSignal handle(NetworkStackLatencyPacket packet) {
// This is to fix a bug in the client where it doesn't load form images
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
updateAttributesPacket.setRuntimeEntityId(1);
List<AttributeData> attributes = Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0f));
updateAttributesPacket.setAttributes(attributes);
// Doesn't work 100% of the time but fixes it most of the time
session.getGeyser().getScheduledThread().schedule(() -> session.sendUpstreamPacket(updateAttributesPacket), 500, TimeUnit.MILLISECONDS);
return super.handle(packet);
}
@Override
public PacketSignal handle(ResourcePackClientResponsePacket packet) {
return originalPacketHandler.handle(packet); // relies on state in the original handler
}
@Override
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
return originalPacketHandler.handle(packet); // relies on state in the original handler
}
}