Skip to content

Commit

Permalink
Reimplement session structure (#869)
Browse files Browse the repository at this point in the history
* Reimplement session structure

Benefits are clear, developers can easily provide custom behaviour by overriding the server and client classes to provide custom channels (like geyser does for localchannel) and also custom channel handlers can be provided, like for ViaVersion support.

Transfers were moved to a flag. There are now ClientSession and ServerSession interfaces which extend Session. This way a ServerSession doesn't have connect methods.
Binding to UnixDomainSockets or any other type of SocketAddress is now supported and the client can connect to those too.
Tcp was renamed to Net because now non-tcp protocols like local channels and udp (for bedrock ViaBedrock) can be used.

* Split up server groups and share channel initializer code

This allows us to share more code between server and client. Additionally, the split worker groups allow mcpl to handle server packets more closely to vanilla and reduce timeouts during load.

* Allow more code sharing for custom implementations

* Fill in arbitrary values for remote address if ipv6 client connects to ipv4 server and vice verse

* Use proper separator for ipv6

* Fix channel method signature

* Update protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java

Co-authored-by: chris <[email protected]>

* Update protocol/src/main/java/org/geysermc/mcprotocollib/protocol/ClientListener.java

Co-authored-by: chris <[email protected]>

* Update protocol/src/main/java/org/geysermc/mcprotocollib/network/net/NetClientSession.java

Co-authored-by: chris <[email protected]>

* Add log

* Implement resolving InetSocketAddresses is unresolved

* Rename net package and drop prefix for most classes

* Move session and server into their own packages

* Rename Net prefix to network

* Add ClientNetworkSession factory and pass props properly when following redirects

---------

Co-authored-by: chris <[email protected]>
  • Loading branch information
AlexProgrammerDE and onebeastchris authored Jan 18, 2025
1 parent 1daf036 commit edc2323
Show file tree
Hide file tree
Showing 32 changed files with 738 additions and 492 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package org.geysermc.mcprotocollib.network.example;

import org.geysermc.mcprotocollib.network.ClientSession;
import org.geysermc.mcprotocollib.network.Server;
import org.geysermc.mcprotocollib.network.Session;
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
import org.geysermc.mcprotocollib.network.tcp.TcpServer;
import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory;
import org.geysermc.mcprotocollib.network.server.NetworkServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;

public class PingServerTest {
Expand All @@ -25,11 +26,14 @@ public static void main(String[] args) {
return;
}

Server server = new TcpServer("127.0.0.1", 25565, TestProtocol::new);
Server server = new NetworkServer(new InetSocketAddress("127.0.0.1", 25565), TestProtocol::new);
server.addListener(new ServerListener(key));
server.bind();

Session client = new TcpClientSession("127.0.0.1", 25565, new TestProtocol(key));
ClientSession client = ClientNetworkSessionFactory.factory()
.setAddress("127.0.0.1", 25565)
.setProtocol(new TestProtocol(key))
.create();
client.connect();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public ServerListener(SecretKey key) {

@Override
public void serverBound(ServerBoundEvent event) {
log.info("SERVER Bound: {}:{}", event.getServer().getHost(), event.getServer().getPort());
log.info("SERVER Bound: {}", event.getServer().getBindAddress());
}

@Override
Expand All @@ -36,14 +36,14 @@ public void serverClosed(ServerClosedEvent event) {

@Override
public void sessionAdded(SessionAddedEvent event) {
log.info("SERVER Session Added: {}:{}", event.getSession().getHost(), event.getSession().getPort());
log.info("SERVER Session Added: {}", event.getSession().getRemoteAddress());
((TestProtocol) event.getSession().getPacketProtocol()).setSecretKey(this.key);
event.getSession().setEncryption(((TestProtocol) event.getSession().getPacketProtocol()).getEncryption());
}

@Override
public void sessionRemoved(SessionRemovedEvent event) {
log.info("SERVER Session Removed: {}:{}", event.getSession().getHost(), event.getSession().getPort());
log.info("SERVER Session Removed: {}", event.getSession().getRemoteAddress());
event.getServer().close(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public EncryptionConfig getEncryption() {
}

@Override
public void newClientSession(Session session, boolean transferring) {
public void newClientSession(Session session) {
session.addListener(new ClientSessionListener());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import net.raphimc.minecraftauth.step.msa.StepCredentialsMsaCode;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.auth.SessionService;
import org.geysermc.mcprotocollib.network.ClientSession;
import org.geysermc.mcprotocollib.network.ProxyInfo;
import org.geysermc.mcprotocollib.network.Server;
import org.geysermc.mcprotocollib.network.Session;
Expand All @@ -20,9 +21,9 @@
import org.geysermc.mcprotocollib.network.event.server.SessionRemovedEvent;
import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
import org.geysermc.mcprotocollib.network.event.session.SessionAdapter;
import org.geysermc.mcprotocollib.network.factory.ClientNetworkSessionFactory;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
import org.geysermc.mcprotocollib.network.tcp.TcpServer;
import org.geysermc.mcprotocollib.network.server.NetworkServer;
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
Expand All @@ -38,6 +39,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
Expand All @@ -50,8 +53,7 @@ public class MinecraftProtocolTest {
private static final boolean SPAWN_SERVER = true;
private static final boolean ENCRYPT_CONNECTION = true;
private static final boolean SHOULD_AUTHENTICATE = false;
private static final String HOST = "127.0.0.1";
private static final int PORT = 25565;
private static final SocketAddress ADDRESS = new InetSocketAddress("127.0.0.1", 25565);
private static final ProxyInfo PROXY = null;
private static final ProxyInfo AUTH_PROXY = null;
private static final String USERNAME = "Username";
Expand All @@ -62,7 +64,7 @@ public static void main(String[] args) {
SessionService sessionService = new SessionService();
sessionService.setProxy(AUTH_PROXY);

Server server = new TcpServer(HOST, PORT, MinecraftProtocol::new);
Server server = new NetworkServer(ADDRESS, MinecraftProtocol::new);
server.setGlobalFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService);
server.setGlobalFlag(MinecraftConstants.ENCRYPT_CONNECTION, ENCRYPT_CONNECTION);
server.setGlobalFlag(MinecraftConstants.SHOULD_AUTHENTICATE, SHOULD_AUTHENTICATE);
Expand Down Expand Up @@ -155,7 +157,11 @@ private static void status() {
sessionService.setProxy(AUTH_PROXY);

MinecraftProtocol protocol = new MinecraftProtocol();
Session client = new TcpClientSession(HOST, PORT, protocol, PROXY);
ClientSession client = ClientNetworkSessionFactory.factory()
.setRemoteSocketAddress(ADDRESS)
.setProtocol(protocol)
.setProxy(PROXY)
.create();
client.setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService);
client.setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, (session, info) -> {
log.info("Version: {}, {}", info.getVersionInfo().getVersionName(), info.getVersionInfo().getProtocolVersion());
Expand Down Expand Up @@ -203,7 +209,12 @@ private static void login() {
SessionService sessionService = new SessionService();
sessionService.setProxy(AUTH_PROXY);

Session client = new TcpClientSession(HOST, PORT, protocol, PROXY);

ClientSession client = ClientNetworkSessionFactory.factory()
.setRemoteSocketAddress(ADDRESS)
.setProtocol(protocol)
.setProxy(PROXY)
.create();
client.setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService);
client.addListener(new SessionAdapter() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public class BuiltinFlags {
*/
public static final Flag<InetSocketAddress> CLIENT_PROXIED_ADDRESS = new Flag<>("client-proxied-address", InetSocketAddress.class);

/**
* Whether the current client is transferring.
*/
public static final Flag<Boolean> CLIENT_TRANSFERRING = new Flag<>("transferring", Boolean.class);

/**
* When set to false, an SRV record resolve is not attempted.
*/
Expand All @@ -34,7 +39,7 @@ public class BuiltinFlags {
* Used by both the server and client.
*/
public static final Flag<Integer> READ_TIMEOUT = new Flag<>("read-timeout", Integer.class);

/**
* Write timeout in seconds.
* Used by both the server and client.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.geysermc.mcprotocollib.network;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* A network client session.
*/
public interface ClientSession extends Session {
/**
* Connects this session to its host and port.
*/
default void connect() {
connect(true);
}

/**
* Connects this session to its host and port.
*
* @param wait Whether to wait for the connection to be established before returning.
*/
void connect(boolean wait);

/**
* Get the proxy used by this session.
*
* @return The proxy used by this session.
*/
@Nullable ProxyInfo getProxy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.geysermc.mcprotocollib.network.event.server.ServerListener;
import org.geysermc.mcprotocollib.network.packet.PacketProtocol;
import org.geysermc.mcprotocollib.network.server.AbstractServer;

import java.net.SocketAddress;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
Expand All @@ -12,18 +14,11 @@
*/
public interface Server {
/**
* Gets the host the session is listening on.
* Gets the bind address the server is listening on.
*
* @return The listening host.
* @return The listening bind address.
*/
String getHost();

/**
* Gets the port the session is listening on.
*
* @return The listening port.
*/
int getPort();
SocketAddress getBindAddress();

/**
* Gets the packet protocol of the server.
Expand Down Expand Up @@ -62,19 +57,28 @@ public interface Server {
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
<T> T getGlobalFlag(Flag<T> flag);
default <T> T getGlobalFlag(Flag<T> flag) {
return getGlobalFlagSupplied(flag, () -> null);
}

/**
* @see #getGlobalFlagSupplied(Flag, Supplier)
*/
default <T> T getGlobalFlag(Flag<T> flag, T def) {
return getGlobalFlagSupplied(flag, () -> def);
}

/**
* Gets the value of the given flag as an instance of the given type.
* If the flag is not set, the specified default value will be returned.
*
* @param <T> Type of the flag.
* @param flag Flag to check for.
* @param def Default value of the flag.
* @param defSupplier Default value supplier.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
<T> T getGlobalFlag(Flag<T> flag, T def);
<T> T getGlobalFlagSupplied(Flag<T> flag, Supplier<T> defSupplier);

/**
* Sets the value of a flag. The flag will be used in sessions if a session does
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.geysermc.mcprotocollib.network;

/**
* A network server session.
*/
public interface ServerSession extends Session {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,21 @@
import org.geysermc.mcprotocollib.network.crypt.EncryptionConfig;
import org.geysermc.mcprotocollib.network.event.session.SessionEvent;
import org.geysermc.mcprotocollib.network.event.session.SessionListener;
import org.geysermc.mcprotocollib.network.netty.FlushHandler;
import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.network.packet.PacketProtocol;
import org.geysermc.mcprotocollib.network.tcp.FlushHandler;

import java.net.SocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

/**
* A network session.
*/
public interface Session {

/**
* Connects this session to its host and port.
*/
void connect();

/**
* Connects this session to its host and port.
*
* @param wait Whether to wait for the connection to be established before returning.
*/
void connect(boolean wait);

/**
* Connects this session to its host and port.
*
* @param wait Whether to wait for the connection to be established before returning.
* @param transferring Whether the session is a client being transferred.
*/
void connect(boolean wait, boolean transferring);

/**
* Gets the host the session is connected to.
*
* @return The connected host.
*/
String getHost();

/**
* Gets the port the session is connected to.
*
* @return The connected port.
*/
int getPort();

/**
* Gets the local address of the session.
*
Expand Down Expand Up @@ -111,7 +79,16 @@ public interface Session {
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
<T> T getFlag(Flag<T> flag);
default <T> T getFlag(Flag<T> flag) {
return this.getFlagSupplied(flag, () -> null);
}

/**
* @see #getFlagSupplied(Flag, Supplier)
*/
default <T> T getFlag(Flag<T> flag, T def) {
return this.getFlagSupplied(flag, () -> def);
}

/**
* Gets the value of the given flag as an instance of the given type. If this
Expand All @@ -120,11 +97,11 @@ public interface Session {
*
* @param <T> Type of the flag.
* @param flag Flag to check for.
* @param def Default value of the flag.
* @param defSupplier Default value supplier.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
<T> T getFlag(Flag<T> flag, T def);
<T> T getFlagSupplied(Flag<T> flag, Supplier<T> defSupplier);

/**
* Sets the value of a flag. This does not change a server's flags if this session
Expand Down Expand Up @@ -286,6 +263,13 @@ default void disconnect(@NonNull Component reason) {
*/
Channel getChannel();

/**
* Returns the executor that handles packet handling.
*
* @return The packet handler executor
*/
Executor getPacketHandlerExecutor();

/**
* Changes the inbound state of the session and then re-enables auto read.
* This is used after a terminal packet was handled and the session is ready to receive more packets in the new state.
Expand Down
Loading

0 comments on commit edc2323

Please sign in to comment.