/**
* This source file is part of WZ Hybrid Bots
* For the latest information, see http://sourceforge.net/projects/wzhybridbots
*
* Copyright (c) 2006 - WzCtf
* For more information about WzCtf, see http://www.wzctf.net
* Also see the license in license.txt
*/
package plugins.LagCheck;
/** The imported classes/interfaces */
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import hybrid.core.HybridConnection;
import hybrid.core.consts.Events;
import hybrid.core.events.ConnectionClosed;
import hybrid.core.events.ConnectionOpened;
import hybrid.core.events.HybridEvent;
import hybrid.core.events.SpawnStarted;
import hybrid.core.events.ss.ChatMessage;
import hybrid.embedded.consts.SSEvents;
import frontend.alphaspawn.ASConnection;
import frontend.alphaspawn.Player;
import frontend.alphaspawn.PluginAdapter;
import frontend.alphaspawn.tools.arena.ASArena;
import frontend.alphaspawn.tools.command.Command;
import frontend.alphaspawn.tools.command.CommandManager;
import plugins.LagCheck.tools.command.Lag;
import wzhybridbots.tools.actions.ServerCommands;
import wzhybridbots.tools.actions.ServerCommandsRetriever;
/**
* This plugin lag check players. Lag checks are done periodicaly, on
* request and on specific events. If a player is caught lagging, it is
* put into spectator mode.
* <br><br>
* To use this plugin, the bot that loads it <b>should</b> implement the
* following interface(s) or a ClassCastException may be thrown:
* <p>
* <br><b>Mandatory</b>:<br>
* {@link wzhybridbots.tools.actions.ServerCommandsRetriever ServerCommandsRetriever}<br>
* <br><b>Optional</b>:<br>
* None<br>
* </p>
*
* @author Witlospock
* @version 1.0
*/
public class LagCheck extends PluginAdapter {
/** A connection object used to track and handle commands */
protected ASConnection objConnection = null;
/** A server commands object used to call server commands */
protected ServerCommands objServerCommands = null;
/** A regex pattern for the lag response (VIE style)*/
protected Pattern objVIELagPattern = null;
/** A regex pattern for the info response (VIE style)*/
protected Pattern objVIEInfoPattern = null;
/** A regex pattern for the lag response (ASSS style)*/
protected Pattern objASSSLagPattern = null;
/** A regex pattern for the info response (ASSS style)*/
protected Pattern objASSSInfoPattern = null;
/** The lag command*/
protected Command objLagCmd = null;
/**
* Default class constructor.
*/
public LagCheck () {
super();
init();
}
/**
* Class constructor.
*
* @param pluginName The name of the plugin.
*/
public LagCheck ( String pluginName ) {
super( pluginName );
init();
}
/**
* Initialize the object.
*/
protected void init () {
// Try to set the ServerCommands object
setServerCommands();
// Get the first available ASConnection that correspond to the plugin needs
for ( Iterator iter = this.objBotSpawn.getConnections().iterator() ; iter.hasNext() ; ) {
HybridConnection conn = (HybridConnection)iter.next();
// Sets the connection
if ( setConnection( conn ) ) {
break;
}
}
// Sets the patterns for the lag response
// Here are the patterns we want to match
//
// Lag - VIE Style
//
// PING Current:0 ms Average:0 ms Low:0 ms High:0 ms S2C: 0.0% C2S: 0.0% S2CWeapons: 0.0%
//
//
// Info - VIE Style
//
// IP:127.0.0.1 TimeZoneBias:240 Freq:8025 TypedName:Developper Demo:0 MachineId:521780950
// Ping:0ms LowPing:0ms HighPing:0ms AvePing:0ms
// LOSS: S2C:0.0% C2S:0.0% S2CWeapons:0.0% S2C_RelOut:2(164)
// S2C:20-->19 C2S:41-->42
// C2S CURRENT: Slow:0 Fast:14 0.0% TOTAL: Slow:0 Fast:0 0.0%
// S2C CURRENT: Slow:0 Fast:0 0.0% TOTAL: Slow:0 Fast:0 0.0%
// TIME: Session: 0:00:00 Total: 0:00:00 Created: 7-24-2006 16:39:34
// Bytes/Sec:7 LowBandwidth:0 MessageLogging:0 ConnectType:UnknownNotRAS
//
//
// Lag - ASSS Style
//
// ?!?
//
//
// Info - ASSS Style
//
// ?!?
////////////////////////////////////////////////////////////
this.objVIELagPattern = Pattern.compile( "PING.+?" +
"Current:\\s*?(\\d+).+?" +
"Average:\\s*?(\\d+).+?" +
"Low:\\s*?(\\d+).+?" +
"High:\\s*?(\\d+).+?" +
"S2C:\\s*?(\\d+\\.\\d+).+?" +
"C2S:\\s*?(\\d+\\.\\d+).+?" +
"S2CWeapons:\\s*?(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE );
this.objVIEInfoPattern = Pattern.compile( "Ping:\\s*?(\\d+).+?" +
"LowPing:\\s*?(\\d+).+?" +
"HighPing:\\s*?(\\d+).+?" +
"AvePing:\\s*?(\\d+)" +
"[\\s\\S]+?" +
"S2C:\\s*?(\\d+\\.\\d+).+?" +
"C2S:\\s*?(\\d+\\.\\d+).+?" +
"S2CWeapons:\\s*?(\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE );
this.objASSSLagPattern = this.objVIELagPattern;
this.objASSSInfoPattern = this.objVIEInfoPattern;
}
/**
* Returns true if the two objects are equals, false otherwise.
*
* @param o The object to compare with.
* @return see above.
*/
public boolean equals ( Object o ) {
// Compare the objects structure
if ( o == this ) { return true; }
if ( o == null ) { return false; }
if ( !(o instanceof LagCheck ) ) { return false; }
// Compare the objects data
if ( o.hashCode() != this.hashCode() ) { return false; }
// Must return true if plugins are of the same type
// so no 2 identical plugins are registered
return this.getClass().isInstance( o );
}
/**
* Returns the hash code of the object.
*
* @return see above.
*/
public int hashCode () {
return 0;
}
/**
* Called when the plugin is being loaded and is ready
* to receive events.
*/
protected void enable () {
// Continue the initialization + register the command(s)
activatePlugin();
}
/**
* Called when the plugin is being desactivated and won't
* receive anymore events until it is re-enable.
*/
protected void disable () {
// Remove commands
removeCommand();
}
/**
* Called when an event should be process by the plugin.
*
* @param event The event to process.
* @return The event itself so it can be process by other plugin. If null
* returned, the event won't be process by other plugins.
*/
protected HybridEvent processEvent ( HybridEvent event ) {
// Checks for a valid event
if ( event == null || !event.isAlive() ) { return event; }
// Checks the type of events
switch ( event.getGroupID() )
{
// Core Events
case Events.Group:
switch ( event.getEventID() )
{
// An HybridConnection has been open
case Events.ConnectionOpened:
setConnection( ((ConnectionOpened) event).getConnection() );
activatePlugin();
break;
// An HybridConnection has been close
case Events.ConnectionClosed:
removeConnection( ((ConnectionClosed) event).getConnection() );
removeCommand();
break;
// A bot is about to be activated
case Events.SpawnStarted:
if ( this.objBotSpawn.equals( ((SpawnStarted)event ).getBotSpawn() ) ) {
// The plugin's bot is about to start
// Activate the plugin
activatePlugin();
}
break;
}
break;
// SS Event
case SSEvents.Group:
switch ( event.getEventID() )
{
// An HybridConnection has been open
case SSEvents.ChatMessage:
checkLag( (ChatMessage)event );
break;
}
break;
}
// Return the event to be process by the next plugin
return event;
// TODO Wait for specfic event like flag-pick to lag check
}
/**
* Called when the bot is shutingdown. It should be use to do
* some cleanup.
*/
protected void deconstruct () {
}
/**
* Sets the connection object to use by this plugin.
*
* @param conn The connection object to use.
* @return true if the connection object will be use, false otherwise.
*/
protected boolean setConnection ( HybridConnection conn ) {
// Checks if a connection is already use
if ( this.objConnection != null ) { return false; }
// Checks if the connection object is of the right type
if ( !(conn instanceof ASConnection) ) { return false; }
// Sets the connection object
this.objConnection = (ASConnection)conn;
this.objConnection.registerArena( ASArena.getInstance() );
return true;
}
/**
* Remove the connection object used by this plugin.
*
* @param conn The connection object to remove.
* @return true if the connection object has been remove, false otherwise.
*/
protected boolean removeConnection ( HybridConnection conn ) {
// Checks if the connection is valid
if ( this.objConnection == null || conn == null ) { return false; }
// Checks if it is the same connection object
if ( this.objConnection == conn ) {
// Removes the connection object
this.objConnection.removeArena( ASArena.getInstance() );
this.objConnection = null;
return true;
}
return false;
}
/**
* Registers the command used by this plugin.
*/
protected void registerCommand () {
// Get the command manager
CommandManager cmdMgr = CommandManager.getInstance();
// Check if the plugin command can be registered
if ( this.objLagCmd == null &&
this.objConnection != null &&
this.objServerCommands != null ) {
// Register the "lag" command
this.objLagCmd = new Lag( this.objConnection, this.objServerCommands );
cmdMgr.registerCommand( "!lag", this.objLagCmd );
}
}
/**
* Removes the command used by this plugin.
*/
protected void removeCommand () {
// Get the command manager
CommandManager cmdMgr = CommandManager.getInstance();
// Check if the lag command can be remove
if ( this.objLagCmd != null ) {
// Remove the "lag" command
cmdMgr.removeCommand( "!lag", this.objLagCmd );
this.objLagCmd = null;
}
}
/**
* Sets the ServerCommands of the plugin if available at the time.
*
* @return true if the ServerCommands object was set, false otherwise.
* @throws ClassCastException if the bot that loads this plugin doesn't
* support the {@link wzhybridbots.tools.actions.ServerCommands ServerCommands}
* interface.
*/
protected boolean setServerCommands () {
// Check if the object has been already obtain
if ( this.objServerCommands != null ) { return true; }
// Check if the bot support the requiered interface
if ( !(this.objAdapter instanceof ServerCommandsRetriever ) ) {
// The bot doesn't implement the required interface
throw new ClassCastException();
}
// Get the object, null if not available at this time.
ServerCommandsRetriever retriever = (ServerCommandsRetriever)this.objAdapter;
this.objServerCommands = retriever.getServerCommands();
return this.objServerCommands != null;
}
/**
* Finishes the initialization of the plugin if not in a ready state.
*/
protected void activatePlugin () {
// Try to set the ServerCommands object
setServerCommands();
// Try to register the command(s) if not already registered
registerCommand();
}
/**
* Analyzes the lag answer from the server.
*
* @param chatMsg The chat message received.
*/
protected void checkLag ( ChatMessage chatMsg ) {
// TODO Put the code to analyze the string
// Do some checkup on the string to be sure it is the answer
// from the server and not some player message.
int playerID = chatMsg.getPlayerID();
String message = chatMsg.getMessage();
ASArena arena = ASArena.getInstance();
Player sender = arena.getPlayer( playerID );
if ( sender != null ) {
// A player sended the message
// We are waiting for a message from the server...not a player
return;
}
// The message comes from the server... lets parse it
Matcher matcher;
boolean match = false;
// TODO Check for limits and update player structure
// Check for different style
// Lag - VIE Style
matcher = this.objVIELagPattern.matcher( message );
if ( !match && matcher.find() && matcher.groupCount() == 7 ) {
// There is a match for Lag - VIE Style
match = true;
}
// Info - VIE Style
matcher = this.objVIEInfoPattern.matcher( message );
if ( !match && matcher.find() && matcher.groupCount() == 7 ) {
// There is a match for Info - VIE Style
match = true;
}
// Lag - ASSS Style
matcher = this.objASSSLagPattern.matcher( message );
if ( !match && matcher.find() && matcher.groupCount() == 7 ) {
// There is a match for Lag - ASSS Style
match = true;
}
// Info - ASSS Style
matcher = this.objASSSInfoPattern.matcher( message );
if ( !match && matcher.find() && matcher.groupCount() == 7 ) {
// There is a match for Info - ASSS Style
match = true;
}
}
}