611 lines
14 KiB
Ucode
611 lines
14 KiB
Ucode
class IRCLink extends BufferedTCPLink;
|
|
|
|
var IpAddr ServerIpAddr;
|
|
|
|
var string ServerAddress;
|
|
var int ServerPort;
|
|
|
|
var string NickName;
|
|
var string UserIdent;
|
|
var string FullName;
|
|
var string DefaultChannel;
|
|
|
|
var localized string InvalidAddressText;
|
|
var localized string ErrorBindingText;
|
|
var localized string ResolveFailedText;
|
|
var localized string ConnectedText;
|
|
var localized string ConnectingToText;
|
|
var localized string TimeOutError;
|
|
var localized string InviteString;
|
|
var localized string NickInUseText;
|
|
var localized string NickInvalidText;
|
|
|
|
|
|
var IRC_System SystemPage;
|
|
|
|
var string DisconnectReason;
|
|
var string VersionString;
|
|
|
|
var transient float SinceLastLevCheck;
|
|
|
|
function PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
Disable('Tick');
|
|
}
|
|
|
|
function Connect(IRC_System InSystemPage, string InServer, string InNickName, string InUserIdent, string InFullName, string InDefaultChannel)
|
|
{
|
|
local int i;
|
|
|
|
log("IRCLink Connect:"$InServer$InNickName$InUserIdent$InFullName$InDefaultChannel,'IRC');
|
|
|
|
SystemPage = InSystemPage;
|
|
NickName = InNickName;
|
|
FullName = InFullName;
|
|
UserIdent = InUserIdent;
|
|
DefaultChannel = InDefaultChannel;
|
|
|
|
i = InStr(InServer, ":");
|
|
if(i == -1)
|
|
{
|
|
ServerAddress = InServer;
|
|
ServerPort = 6667;
|
|
}
|
|
else
|
|
{
|
|
ServerAddress = Left(InServer, i);
|
|
ServerPort = Int(Mid(InServer, i+1));
|
|
}
|
|
|
|
ResetBuffer();
|
|
ServerIpAddr.Port = ServerPort;
|
|
SetTimer(20, False);
|
|
SystemPage.SystemText( ConnectingToText@ServerAddress );
|
|
Resolve( ServerAddress );
|
|
}
|
|
|
|
function string ChopLeft(string Text)
|
|
{
|
|
while(Text != "" && InStr(": !", Left(Text, 1)) != -1)
|
|
Text = Mid(Text, 1);
|
|
return Text;
|
|
}
|
|
|
|
function string RemoveNickPrefix(string Nick)
|
|
{
|
|
while(Nick != "" && InStr(":@+%", Left(Nick, 1)) != -1)
|
|
Nick = Mid(Nick, 1);
|
|
return Nick;
|
|
}
|
|
|
|
function string Chop(string Text)
|
|
{
|
|
while(Text != "" && InStr(": !", Left(Text, 1)) != -1)
|
|
Text = Mid(Text, 1);
|
|
while(Text != "" && InStr(": !", Right(Text, 1)) != -1)
|
|
Text = Left(Text, Len(Text)-1);
|
|
|
|
return Text;
|
|
}
|
|
|
|
function Resolved( IpAddr Addr )
|
|
{
|
|
// Set the address
|
|
ServerIpAddr.Addr = Addr.Addr;
|
|
|
|
// Handle failure.
|
|
if( ServerIpAddr.Addr == 0 )
|
|
{
|
|
if(SystemPage != None)
|
|
{
|
|
SystemPage.SystemText( InvalidAddressText );
|
|
SystemPage.Disconnect();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Display success message.
|
|
Log( "UT2004 IRCLink: Server is "$ServerAddress$":"$ServerIpAddr.Port,'IRC' );
|
|
|
|
// Bind the local port.
|
|
if( BindPort() == 0 )
|
|
{
|
|
if(SystemPage != None)
|
|
{
|
|
SystemPage.SystemText( ErrorBindingText );
|
|
SystemPage.Disconnect();
|
|
}
|
|
return;
|
|
}
|
|
|
|
Open( ServerIpAddr );
|
|
}
|
|
|
|
event Closed()
|
|
{
|
|
if(SystemPage != None)
|
|
SystemPage.Disconnect();
|
|
}
|
|
|
|
// Host resolution failue.
|
|
function ResolveFailed()
|
|
{
|
|
if(SystemPage != None)
|
|
{
|
|
SystemPage.SystemText(ResolveFailedText);
|
|
SystemPage.Disconnect();
|
|
}
|
|
}
|
|
|
|
event Timer()
|
|
{
|
|
if(SystemPage != None)
|
|
{
|
|
SystemPage.SystemText( TimeOutError );
|
|
SystemPage.Disconnect();
|
|
log("Failed to resolve "$ServerAddress,'IRC');
|
|
}
|
|
return;
|
|
}
|
|
|
|
event Opened()
|
|
{
|
|
SetTimer(0, False);
|
|
if(SystemPage != None)
|
|
SystemPage.SystemText(ConnectedText);
|
|
Enable('Tick');
|
|
GotoState('LoggingIn');
|
|
}
|
|
|
|
function Tick(float DeltaTime)
|
|
{
|
|
local string Line;
|
|
|
|
DoBufferQueueIO();
|
|
if(ReadBufferedLine(Line))
|
|
ProcessInput(Line);
|
|
|
|
Super.Tick(DeltaTime);
|
|
|
|
if( GetStateName() == 'LoggedIn' )
|
|
{
|
|
// Every 5 seconds, keep the IRC client away string up to date.
|
|
SinceLastLevCheck += DeltaTime;
|
|
if(SinceLastLevCheck > 5)
|
|
{
|
|
if(SystemPage != None)
|
|
SystemPage.UpdateAway();
|
|
|
|
SinceLastLevCheck = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
function SendCommandText(string Text)
|
|
{
|
|
local int i, j;
|
|
local string Cmd, Temp, Temp2;
|
|
|
|
// Process fake commands like /MSG
|
|
if(Left(Text, 4) ~= "MSG ")
|
|
Text = "PRIVMSG "$Mid(Text, 4);
|
|
if(Left(Text, 6) ~= "LEAVE ")
|
|
Text = "PART "$Mid(Text, 6);
|
|
|
|
// Add colons for commands: PRIVMSG, QUIT, KILL, KICK, NOTICE
|
|
i = InStr(Text, " ");
|
|
if(i != -1)
|
|
{
|
|
Cmd = Caps(Left(Text, i));
|
|
Text = Cmd$Mid(Text, i);
|
|
|
|
switch(Cmd)
|
|
{
|
|
// CMD nick :message
|
|
case "PRIVMSG":
|
|
case "NOTICE":
|
|
case "KILL":
|
|
Temp = ChopLeft(Mid(Text, i+1));
|
|
i = InStr(Temp, " ");
|
|
if(i != -1)
|
|
Text = Cmd@Left(Temp, i)$" :"$ChopLeft(Mid(Temp, i+1));
|
|
break;
|
|
// hack for CTCP
|
|
case "CTCP":
|
|
Temp = ChopLeft(Mid(Text, i+1));
|
|
i = InStr(Temp, " ");
|
|
if(i != -1)
|
|
Text = "PRIVMSG "$Left(Temp, i)$" :"$Chr(1)$ChopLeft(Mid(Temp, i+1))$Chr(1);
|
|
break;
|
|
// CMD #channel nick :message
|
|
case "KICK":
|
|
Temp = ChopLeft(Mid(Text, i+1));
|
|
i = InStr(Temp, " ");
|
|
if(i != -1)
|
|
{
|
|
Temp2 = ChopLeft(Mid(Temp, i+1));
|
|
j = InStr(Temp2, " ");
|
|
if(j != -1)
|
|
Text = Cmd@Left(Temp, i)@Left(Temp2, j)$" :"$ChopLeft(Mid(Temp2, j+1));
|
|
}
|
|
break;
|
|
// CMD :message
|
|
case "QUIT":
|
|
Text = Cmd$" :"$ChopLeft(Mid(Text, i+1));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
Text = Caps(Text);
|
|
|
|
SendBufferedData(Text$CRLF);
|
|
}
|
|
|
|
function SendChannelText(string Channel, string Text)
|
|
{
|
|
SendBufferedData("PRIVMSG "$Channel$" :"$Text$CRLF);
|
|
}
|
|
|
|
function SendChannelAction(string Channel, string Text)
|
|
{
|
|
SendBufferedData("PRIVMSG "$Channel$" :"$Chr(1)$"ACTION "$Text$Chr(1)$CRLF);
|
|
}
|
|
|
|
function ProcessInput(string Line)
|
|
{
|
|
// Respond to PING
|
|
if(Left(Line, 5) == "PING ")
|
|
SendBufferedData("PONG "$Mid(Line, 5)$CRLF);
|
|
}
|
|
|
|
state LoggingIn
|
|
{
|
|
function Timer()
|
|
{
|
|
SendBufferedData("NICK "$NickName$CRLF);
|
|
SetTimer(1, false);
|
|
}
|
|
|
|
function ProcessInput(string Line)
|
|
{
|
|
local string Temp;
|
|
local IRC_NewNick NewNickDialog;
|
|
|
|
log("LoggingIn: "$Line,'IRC');
|
|
|
|
Temp = ParseDelimited(Line, " ", 2);
|
|
|
|
if(ParseDelimited(Line, " ", 1)== "ERROR")
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, ":", 2, True)));
|
|
|
|
if( Temp == "433" )
|
|
{
|
|
// Nick in use
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
|
|
SetTimer(0, false);
|
|
|
|
// If we dont have it up already..
|
|
if( IRC_NewNick(SystemPage.Controller.TopPage()) == None )
|
|
{
|
|
// Pop up 'choose a new nickname' dialog
|
|
if( SystemPage.Controller.OpenMenu("xinterface.IRC_NewNick") )
|
|
{
|
|
NewNickDialog = IRC_NewNick(SystemPage.Controller.TopPage());
|
|
NewNickDialog.NewNickPrompt.Caption = NickInUseText;
|
|
NewNickDialog.SystemPage = SystemPage;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if( Temp=="432" )
|
|
{
|
|
// Invalid nick
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
|
|
SetTimer(0, false);
|
|
|
|
if( IRC_NewNick(SystemPage.Controller.TopPage()) == None )
|
|
{
|
|
if( SystemPage.Controller.OpenMenu("xinterface.IRC_NewNick") )
|
|
{
|
|
NewNickDialog = IRC_NewNick(SystemPage.Controller.TopPage());
|
|
NewNickDialog.NewNickPrompt.Caption = NickInvalidText;
|
|
NewNickDialog.SystemPage = SystemPage;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if( Int(Temp) != 0 )
|
|
{
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
|
|
SetTimer(0, False);
|
|
GotoState('LoggedIn');
|
|
}
|
|
|
|
Global.ProcessInput(Line);
|
|
}
|
|
|
|
function SendCommandText(string Text)
|
|
{
|
|
Global.SendCommandText(Text);
|
|
if(ParseDelimited(Text, " ", 1) ~= "NICK")
|
|
{
|
|
SystemPage.ChangedNick(NickName, Chop(ParseDelimited(Text, " ", 2)));
|
|
}
|
|
}
|
|
|
|
Begin:
|
|
SendBufferedData("USER "$UserIdent$" localhost "$ServerAddress$" :"$FullName$CRLF);
|
|
SendBufferedData("NICK "$NickName$CRLF);
|
|
SetTimer(1, false);
|
|
}
|
|
|
|
state LoggedIn
|
|
{
|
|
function ProcessInput(string Line)
|
|
{
|
|
local string Temp, Temp2, Temp3;
|
|
local bool bAddModifier;
|
|
local int i;
|
|
local string Command;
|
|
|
|
Global.ProcessInput(Line);
|
|
|
|
if( SystemPage == None )
|
|
return;
|
|
|
|
//log("ProcessInput: "$Line,'IRC');
|
|
|
|
Command = ParseDelimited(Line, " ", 2);
|
|
|
|
if(ParseDelimited(Line, " ", 1) == "ERROR")
|
|
{
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, ":", 2, True)));
|
|
}
|
|
else if(Command == "JOIN")
|
|
{
|
|
Temp = ParseDelimited(Line, ":!", 2);
|
|
//Log("JOIN:"$Line$" Temp:"$Temp$" NickName:"$NickName);
|
|
if(Temp ~= NickName)
|
|
Temp = "";
|
|
SystemPage.JoinedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
|
|
}
|
|
else if(Command == "PART")
|
|
{
|
|
Temp = ParseDelimited(Line, ":!", 2);
|
|
if(Temp ~= NickName)
|
|
Temp = "";
|
|
SystemPage.PartedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
|
|
}
|
|
else if(Command == "NICK")
|
|
{
|
|
SystemPage.ChangedNick(ParseDelimited(Line, ":!", 2), Chop(ParseDelimited(Line, " ", 3)));
|
|
}
|
|
else if(Command == "QUIT")
|
|
{
|
|
SystemPage.UserQuit(ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, " ", 3, True)));
|
|
}
|
|
else if(Command == "353") // NAMES
|
|
{
|
|
Temp2 = ParseDelimited(Line, "#", 2);
|
|
Temp2 = ParseDelimited(Temp2, " :", 1);
|
|
|
|
Temp = ParseDelimited(Line, ":", 3, True);
|
|
while(Temp != "")
|
|
{
|
|
// Nickname
|
|
Temp3 = ParseDelimited(Temp, " ", 1);
|
|
|
|
SystemPage.UserInChannel("#"$Temp2, RemoveNickPrefix(Temp3));
|
|
|
|
if(Left(Temp3, 1) == "@")
|
|
SystemPage.ChangeOp("#"$Temp2, RemoveNickPrefix(Temp3), True);
|
|
else
|
|
if(Left(Temp3, 1) == "%")
|
|
SystemPage.ChangeHalfOp("#"$Temp2, RemoveNickPrefix(Temp3), True);
|
|
else
|
|
if(Left(Temp3, 1) == "+")
|
|
SystemPage.ChangeVoice("#"$Temp2, RemoveNickPrefix(Temp3), True);
|
|
|
|
Temp = ParseDelimited(Temp, " ", 2, True);
|
|
}
|
|
}
|
|
else if(Command == "333") // Channel formed info
|
|
{
|
|
}
|
|
else if(Command == "366") // End of NAMES
|
|
{
|
|
}
|
|
else if(Command == "331") // RPL_NOTOPIC
|
|
{
|
|
}
|
|
else if(Command == "332") // RPL_TOPIC
|
|
{
|
|
Temp = Chop(ParseDelimited(Line, " ", 4));
|
|
Temp2 = Chop(ParseDelimited(Line, " ", 5));
|
|
|
|
SystemPage.ChangeTopic(Temp, Temp2);
|
|
}
|
|
else if(Command == "341") // RPL_INVITING
|
|
{
|
|
}
|
|
else if(Command == "301") // RPL_AWAY
|
|
{
|
|
SystemPage.IsAway(Chop(ParseDelimited(Line, " ", 4)), ChopLeft(ParseDelimited(Line, ":", 3, True)));
|
|
}
|
|
else if(Command == "NOTICE")
|
|
{
|
|
Temp = ParseDelimited(Line, ": ", 2);
|
|
Temp2 = ParseDelimited(Line, ":! ", 2);
|
|
|
|
if(InStr(Temp, "!") != -1 && InStr(Temp2, ".") == -1)
|
|
{
|
|
// it's a Nick.
|
|
Temp = ChopLeft(ParseDelimited(Line, " ", 4, True));
|
|
if(Asc(Left(Temp, 1)) == 1 && Asc(Right(Temp, 1)) == 1)
|
|
SystemPage.CTCP("", Temp2, Mid(Temp, 1, Len(Temp) - 2));
|
|
else
|
|
SystemPage.UserNotice(Temp2, Temp);
|
|
}
|
|
else
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
|
|
}
|
|
else if(Int(Command)!= 0)
|
|
{
|
|
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
|
|
}
|
|
else if(Command == "MODE")
|
|
{
|
|
// channel mode
|
|
Temp = Chop(ParseDelimited(Line, " ", 4));
|
|
// channel
|
|
Temp3 = Chop(ParseDelimited(Line, " ", 3));
|
|
i = 5;
|
|
bAddModifier = True;
|
|
while(Temp != "")
|
|
{
|
|
Temp2 = Left(Temp, 1);
|
|
if(Temp2 == "+")
|
|
bAddModifier = True;
|
|
if(Temp2 == "-")
|
|
bAddModifier = False;
|
|
|
|
if(Temp2 == "o")
|
|
{
|
|
SystemPage.ChangeOp(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
|
|
i++;
|
|
}
|
|
|
|
if(Temp2 == "h")
|
|
{
|
|
SystemPage.ChangeHalfOp(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
|
|
i++;
|
|
}
|
|
|
|
if(Temp2 == "v")
|
|
{
|
|
SystemPage.ChangeVoice(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
|
|
i++;
|
|
}
|
|
|
|
Temp = Mid(Temp, 1);
|
|
}
|
|
|
|
SystemPage.ChangeMode(Temp3, ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, " ", 4, True)));
|
|
|
|
}
|
|
else if(Command == "KICK")
|
|
{
|
|
// FIXME: handle multiple kicks in a single message
|
|
SystemPage.KickUser(Chop(ParseDelimited(Line, " ", 3)), Chop(ParseDelimited(Line, " ", 4)), ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, ":", 3, True)));
|
|
}
|
|
else if(Command == "INVITE")
|
|
{
|
|
SystemPage.SystemText(ParseDelimited(Line, ":!", 2)@InviteString@ParseDelimited(Line, ":", 3));
|
|
}
|
|
else if(Command == "PRIVMSG")
|
|
{
|
|
Temp = Chop(ParseDelimited(Line, " ", 3));
|
|
Temp2 = ChopLeft(ParseDelimited(Line, " ", 4, True));
|
|
|
|
if(Mid(Temp2, 1, 7) == "ACTION " && Asc(Left(Temp2, 1))==1 && Asc(Right(Temp2, 1))==1)
|
|
{
|
|
Temp2 = Mid(Temp2, 8);
|
|
Temp2 = Left(Temp2, Len(Temp2) - 1);
|
|
|
|
if(Temp != "" && InStr("&#@", Left(Temp, 1)) != -1)
|
|
SystemPage.ChannelAction(Temp, ParseDelimited(Line, ":!", 2), Temp2);
|
|
else
|
|
SystemPage.PrivateAction(ParseDelimited(Line, ":!", 2), Temp2);
|
|
}
|
|
else
|
|
if(Asc(Left(Temp2, 1))==1 && Asc(Right(Temp2, 1))==1)
|
|
{
|
|
Temp2 = Mid(Temp2, 1, Len(Temp2) - 2);
|
|
|
|
switch(Temp2)
|
|
{
|
|
case "VERSION":
|
|
// if _RO_
|
|
SendBufferedData("NOTICE "$ParseDelimited(Line, ":!", 2)$" :"$Chr(1)$"VERSION "$VersionString$Level.ROVersion$Chr(1)$CRLF);
|
|
//else
|
|
//SendBufferedData("NOTICE "$ParseDelimited(Line, ":!", 2)$" :"$Chr(1)$"VERSION "$VersionString$Level.EngineVersion$Chr(1)$CRLF);
|
|
SystemPage.CTCP(Temp, ParseDelimited(Line, ":!", 2), Temp2);
|
|
break;
|
|
default:
|
|
SystemPage.CTCP(Temp, ParseDelimited(Line, ":!", 2), Temp2);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(Temp != "" && InStr("&#@", Left(Temp, 1)) != -1)
|
|
SystemPage.ChannelText(Temp, ParseDelimited(Line, ":!", 2), Temp2);
|
|
else
|
|
SystemPage.PrivateText(ParseDelimited(Line, ":!", 2), Temp2);
|
|
}
|
|
}
|
|
else if(Command == "TOPIC")
|
|
{
|
|
Temp = Chop(ParseDelimited(Line, " ", 3));
|
|
Temp2 = ChopLeft(ParseDelimited(Line, " ", 4, True));
|
|
|
|
SystemPage.ChangeTopic(Temp, Temp2);
|
|
}
|
|
//SystemPage.RefreshUI();
|
|
}
|
|
Begin:
|
|
if(DefaultChannel != "")
|
|
JoinChannel(DefaultChannel);
|
|
}
|
|
|
|
function JoinChannel(string Channel)
|
|
{
|
|
log("IRCLink: JoinChannel: "$Channel,'IRC');
|
|
if(Left(Channel, 1) == "#")
|
|
SendBufferedData("JOIN "$Channel$CRLF);
|
|
}
|
|
|
|
function PartChannel(string Channel)
|
|
{
|
|
if(Left(Channel, 1) == "#")
|
|
SendBufferedData("PART "$Channel$CRLF);
|
|
}
|
|
|
|
function SetNick(string NewNick)
|
|
{
|
|
SendBufferedData("NICK "$NewNick$CRLF);
|
|
}
|
|
|
|
function SetAway(string AwayText)
|
|
{
|
|
SendBufferedData("AWAY :"$AwayText$CRLF);
|
|
}
|
|
|
|
function DestroyLink()
|
|
{
|
|
SystemPage = None;
|
|
if(IsConnected())
|
|
{
|
|
SendText("QUIT :"$DisconnectReason$CRLF);
|
|
Close();
|
|
}
|
|
else
|
|
Destroy();
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
InvalidAddressText="Invalid server address, aborting."
|
|
ErrorBindingText="Error binding local port, aborting."
|
|
TimeOutError="Timeout connecting to server."
|
|
ResolveFailedText="Failed to resolve server address, aborting."
|
|
ConnectedText="Connected."
|
|
ConnectingToText="Connecting to"
|
|
DisconnectReason="Disconnected"
|
|
VersionString="Killing Floor IRC Client version "
|
|
InviteString="invites you to join"
|
|
NickInUseText="Nickname Already In Use"
|
|
NickInvalidText="Nickname Is Invalid"
|
|
}
|