Disconnect client if idle

Thanks SharpAceX for Guida,
which I used as a reference.
This commit is contained in:
P0nk
2021-06-24 22:07:22 +02:00
parent 5651bee2b5
commit 80cacd609a
4 changed files with 83 additions and 13 deletions

View File

@@ -66,7 +66,11 @@
<artifactId>netty-buffer</artifactId>
<version>4.1.65.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.65.Final</version>
</dependency>
<!-- Logging -->
<dependency>

View File

@@ -26,6 +26,7 @@ import config.YamlConfig;
import constants.game.GameConstants;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import net.MaplePacketHandler;
import net.PacketProcessor;
import net.netty.InvalidPacketHeaderException;
@@ -51,6 +52,7 @@ import scripting.npc.NPCScriptManager;
import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.ThreadManager;
import server.TimerManager;
import server.life.MapleMonster;
import server.maps.FieldLimit;
import server.maps.MapleMap;
@@ -70,10 +72,12 @@ import java.sql.*;
import java.util.Date;
import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
public class MapleClient extends ChannelInboundHandlerAdapter {
private static final Logger log = LoggerFactory.getLogger(MapleClient.class);
private static final Set<Short> ignoredDebugRecvPackets = Set.of((short) 167, (short) 197, (short) 89, (short) 91, (short) 41, (short) 188, (short) 107);
public static final int LOGIN_NOTLOGGEDIN = 0;
public static final int LOGIN_SERVER_TRANSITION = 1;
@@ -83,12 +87,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
public static final String CLIENT_NIBBLEHWID = "HWID2";
public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP";
private String hostAddress;
private String remoteAddress;
private volatile boolean inTransition;
private MapleAESOFB send;
private MapleAESOFB receive;
private final IoSession session;
private io.netty.channel.Channel ioChannel;
private PacketProcessor packetProcessor;
private MapleCharacter player;
private int channel = 1;
@@ -98,7 +104,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
private Calendar birthday = null;
private String accountName = null;
private int world;
private long lastPong;
private volatile long lastPong;
private int gmlevel;
private Set<String> macs = new HashSet<>();
private Map<String, ScriptEngine> engines = new HashMap<>();
@@ -149,13 +155,19 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
if (!Server.getInstance().isOnline()) {
ctx.channel().close();
return;
}
String hostAddress = "null";
try {
hostAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
} catch (NullPointerException npe) {
log.warn("Unable to get remote address for client", npe);
}
this.hostAddress = hostAddress;
this.remoteAddress = hostAddress;
this.ioChannel = ctx.channel();
}
@Override
@@ -168,6 +180,11 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
short opcode = packet.readShort();
final MaplePacketHandler handler = packetProcessor.getHandler(opcode);
if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(opcode)) {
log.debug("Received packet id {}", opcode);
}
if (handler != null && handler.validateState(this)) {
// TODO: pass InPacket directly to handler once all handlers have been ported,
// this is just a temporary workaround
@@ -185,15 +202,50 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
super.channelRead(ctx, msg);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object event) {
if (event instanceof IdleStateEvent idleEvent) {
checkIfIdle(idleEvent);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (player != null) {
log.warn("Exception caught by {}", player, cause);
}
if (cause instanceof InvalidPacketHeaderException) {
// TODO close session through MapleSessionCoordinator
} else if (cause instanceof IOException) {
closeSession();
} else {
}
super.exceptionCaught(ctx, cause);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
try {
// client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex
if (!inTransition) {
disconnect(false, false);
}
} catch (Throwable t) {
log.warn("Account stuck", t);
}
}
public void closeSession() {
ioChannel.close();
}
public void disconnectSession() {
ioChannel.disconnect();
}
public MapleAESOFB getReceiveCrypto() {
return receive;
}
@@ -206,8 +258,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
return session;
}
public String getHostAddress() {
return hostAddress;
public String getRemoteAddress() {
return remoteAddress;
}
public boolean isInTransition() {
@@ -1101,6 +1153,24 @@ public class MapleClient extends ChannelInboundHandlerAdapter {
}
}
public void checkIfIdle(final IdleStateEvent event) {
final long pingedAt = System.currentTimeMillis();
announce(MaplePacketCreator.getPing());
TimerManager.getInstance().schedule(() -> {
try {
if (lastPong < pingedAt) {
if (ioChannel.isActive()) {
log.info("Disconnected {} due to being idle. Idle state: {}", remoteAddress, event.state());
updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN);
disconnectSession();
}
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}, TimeUnit.SECONDS.toMillis(15));
}
public String getHWID() {
return hwid;
}

View File

@@ -5,6 +5,7 @@ import constants.net.ServerConstants;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import tools.MaplePacketCreator;
import java.util.concurrent.atomic.AtomicLong;
@@ -16,6 +17,7 @@ public abstract class ServerChannelInitializer extends ChannelInitializer<Socket
final InitializationVector sendIv = InitializationVector.generateSend();
final InitializationVector recvIv = InitializationVector.generateReceive();
socketChannel.writeAndFlush(Unpooled.wrappedBuffer(MaplePacketCreator.getHello(ServerConstants.VERSION, sendIv, recvIv)));
socketChannel.pipeline().addFirst("IdleStateHandler", new IdleStateHandler(30, 30, 0));
socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.of(sendIv, recvIv)));
socketChannel.pipeline().addLast("MapleClient", client);
}

View File

@@ -26,8 +26,6 @@ import client.MapleClient;
import config.YamlConfig;
import net.MaplePacketHandler;
import net.server.Server;
import net.server.coordinator.session.MapleSessionCoordinator;
import org.apache.mina.core.session.IoSession;
import tools.BCrypt;
import tools.DatabaseConnection;
import tools.HexTool;
@@ -54,13 +52,9 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase();
}
private static String getRemoteIp(IoSession session) {
return MapleSessionCoordinator.getSessionRemoteAddress(session);
}
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String remoteHost = getRemoteIp(c.getSession());
String remoteHost = c.getRemoteAddress();
if (remoteHost.contentEquals("null")) {
c.announce(MaplePacketCreator.getLoginFailed(14)); // thanks Alchemist for noting remoteHost could be null
return;