package net.md_5.bungee.forge;
import java.util.ArrayDeque;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.protocol.packet.PluginMessage;
/**
* Handles the Forge Client data and handshake procedure.
*/
@RequiredArgsConstructor
public class ForgeClientHandler
{
@NonNull
private final UserConnection con;
/**
* The users' mod list.
*/
@Getter
@Setter(AccessLevel.PACKAGE)
private Map<String, String> clientModList = null;
private final ArrayDeque<PluginMessage> packetQueue = new ArrayDeque<PluginMessage>();
@NonNull
@Setter(AccessLevel.PACKAGE)
private ForgeClientHandshakeState state = ForgeClientHandshakeState.HELLO;
private PluginMessage serverModList = null;
private PluginMessage serverIdList = null;
/**
* Gets or sets a value indicating whether the '\00FML\00' token was found
* in the handshake.
*/
@Getter
@Setter
private boolean fmlTokenInHandshake = false;
/**
* Handles the Forge packet.
*
* @param message The Forge Handshake packet to handle.
*/
public void handle(PluginMessage message) throws IllegalArgumentException
{
if ( !message.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) )
{
throw new IllegalArgumentException( "Expecting a Forge Handshake packet." );
}
message.setAllowExtendedPacket( true ); // FML allows extended packets so this must be enabled
ForgeClientHandshakeState prevState = state;
packetQueue.add( message );
state = state.send( message, con );
if ( state != prevState ) // state finished, send packets
{
synchronized ( packetQueue )
{
while ( !packetQueue.isEmpty() )
{
ForgeLogger.logClient( ForgeLogger.LogDirection.SENDING, prevState.name(), packetQueue.getFirst() );
con.getForgeServerHandler().receive( packetQueue.removeFirst() );
}
}
}
}
/**
* Receives a {@link PluginMessage} from ForgeServer to pass to Client.
*
* @param message The message to being received.
*/
public void receive(PluginMessage message) throws IllegalArgumentException
{
state = state.handle( message, con );
}
/**
* Resets the client handshake state to HELLO, and, if we know the handshake
* has been completed before, send the reset packet.
*/
public void resetHandshake()
{
state = ForgeClientHandshakeState.HELLO;
con.unsafe().sendPacket( ForgeConstants.FML_RESET_HANDSHAKE );
}
/**
* Sends the server mod list to the client, or stores it for sending later.
*
* @param modList The {@link PluginMessage} to send to the client containing
* the mod list.
* @throws IllegalArgumentException Thrown if the {@link PluginMessage} was
* not as expected.
*/
public void setServerModList(PluginMessage modList) throws IllegalArgumentException
{
if ( !modList.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) || modList.getData()[0] != 2 )
{
throw new IllegalArgumentException( "modList" );
}
this.serverModList = modList;
}
/**
* Sends the server ID list to the client, or stores it for sending later.
*
* @param idList The {@link PluginMessage} to send to the client containing
* the ID list.
* @throws IllegalArgumentException Thrown if the {@link PluginMessage} was
* not as expected.
*/
public void setServerIdList(PluginMessage idList) throws IllegalArgumentException
{
if ( !idList.getTag().equalsIgnoreCase( ForgeConstants.FML_HANDSHAKE_TAG ) || idList.getData()[0] != 3 )
{
throw new IllegalArgumentException( "idList" );
}
this.serverIdList = idList;
}
/**
* Returns whether the handshake is complete.
*
* @return <code>true</code> if the handshake has been completed.
*/
public boolean isHandshakeComplete()
{
return this.state == ForgeClientHandshakeState.DONE;
}
public void setHandshakeComplete()
{
this.state = ForgeClientHandshakeState.DONE;
}
/**
* Returns whether we know if the user is a forge user. In FML 1.8, a "FML"
* token is included in the initial handshake. We can use that to determine
* if the user is a Forge 1.8 user.
*
* @return <code>true</code> if the user is a forge user.
*/
public boolean isForgeUser()
{
return fmlTokenInHandshake || clientModList != null;
}
}