rott/kf_sources/GUI2K4/Classes/UT2K4IRCLink.uc
2026-07-14 20:27:09 +07:00

709 lines
16 KiB
Ucode

class UT2K4IRCLink 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 UT2K4IRC_System SystemPage;
var string DisconnectReason;
var string VersionString;
struct CommandAlias
{
var() string AliasText;
var() string RealCommand;
};
var config array<CommandAlias> Shortcuts;
var transient float SinceLastLevCheck;
function PostBeginPlay()
{
Super.PostBeginPlay();
Disable('Tick');
}
function CloseMe()
{
if ( SystemPage != None )
SystemPage.CloseLink(Self, False);
else DestroyLink();
}
function Connect(UT2K4IRC_System InSystemPage, string InServer, string InNickName, string InUserIdent, string InFullName, string InDefaultChannel)
{
local int i;
log("UT2K4IRCLink 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 );
CloseMe();
}
return;
}
// Display success message.
Log( "UT2004 UT2K4IRCLink: Server is "$ServerAddress$":"$ServerIpAddr.Port,'IRC' );
// Bind the local port.
if( BindPort() == 0 )
{
if(SystemPage != None)
{
SystemPage.SystemText( ErrorBindingText );
CloseMe();
}
return;
}
Open( ServerIpAddr );
}
event Closed()
{
CloseMe();
}
// Host resolution failue.
function ResolveFailed()
{
if(SystemPage != None)
{
SystemPage.SystemText(ResolveFailedText);
CloseMe();
}
}
event Timer()
{
if(SystemPage != None)
{
SystemPage.SystemText( TimeOutError );
CloseMe();
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 Cmd)
{
local int i;
local string Text, Temp;
// Add colons for commands: PRIVMSG, QUIT, KILL, KICK, NOTICE
Divide(Cmd, " ", Cmd, Text);
ReplaceCommandAlias(Cmd);
// log("IRCLink::SendCommandText() Cmd:"$Cmd,'IRC');
// log("IRCLink::SendCommandText() Text:"$Text, 'IRC');
switch(Cmd)
{
// CMD nick :message
case "PRIVMSG":
case "NOTICE":
case "KILL":
if ( Text == "" )
break;
Text = ChopLeft(Text);
if ( Divide(Text, " ", Temp, Text) )
{
Temp @= ":" $ ChopLeft(Text);
Text = Temp;
}
break;
// disable CTCP
case "CTCP":
break;
// CMD #channel nick :message
case "KICK":
if ( Text == "" )
break;
Text = ChopLeft(Text);
if ( Divide(Text, " ", Temp, Text) )
{
i = InStr(Text, " ");
if (i != -1)
Temp @= ":"$Text; // adding reason for kick
else if ( Text != "" )
Temp @= Text;
Text = Temp;
}
Text = SystemPage.GetCurrentChannelName() @ Text; // adding channel name
break;
// CMD :message
case "QUIT":
if (Text != "")
Text = " :"$ChopLeft(Text);
break;
case "JOIN":
JoinChannel(Text);
return;
case "PART":
if ( Text == "" )
Text = SystemPage.GetCurrentChannelName();
PartChannel(Text);
return;
}
if ( Cmd != "" )
SendBufferedData( Cmd $ Eval(Text != "", " " $ Text, "") $ CRLF );
}
function ReplaceCommandAlias(out string Text)
{
local int i;
for (i = 0; i < Shortcuts.Length; i++)
{
if (Shortcuts[i].AliasText ~= Text)
{
Text = Shortcuts[i].RealCommand;
return;
}
}
}
function SendBufferedData(string Text)
{
ReplaceText(Text, "$*", "");
ReplaceText(Text, "#*", "");
// log("IRCLink::SendingBufferedData >"$ Text );
Super.SendBufferedData(Text);
}
function SendChannelText(string Channel, string Text)
{
if ( Channel == "" )
Channel = SystemPage.GetCurrentChannelName();
SendBufferedData("PRIVMSG "$Channel$" :"$Text$CRLF);
}
function SendChannelAction(string Channel, string Text)
{
if ( Channel == "" )
Channel = SystemPage.GetCurrentChannelName();
if (Left(Channel,1) != "#")
Channel = "#" $ Channel;
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;
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);
SystemPage.NotifyNickInUse();
}
else
if( Temp=="432" )
{
// Invalid nick
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
SetTimer(0, false);
SystemPage.NotifyInvalidNick();
}
else
if( SystemPage.IsDigit(Temp) )
{
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
SetTimer(0, False);
GotoState('LoggedIn');
}
Global.ProcessInput(Line);
}
function SendCommandText(string Text)
{
if(ParseDelimited(Text, " ", 1) ~= "NICK")
{
SystemPage.ChangedNick(NickName, Chop(ParseDelimited(Text, " ", 2)));
}
Global.SendCommandText(Text);
}
Begin:
if (SystemPage.Link != None && SystemPage.Link != Self)
{
SystemPage.CloseLink(SystemPage.Link, True);
SystemPage.Link = Self;
}
else if (SystemPage.Link == None)
SystemPage.Link = Self;
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("LoggedIn::ProcessInput >"$Line,'IRC');
Command = ParseDelimited(Line, " ", 2);
if(ParseDelimited(Line, " ", 1) == "ERROR")
{
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, ":", 2, True)));
}
switch ( Command )
{
case "JOIN":
Temp = ParseDelimited(Line, ":!", 2);
//Log("JOIN:"$Line$" Temp:"$Temp$" NickName:"$NickName);
if(Temp ~= NickName)
Temp = "";
SystemPage.JoinedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
break;
case "PART":
Temp = ParseDelimited(Line, ":!", 2);
if(Temp ~= NickName)
Temp = "";
SystemPage.PartedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
break;
case "NICK":
SystemPage.ChangedNick(ParseDelimited(Line, ":!", 2), Chop(ParseDelimited(Line, " ", 3)));
break;
case "QUIT":
SystemPage.UserQuit(ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, " ", 3, True)));
break;
case "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);
}
break;
case "333": // Channel formed info
case "366": // End of NAMES
case "331": // RPL_NOTOPIC
break;
case "332": // RPL_TOPIC
Temp = Chop(ParseDelimited(Line, " ", 4));
Temp2 = Chop(ParseDelimited(Line, " ", 5, True));
SystemPage.ChangeTopic(Temp, Temp2);
break;
case "341": // RPL_INVITING
break;
case "301": // RPL_AWAY
SystemPage.PrintAwayMessage(Chop(ParseDelimited(Line, " ", 4)), ChopLeft(ParseDelimited(Line, ":", 3, True)));
break;
case "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)));
break;
case "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)));
break;
case "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)));
break;
case "INVITE":
SystemPage.SystemText(ParseDelimited(Line, ":!", 2)@InviteString@ParseDelimited(Line, ":", 3));
break;
case "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);
}
break;
case "TOPIC":
Temp = Chop(ParseDelimited(Line, " ", 3));
Temp2 = ChopLeft(ParseDelimited(Line, " ", 4, True));
SystemPage.ChangeTopic(Temp, Temp2);
break;
case "305": // No longer away
case "306": // Now marked as away
break;
case "475": // ERR_BADCHANNELKEY
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
SystemPage.NotifyChannelKey(Chop(ParseDelimited(ChopLeft(ParseDelimited(Line, " ", 4, True)), ":", 1)));
break;
default:
if ( SystemPage.IsDigit(Command) )
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
break;
}
}
Begin:
if(DefaultChannel != "")
JoinChannel(DefaultChannel);
}
function JoinChannel(string Channel)
{
log("UT2K4IRCLink: JoinChannel: "$Channel,'IRC');
if ( Channel == "" )
Channel = SystemPage.GetCurrentChannelName();
if(Left(Channel, 1) != "#")
Channel = "#" $ Channel;
SendBufferedData("JOIN "$Channel$CRLF);
}
function PartChannel(string Channel)
{
if ( Channel == "" )
Channel = SystemPage.GetCurrentChannelName();
if(Left(Channel, 1) != "#")
Channel = "#" $ Channel;
SendBufferedData("PART "$Channel$CRLF);
}
function SetNick(string NewNick)
{
if ( NewNick == "" )
return;
if ( NewNick ~= "chanserv" )
return;
if ( NewNick ~= "q" )
return;
if ( NewNick ~= "nickserv" )
return;
SendCommandText("NICK "$NewNick);
}
function SetAway(string AwayText)
{
SendBufferedData("AWAY :"$AwayText$CRLF);
}
function DestroyLink()
{
SystemPage = None;
SetTimer(0.0,False);
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"
Shortcuts(0)=(AliasText="MSG",RealCommand="PRIVMSG")
Shortcuts(1)=(AliasText="LEAVE",RealCommand="PART")
Shortcuts(2)=(AliasText="J",RealCommand="JOIN")
Shortcuts(3)=(AliasText="P",RealCommand="PART")
Shortcuts(4)=(AliasText="N",RealCommand="NICK")
}