Disconnect client if idle
Thanks SharpAceX for Guida, which I used as a reference.
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user