Added PacketThrottler, fixed AFK message, and added the AFK timeout to the config.

This commit is contained in:
CodeForFame 2012-01-07 13:46:26 -06:00
parent ff86ee7bfb
commit d20ca77221
6 changed files with 160 additions and 5 deletions

View File

@ -81,4 +81,12 @@
<!-- The class that should be used for the DataStore -->
<entry key="data-store">org.moparscape.msc.gs.persistence.impl.XMLUsingXStream</entry>
<!-- The amount of packets per second before the user is disconnected. -->
<entry key="packet-per-second-threshold">15</entry>
<!-- Alert when someone has broken the threshold? -->
<entry key="packet-per-second-alert">true</entry>
<!-- The afk timeout in minutes -->
<entry key="afk-timeout">5</entry>
</properties>

View File

@ -43,6 +43,9 @@ public class Config {
OS_LEVEL_THROTTLE_ALERT, OS_LEVEL_UNBLOCK_FAILED_ALERT,
CONGRATS_FOR_MAX_LEVEL;
public static String DATA_STORE;
public static int PACKET_PER_SECOND_THRESHOLD;
public static boolean PACKET_PER_SECOND_ALERT;
public static int AFK_TIMEOUT;
static {
loadEnv();
@ -130,6 +133,11 @@ public class Config {
.getProperty("max-level-congrats"));
DATA_STORE = props.getProperty("data-store");
PACKET_PER_SECOND_THRESHOLD = Integer.parseInt(props.getProperty("packet-per-second-threshold"));
PACKET_PER_SECOND_ALERT = Boolean.parseBoolean(props.getProperty("packet-per-second-alert"));
AFK_TIMEOUT = Integer.parseInt(props.getProperty("afk-timeout"));
props.clear();

View File

@ -14,6 +14,7 @@ import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import org.moparscape.msc.config.Config;
import org.moparscape.msc.gs.connection.RSCConnectionHandler;
import org.moparscape.msc.gs.connection.filter.ConnectionFilter;
import org.moparscape.msc.gs.connection.filter.PacketThrottler;
import org.moparscape.msc.gs.core.GameEngine;
import org.moparscape.msc.gs.core.LoginConnector;
import org.moparscape.msc.gs.event.DelayedEvent;
@ -46,7 +47,6 @@ public class Server {
displayConfigDefaulting(configFile);
}
Config.initConfig(configFile);
world = Instance.getWorld();
try {
@ -156,6 +156,8 @@ public class Server {
acceptor = new SocketAcceptor(Runtime.getRuntime()
.availableProcessors() + 1, Executors.newCachedThreadPool());
acceptor.getFilterChain().addFirst("packetthrottler",
PacketThrottler.getInstance());
acceptor.getFilterChain().addFirst("connectionfilter",
new ConnectionFilter());
IoAcceptorConfig config = new SocketAcceptorConfig();
@ -252,7 +254,7 @@ public class Server {
public static Server getServer() {
return server;
}
private static void displayConfigDefaulting(String file) {
System.out.println("Defaulting to use " + file);
}

View File

@ -0,0 +1,113 @@
package org.moparscape.msc.gs.connection.filter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.jcip.annotations.ThreadSafe;
import org.apache.mina.common.IoFilterAdapter;
import org.apache.mina.common.IoSession;
import org.moparscape.msc.config.Config;
import org.moparscape.msc.gs.Instance;
import org.moparscape.msc.gs.alert.AlertHandler;
import org.moparscape.msc.gs.event.DelayedEvent;
import org.moparscape.msc.gs.model.Player;
import org.moparscape.msc.gs.util.annotation.Singleton;
/**
*
* This filter will disconnect players that have exceeded the packet per second
* threshold, and if configured, send an alert.
*
* @author CodeForFame
*
*/
@ThreadSafe
@Singleton
public class PacketThrottler extends IoFilterAdapter {
/////////////////////////////////////////
///////// Singleton Boilerplate /////////
/////////////////////////////////////////
private static final PacketThrottler instance = new PacketThrottler();
public static PacketThrottler getInstance() {
return instance;
}
/////////////////////////////////////////
/////// Singleton Boilerplate End ///////
/////////////////////////////////////////
/**
* The map of username hashes to packet per second.
*/
private Map<Long, Integer> playerToPacketCount = new ConcurrentHashMap<Long, Integer>();
private PacketThrottler() {
// Clears the count every second, so we can check the packets per second.
Instance.getDelayedEventHandler().add(new DelayedEvent(null, 1000) {
@Override
public void run() {
playerToPacketCount.clear();
}
});
}
@Override
public void messageReceived(NextFilter nextFilter, IoSession session,
Object message) {
Player player = (Player) session.getAttachment();
if (session.isClosing() || player.destroyed()) {
return;
}
int count = incrementAndGet(player.getUsernameHash());
if (count > Config.PACKET_PER_SECOND_THRESHOLD) {
if (Config.PACKET_PER_SECOND_ALERT) {
// If the player is initialized, then use the username,
// otherwise use the IP.
String s = (player.isInitialized() ? player.getUsername()
: player.getCurrentIP())
+ " has exceeded the packet per second threshold";
// Sends an alert with a priority of 2.
AlertHandler.sendAlert(s, 2);
}
// Destroys the user and discards the packet.
player.destroy(true);
return;
}
nextFilter.messageReceived(session, message);
}
/**
* Increments and returns the current count.
*
* @param hash
* - The hash of the player.
*/
private int incrementAndGet(long hash) {
final int count;
// Even though operations are atomic, there are multiple, therefore it
// needs to be synchronized.
synchronized (playerToPacketCount) {
// If it is null, it will default to 0
count = playerToPacketCount.get(hash) + 1;
// Update/Create entry
playerToPacketCount.put(hash, count);
}
return count;
}
}

View File

@ -3,6 +3,7 @@ package org.moparscape.msc.gs.core;
import java.util.ArrayList;
import java.util.List;
import org.moparscape.msc.config.Config;
import org.moparscape.msc.gs.Instance;
import org.moparscape.msc.gs.builders.GameObjectPositionPacketBuilder;
import org.moparscape.msc.gs.builders.ItemPositionPacketBuilder;
@ -396,13 +397,17 @@ public final class ClientUpdater {
if (curTime - p.getLastPing() >= 30000) {
p.destroy(false);
} else if (p.warnedToMove()) {
if (curTime - p.getLastMoved() >= 960000 && p.loggedIn()) {
if (curTime - p.getLastMoved() >= ((Config.AFK_TIMEOUT + 1) * 60000)
&& p.loggedIn()) {
p.destroy(false);
}
} else if (curTime - p.getLastMoved() >= 900000) {
} else if (curTime - p.getLastMoved() >= (Config.AFK_TIMEOUT * 60000)
&& !p.warnedToMove()) {
p.getActionSender()
.sendMessage(
"@cya@You have not moved for 15 mins, please move to a new area to avoid logout.");
"@cya@You have not moved for "
+ Config.AFK_TIMEOUT
+ " mins, please move to a new area to avoid logout.");
p.warnToMove();
}
}

View File

@ -0,0 +1,19 @@
package org.moparscape.msc.gs.util.annotation;
import java.lang.annotation.*;
/**
*
* This annotation is used to denote that a Java class is a singleton. Scala
* objects do not need this annotation, because they are singletons by
* definition.
*
* @author CodeForFame
*
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Singleton {
}