mL-mrc-client/mrc_client.mps
2022-10-16 20:55:17 +01:00

2025 lines
62 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// ::::: __________________________________________________________________ :::::
// : ____\ ._ ____ _____ __. ____ ___ _______ .__ ______ .__ _____ .__ _. /____ :
// __\ .___! _\__/__ / _|__ / _/_____ __| \ gRK __|_ \ __ |_ \ !___. /__
// \ ! ___/ |/ /___/ | \__\ ._/ __\/ \ \___/ |/ \/ \_./ \___ ! /
// /__ (___ /\____\____|\ ____| / /___|\ ______. ____\|\ ___) __\
// /____ \_/ ___________ \_/ __ |__/ _______ \_/ ____ |___/ _____ \_/ ____\
// : /________________________________________________________________\ :
// ::::: + p H E N O M p R O D U C T I O N S + :::::
// ==============================================================================
//
// -----------------------------------------
// - modName: MRC Mystic Client -
// - majorVersion: 1.2 -
// - minorVersion: 9a -
// - author: Stackfault -
// - publisher: Phenom Productions -
// - website: https://www.phenomprod.com -
// - email: stackfault@bottomlessabyss.net -
// - bbs: bbs.bottomlessabyss.net:2023 -
// -----------------------------------------
//
// Update history
//
// Version 1.1 - Gryphon // Cyberia BBS
// Version 1.1.4 - Stackfault [sf] <phENom> // The Bottomless Abyss
// Version 1.2.5 - Stackfault [sf] <phENom> // The Bottomless Abyss
// Version 1.2.7 - Stackfault [sf] <phENom> // The Bottomless Abyss
// Version 1.2.9 - Stackfault [sf] <phENom> // The Bottomless Abyss
// Version 1.2.9a - Stackfault [sf] <phENom> // The Bottomless Abyss
// Version 1.2.9b - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9c - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9d - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9e - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9f - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9g - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
// Version 1.2.9h - MeaTLoTioN [mL] <grAvY> // The Quantum Wormhole
//
// List of changes/fixes:
//
// v1.1.4
// - Input buffer is now unblocking, you can see received chats while typing
// - Input buffer length is maxed, with color coded character counter
// - Input buffer history still available with UP/DOWN arrow keys
// - Fixed a race condition when line cannot be word wrapped
// - Added CPU release in each loops
// - Visible heartbeat animation when server refresh is active
// - Improved responsiveness and buffer refresh rate
// - Enlarged view port, now using the full 24 lines screen height
// - Now shows connected chatters when logging in
// - Welcome text at connection
// - New /changes command
// - 100% compatible with current MRC server implementation
// - Various small fixes
// - Implemented command whitelisting around pipe codes
// - Implemented colors live switching with PgUp/PgDn
// - Implemented timestamp highlighting on nick mention
// - Various validation of string input and length checking
// - Show private message when sent
// - Changed mention indicator to better support present/absent clock
// - Change wrapping on multiline chats to better use available screen space
// - Added information scroller
//
// v1.2.5
// - Added message queuing serialization
// - Added multi-line packet handling
// - Updated scrolling ticker behavior
// - Added new server commands support
// - Stale queue indicator (heartbeat change color)
// - User now marked as unavailable for node message when in MRC
// - Increased topic to 55 chars
// - Added file-locking check via exclusive open
// - Tilde character handling
// - Nick autocomplete
// - Fixed and optimized clear screen
// - Many other smaller changes
// - Small issues fixed and code cleanup
//
// v1.2.7 (Hotfix release)
// - Server banner support
// - Several fixes
// - Client/Server latency display
// - Changes scroller/banner behavior
//
// v1.2.9 (Hotfix release)
// - Topic can now contain colon
// - Addressed some bleeding in banners
// - Added server hello/iamhere dialog support
// - Fixed screen update for some calls
// - Fixed issue with userlist server refresh
// - Fixed issue with server-side banners
// - Added server stats in scrolling banner
// - Reply to last received private message using /r
// - Word-wrapping routine redone to support long strings
// - Updated skin to be more intuitive re latency, buffer, etc
// - Redone the formatting of the /SET features display
// - Added missing /SET command to code
// - Converted all loop timing events to prime numbers to avoid collisions
// - Addressed the cause for random crash
//
// v1.2.9a (Hotfix release)
// - Fixed scroller issues with some OS/Architecture combinations
// - Added support for scroller background color in customization block
// - Added an easter surprise
//
// v1.2.9b (Text Themes addition) [mL]
// - Added two new text themes; /kewl /leet
// - /kewl: aBCDeFGHiJKLMNoPQRSTuVWXYZ
// - /leet: 4BCD3FGH1JK1MN0PQR57UVWXYZ
//
// v1.2.9c (Text Themes addition) [mL]
// - Added a new text themes; /jumble
// - /jumble: This basically scrambles each word as if the computer had dyslexia
//
// v1.2.9d (Text Themes addition) [mL]
// - Added a new text themes; /rot13
// - /rot13: This basically uses the Rot13 cipher to scramble each word or unscramble
//
// v1.2.9e (qUAntUm RaDio! Now and Next display) [mL]
// - Added /qrstat to show what's playing now and next on qUAntUm RaDio!
//
// v1.2.9f (Making a private message more obvious) [mL]
// - When sending a private message using either /m /msg /t or /r the input
// - bar will turn red with white text so you know that you're typing a private
// - message and not a public message.
//
// v1.2.9g (Making a broadcast message more obvious) [mL]
// - When sending a broadcast message using /b the input bar will now turn
// - green with white text so you know that you're typing a broadcast message.
//
// v1.2.9h (Add convert smilies) [mL]
// - When the setting is enabled in your /set list to convert smilies
// - then any time someone says =) or :) it will convert it to the ascii
// - smile characters
//
// *************************************************************************
// * Starting with this release, only the current and previous version *
// * of the client will be able to connect to the server, make sure *
// * to keep your installation updated. *
// *************************************************************************
//
// Installation instructions
//
// MRC Central server has relocated, please use the address below.
// ====================================================================
// mrc.bottomlessabyss.net on port 5000
// ====================================================================
//
// - This release if distributed as a complete installation, including
// both the mrc_client.py and mrc_client.mps.
// - Make sure you update both to benefit all the changes.
// - Some customization variables added, specifically around input bar
//
Uses Cfg
Uses User
Const MRCVersion = 'Multi Relay Chat MPL v1.2.9h [mL]'
Const CLBuffer = 25
Const InputSize = 255
Const MaxBuffer = 140 // Max input buffer limit [sf]
Type MRCRec = Record
FromUser : String[30]
FromSite : String[30]
FromRoom : String[30]
ToUser : String[30]
ToSite : String[30]
ToRoom : String[30]
Message : String[InputSize]
End
Type UserRec = Record
RecIdx : Integer
PermIdx : Integer
EnterChatMe : String[80]
EnterChatRoom : String[80]
EnterRoomMe : String[80]
EnterRoomRoom : String[80]
LeaveChatMe : String[80]
LeaveChatRoom : String[80]
LeaveRoomMe : String[80]
LeaveRoomRoom : String[80]
Name : String[80]
DefaultRoom : String[80]
Temp1 : String[80]
Temp5 : String[80]
Temp6 : String[80]
Temp7 : String[80]
NameColor : String[16]
LtBracket : String[16]
RtBracket : String[16]
UseClock : Boolean
ClockFormat : Boolean
ShowSmilies : Boolean
End
Var Plyr : UserRec
Var WinTL, WinTT : Byte = 0
Var WinBL, WinBB : Byte = 0
Var ChatLines : Array [1..CLBuffer] of String[160]
Var WinAttr : Byte = 0
Var WinSize : Integer = 0
Var PromptX : Byte = 0
Var PromptY : Byte = 0
Var PromptAttr : Byte = 0
Var RoomX, RoomY : Byte = 0
Var RoomAttr : Byte = 0
Var TopicX, TopicY : Byte = 0
Var TopicAttr : Byte = 0
Var MyNamePrompt : String = ''
Var MyChatRoom : Integer = 1
Var Loop : Integer = 1
Var SiteTag : String = ''
Var UserTag : String = ''
Var MyRoom : String = ''
Var MyTopic : String = ''
Var ServFile : MRCRec
Var BBSTempStub : String = ''
Var ChatLog : String = ''
Var PInUse : String = ''
Var Scroller : String = ''
Var UserFile : String = JustPath(Progname)+'mrcusers.dat'
Var ChatSeed : Integer = 0
Var NodeMsgFlag : Boolean = False
Var BufferHist : Array [1..10] of String [160] // Buffer history [sf]
Var BannerList : Array [1..20] of String [160] // Header banners [sf]
Var BannerOff : Byte = 0 // Banner scrolling offset [sf]
Var BanIdx : Byte = 1 // Banner index [sf]
Var ScrollWait : Byte = 0 // Banner scrolling wait [sf]
Var HBClr : Byte = 11 // Color of Heartbeat [sf]
Var RoomUsers : String = '' // Comma delimited user list [sf]
Var UserIdx : Byte = 1 // Index of UserList search [sf]
Var LastUSearch : String = '' // Last user search string [sf]
Var LastPrivMsg : String = '' // Last private message received [sf]
Var Latency : Integer = 0 // Network latency in ms [sf]
Var MRCStats : String = '' // MRC Stats string [sf]
Var RefreshChat : Boolean = False
Var IMaxBuffer : Byte = MaxBuffer
// Align this path with the Python client config [sf]
// Default: mystic/data/mrc
Var SvrQueuePath : String = CfgDataPath + 'mrc'
//
// Beginning of customization variables block [CUST]
//
Var LatencyColor : String = '' // Default color of latency indicator [ml]
Var RoomUsersColor : String = '' // Default color of roomusers indicator [ml]
// Heartbeat animation sequence [sf]
Var Heartbeat : String = Chr(176) + Chr(177) + Chr(178) + Chr(219) + Chr(178) + Chr(177)
Var HeartbeatX : Byte = 76 // X position of HeartBeat [sf]
Var HeartbeatY : Byte = 23 // Y position of HeartBeat [sf]
Var HeartbeatGood : Byte = 10 // Color of HeartBeat when local queue is flowing [sf]
Var HeartbeatBad : Byte = 12 // Color of HeartBeat when local queue is stalled [sf]
Var LatX : Byte = 12
Var LatY : Byte = 23
Var LatCol : Byte = 7
Var UsrX : Byte = 29
Var UsrY : Byte = 23
Var UsrCol : Byte = 7
Var CounterX1 : Byte = 60 // X position of characters counter [sf]
Var CounterY1 : Byte = 23 // X position of characters counter [sf]
Var CounterX2 : Byte = 64 // X position of max characters count [sf]
Var CounterY2 : Byte = 23 // X position of max characters count [sf]
//Var InputBg : String = Chr(250) // Input background character [sf]
Var InputBg : String = Chr(32) // Input background character [sf]
Var InputClr : String = '|16|17|07' // Input field color [sf]
Var Cursor : String = Chr(178) // Virtual Cursor Character
Var CursorBg : String = '|25' // Cursor Background color
//Var CIdx,TIdx : Byte = 7 // Chat text color index [sf]
Var CIdx,TIdx : Byte = 10 // Chat text color index [sf] [ml]
Var MClr : String = '|24|10' // Mention indicator colors (background+foreground)
Var MChr : String = Chr(175) // Mention indicator character
Var TClr : Byte = 8 // Timestamp color
Var BBg : Byte = 16 // Scroller background (16-23) [sf]
Var BClr : Byte = 11 // Scroller color [sf]
Var BFad1 : Byte = 3 // Scroller fader color 1 [sf]
Var BFad2 : Byte = 8 // Scroller fader color 2 [sf]
Var BannerX : Byte = 43 // XPosition of banner [sf]
Var BannerY : Byte = 2 // YPosition of banner [sf]
Var BannerLen : Byte = 36 // Length of banner [sf]
Var ScrollDly : Byte = 0 // Banner scrolling start delay
Var ScrollSpeed : Byte = 15 // Scroll speed factor (Lower = Faster)
// Define banner to scroll, loaded at each banner change (for dynamic content for example)
Procedure LoadBanners
Begin
BannerList[1] := MRCVersion + MRCStats
BannerList[2] := 'Find more about the connected BBSes using the /INFO command'
BannerList[3] := 'Give a try to the new nick auto-completion feature using the TAB key'
BannerList[4] := 'Reply to your last received private message using the /R shortcut'
BannerList[5] := 'Wanna have a go at tqwCTF? Ask MeaTLoTioN for more information'
BannerList[6] := 'Thanks to all who join thE grAvY trAIn each Friday, 29 user record! (2022-04-08)'
// SysOp can add their own banners
End
//
// End of customization variables block [CUST]
//
Function ReadPlyr(I:Integer):Boolean
Var Ret : Boolean = False
Var Fptr : File
Begin
fAssign(Fptr,UserFile,66)
fReset(Fptr)
If IoResult = 0 Then Begin
fSeek(Fptr,(I-1)*SizeOf(Plyr))
If Not fEof(fptr) Then Begin
fReadRec(Fptr,Plyr)
Ret:=True
End
fClose(Fptr)
End
ReadPlyr:=Ret
End
Procedure SavePlyr(I:Integer)
Var Fptr : File
Begin
fAssign(Fptr,Userfile,66)
fReset(Fptr)
If IoResult = 0 Then
fSeek(Fptr,(I-1)*SizeOf(Plyr))
Else Begin
Plyr.RecIdx:=1
fReWrite(Fptr)
End
fWriteRec(Fptr,Plyr)
fClose(Fptr)
End
Function FindPlyr:Integer
Var X,Ret : Integer = 0
Var Done : Boolean = False
Var UN : String = ''
Begin
X:=1
UN:=Upper(StripMCI(Replace(UserAlias,' ','_')))
While ReadPlyr(X) And Not Done Do Begin
If StripMCI(Upper(Plyr.Name)) = UN Then Begin
Done:=True
Ret:=X
End
X:=X+1
End
FindPlyr:=Ret
End
Procedure NewPlyr
Var I : Integer = 0
Begin
I:=0
While ReadPlyr(I+1) Do I:=I+1
Plyr.RecIdx :=I+1
Plyr.PermIdx :=UserIndex
Plyr.EnterChatMe :='|07- |15You have entered chat'
Plyr.EnterChatRoom :='|07- |11%1 |03has arrived!'
Plyr.LeaveChatMe :='|07- |12You have left chat.'
Plyr.LeaveChatRoom :='|07- |12%1 |04has left chat.'
Plyr.EnterRoomMe :='|07- |11You are now in |02%3'
Plyr.LeaveRoomRoom :='|07- |02%1 |10has left the room.'
Plyr.LeaveRoomMe :='|07- |10You have left room |02%4'
Plyr.EnterRoomRoom :='|07- |11%1 |03has entered the room.'
Plyr.Defaultroom :='lobby'
Plyr.NameColor :='|11'
Plyr.LtBracket :='|03<'
Plyr.RtBracket :='|03>'
Plyr.UseClock :=True
Plyr.ClockFormat :=False
Plyr.ShowSmilies :=False
Plyr.Name:=StripMCI(Replace(UserAlias,' ','_'))
SavePlyr(Plyr.RecIdx)
End
Procedure CleanOut
Var X : Byte = 0
Begin
For X:=1 To 20 Do BannerList[X] := ''
For X:=1 To 10 Do BufferHist[X] := ''
For X:=1 To CLBuffer Do ChatLines[X] := ''
FindFirst(CfgTempPath + '*.mrc',66)
While DosError = 0 Do Begin
If FileExist(CfgTempPath+DirName) Then
FileErase(CfgTempPath+DirName)
FindNext
End
FindClose
If FileExist(PInUse) Then
fileErase(PInUse)
If FileExist(ChatLog) Then
fileErase(ChatLog)
End
// Add Banners from Server [sf]
Procedure AddBanner(Text:String)
Var BanExist : Boolean = False
Begin
BanIdx := 1
Repeat
If BannerList[BanIdx] = Text Then
BanExist := True
BanIdx := BanIdx + 1
Until Length(BannerList[BanIdx]) = 0 or BanIdx > 20
If BanExist = False Then
Begin
If BanIdx < 21 Then
BannerList[BanIdx] := Text
BanIdx := 1
End
End
Procedure UpdateScreen
Begin
WriteXY(RoomX,RoomY,RoomAttr,PadRt('#'+MyRoom,30,' '))
WriteXY(TopicX,TopicY,TopicAttr,PadRt(MyTopic,55,' '))
WriteXY(LatX, LatY, LatCol, LatencyColor + PadLt(Int2Str(Latency), 3, ' ')) // [ml]
WriteXY(UsrX, UsrY, UsrCol, RoomUsersColor + PadLt(Int2Str(WordCount(RoomUsers,',')), 2, ' ')) // [ml]
End
Procedure ShowChat(Top:Integer)
Var C,T,L,Y,X : Integer = 0
Var G,V,W : String = ''
Var N2D : Boolean = True
Begin
If RefreshChat Then
Begin
Y:=CLBuffer-WinSize-Top
Write('|08')
For X:=1 To WinSize+1 Do Begin
GoToXy(1,WinTT+X-1)
//Write(ChatLines[Y]+'|16|$X80 ')
Write(ChatLines[Y]+'|16|[K ')
Y:=Y+1
End
RefreshChat := False
End
End
// Return PIPE code based on byte
Function GetPipe(C:Byte) : String
Begin
If C < 32 Then
GetPipe := '|' + PadLT(Int2Str(C), 2, '0')
End
// Parse chat into a message record
Function ParseChat(S:String) : MrcREc
Var MR : MrcRec
Begin
MR.FromUser := WordGet(1,S,'~')
MR.FromSite := WordGet(2,S,'~')
MR.FromRoom := WordGet(3,S,'~')
MR.ToUser := WordGet(4,S,'~')
MR.ToSite := WordGet(5,S,'~')
MR.ToRoom := WordGet(6,S,'~')
MR.Message := WordGet(7,S,'~')
ParseChat := MR
End
Procedure CheckUserlist(U:String)
Var X : Integer = 0
Var A : Boolean = True
Begin
For X := 1 to WordCount(RoomUsers, ',') Do
If Upper(U) = Upper(WordGet(X, RoomUsers, ',')) Then
A := False
If U = 'SERVER' or U = 'CLIENT' Then
A := False
If A = True Then
Begin
If Length(RoomUsers) > 0 Then
RoomUsers := RoomUsers + ',' + U
Else
RoomUsers := U
End
End
Procedure RedrawScreen
Begin
DispFile('mrcmain')
UpdateScreen
RefreshChat:=True
ShowChat(0)
End
// Cut string based on available space
// Word-wrap or string-cut based on the best option
Function StringCutter(S:String;L:Byte):Byte
Var P,W,O : Byte = 0
Var S1,S2 : String = ''
Var Done : Boolean = False
Begin
P := Length(S)
Repeat
S1 := Copy(S,1,P)
If Length(StripMCI(S1)) < L Then
Begin
O := WordCount(S1, ' ')
W := WordPos(O, S1, ' ')
If Length(StripMCI(S)) < L Or W < P - 10 Then
W := 0
If W > 0 Then
Begin
StringCutter := W - 1
Done := True
End
Else
Begin
StringCutter := P
Done := True
End
End
P := P-1
Until Done = True
End
Procedure Add2Chat(S:String)
Var E,W,L,B,A,X : Integer = 0
Var DS,S1,S2,S3,S4 : String = ''
Var HL : String = ''
Var ALL : String = '@ROOM'
Var SM1,SM2,SM3 : Integer
Begin
// Try and replace a :) or :D with a  or 
If Plyr.ShowSmilies Then
Begin
While Pos(':)', S) > 0 Do
Begin
SM1:=Pos(':)', S)
SM2:=SM1+1
Delete(S,SM2,1)
S[SM1]:=Chr(1)
End
While Pos(':D', S) > 0 Do
Begin
SM1:=Pos(':D', S)
SM2:=SM1+1
Delete(S,SM2,1)
S[SM1]:=Chr(2)
End
While Pos('=)', S) > 0 Do
Begin
SM1:=Pos('=)', S)
SM2:=SM1+1
Delete(S,SM2,1)
S[SM1]:=Chr(2)
End
End
// Highlight on nick mention [sf]
HL := '|16|00.'
If Pos(Upper(UserTag), Upper(StripMCI(Copy(S, Length(WordGet(1,S,' ')), Length(S))))) > 0 Then
Begin
Write('|[X02|[Y02|16|00|BE|07')
HL := MClr + MChr + '|16|07'
End
If Pos(Upper(ALL), Upper(StripMCI(Copy(S, Length(WordGet(1,S,' ')), Length(S))))) > 0 Then
Begin
Write('|[X02|[Y02|16|00|BE|07')
HL := MClr + MChr + '|16|07'
End
S3:=' ' + Chr(28) + ' '
If Plyr.UseClock Then Begin
Begin
DS:=TimeStr(DateTime,Plyr.ClockFormat)
If Not Plyr.ClockFormat Then
Delete(DS,6,3)
S:=GetPipe(TClr)+DS+HL+'|16|07'+S+'|16'
S3:=StrRep(' ', Length(DS)) + S3
End
Else
S:=HL+'|16|07'+S
S1:=S
Repeat
Delay(5)
W := StringCutter(S1,79)
If Length(S1) > W Then
Begin
S2 := Copy(S1, W+1, Length(S1))
S4 := Copy(S1, 1, W)
S1 := S4
End
Else
S2 := ''
If (S1 <> '') Then
Begin
For X:=2 To CLBuffer Do
ChatLines[X-1]:=ChatLines[X]
ChatLines[CLBuffer]:='|16'+S1
AppendText(ChatLog,ChatLines[CLBuffer])
RefreshChat := True
S1:=S3+S2
End
Until S2=''
End
// Check if stale outbound messages are stuck in queue
Procedure CheckStale
Begin
HBClr := HeartbeatGood
FindFirst(SvrQueuePath + PathChar + '*.mrc', 63)
While DOSError = 0 Do
Begin
If DateTime > DirTime Then
HBClr := HeartbeatBad
FindNext
End
FindClose
End
Procedure MakeChatEntry(S:String)
Var Fil : String = SvrQueuePath + PathChar +
Int2Str(NodeNum) + Int2Str(ChatSeed) +
Int2Str(Random(9))+Int2Str(Random(9)) + '.mrc'
Begin
AppendText(Fil,S)
ChatSeed:=ChatSeed+1
End
Procedure SendOut(FU,FS,FR,TU,TS,TR,S:String)
Var TX : String = ''
Begin
TX:=FU+'~'+FS+'~'+FR+'~'+TU+'~'+TS+'~'+TR+'~'+S+'~'
MakeChatEntry(TX)
End
Procedure SendToMe(S:String)
Begin
Add2Chat(S)
ShowChat(0)
End
Procedure SendToAllNotMe(S:String)
Begin
SendOut(UserTag,SiteTag,MyRoom,'NOTME','','',S)
End
Procedure SendToRoomNotMe(S:String)
Begin
SendOut(UserTag,SiteTag,MyRoom,'NOTME','',MyRoom,S)
End
Procedure SendToAll(S:String)
Begin
SendOut(UserTag,SiteTag,MyRoom,'','','',S)
End
Procedure SendToRoom(S:String)
Begin
If Pos('@ROOM',Upper(S)) > 0 Then
SendOut(UserTag,SiteTag,MyRoom,'','',MyRoom,'|21|15'+S)
Else
SendOut(UserTag,SiteTag,MyRoom,'','',MyRoom,S)
End
Procedure SendToUser(U,S:String)
Begin
SendOut(UserTag,SiteTag,MyRoom,U,'','',S)
End
Procedure SendToClient(S:String)
Begin
SendOut(UserTag,SiteTag,MyRoom,'CLIENT',SiteTag,MyRoom,S)
End
Procedure SendToServer(S:String)
Var SS : String
Begin
SendOut(UserTag,SiteTag,MyRoom,'SERVER',SiteTag,MyRoom,S)
End
Procedure ProcessChat(MR:MRCRec)
Var Ok2Send : Boolean = True
Var Command,Opt1,Opt2,Stats : String = ''
Begin
// Handle topic set from server
If Mr.FromUser = 'SERVER' and
Pos('ROOMTOPIC',Mr.Message) > 0 Then
Begin
Ok2Send := False
Command:=WordGet(1,Mr.Message,':')
opt1:=WordGet(2,Mr.Message,':')
opt2:=Copy(Mr.Message,
WordPos(3,Mr.Message,':'),
Length(Mr.Message)-WordPos(3,Mr.Message,':')+1)
If Opt1 = MyRoom Then Begin
MyTopic:=Opt2
UpdateScreen
End
End
// Handle userlist from server
If Mr.FromUser = 'SERVER' and
Pos('USERLIST',Mr.Message) > 0 Then
Begin
Ok2Send := False
Command := WordGet(1,Mr.Message,':')
opt1 := WordGet(2,Mr.Message,':')
RoomUsers := opt1
WriteXY(UsrX, UsrY, UsrCol, RoomUsersColor + PadLt(Int2Str(WordCount(RoomUsers,',')), 2, ' ')) // [ml]
End
// Handle client hello request
If Mr.FromUser = 'SERVER' and
Mr.Message = 'HELLO' Then
Begin
Ok2Send := False
SendToServer('IAMHERE')
End
// Handle Latency from client
If Mr.FromUser = 'SERVER' and
Pos('LATENCY',Mr.Message) > 0 Then
Begin
Ok2Send := False
Latency := Str2Int(WordGet(2,Mr.Message,':'))
If Latency > 999 Then
Latency := 999
If Latency > 250 Then // [ml]
LatencyColor := '' // [ml]
Else // [ml]
LatencyColor := '' // [ml]
WriteXY(LatX, LatY, LatCol, LatencyColor + PadLt(Int2Str(Latency), 3, ' ')) // [ml]
End
// Handle stats from client
If Mr.FromUser = 'SERVER' and
Pos('STATS',Mr.Message) > 0 Then
Begin
Ok2Send := False
Stats := WordGet(2,Mr.Message,':')
If Length(Stats) > 0 Then
Begin
MRCStats := ' :: Server Stats >> BBSes:' + WordGet(1, Stats, ' ') +
' Rooms:' + WordGet(2, Stats, ' ') +
' Users:' + WordGet(3, Stats, ' ')
LoadBanners
End
End
// Handle Banners from server
If Mr.FromUser = 'SERVER' and
Pos('BANNER',Mr.Message) > 0 Then
Begin
Ok2Send := False
AddBanner(WordGet(2,Mr.Message,':'))
End
// Message for another room
If MR.ToRoom <> '' Then
if Upper(MR.ToRoom) <> Upper(MyRoom) Then
Ok2Send:=False
// Message not empty and not for me
If MR.ToUser <> '' Then
If Mr.ToUser <> 'NOTME' Then
If Pos(Upper(MR.ToUser),Upper(UserTag))=0
Then Ok2Send:=False
Else
If Mr.ToUser <> 'NOTME' Then
If Upper(Mr.FromUser) = Upper(UserTag) Then
Ok2Send:=False
If Upper(Mr.FromUser) = Upper(UserTag) and
Mr.ToUser = 'NOTME' Then
Ok2Send:=False
If Mr.FromUser <> 'SERVER' and Mr.FromUser <> 'CLIENT' Then
If Upper(Mr.ToUser) = Upper(UserTag) Then
LastPrivMsg := Mr.FromUser
If Ok2Send Then
Begin
Add2Chat(MR.Message)
CheckUserlist(MR.FromUser)
End
End
Procedure ReadChatFiles
Var F1 : File
Var S : String = ''
Var Ret : Boolean = False
Var TLines : Array [1..100] of String [255]
Var TSorted : Array [1..100] of String [255]
Var TStamp : LongInt = 0
Var LCount : Byte = 0
Var Largest : LongInt = 0
Var LIndex : Byte = 0
Var A,B,F,L : Byte = 0
Begin
FindFirst(CfgTempPath+'*.mrc',66)
While DOSError = 0 Do Begin
Ret:=True
fAssign(F1,CfgTempPath+DirName,66)
fReset(F1)
L:=0
While Not fEof(F1) Do Begin
fReadLn(F1,S)
LCount:=LCount+1
TStamp := Str2Int(Copy(DirName, 1, 8)) + L
TLines[LCount] := Int2Str(TStamp) + ' ' + S
L:=L+1
End
fClose(F1)
fileErase(CfgTempPath+DirName)
FindNext
End
FindClose
// Little home-made sorting routine
// Sort loop 1 - Populate array from largest to lowest
For A:=LCount DownTo 1 Do
Begin
// Sort loop 2 - Read original array from
Largest := 0
For B:=1 to LCount Do
Begin
If Str2Int(WordGet(1, TLines[B], ' ')) > Largest Then
Begin
Largest := Str2Int(WordGet(1, TLines[B], ' '))
LIndex := B
End
End
TSorted[A] := Copy(TLines[LIndex],
WordPos(2, TLines[LIndex], ' '),
Length(TLines[LIndex]) - WordPos(2, TLines[LIndex], ' ') + 1)
TLines[LIndex] := '0'
End
// Process messages from the sorted array
For F:=1 to LCount Do
Begin
If WordCount(TSorted[F], '~') > 6 Then
Begin
ServFile:=ParseChat(TSorted[F])
ProcessChat(ServFile)
End
End
If Ret Then ShowChat(0)
End
Function UpdateStrings(S,M,U,NR,OR:String):String
Begin
S:=Replace(S,'%1',M)
S:=Replace(S,'%2',U)
S:=Replace(S,'%3','#'+NR)
S:=Replace(S,'%4','#'+OR)
UpdateStrings:=S
End
// Display error message to chat window [sf]
Procedure ShowError(S:String)
Begin
Add2Chat('|15!|12 ' + S)
ShowChat(0)
End
Procedure JoinRoom(S:String;B:Boolean)
Var NewRoom,OldRoom : String = ''
Begin
If Length(S) > 30 Then
ShowError('Room name is limited to 30 chars max')
Else Begin
If Length(S) > 0 Then Begin
OldRoom:=MyRoom
NewRoom:=lower(S)
StripB(S,'#')
SendToServer('NEWROOM:'+MyRoom+':'+S)
If B Then Begin
SendToMe(UpdateStrings(Plyr.LeaveRoomMe,Plyr.Name,'',NewRoom,OldRoom))
SendToRoomNotMe(UpdateStrings(Plyr.LeaveRoomRoom,Plyr.Name,'',NewRoom,OldRoom))
MyRoom:=NewRoom
SendToMe(UpdateStrings(Plyr.EnterRoomMe,Plyr.Name,'',NewRoom,OldRoom))
SendToRoomNotMe(UpdateStrings(Plyr.EnterRoomRoom,Plyr.Name,'',NewRoom,OldRoom))
End
MyRoom:=S
SetPromptInfo(4,'#'+S)
UpdateScreen
SendToServer('USERLIST')
End
End
End
Procedure ChangeNick(LRNC,N:String;Announce:Boolean)
Var ON : String = ''
Begin
Case LRNC Of
// 'N': Plyr.Name:=StripMCI(N)
// Limit left bracket to 1 visible character [sf]
'L': Begin
If Length(StripMCI(N)) > 1 Then
ShowError('Left bracket max length is 1 char')
Else
Plyr.LtBracket:=N
End
// Limit right bracket to 8 visible character [sf]
// Record length stays at 16 for compatibility
'R': Begin
If Length(StripMCI(N)) > 8 or Length(N) > 16 Then
ShowError('Right brackets max length is 8 chars (16 including Pipe codes)')
Else
Plyr.RtBracket:=N
End
// Make sure Nick color is a color PIPE code [sf]
'C': Begin
If Length(StripMCI(N)) > 0 or Length(N) <> 3 Then
ShowError('Only color pipe codes allowed for nick color')
Else
Plyr.NameColor:=N
End
End
SavePlyr(Plyr.RecIdx)
MyNamePrompt:=Plyr.LtBracket+Plyr.NameColor+StripMCI(Plyr.Name)+Plyr.RtBracket+'|16|07 '
End
Procedure Init
Var X,Y : Integer = 0
Var K,S : String = ''
Begin
S:=Int2Str(NodeNum)
For X:=1 To 3 Do
S:=S+Int2Str(Random(9))
ChatSeed:=Str2Int(S)
ChatLog:=CfgTempPath+'mrcchat.log'
PInUse:=CfgTempPath+'tchat.inuse'
If Upper(UserAlias) = 'SERVER' or
Upper(UserAlias) = 'CLIENT' or
Upper(UserAlias) = 'NOTME' Then
Begin
WriteLn('|16|12|CL|CRUnfortunately, your User Alias is a reserved word and therefore cannot be used.')
WriteLn('|12Please ask your SysOp to change your User Alias to use MRC.')
WriteLn('|CR|PA|07|CL')
Halt
End
BBSTempStub:=CfgTempPath
Y:=Pos(Int2Str(NodeNum),BBSTempStub)
If Y > 0 Then
Delete(BBSTempStub,Y,Length(Int2Str(NodeNum))+1)
Y:=FindPlyr
If Y = 0 Then NewPlyr
Else ReadPlyr(Y)
SiteTag:=StripMCI(Replace(MCI2Str('BN'),' ','_'))
UserTag:=StripMCI(Replace(UserAlias,' ','_'))
ChangeNick('N',UserTag,False)
DispFile('mrcmain')
GetScreenInfo(1,WinTL,WinTT,WinAttr)
GetScreenInfo(2,WinBL,WinBB,WinAttr)
GetScreenInfo(3,PromptX,PromptY,PromptAttr)
GetScreenInfo(4,RoomX,RoomY,RoomAttr)
GetScreenInfo(5,TopicX,TopicY,TopicAttr)
WinSize:=WinBB-WinTT
AppendText(PInUse,'0')
MenuCmd('NA','mUltI rElAY chAttIng')
End
Procedure DoHelp
Begin
Write('|16|11')
DispFile('mrchelp')
RedrawScreen
End
Procedure DoWho
Begin
Write('|16|11')
MenuCmd('NW','')
RedrawScreen
End
Procedure ChangeTopic(S:String)
Var R : String = ''
Begin
if Length(S) > 55 Then
ShowError('Topic is limited to 55 chars max')
Else Begin
SendToServer('NEWTOPIC:'+MyRoom+':'+S)
UpdateScreen
End
End
Procedure DoPrivateMsg(S:String)
Var M,U : String = ''
Var L : Integer = 0
Begin
Var WC : Byte = WordCount(S, ' ')
if WC > 2 Then
Begin
U:=Upper(WordGet(2,S,' '))
L:=Pos(U,Upper(S))
L:=L+Length(U)+1
M:='|15* |08(|15'+Plyr.Name+'|08/|14PrivMsg|08) |07'+Copy(S,L,Length(S)-L+1)
SendToUser(U,M)
Add2Chat('|15* |08(|14PrivMsg|08->|15' + U + '|08) '+ GetPipe(CIdx) + Copy(S,L,Length(S)-L+1))
ShowChat(0)
End
End
Procedure DoBroadcast(S:String)
Var M : String = ''
Begin
M:='|15* |08(|15'+Plyr.Name+'|08/|14Broadcast|08) |07'+Copy(S,4,Length(S)-3)
SendToAll(M)
End
Procedure DoMeAction(S:String)
Var R : String = ''
Begin
R:=Copy(S,5,Length(S)-4)
SendToRoom('|15* |13'+Plyr.Name+' ' + R)
End
Procedure DoTroutAction(S:String)
Var R : String = ''
Var Trout : String = ''
Var Tweet : String = ''
Begin
Trout:='|12<''),))>< |15whaaaPish|07'
R:=Copy(S,8,Length(S)-4)
SendToRoom('|15* |13'+Plyr.Name+' |14troutslaps |13'+R+': '+Trout)
Tweet:='/mystic/scripts/tweetme/twitter.py "'+Plyr.Name+' troutslaps '+R+': <''),))>< whaaaPish #retrocomputing #bbs #mrc #troutslap #sysop #mysticbbs #tqwNet"'
MenuCmd('DD', Tweet);
End
// Buffer history handling [sf]
Procedure AddToBufferHistory(B:String)
Var I : Byte = 0
Begin
For I := 10 DownTo 2 Do
Begin
If Length(BufferHist[I-1]) > 0 Then
BufferHist[I] := BufferHist[I-1]
End
BufferHist[2] := B
End
// Select next banner from the defined list [sf]
Procedure NextBanner
Begin
Repeat
BanIdx := BanIdx + 1
If BanIdx > 20 Then BanIdx := 1
Until Length(BannerList[BanIdx]) > 0
ScrollWait := 0
BannerOff := 0
End
// Display and scroll banners [sf]
Function ScrollBanner
Var BS: String = StripMCI(BannerList[BanIdx])
Begin
// This is a scrolling banner
// Add white padding for nice scroll entry/exit
BS:=StrRep(' ', BannerLen) + BS + StrRep(' ', BannerLen)
// Initial display before we start scrolling
If ScrollWait = 0 Then
Begin
BS:=GetPipe(BBg)+GetPipe(BClr) + Copy(BS, 1, BannerLen-2) +
GetPipe(BFad1) + Copy(BS, BannerLen-1, 1) +
GetPipe(BFad2) + Copy(BS, BannerLen, 1) + '|16'
BannerOff := BannerOff + 1
GoToXy(BannerX, BannerY)
Write(BS)
GoToXy(HeartBeatX, HeartBeatY)
End
// We have made it to the end
If BannerOff > Length(BS) - BannerLen Then
Begin
BS:=GetPipe(BBg)+GetPipe(BFad2) + Copy(BS, BannerOff, 1) +
GetPipe(BFad1) + Copy(BS, BannerOff+1, 1) +
GetPipe(BClr) + Copy(BS, BannerOff+2, BannerLen-2) + '|16'
GoToXy(BannerX, BannerY)
Write(BS)
GoToXy(HeartBeatX, HeartBeatY)
ScrollWait := 0
NextBanner
End
// Let's start the scrolling shall we
If ScrollWait > ScrollDly Then
Begin
BS:=GetPipe(BBg)+GetPipe(BFad2) + Copy(BS, BannerOff, 1) +
GetPipe(BFad1) + Copy(BS, BannerOff+1, 1) +
GetPipe(BClr) + Copy(BS, BannerOff+2, BannerLen-4) +
GetPipe(BFad1) + Copy(BS, BannerOff+BannerLen-2, 1) +
GetPipe(BFad2) + Copy(BS, BannerOff+BannerLen-1, 1) + '|16'
BannerOff := BannerOff + 1
GoToXy(BannerX, BannerY)
Write(BS)
GoToXy(HeartBeatX, HeartBeatY)
End
// Not yet
Else
Begin
ScrollWait := ScrollWait + 1
GoToXy(HeartBeatX, HeartBeatY)
End
End
// Buffer history seeker [sf]
Function GetBufferIndex(Idx:Byte;Dir:Integer) : Byte
Var NIdx: Byte = 0
Begin
NIdx := Idx + Dir
GetBufferIndex := Idx
If NIdx > 1 and NIdx < 10 and Length(BufferHist[NIdx]) > 0 Then
GetBufferIndex := NIdx
If NIdx = 1 Then
GetBufferIndex := NIdx
End
// Color Index seeker [sf]
Function GetColorIndex(Idx:Byte;Dir:Integer) : Byte
Var MIdx: Byte = 0
Begin
MIdx := Idx + Dir
// Span from Blue...
If MIdx < 1 Then
MIdx := 1
// ... to Bright White
If MIdx > 15 Then
MIdx := 15
GetColorIndex := MIdx
End
Function RainbowString(InString:String) : String
Var PlString : String = ''
Var RbString : String = ''
Var PlStrPos : Byte = 0
Var PlStrLen : Byte = 0
Var X : Byte = 0
Begin
PlStrPos := WordPos(2, InString, ' ')
PlStrLen := Length(InString) - (PlStrPos-1)
PlString := Copy(InString, PlStrPos, PlStrLen)
For X:=1 To PlStrLen Do
RbString := RbString + GetPipe(Random(14)+1) + Copy(PlString, X, 1)
RainbowString := RbString
End
//Kewl Speak by [mL]
Function KewlString(InString:String): String
Var PlString : String = ''
Var KlString : String = ''
Var PlStrPos : Byte = 0
Var PlStrLen : Byte = 0
Var X : Byte = 0
Begin
PlStrPos := WordPos(2, InString, ' ')
PlStrLen := Length(InString) - (PlStrPos-1)
PlString := Copy(InString, PlStrPos, PlStrLen)
For X:=1 to PlStrLen Do
Begin
Case Upper(PlString[X]) Of
'A','E','I','O','U': PlString[X] := Lower(PlString[X])
Else
PlString[X] := Upper(PlString[X])
End
KlString := KlString + Copy(PlString, X, 1)
End
KewlString := KlString
End
// Leet Speak by [mL]
Function LeetString(InString:String): String
Var PlString : String = ''
Var LtString : String = ''
Var PlStrPos : Byte = 0
Var PlStrLen : Byte = 0
Var X : Byte = 0
Begin
PlStrPos := WordPos(2, InString, ' ')
PlStrLen := Length(InString) - (PlStrPos-1)
PlString := Copy(InString, PlStrPos, PlStrLen)
For X:=1 to PlStrLen Do
Begin
Case Upper(PlString[X]) Of
'A': PlString[X] := '4'
'E': PlString[X] := '3'
'I': PlString[X] := '1'
'L': PlString[X] := '1'
'O': PlString[X] := '0'
'S': PlString[X] := '5'
'T': PlString[X] := '7'
Else
PlString[X] := Upper(PlString[X])
End
LtString := LtString + Copy(PlString, X, 1)
End
LeetString := LtString
End
//Jumble Speak by [mL]
Function JumbleString(InString:String): String
Var PlString : String = ''
Var JlString : String = ''
Var Tweet : String = ''
Var PlStrPos : Byte = 0
Var PlStrLen : Byte = 0
Var X : Byte = 0
Var Y : Byte = 0
Begin
Var WC : Byte = WordCount(InString, ' ')
For X:=2 to WC Do
Begin
Var WG : String = WordGet(X, InString, ' ')
Var WL : Byte = Length(WG)
Var TmpString : String = ''
If WL > 3 Then
Begin
TmpString:=WG[1]
For Y:= WL-1 downto 2 Do
Begin
TmpString:=TmpString+WG[Y]
End
TmpString:=TmpString+WG[WL]
End
Else
Begin
TmpString:=WG
End
JlString:=JlString+TmpString
Var JC : Byte = WordCount(JlString, ' ')
If WC-1 > JC Then
JlString:=JlString+' '
End
JumbleString := JlString
Tweet := '/mystic/scripts/tweetme/twitter.py "'+Plyr.Name+' just jumbled their words in MRC: '+JlString+' #bbs #mrc #jumble #mysticbbs #tqwNet"'
//MenuCmd('DD', Tweet);
End
// Rot13 String by [mL]
Function Rot13String(InString:String): String
Var X : Byte = 0;
Var Y : Byte = 0;
Var NormL : String = 'abcdefghijklmnopqrstuvwxyz';
Var RotL : String = 'nopqrstuvwxyzabcdefghijklm';
Begin
Var WC : Byte = WordCount(InString, ' ')
Var TmpString : String = ''
For X := 2 to WC Do
Begin
Var WG : String = WordGet(X, InString, ' ')
Var WL : Byte = Length(WG)
Var TmpChr : String
Var TmpPos : Byte = 0;
For Y := 1 to WL Do
Begin
TmpChr := Lower(WG[Y])
TmpPos := Pos(TmpChr, NormL)
If TmpPos > 0 Then
TmpString := TmpString+RotL[TmpPos]
Else
TmpString := TmpString+WG[Y]
End
If X < WC Then
TmpString := TmpString+' '
End
Rot13String := TmpString
End
Function InputLine:String
Var IX,UL : Integer = 0
Var Ch : Char = #13
Var IBuf : String = '' // Input buffer [sf]
Var DBuf : String = '' // Displayed buffer [sf]
Var RBuf : Boolean = False // Refresh buffer flag [sf]
Var Done : Boolean = False // Done getting input (Enter) [sf]
Var Anim : Byte = 1 // Animation step [sf]
Var BIdx : Byte = 1 // Buffer Current Idx [sf]
Var NIdx : Byte = 1 // Buffer Target Idx [sf]
Var CClr : Byte = 7 // Char counter color [sf]
Var Words : String = ''
Var LastW : String = ''
Var PrefixLen : Byte = 0
Var SM1,SM2,SM3 : Byte
Begin
UL:=Length(StripMCI(MyNamePrompt))
IX:=PromptX+Length(StripMCI(MyNamePrompt))
// Derive the maximum allowed buffer bnased on packet fields
PrefixLen := Length(UserTag) + Length(SiteTag) + Length(MyRoom)*2 + Length(MyNamePrompt) + 20
// Init the characters counter [sf]
WriteXY(CounterX1, CounterY1, 7, PadLt(Int2Str(Length(IBuf)), 3, '0'))
WriteXY(CounterX2, CounterY2, 7, PadLt(Int2Str(IMaxBuffer), 3, '0'))
// Init the buffer input bar [sf]
GoToXy(PromptX,PromptY)
//Write('|16' + MyNamePrompt + InputClr + GetPipe(CIdx) + DBuf + CursorBg + Cursor + InputClr + '|$X78' + InputBg)
Write('|16' + MyNamePrompt + InputClr + GetPipe(CIdx) + DBuf + CursorBg + Cursor + InputClr + '|[K' + InputBg)
Repeat
While Not Keypressed Do
Begin
// Improved polling time to keyboard [sf]
Delay(10)
// Read chat files every 7 cycles [sf]
// Slightly improve file access rate
If Loop % 7 = 0 Then
ReadChatFiles
// Read chatroom userlist [sf]
If Loop % 1999 = 0 Then
SendToServer('USERLIST')
// Heartbeat to server every 12000 cycles
// Maintain server heartbeat rate
If Loop % 11987 = 0 Then Begin
SendToServer('IAMHERE')
End
// Also re-read banners from server and add new ones
If Loop > 47948 Then Begin
SendToServer('BANNERS')
Loop:=1
End
If Loop % ScrollSpeed = 0 Then
ScrollBanner
If Loop % 499 = 0 Then
CheckStale
If Loop % 997 = 0 Then
Begin
If HBClr = HeartbeatGood Then
SendToClient('LATENCY')
End
// Animate the heartbeat animation every 53 cycles [sf]
If Loop % 53 = 0 Then
Begin
Anim := Anim + 1
If Anim > Length(HeartBeat) Then
Anim := 1
WriteXY(HeartBeatX, HeartBeatY, HBClr, Copy(HeartBeat, Anim, 1))
GoToXy(HeartBeatX, HeartBeatY)
End
Loop:=Loop+1
End
// Handle arrow keys [sf]
AllowArrow := True
Ch := ReadKey
// Buffer history handling [sf]
If IsArrow Then
Begin
Case Ch Of
Chr(72) : NIdx := GetBufferIndex(BIdx, 1) // Scroll back input (Up)
Chr(80) : NIdx := GetBufferIndex(BIdx, -1) // Scroll forward input (Dn)
Chr(81) : TIdx := GetColorIndex(CIdx, -1) // Decrease color index (PgDn)
Chr(73) : TIdx := GetColorIndex(CIdx, 1) // Increase color index (PnUp)
Else
NIdx := BIdx
TIdx := CIdx
End
Ch := Chr(0)
If BIdx <> NIdx Then
Begin
IBuf := BufferHist[NIdx]
BIdx := NIdx
RBuf := True
End
Else
If CIdx <> TIdx Then
Begin
CIdx := TIdx
RBuf := True
End
End
// PIPE codes whitelist [sf]
If Copy(IBuf, Length(IBuf), 1) = '|' Then
Begin
Case Ch Of
'0',
'1',
'2',
'3',
'|',
Chr(32),
Chr(8),
Chr(27) : Ch := Ch
Else
Ch := Chr(0)
End
End
If Ch = Chr(32) and Length(IBuf) < 1 Then
Ch := Chr(0)
// Nick auto-completion [sf]
If Ch = Chr(9) Then
Begin
Var WC : Byte = WordCount(IBuf, ' ') // Count of words in buffer
Var LW : String = WordGet(WC, IBuf, ' ') // Last word in buffer
Var WL : Byte = Length(LW) // Length of the last word in buffer
// Define display if at the beginning or mid-sentence
Var Tail : String = ''
If WC < 2 Then
Tail := ': '
Else
Tail := ' '
If LastUSearch = '' Then
LastUSearch := LW
If WL > 0 and Length(RoomUsers) > 0 Then
Begin
Var PF :String = Copy(IBuf, 1, WordPos(WC, IBuf, ' ')-1) // Buffer prefix
Var UMatch : Boolean = False
Var SLoop : Byte = 0
While Not UMatch Do
Begin
If UserIdx > WordCount(RoomUsers, ',') Then UserIdx := 1
Var UHandle : String = WordGet(UserIdx, RoomUsers, ',')
If Length(UHandle) > 0 and
Upper(LastUSearch) = Upper(Copy(UHandle, 1, Length(LastUSearch))) Then
Begin
UMatch := True
IBuf := PF + UHandle + Tail
End
UserIdx:=UserIdx+1
SLoop:=SLoop+1
If SLoop > WordCount(RoomUsers, ',') Then UMatch := True
End
End
End
Else
Begin
LastUSearch := ''
UserIdx := 1
End
IMaxBuffer := 255 - PrefixLen
If IMaxBuffer > MaxBuffer Then IMaxBuffer := MaxBuffer
If Lower(WordGet(1, IBuf, ' ')) = '/rainbow' Then
IMaxBuffer := Abs(IMaxBuffer/4) - 4
If Lower(WordGet(1, IBuf, ' ')) = '/t' Then
Begin
CIdx := 15
TIdx := 15
CursorBg := '|28'
InputClr := '|20|15|07'
End
Else If Lower(WordGet(1, IBuf, ' ')) = '/r' Then
Begin
CIdx := 15
TIdx := 15
CursorBg := '|28'
InputClr := '|20|15|07'
End
Else If Lower(WordGet(1, IBuf, ' ')) = '/m' Then
Begin
CIdx := 15
TIdx := 15
CursorBg := '|28'
InputClr := '|20|15|07'
End
Else If Lower(WordGet(1, IBuf, ' ')) = '/msg' Then
Begin
CIdx := 15
TIdx := 15
CursorBg := '|28'
InputClr := '|20|15|07'
End
Else If Lower(WordGet(1, IBuf, ' ')) = '/b' Then
Begin
CIdx := 15
TIdx := 15
CursorBg := '|26'
InputClr := '|18|15|07'
End
Else
Begin
//CIdx := 10
//TIdx := 10
CursorBg := '|25'
InputClr := '|16|17|07'
End
// Send to server and add to buffer history [sf]
If Ch = Chr(13) Then
Begin
// Try and replace a :) or :D with a  or 
If Plyr.ShowSmilies Then
Begin
While Pos(':)', IBuf) > 0 Do
Begin
SM1:=Pos(':)', IBuf)
SM2:=SM1+1
Delete(IBuf,SM2,1)
IBuf[SM1]:=Chr(1)
End
While Pos(':D', IBuf) > 0 Do
Begin
SM1:=Pos(':D', IBuf)
SM2:=SM1+1
Delete(IBuf,SM2,1)
IBuf[SM1]:=Chr(2)
End
While Pos('=)', IBuf) > 0 Do
Begin
SM1:=Pos('=)', IBuf)
SM2:=SM1+1
Delete(IBuf,SM2,1)
IBuf[SM1]:=Chr(2)
End
End
If Lower(WordGet(1, IBuf, ' ')) = '/rainbow' Then
InputLine := RainbowString(IBuf)
Else If Lower(WordGet(1, IBuf, ' ')) = '/kewl' Then
InputLine := KewlString(IBuf)
Else If Lower(WordGet(1, IBuf, ' ')) = '/leet' Then
InputLine := LeetString(IBuf)
Else If Lower(WordGet(1, IBuf, ' ')) = '/jumble' Then
InputLine := JumbleString(IBuf)
Else If Lower(WordGet(1, IBuf, ' ')) = '/rot13' Then
InputLine := Rot13String(IBuf)
Else
InputLine := IBuf
AddToBufferHistory(IBuf)
IBuf := ''
BIdx := 1
IMaxBuffer := MaxBuffer
WriteXY(CounterX1, CounterY1, 7, PadLt(Int2Str(Length(IBuf)), 3, '0'))
WriteXY(CounterX2, CounterY2, 7, PadLt(Int2Str(IMaxBuffer), 3, '0'))
Done := True
//CIdx := 10
//TIdx := 10
CursorBg := '|25'
InputClr := '|16|17|07'
End
Else
// Clear input buffer with ESC [sf]
If Ch = Chr(27) Then
Begin
IBuf := ''
BIdx := 1
RBuf := True
End
Else
Begin
// Handle backspace [sf]
If Ch = Chr(8) Then
Begin
Delete (IBuf, Length(IBuf), 1)
RBuf := True
End
Else
// Allow characters between #32 and #126 only [sf]
Begin
If Ord(Ch) > 31 and Ord(Ch) < 127 Then
Begin
// Limit input buffer length [sf]
If Length(IBuf) < IMaxBuffer Then
Begin
IBuf := IBuf + Ch
RBuf := True
End
End
// Ignore any other character [sf]
Else
Ch := ''
End
End
if Upper(Copy(IBuf,1,3)) = '/R ' and LastPrivMsg <> '' Then
IBuf := '/t ' + LastPrivMsg + ' '
// Refresh buffer only if changed [sf]
If RBuf Then
Begin
// Scroll input buffer [sf]
If Length(IBuf) > 78-PromptX-UL Then
DBuf := Copy(IBuf, Length(IBuf) - (77-PromptX-UL), 78-PromptX-UL)
Else
DBuf := IBuf
// Update input bar [sf]
GoToXy(PromptX,PromptY)
//Write('|16' + MyNamePrompt + InputClr + GetPipe(CIdx) + DBuf + CursorBg + Cursor + InputClr + '|$X78' + InputBg + '|16')
Write('|16' + MyNamePrompt + InputClr + GetPipe(CIdx) + DBuf + CursorBg + Cursor + InputClr + '|[K' + InputBg + '|16')
// Handle counter color coding [sf]
CClr := 7
If Length(IBuf) > IMaxBuffer -20 Then
CClr := 14
If Length(IBuf) > IMaxBuffer -10 Then
CClr := 12
// Update characters counter [sf]
WriteXY(CounterX1, CounterY1, CClr, PadLt(Int2Str(Length(IBuf)), 3, '0'))
WriteXY(CounterX2, CounterY2, CClr, PadLt(Int2Str(IMaxBuffer), 3, '0'))
End
GoToXy(PromptX,PromptY)
Until Done
End
// Clear screen
Procedure DoCls
Var X : Integer = 0
Begin
For X:=1 To CLBuffer Do
ChatLines[X]:=''
RedrawScreen
End
Procedure DoScrollBack
Begin
//MenuCmd('GV','mrcscrl;x;y;'+ChatLog+';END')
MenuCmd('GV','mrcscrl;'+ChatLog+' /end')
RedrawScreen
End
Procedure ShowWelcome
Begin
// Welcome info text [sf]
Add2Chat('* |10Welcome to ' + MRCVersion)
Add2Chat('* |15ESC|10 to clear input buffer, |15UP|10/|15DN|10 arrows for buffer history')
Add2Chat('* |15PGUP|10/|15PGDN|10 to change your chat text color and |15TAB|10 for nick completion')
Add2Chat('* |10The bottom-right heartbeat indicates your status with BBS and server')
Add2Chat('* |10Your maximum message length is |15' + Int2Str(IMaxBuffer)+ '|10 characters')
ShowChat(0)
End
Procedure ShowChanges
Begin
// Changes info text [sf]
Add2Chat('* |15List of changes from MRC v1.1')
Add2Chat('* |10Completely redesigned Input routine [sf]')
Add2Chat('* |10Ability to receive chat while typing (non-blocking) [sf]')
Add2Chat('* |10Built-in input buffer history [sf]')
Add2Chat('* |10Chat text color changing using PgUp and PgDn [sf]')
Add2Chat('* |10Visual indicator when your nick is mentioned [sf]')
Add2Chat('* |10Input buffer with color coded characters counter [sf]')
Add2Chat('* |10Server latency and synchronization heartbeat indicator [sf]')
Add2Chat('* |10Enlarged view port, more lines are available for the chat [sf]')
Add2Chat('* |10Customizable information scroller [sf]')
Add2Chat('* |10Improvement of performance and responsiveness of the interface [sf]')
Add2Chat('* |10Brand new backend for improved speed and scalability [sf]')
Add2Chat('* |10Nick auto-completion using TAB [sf]')
Add2Chat('* |10Reply to last private message using /r [sf]')
Add2Chat('* |10Add |12t|04rOUt |12s|04lAp|10 using |12/trout|10 [mL]')
Add2Chat('* |10Add |14r|06Ot|131|053 |10encoding using |12/rot13|10 [mL]')
//Add2Chat('* |10')
ShowChat(0)
End
Procedure EnterChat
Begin
ShowWelcome // Show welcome text [sf]
Add2Chat(UpdateStrings(Plyr.EnterChatMe,Plyr.Name,'',MyRoom,MyRoom))
SendToAllNotMe(UpdateStrings(Plyr.EnterChatRoom,Plyr.Name,'',MyRoom,MyRoom))
Delay(20)
SendToServer('IAMHERE')
Delay(20)
SendToServer('BANNERS')
ShowChat(0)
End
Procedure LeaveChat
Var Str1 : String = ''
Begin
Add2Chat(UpdateStrings(Plyr.LeaveChatMe,Plyr.Name,'',MyRoom,MyRoom))
SendToAllNotMe(UpdateStrings(Plyr.LeaveChatRoom,Plyr.Name,'',MyRoom,MyRoom))
ShowChat(0)
Delay(500)
SendToServer('LOGOFF');
ShowChat(0)
End
Procedure DoSetList
Var R,S,T : String = ''
Var B : Boolean = False
Begin
S:='False'
If Plyr.UseClock Then
S:='True'
T:='False'
If Plyr.ShowSmilies Then
T:='True'
R:='12Hour (HH:MMa or HHMMp)'
If Not Plyr.ClockFormat Then
R:='24Hour (HH:MM)'
B:=Plyr.UseClock
Plyr.UseClock:=False
Add2Chat('|11List of current |15/SET |11values from your account')
Add2Chat('|15ENTERCHATME |08:|07 '+Plyr.EnterChatMe)
Add2Chat('|15ENTERCHATROOM |08:|07 '+Plyr.EnterChatRoom)
Add2Chat('|15ENTERROOMME |08:|07 '+Plyr.EnterRoomMe)
Add2Chat('|15ENTERROOMROOM |08:|07 '+Plyr.EnterRoomRoom)
Add2Chat('|15LEAVECHATME |08:|07 '+Plyr.LeaveChatMe)
Add2Chat('|15LEAVECHATROOM |08:|07 '+Plyr.LeaveChatRoom)
Add2Chat('|15LEAVEROOMME |08:|07 '+Plyr.LeaveRoomMe)
Add2Chat('|15LEAVEROOMROOM |08:|07 '+Plyr.LeaveRoomRoom)
Add2Chat('|15DEFAULTROOM |08:|07 '+Plyr.DefaultRoom)
Add2Chat('|15NICKCOLOR |08:|07 '+Plyr.NameColor+Plyr.Name)
Add2Chat('|15LTBRACKET |08:|07 '+Plyr.LtBracket)
Add2Chat('|15RTBRACKET |08:|07 '+Plyr.RtBracket)
Add2Chat('|15USECLOCK |08:|07 '+S)
Add2Chat('|15CLOCKFORMAT |08:|07 '+R)
Add2Chat('|15SHOWSMILIES |08:|07 '+T+ ' |08(under construction)|07')
ShowChat(0)
Plyr.UseClock:=B
End
Procedure DoSetHelp
Var B : Boolean = False
Begin
B:=Plyr.UseClock
Plyr.UseClock:=False
Add2Chat('|15/SET |08<|03tag|08> <|03text|08>')
Add2Chat('|11Use |15SET |11to set various fields to your account')
Add2Chat('|15HELP |03This helps message')
Add2Chat('|15LIST |03List all fields and tabs')
Add2Chat('|15ENTERCHATME |03Displayed to |11me |03when I enter chat.')
Add2Chat('|15ENTERCHATROOM |03Displayed to |11room |03when I enter chat.')
Add2Chat('|15ENTERROOMME |03Displayed to |11me |03when I enter room.' )
Add2Chat('|15ENTERROOMROOM |03Displayed to |11room |03when I enter room.' )
Add2Chat('|15LEAVECHATME |03Displayed to |11me |03when I leave chat.' )
Add2Chat('|15LEAVECHATROOM |03Displayed to |11room |03when I leave chat.' )
Add2Chat('|15LEAVEROOMME |03Displayed to |11me |03when I leave room.')
Add2Chat('|15LEAVEROOMROOM |03Displayed to |11room |03when I leave room.')
Add2Chat('|15DEFAULTROOM |03Join this room when you join chat.')
Add2Chat('|15NICKCOLOR |03Change my nickname color |11(MCI Pipe codes).' )
Add2Chat('|15LTBRACKET |03Change my left bracket / color |11(MCI Pipe codes).' )
Add2Chat('|15RTBRACKET |03Change my right bracket / color |11(MCI Pipe codes).' )
Add2Chat('|15USECLOCK |03(|15Y|03/|15N|03) Use timestamp in chat')
Add2Chat('|15CLOCKFORMAT |1112 |03or |1124 |03hour clock format')
Add2Chat('|15SHOWSMILIES |03Convert smilies into ANSI smilies (under construction)')
ShowChat(0)
Plyr.UseClock:=B
End
Procedure ChangeClock(T:Integer;S:String)
Begin
S:=StripB(Upper(S),' ')
Case T Of
1: Begin
If Pos('YE',S) > 0 Or Pos('TR',S) > 0 Then Begin
Plyr.UseClock:=True
Add2Chat('|11CLOCKFORMAT |08: |15True')
End Else Begin
If Pos('NO',S) > 0 Or Pos('FA',S) > 0 Then Begin
Plyr.UseClock:=False
Add2Chat('|11CLOCKFORMAT |08: |15False')
End Else
Add2Chat('|11Usage: |15/SET USECLOCK YES||TRUE|08 or |15/SET USECLOCK NO||FALSE')
End
ShowChat(0)
End
2: Begin
If S = '12' Then Begin
Plyr.ClockFormat:=True
Add2Chat('|07CLOCKFORMAT |08: |0712 hour')
End Else Begin
If S = '24' Then Begin
Plyr.ClockFormat:=False
Add2Chat('|07CLOCKFORMAT |08: |0724 hour')
End Else
Add2Chat('|11Usage: |08"|03/SET CLOCKFORMAT 12|08" or "|03/SET CLOCKFORMAT 24|08"')
End
ShowChat(0)
End
End
SavePlyr(Plyr.RecIdx)
End
Procedure ChangeSmilies(T:Integer;S:String)
Begin
S:=StripB(Upper(S),' ')
Case T Of
1: Begin
If Pos('YE',S) > 0 Or Pos('TR',S) > 0 Then Begin
Plyr.ShowSmilies:=True
Add2Chat('|11SHOWSMILIES |08: |15True')
End Else Begin
If Pos('NO',S) > 0 Or Pos('FA',S) > 0 Then Begin
Plyr.ShowSmilies:=False
Add2Chat('|11SHOWSMILIES |08: |15False')
End Else
Add2Chat('|11Usage: |15/SET SHOWSMILIES YES||TRUE|08 or |15/SET SHOWSMILIES NO||FALSE')
End
ShowChat(0)
End
End
SavePlyr(Plyr.RecIdx)
End
Procedure DoSet(Line:String)
Var Tag,Txt : String = ''
Var P : Integer = 0
Begin
Tag:=WordGet(1,Line,' ')
P:=Length(Tag)+1
Delete(Line,1,P)
StripB(line,' ')
Case Upper(Tag) Of
'HELP': DoSetHelp
'LIST': DoSetList
'ENTERCHATME' : Plyr.EnterChatMe:=Line
'ENTERCHATROOM' : Plyr.EnterChatRoom:=Line
'ENTERROOMME' : Plyr.EnterRoomMe:=Line
'ENTERROOMROOM' : Plyr.EnterRoomRoom:=Line
'LEAVECHATME' : Plyr.LeaveChatMe:=Line
'LEAVECHATROOM' : Plyr.LeaveChatRoom:=Line
'LEAVEROOMME' : Plyr.LeaveRoomMe:=Line
'LEAVEROOMROOM' : Plyr.LeaveRoomRoom:=Line
'DEFAULTROOM' : Plyr.DefaultRoom:=Line
'NICKCOLOR' : ChangeNick('C',Line,False)
'LTBRACKET' : ChangeNick('L',Line,False)
'RTBRACKET' : ChangeNick('R',Line,False)
'USECLOCK' : ChangeClock(1,Line)
'CLOCKFORMAT' : ChangeClock(2,Line)
'SHOWSMILIES' : ChangeSmilies(1,Line)
'' : DoSetHelp
End
SavePlyr(Plyr.RecIdx)
End
Procedure DLChatLog
Var X,Y,TS,DS,TempChat : String = ''
Var fptr : File
Begin
DS:=Replace(DateStr(DateTime,1),'/','')
TS:=Replace(TimeStr(DateTime,False),':','')
TempChat:=CfgTempPath+'mrc_chat_'+Replace(SiteTag,' ','_')+'_'+DS+'_'+TS+'.log'
Write('|16|11|CL')
If InputYN('Strip MCI color codes? ') Then Begin
fAssign(fptr,ChatLog,66)
fReset(Fptr)
While Not fEof(Fptr) Do Begin
fReadLn(Fptr,X)
Y:=StripMCI(X)
AppendText(TempChat,Y)
End
fClose(Fptr)
End Else
FileCopy(ChatLog,TempChat)
MenuCmd('F3',TempChat);
FileErase(TempChat)
RedrawScreen;
End
Procedure Main
Var Done : Boolean = False
Var RestOfLine : String = ''
Var W1,W2,UIL : String = ''
Var IL : String = ''
Begin
Loop:=1
UpdateScreen
Repeat
Delay(10)
IL:=InputLine
// Support slash commands even if prefixed with a PIPE code
If Pos('/',StripMCI(IL)) = 1 Then Begin
W1:=Upper(WordGet(1,StripMCI(IL),' '))
W2:=WordGet(2,IL,' ')
RestOfLine:=IL
Delete(RestOfLine,1,Length(W1))
RestOfLine:=StripB(RestOfLine,' ')
Case W1 Of
'/CHANGES' : ShowChanges // Display changes [sf]
'/?' : DoHelp
'/B' : DoBroadcast(IL)
'/BBSES' : SendToServer('CONNECTED')
'/CHANNEL' : SendToServer('CHANNEL')
'/CHATTERS' : SendToServer('CHATTERS')
'/CLS' : DoCls
'/DLCHATLOG' : DLChatLog
'/JOIN' : JoinRoom(W2,True)
'/LIST' : SendToServer('LIST')
'/ME' : DoMeAction(IL)
'/TROUT' : DoTroutAction(IL)
'/Q','/QUIT' :
Begin
LeaveChat
Done := True
End
'/ROOMS' : SendToServer('LIST')
'/SCROLL' : DoScrollBack
'/SET' : DoSet(RestOfLine)
'/TOPIC' : ChangeTopic(RestOfLine)
'/T','/MSG','/M',
'/TELL' : DoPrivateMsg(IL)
'/USERS' : SendToServer('USERS')
'/WHO' : DoWho
'/WHOON' : SendToServer('WHOON')
'/MOTD' : SendToServer('MOTD')
'/HELP' : SendToServer('HELP')
'/INFO' : SendToServer('INFO '+W2)
'/QUOTE' : SendToServer(RestOfLine)
'/VERSION' :
Begin
SendToServer('VERSION')
Add2Chat('|07- |13'+MRCVersion)
End
End
End Else Begin
If Length(IL) > 0 Then
Begin
IL:=Replace(IL, '~', ' ')
SendToRoom(MyNamePrompt+GetPipe(CIdx)+IL)
End
End
Until Done
End
Begin
CleanOut
GetThisUser
// Check if we toggle the NodeMsgFlag
If ACS('OA') Then
Begin
NodeMsgFlag:=True
MenuCmd('GE', '18')
End
Init
RedrawScreen
EnterChat
JoinRoom(Plyr.DefaultRoom,False)
LoadBanners
NextBanner
// ScrollBanner
Main
Write('|16|11|CL')
// Reset the NodeMsgFlag if changed
If NodeMsgFlag Then
MenuCmd('GE', '18')
CleanOut
Halt
End