{$VERSION 2.6.0} type TEvulPlayer = record Nick,IP: String; Level: Byte; end; TPlayer= record Ranked: Boolean; BigPoint: Boolean; end; const BADWORD_LIST = 'scripts/badwords.txt'; PUNISH_KICK = 3; PUNISH_BAN = 4; PUNISH_BAN_TIME = 180; PUNISH_WARN_MSG = 'Stop it!'; PUNISH_KICK_MSG = 'You have been warned! KICKED!'; PUNISH_BAN_MSG = 'Enough is enough! BANNED!'; REASON_SWEAR = 'Swearing is not allowed!'; REASON_CAMP = 'Camping is not allowed!'; var BadWords: TStringArray; EvulPlayers: Array of TEvulPlayer; Players: Array[1..32] of TPlayer; const //Teams ALPHA = 1; BRAVO = 2; CHARLIE = 3; DELTA = 4; SPECTATOR = 5; //Game Modes DEATHMATCH = 0; POINTMATCH = 1; TEAMMATCH = 2; CTF = 3; RAMBO = 4; INF = 5; HTF = 6; //Weapons DEAGLES = 1; HKMP5 = 2; AK74 = 3; STEYR = 4; SPAS = 5; RUGER = 6; M79 = 7; BARRET = 8; MINIMI = 9; MINIGUN = 10; FLAMER = 11; BOW = 12; FLAMEBOW = 13; SOCOM = 0; KNIFE = 14; CHAINSAW = 15; LAW = 16; //Mine UNP_CNTDOWNLENGTH = 5; //-- /unpause: countdown length in seconds AUTOADMIN_UPDATE = 20; //-- AUTOADMIN update frequency minutes PLAYER_STATS_ON = 1; //PlAYER_STATS_URL='http://127.0.0.1/zitrostats/PlayerStats.php'; PlAYER_STATS_URL='http://server.the-black-army.de/zitrostats/PlayerStats.php'; var iUnpauseTarget: integer; //-- /unpause inpause,startunpause: boolean; IPList,bnames: TStringArray; FirstBlood: boolean; function MyTrim(const S:String):String; var i:integer; begin Result:=S; for i:=0 to 31 do Result:= strreplace(Result,chr(i),''); Result:= trim(Result); end; function xsplit(const source: string; const delimiter: string):TStringArray; var i,x,d,l:integer; s:string; begin d:=length(delimiter); x:=0; i:=1; l:=length(source)+1; Result:= []; SetArrayLength(Result,1); while(i 0)) then command('/gamemode 0') else if ((gm = 'pm') and (GameStyle <> 1)) then command('/gamemode 1') else if (((gm = 'tm') or (gm ='td'))and(GameStyle <> 2)) then command('/gamemode 2') else if ((gm = 'ct') and (GameStyle <> 3)) then command('/gamemode 3') else if ((gm = 'rm') and (GameStyle <> 4)) then command('/gamemode 4') else if ((gm = 'in') and (GameStyle <> 5)) then command('/gamemode 5') else if ((gm = 'ht') and (GameStyle <> 6)) then command('/gamemode 6'); end; function LoadBadWords:Boolean; begin BadWords:=[]; BadWords:= xsplit(ReadFile(BADWORD_LIST),#13#10); if BadWords[ArrayHigh(BadWords)] = '' then SetArrayLength(BadWords,ArrayHigh(BadWords)); Writeln('BadWords loaded'); Result:=true; end; procedure OverviewBadWords(const ID:byte); var i:integer; begin for i:=0 to ArrayHigh(BadWords) do begin SayToID(ID,'"'+BadWords[i]+'"'); end; SayToID(ID,'BadWords Total: '+ INttostr(getArrayLength(BadWords))); end; procedure AddBadWord(const ID: Byte; BadWord: String); begin BadWord:=LowerCase(Trim(BadWord)); WriteLnFile(BADWORD_LIST,BadWord); SetArrayLength(BadWords,GetArrayLength(BadWords)+1); BadWords[ArrayHigh(BadWords)]:= BadWord; SayToID(ID,'Badword "'+BadWord+'" added'); end; procedure DelBadWord(const ID: Byte; BadWord: String); var i,z:integer; s:string; found: Boolean; begin BadWord:=LowerCase(Trim(BadWord)); for i:=0 to ArrayHigh(BadWords) do begin if (BadWord=BadWords[i]) then begin for z:=i to ArrayHigh(BadWords)-1 do begin BadWords[z]:= BadWords[z+1]; end; SetArrayLength(BadWords,ArrayHigh(BadWords)); Found:= true; break; end; end if Found then begin for i:=0 to ArrayHigh(BadWords) do begin s:=s+BadWords[i]+chr(13)+chr(10); end; WriteFile(BADWORD_LIST,s); SayToID(ID,'BadWord "'+BadWord+'" deleted'); end else SayToID(ID,'BadWord "'+BadWord+ ' not found'); end; procedure ForgiveEvuls; var i,z:Integer; begin for i:=0 to GetArrayLength(EvulPlayers)-1 do begin if EvulPlayers[i].Level = 1 then begin for z:=i to GetArrayLength(EvulPlayers)-2 do begin EvulPlayers[z]:= EvulPlayers[z+1]; end; SetArrayLength(EvulPlayers,GetArrayLength(EvulPlayers)-1); end else EvulPlayers[i].Level := EvulPlayers[i].Level -1; end; end; procedure PunishEvulPlayer(const ID: Byte; const Reason:String); var i,z:integer; sIP,sNick:String; Found: Boolean; begin sIP:= IDToIP(ID); sNick:= IDtoName(ID); for i:=0 to GetArrayLength(EvulPlayers)-1 do begin if ((sIP = EvulPlayers[i].IP) or (sNick = EvulPlayers[i].Nick)) then begin EvulPlayers[i].Level := EvulPlayers[i].Level + 1; if (PUNISH_BAN <= EvulPlayers[i].Level) then begin SayToID(ID,Reason); SayToID(ID,PUNISH_BAN_MSG); BanPlayerReason(ID,PUNISH_BAN_TIME,Reason) end else if (PUNISH_KICK <= EvulPlayers[i].Level) then begin SayToID(ID,Reason); SayToID(ID,PUNISH_KICK_MSG); KickPlayer(ID); end else begin Command('/kill '+IntToStr(ID)); SayToID(ID,PUNISH_WARN_MSG); SayToID(ID,Reason); SetScore(ID,0); end; Found:= True; break; end; end; if not found then begin if ServerPort<>23073 then begin SetArrayLength(EvulPlayers,GetArrayLength(EvulPlayers)+1); z:= GetArrayLength(EvulPlayers)-1; EvulPLayers[z].Nick:= sNick; EvulPLayers[z].IP:= sIP; EvulPLayers[z].Level:= 1; Command('/kill '+IntToStr(ID)); end; SayToID(ID,PUNISH_WARN_MSG); SayToID(ID,Reason); end; end; procedure CheckBadWords(const ID:Byte; var Text:String); var i: integer; begin if not Players[ID].BigPoint then for i:=0 to ArrayHigh(BadWords) do begin if MaskCheck(Text,BadWords[i]) then begin If not MaskCheck(Text,'*nu*bia*') then begin //SayToID(ID,); PunishEvulPlayer(ID,REASON_SWEAR); break; end; end; end; end; procedure DoUnpause; begin //Command('/pause'); if (inpause AND NOT startunpause) then begin command('/say unpausing server in '+IntToStr(UNP_CNTDOWNLENGTH)+' seconds'); startunpause := true; iUnpauseTarget := StrToInt(FormatDate('ss'))+ UNP_CNTDOWNLENGTH; if (iUnpauseTarget > 59) then iUnpauseTarget := iUnpauseTarget-60; end; //Command('/pause'); ThreadFunc([],'PauseBack') end; procedure PauseBack; var i: Integer; begin Command('/pause'); for i:=0 to 25 do begin Sleep(1); Command('/pause'); end; end; procedure DoPause(const ID:integer); begin if (NOT inpause) then begin command('/say PAUSED '); SayToID(ID,'Use /unp to unpause'); inpause := true; end else begin SayToID(ID,'Its already paused. Use /unp to unpause'); //command('/pause'); end; end; procedure DoUnpauseCountdown; var secleft,secnow:integer; begin Command('/pause'); SayToID(1,':O'); secnow :=StrToInt(FormatDate('ss')); if (secnow <> iUnpauseTarget) then begin // countdown output secleft := iUnpauseTarget - secnow; if (secleft<0) then secleft := iUnpauseTarget - secnow + 60; command('/say '+IntToStr(secleft)); end else begin // unpausing command('/say Go!'); command('/unpause'); inpause := false; startunpause := false; end; end; procedure DNSUpdate; var i: Integer; begin //Sleep(20000); SetArrayLength(IPList,0); IPList := xsplit(ReadFile('autoadmin_dns.txt'),chr(10)); for i := 0 to ArrayHigh(IPList) do begin //writeln(IPList[i]); //IPList[i]:= GetURL('http://127.0.0.1/resolve.php?ip='+IPList[i]); IPList[i]:= Mytrim(GetURL('http://127.0.0.1/resolve.php?ip='+IPList[i])); //Writeln('=> '+IPList[i]); end; WriteLn('AutoAdmin-DNS-Updated'); end; procedure ActivateServer(); begin bNames:= xsplit(ReadFile('bnames.txt'),#13#10); if ServerPort<> 23073 then Firstblood := true else Firstblood := false; if(bNames[ArrayHigh(bNames)]='') then SetArrayLength(bNames,ArrayHigh(bNames)); if LoadBadWords then DNSUpdate; //ThreadFunc([],'LoadBadWords'); //sleep(500); //ThreadFunc([],'DNSUpdate'); end; procedure AppOnIdle(Ticks: integer); begin if Ticks mod (3600 * AUTOADMIN_UPDATE) = 0 then ThreadFunc([],'DNSUpdate'); //DNSUpdate; if (startunpause) then DoUnpauseCountdown; end; procedure AddBadName(const ID: Byte;const Pattern: String; const Ban: Boolean); var Parts: TStringArray; m,i: integer; S:String; begin if Ban then begin Parts:= xsplit(Pattern,':'); try m:= StrToInt(Parts[ArrayHigh(Parts)]); except m:=0; end; s:= Parts[0]; for i:=1 to ArrayHigh(Parts)-1 do Begin s:= s +':'+ Parts[i]; end; s:= s + ':ban:' + IntToStr(m)+ ';'; end else begin s:= Pattern+':kick;'; end; WriteLnFile('bnames.txt',s); SetArrayLength(bNames,ArrayHigh(bNames)+2); bNames[ArrayHigh(bNames)]:= s; SayToID(ID,'Line: >'+s+ '< added'); end; procedure DelBadName(const ID: Byte;const Pattern: String); var i,z:integer; s:string; found: Boolean; begin z:= Length(Pattern)+1; for i:=0 to ArrayHigh(bNames) do begin if (LowerCase(Copy(bNames[i],1,z))=LowerCase(Pattern)+':') then begin for z:=i to ArrayHigh(bNames)-1 do begin bnames[z]:= bnames[z+1]; end; SetArrayLength(bNames,ArrayHigh(bNames)); Found:= true; end; end if Found then begin for i:=0 to ArrayHigh(bnames) do begin s:=s+bnames[i]+chr(13)+chr(10); end; WriteFile('bnames.txt',s); SayToID(ID,Pattern+' deleted'); end else SayToID(ID,Pattern+ ' not found'); end; procedure OnCamp(const ID: Byte); begin //SayToID(ID,); PunishEvulPlayer(ID,REASON_CAMP); end; procedure PutMap(const ID:Byte; const Map:String); begin SayToID(ID,'I guess you meant '''+Map+''''); Command('/map '+Map); end; Procedure HelpToChangeMap(const ID:Byte; Map: String); begin Map:= Trim(Map); SayToID(ID,'Searchin for '+map); if not FileExists('maps/'+Map+'.PMS') then begin Case lowercase(Map) of 'nub','nubia','nuubia','ctf_nubia','ctf_nuubia','ctf_nub': PutMap(ID,'ctf_Nuubia'); 'laos','loas','ctf_laos','ctf_loas': PutMap(ID,'ctf_Laos'); 'ash','ahs','ctf_ash','ctf_ahs': PutMap(ID,'ctf_Ash'); 'b2b','ctf_b2b': PutMap(ID,'ctf_B2b'); 'cobra','ctf_cobra': PutMap(ID,'ctf_Cobra'); 'conquest','ctf_conquest': PutMap(ID,'ctf_Conquest'); 'd2','death2','daeth2','ctf_d2','ctf_daeth2','ctf_death2': PutMap(ID,'ctf_Death2'); 'div','division','ctf_div','ctf_division': PutMap(ID,'ctf_Division'); 'dd2','dropdown2','ctf_dropdown2': PutMap(ID,'ctf_Dropdown2'); 'equi','equinox','ctf_equi','ctf_equinox': PutMap(ID,'ctf_Equinox'); 'kampf','ctf_kampf': PutMap(ID,'ctf_Kampf'); 'lanu','lanub','lanubya','lanubia','ctf_lanub','ctf_lanubia','ctf_lanubya': PutMap(ID,'ctf_Lanubya'); 'm2','maya2','ctf_maya2': PutMap(ID,'ctf_Maya2'); 'maya','ctf_maya': PutMap(ID,'ctf_Maya'); 'ruin','ruins','ctf_ruins': PutMap(ID,'ctf_Ruins'); 'run','ctf_run': PutMap(ID,'ctf_Run'); 'snake','snakebyte','snakebite','ctf_snakebyte','ctf_snakebite': PutMap(ID,'ctf_Snakebite'); 'steel','ctf_steel': PutMap(ID,'ctf_Steel'); 'viet','ctf_viet': PutMap(ID,'ctf_Viet'); 'vol','voland','ctf_vol','ctf_voland': PutMap(ID,'ctf_Voland'); end; end; end; function OnCommand(ID: Byte; Text: string): boolean; begin if(text='/test') then begin if FileExists('test') then SayToID(ID,'test works') else SayToID(ID,'test failed'); end; //NOTE: This function will be called when an admin types a / command. //Writeln('cmd "'+text+'" "'+Copy(Text,1,4)+'"'); Result := false; // Return true if you want to ignore the command typed. //if (Copy(Text,1,5)= '/say ') then WriteLn('/say '+Copy(Text,6,Length(Text)-5)+' ('+IDToName(ID)+')'); if (Copy(Text,1,4) = '/map') then ThreadFunc([ID,Copy(Text,5,30)],'HelpToChangeMap'); Text := lowercase(Text); if (Copy(Text,1,9) = '/gamemode') then Gamemode(Copy(Text,11,2)) else if (Copy(Text,1,6) = '/pause') then DoPause(ID) else if (Copy(Text,1,4) = '/unp') then DoUnpause else if ((Copy(Text,1,8) = '/restart') or (Copy(Text,1,8) = '/nextmap') or (Copy(Text,1,5) = '/map ')) then begin inpause:= false; startunpause:=false; end else if (Copy(Text,1,11) = '/addbadname') then begin if (Copy(Text,12,1)= 'b') then AddBadName(ID,Copy(Text,14,21),True) else AddBadName(ID,Copy(Text,13,16),False); end else if (Copy(Text,1,11) = '/delbadname') then DelBadName(ID,Copy(Text,13,16)) else if (Copy(Text,1,11) = '/addbadword') then AddBadWord(ID,Copy(Text,13,200)) else if (Copy(Text,1,11) = '/delbadword') then DelBadWord(ID,Copy(Text,13,200)) else if (Copy(Text,1,11) = '/ovebadword') then OverviewBadWords(ID) else if (Copy(Text,1,5) = '/camp') then OnCamp(StrToInt(Trim(Copy(Text,7,2)))); end; procedure DoGetRanking(const ID:Byte); var Response: String; ResponseLines: TStringArray; i: integer; begin try Response:= GetURL(PLAYER_STATS_URL+'?name='+HTTPEncode(IDtoName(ID))); ResponseLines:= xsplit(Response,chr(13)); for i:=0 to ArrayHigh(ResponseLines) do begin Response:= MyTrim(ResponseLines[i]); If Players[ID].Ranked then SaytoID(ID,Response) else SaytoID(0,Response); end; Players[ID].Ranked:= True; except WriteLn('bug!'); end; end; procedure DoGetPrivRanking(const ID:Byte); var oldstate : Boolean; begin oldstate := Players[ID].Ranked; Players[ID].Ranked := true; DoGetRanking(ID); Players[ID].Ranked := oldstate; end; function OnPlayerCommand(ID: Byte; Text: string): boolean; begin //NOTE: This function will be called when [_ANY_] player types a / command. if (text = '/rank') then DoGetPrivRanking(ID); Result := false; //Return true if you want disable the command typed. end; procedure OnException(ErrorMessage: string); begin WriteLn(ErrorMessage); //WriteFile('ErrorLog.txt', ErrorMessage); end; //NETWORKCORE.PAS function HTTPEncode_(Str: String):String; var i: integer; s: string; begin for i:=1 to length(Str) do begin s:= Copy(Str,i,1); if s=' ' then result:=result+'%20' else if s='!' then result:=result+'%21' else if s='"' then result:=result+'%22' else if s='#' then result:=result+'%23' else if s='$' then result:=result+'%24' else if s='%' then result:=result+'%25' else if s='&' then result:=result+'%26' else if s='''' then result:=result+'%27' else if s='(' then result:=result+'%28' else if s=')' then result:=result+'%29' else if s='*' then result:=result+'%2A' else if s='+' then result:=result+'%2B' else if s=',' then result:=result+'%2C' else if s='-' then result:=result+'%2D' else if s='.' then result:=result+'%2E' else if s='/' then result:=result+'%2F' else if s=':' then result:=result+'%3A' else if s=';' then result:=result+'%3B' else if s='<' then result:=result+'%3C' else if s='=' then result:=result+'%3D' else if s='>' then result:=result+'%3E' else if s='?' then result:=result+'%3F' else if s='@' then result:=result+'%40' else if s='[' then result:=result+'%5B' else if s='\' then result:=result+'%5C' else if s=']' then result:=result+'%5D' else if s='^' then result:=result+'%5E' else if s='_' then result:=result+'%5F' else if s='`' then result:=result+'%60' else if s='{' then result:=result+'%7B' else if s='|' then result:=result+'%7C' else if s='}' then result:=result+'%7D' else if s='~' then result:=result+'%7E' else if s='§' then result:=result+'%A7' else if s='²' then result:=result+'%B2' else if s='³' then result:=result+'%B3' else if s='´' then result:=result+'%B4' else if s='µ' then result:=result+'%B5' else result:=result+s; end; end; function OnRequestGame(IP: string; State: integer): integer; var i: integer; begin //if not NumPlayers=MaxPlayers then for i:=0 to ArrayHigh(IPList) do begin if (IP = IPList[i]) then begin State := 1; // OK WriteLn('VIP detected...'); command('/unban '+IP); command('/admip '+IP); break; end; end; result := State; // Return new state end; procedure OnWeaponChange(ID, PrimaryNum, SecondaryNum: byte); begin end; procedure CheckBadName(ID:Byte); var bType, wtf: TStringArray; i:integer; Nickname: String; begin Nickname:= LowerCase(IDtoName(ID)); for i:=0 to ArrayHigh(bNames) do begin SetArrayLength(bType,0); bType := xsplit(bNames[i],':'); if MaskCheck(Nickname,bType[0]) then begin if Copy(bType[1],1,5) = 'kick;' then begin KickPlayer(ID); end; if Copy(bType[1],1,3) = 'ban' then begin wtf := xsplit(bType[2],';'); if wtf[0] = '' then begin wtf[0] := '0'; end; BanPlayer(ID, StrtoInt(wtf[0])); end; end; end; end; procedure AddBot(const Team:Byte); begin Sleep(500); if Team=0 then Command('/addbot DoNKeY') else Command('/addbot'+IntToStr(Team)+' DoNKeY'); Sleep(1500); BotChat(NameToID('DoNKeY'),'Welcome! Sense there is nobody else here, so I''ll entertain you for a little while.'); end; procedure OnJoinGame(ID, Team: byte); begin ThreadFunc([ID],'CheckBadName'); if ServerPort<>23073 then begin //WriteLN('oj '+inttostr(numplayers)); Case NumPlayers of 1: if Team=1 then ThreadFunc([2],'AddBot') else if Team=0 then ThreadFunc([0],'AddBot') else ThreadFunc([1],'AddBot'); 3: if NumBots>0 then ThreadFunc([],'DelBot'); end; if (Copy(IDToName(ID),1,10)='#tba.') then Players[ID].BigPoint:= True; end; end; procedure OnJoinTeam(ID, Team: byte); begin // FairTeams - BEGIN if ( ServerPort <> 23073) then case Team of 1: if (AlphaPlayers > BravoPlayers +1) then begin Command('/say Kinda unfair '+ IDtoName(ID) +'!'); Command('/setteam2 '+ IntToStr(ID)); end; 2: if (BravoPlayers > Alphaplayers +1) then begin Command('/say Kinda unfair '+ IDtoName(ID) +'!'); Command('/setteam1 '+ IntToStr(ID)); end; end; // FairTeams -END end; procedure MoveOneTo(Team: Byte); var i: integer; begin for i:=MaxPlayers+1 downto 1 do if ( (GetPlayerStat(i,'team')<> 0) and (GetPlayerStat(i,'team')<> Team) and (GetPlayerStat(i,'active')= true) and (GetPlayerStat(i,'flagger')= false) ) then begin Command('/setteam'+IntToStr(Team)+' '+IntToStr(i)); WriteConsole(0,'Teams balanced!',$EE81FAA1); break; end; end; procedure BalanceTeams; begin if ServerPort<>23073 then begin if AlphaPlayers+1 < BravoPlayers then MoveOneTo(1) else if AlphaPlayers > BravoPlayers+1 then MoveOneTo(2) else WriteConsole(0,'Teams quite fair!',$EE81FAA1); end; end; procedure DelBot; begin sleep(2000); Command('/kick DoNKeY'); sleep(1000); BalanceTeams; end; procedure OnLeaveGame(ID, Team: byte; Kicked: boolean); begin if ServerPort<>23073 then begin Players[ID].BigPoint:= False; //Writeln('Np '+IntToStr(NumPlayers)+' NBots '+IntToStr(NumBots)); case NumPlayers of 2: if NumBots>0 then ThreadFunc([],'DelBot') else ThreadFunc([Team],'AddBot'); end; end; //ThreadFunc([ID,Team,Kicked],'MyOnLeaveGame'); end; procedure OnFlagGrab(ID, TeamFlag: byte; GrabbedInBase: boolean); begin end; procedure OnFlagReturn(ID, TeamFlag: byte); begin end; procedure OnFlagScore(ID, TeamFlag: byte); begin end; procedure OnMapChange(NewMap: string); var i:integer; begin if ServerPort<> 23073 then begin Firstblood:=true; ThreadFunc([],'ForgiveEvuls'); end; For i:=1 to 32 do begin Players[i].Ranked:= false; end; end; procedure OnPlayerKill(Killer, Victim: byte; Weapon: string); begin if Serverport <> 23073 then begin //FirstBlood - begin if (Killer <> Victim) then begin if FirstBlood then begin FirstBlood := false; WriteConsole(0,'FIRSTBLOOD! '+IdToName(Victim)+' killed by '+IdToName(Killer),$0000FFFF); WriteConsole(0,IdToName(Killer)+' gained +5 extra points !!',$0000FFFF); SetScore(Killer,GetPlayerStat(Killer,'Kills')+5); DrawText(0,'FIRST BLOOD !',330,$00FF0000,0.2,100,370); //DrawText(0,'Hello big world!',6030,RGB(255,255,255),0.20,40,240); end; //firstblood - end if GetPlayerStat(Victim,'flagger') then begin WriteConsole(Killer,'+5 extra points for killing the Flagger',$0000FFFF); SetScore(Killer,GetPlayerStat(Killer,'Kills')+5); end; if GetPlayerStat(Killer,'flagger') then begin WriteConsole(Killer,'+1 extra point for killing as Flagger',$0000FFFF); SetScore(Killer,GetPlayerStat(Killer,'Kills')+1); if ((GetPlayerStat(Killer,'flagger')) and (GetPlayerStat(Victim,'flagger'))) then begin WriteConsole(Killer,'+5 ComboBonus',$0000FFFF); SetScore(Killer,GetPlayerStat(Killer,'Kills')+5); end; end; if Players[Victim].BigPoint then begin WriteConsole(Killer,'+1 BIGPOINT',$0000FFFF); SetScore(Killer,GetPlayerStat(Killer,'Kills')+1); end; end; end; end; function OnPlayerDamage(Victim, Shooter: byte; Damage: integer): integer; begin // Victim = Player Damaged // Shooter = Player doing the damage result := Damage; end; procedure OnPlayerRespawn(ID: byte); begin end; procedure OnPlayerSpeak(ID: byte; Text: string); begin Text:= Lowercase(Text); if (MaskCheck(Text,'rank*') or MaskCheck(Text,'?rank*')) and (PLAYER_STATS_ON=1) then DoGetRanking(ID) else if (MaskCheck(Text,'teams*') or MaskCheck(Text,'?teams*')) then BalanceTeams else if (MaskCheck(Text,'nextmap*') or MaskCheck(Text,'?nextmap*')) then Command('/say Next map: ' + NextMap) else if (MaskCheck(Text,'*pause*') or MaskCheck(Text,'*stop*')) and (ServerPort = 23073) then begin command('/pause'); DoPause(ID); end else ThreadFunc([ID,Text],'CheckBadWords'); end; // ADMINCORE.PAS procedure OnAdminConnect(IP: string); begin end; procedure OnAdminDisconnect(IP: string); begin end; // Custom-function used in OnAdminMessage procedure ShowPlayers(IP: String); var i: Integer; s: String; begin for i:=0 to 32 do begin if(IDToName(i)<>'') then begin s:= s+ IDToName(i)+', '; end; end; s:= 'Players ('+IntToStr(NumPlayers)+') '+ Copy(s,1,Length(s)-2); TCPAdminPM(IP,s); end; procedure OnAdminMessage(IP, Msg: string); begin if (Copy(Msg,1,8) = '/players') then Showplayers(IP); end;