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" }