Prepare fixtures

This commit is contained in:
dkanus 2026-07-14 20:27:09 +07:00
commit 9c94356263
6021 changed files with 722805 additions and 22 deletions

View file

@ -0,0 +1,961 @@
Class AdminMenuHandle extends Info;
var bool bMainMenuOpened,bSendCat,bSendWep,bConfigDirty;
var KFPlayerController PlayerOwner;
var UI_Replication Rep;
var array<int> PlayerList;
var int EditingIndex,EditingCategory;
var ServerStStats EditingStats;
var() name MainMenuID,EditStatMenuID,StatEditID,EditTraderID,AddWeaponID,EditWeaponID,AddCategoryID,EditCategoryID,ErrorID,CharacterEditID,EditSettingsID;
var ServerPerksMut MutatorOwner;
var string PendingDataFeed,PendingCategories,PendingString;
var array<string> StatNames,NewStatValues;
struct FSettingType
{
var byte Type;
var string ExtraValue,Name,PropName;
};
var array<FSettingType> SettingLines;
function PostBeginPlay()
{
PlayerOwner = KFPlayerController(Owner);
if( PlayerOwner==None )
Error("No KFPlayerController as owner.");
else
{
Rep = Spawn(Class'UI_Replication',PlayerOwner);
Rep.DlgWindowClosed = WindowClosed;
Rep.DlgSubmittedValue = SubmittedValue;
}
}
function Destroyed()
{
if( Rep!=None )
Rep.Destroy();
}
function WindowClosed( name ID )
{
if( ID==MainMenuID )
Destroy();
}
function SubmittedValue( name ID, int CompID, string Value );
function Tick( float Delta )
{
if( PlayerOwner==None || Level.Game.bGameEnded )
{
if( PlayerOwner!=None )
PlayerOwner.ClientMessage("Can't edit stats anymore after endgame.");
Destroy();
}
}
auto state MainMenu
{
function BeginState()
{
if( !bMainMenuOpened )
{
bMainMenuOpened = true;
Rep.ClientOpenWindow(MainMenuID,0.4,0.3,"ServerPerks Admin Menu");
Rep.ClientAddComponent(MainMenuID,0,0,true,false,0.1,0.2,0.3,0.2,"Edit Trader","Edit the trader weapons and categories list.");
Rep.ClientAddComponent(MainMenuID,1,0,true,false,0.6,0.2,0.3,0.2,"Edit Characters","Edit the custom characters players may use.");
Rep.ClientAddComponent(MainMenuID,2,0,true,false,0.1,0.5,0.3,0.2,"Edit Settings","Edit misc serverperks settings.");
Rep.ClientAddComponent(MainMenuID,3,0,true,false,0.6,0.5,0.3,0.2,"Edit Stats","Edit individual player stats.");
Rep.ClientAddComponent(MainMenuID,4,2,true,false,0.35,0.82,0.3,0.15,"Close","Close menu");
}
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==MainMenuID )
{
switch( CompID )
{
case 0:
GoToState('EditTrader');
break;
case 1:
GoToState('EditChars');
break;
case 2:
GoToState('EditSettings');
break;
case 3:
GoToState('EditStatsM');
break;
}
}
}
}
state EditSettings
{
function BeginState()
{
local float X,Y;
local int i;
local string S;
local byte T;
bConfigDirty = false;
Rep.ClientOpenWindow(EditSettingsID,0.9,0.9,"Server settings menu");
Rep.ClientAddComponent(EditSettingsID,0,2,true,false,0.85,0.9,0.1,0.05,"Close","Close menu");
X = 0.025;
Y = 0.05;
for( i=0; i<SettingLines.Length; ++i )
{
S = "";
S = MutatorOwner.GetPropertyText(SettingLines[i].PropName);
T = SettingLines[i].Type;
S = SettingLines[i].ExtraValue$S;
Rep.ClientAddComponent(EditSettingsID,(i+1),T,true,false,X,Y,0.35,0.048,S,SettingLines[i].Name);
Y+=0.05;
if( Y>=0.9 )
{
Y = 0.05;
X+=0.4;
}
}
}
function EndState()
{
if( bConfigDirty )
MutatorOwner.SaveConfig();
Rep.ClientCloseWindow(EditSettingsID);
}
function WindowClosed( name ID )
{
if( ID==EditSettingsID )
GoToState('MainMenu');
else Global.WindowClosed(ID);
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==EditSettingsID )
{
if( CompID>=1 && CompID<=SettingLines.Length )
{
bConfigDirty = true;
if( SettingLines[CompID-1].Type==7 )
MutatorOwner.SetPropertyText(SettingLines[CompID-1].PropName,Eval(Value!="0","True","False"));
else MutatorOwner.SetPropertyText(SettingLines[CompID-1].PropName,Value);
}
}
}
}
state EditTrader
{
function BeginState()
{
bConfigDirty = false;
Rep.ClientOpenWindow(EditTraderID,0.6,0.4,"Trader Weapon Editor");
Rep.ClientAddComponent(EditTraderID,0,0,true,false,0.1,0.26,0.14,0.11,"Add","Add a new weapon class to the list.");
Rep.ClientAddComponent(EditTraderID,1,0,true,false,0.3,0.26,0.14,0.11,"Edit","Edit the current weapon class.");
Rep.ClientAddComponent(EditTraderID,2,0,true,false,0.6,0.26,0.14,0.11,"Delete","Remove the current weapon from list.");
Rep.ClientAddComponent(EditTraderID,3,0,true,false,0.1,0.66,0.14,0.11,"Add","Add a new weapon category to the list.");
Rep.ClientAddComponent(EditTraderID,4,0,true,false,0.3,0.66,0.14,0.11,"Edit","Rename the current weapon category.");
Rep.ClientAddComponent(EditTraderID,5,0,true,false,0.6,0.66,0.14,0.11,"Delete","Remove the current weapon category from list.");
Rep.ClientAddComponent(EditTraderID,8,2,true,false,0.35,0.82,0.3,0.15,"Close","Close menu");
SendCategories();
SendWeaponList();
}
function EndState()
{
if( bConfigDirty )
MutatorOwner.SaveConfig();
Rep.ClientCloseWindow(EditTraderID);
Rep.ClientCloseWindow(AddWeaponID);
Rep.ClientCloseWindow(EditWeaponID);
Rep.ClientCloseWindow(AddCategoryID);
Rep.ClientCloseWindow(EditCategoryID);
}
function WindowClosed( name ID )
{
if( ID==EditTraderID )
GoToState('MainMenu');
else Global.WindowClosed(ID);
}
function Tick( float Delta )
{
if( bSendCat )
{
if( Len(PendingCategories)<=200 )
{
bSendCat = false;
Rep.ClientAddComponent(EditTraderID,6,6,true,false,0.025,0.55,0.95,0.1,PendingCategories,"Category:");
EditingCategory = 0;
if( !bSendWep )
UpdateCategoryIndex();
}
else
{
Rep.ClientPendingData(Left(PendingCategories,200));
PendingCategories = Mid(PendingCategories,200);
}
}
else if( bSendWep )
{
if( Len(PendingDataFeed)<=200 )
{
bSendWep = false;
Rep.ClientAddComponent(EditTraderID,7,6,true,false,0.025,0.15,0.95,0.1,PendingDataFeed,"Weapon:");
EditingIndex = 0;
}
else
{
Rep.ClientPendingData(Left(PendingDataFeed,200));
PendingDataFeed = Mid(PendingDataFeed,200);
}
}
Global.Tick(Delta);
}
final function SendCategories()
{
local int i;
bSendCat = true;
PendingCategories = "0;";
for( i=0; i<MutatorOwner.WeaponCategories.Length; ++i )
{
if( i==0 )
PendingCategories $= Repl(MutatorOwner.WeaponCategories[i],":",";");
else PendingCategories $= ":"$Repl(MutatorOwner.WeaponCategories[i],":",";");
}
if( i==0 )
PendingCategories $= "None";
}
final function SendWeaponList()
{
local int i,j;
local string S;
bSendWep = true;
PendingDataFeed = "0;None";
for( i=0; i<MutatorOwner.TraderInventory.Length; ++i )
{
S = MutatorOwner.TraderInventory[i];
j = InStr(S,":");
if( j>=0 )
S = Mid(S,j+1);
PendingDataFeed $= ":"$S;
}
}
final function UpdateWeaponCat( int NewID )
{
local int j;
local string S;
if( EditingIndex>0 && !bSendWep && EditingIndex<=MutatorOwner.TraderInventory.Length )
{
S = MutatorOwner.TraderInventory[EditingIndex-1];
j = InStr(S,":");
if( j>=0 )
S = Mid(S,j+1);
MutatorOwner.TraderInventory[EditingIndex-1] = string(NewID)$":"$S;
bConfigDirty = true;
}
}
final function UpdateCategoryIndex()
{
local int j;
local string S;
if( EditingIndex>0 && !bSendWep && EditingIndex<=MutatorOwner.TraderInventory.Length )
{
S = MutatorOwner.TraderInventory[EditingIndex-1];
j = InStr(S,":");
if( j>=0 )
EditingCategory = int(Left(S,j));
else EditingCategory = 0;
Rep.ClientSetCompValue(EditTraderID,6,string(EditingCategory));
}
}
final function DeleteWeapon()
{
if( EditingIndex>0 && !bSendWep && EditingIndex<=MutatorOwner.TraderInventory.Length )
{
MutatorOwner.TraderInventory.Remove(EditingIndex-1,1);
bConfigDirty = true;
SendWeaponList();
}
}
final function DeleteCategory()
{
local int i,j,n;
local string S;
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.WeaponCategories.Length )
{
for( i=0; i<MutatorOwner.TraderInventory.Length; ++i )
{
S = MutatorOwner.TraderInventory[i];
j = InStr(S,":");
if( j>=0 )
{
n = int(Left(S,j));
S = Mid(S,j+1);
}
else n = 0;
if( n>=EditingCategory )
n = Max(j-1,0);
MutatorOwner.TraderInventory[i] = string(n)$":"$S;
}
MutatorOwner.WeaponCategories.Remove(EditingCategory,1);
bConfigDirty = true;
SendCategories();
SendWeaponList();
}
}
final function EditBoxWindow( name ID, string Title, string Type, string A, string B, optional string C )
{
Rep.ClientOpenWindow(ID,0.7,0.2,Title);
Rep.ClientAddComponent(ID,0,3,false,false,0.025,0.2,0.95,0.4,C,Type);
Rep.ClientAddComponent(ID,1,1,true,false,0.1,0.6,0.3,0.2,A,A$" this weapon "$B$" in trader menu.");
Rep.ClientAddComponent(ID,2,2,true,false,0.6,0.6,0.3,0.2,"Close","Close menu without doing anything");
PendingString = C;
}
final function AddWeapon()
{
EditBoxWindow(AddWeaponID,"Add Weapon","Classname:","Add","class");
}
final function AddNewWeapon()
{
local int i;
local class<KFWeaponPickup> WC;
WC = Class<KFWeaponPickup>(DynamicLoadObject(PendingString,Class'Class'));
if( WC==None )
{
PlayerOwner.ClientMessage("Couldn't add weapon: Couldn't load class '"$PendingString$"'");
return;
}
i = MutatorOwner.TraderInventory.Length;
MutatorOwner.TraderInventory.Length = i+1;
MutatorOwner.TraderInventory[i] = "0:"$string(WC);
bConfigDirty = true;
SendWeaponList();
Rep.ClientCloseWindow(AddWeaponID);
}
final function EditWeapon()
{
local string S;
local int i;
if( EditingIndex>0 && !bSendWep && EditingIndex<=MutatorOwner.TraderInventory.Length )
{
S = MutatorOwner.TraderInventory[EditingIndex-1];
i = InStr(S,":");
if( i>=0 )
S = Mid(S,i+1);
EditBoxWindow(EditWeaponID,"Edit Weapon","Classname:","Edit","class",S);
}
}
final function EditCurrentWeapon()
{
local int i,n;
local class<KFWeaponPickup> WC;
local string S;
if( EditingIndex>0 && !bSendWep && EditingIndex<=MutatorOwner.TraderInventory.Length )
{
WC = Class<KFWeaponPickup>(DynamicLoadObject(PendingString,Class'Class'));
if( WC==None )
{
PlayerOwner.ClientMessage("Couldn't edit weapon: Couldn't load class '"$PendingString$"'");
return;
}
S = MutatorOwner.TraderInventory[EditingIndex-1];
i = InStr(S,":");
if( i>=0 )
{
n = int(Left(S,i));
S = Mid(S,i+1);
}
else n = 0;
MutatorOwner.TraderInventory[EditingIndex-1] = string(n)$":"$string(WC);
bConfigDirty = true;
SendWeaponList();
Rep.ClientCloseWindow(EditWeaponID);
}
}
final function AddCategory()
{
EditBoxWindow(AddCategoryID,"Add Category","Name:","Add","category");
}
final function AddNewCategory()
{
local int i;
i = MutatorOwner.WeaponCategories.Length;
MutatorOwner.WeaponCategories.Length = i+1;
MutatorOwner.WeaponCategories[i] = PendingString;
bConfigDirty = true;
SendCategories();
Rep.ClientCloseWindow(AddCategoryID);
}
final function EditCategory()
{
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.WeaponCategories.Length )
EditBoxWindow(EditCategoryID,"Edit Category","Name:","Edit","category",MutatorOwner.WeaponCategories[EditingCategory]);
}
final function EditCurrentCategory()
{
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.WeaponCategories.Length )
{
MutatorOwner.WeaponCategories[EditingCategory] = PendingString;
bConfigDirty = true;
SendCategories();
Rep.ClientCloseWindow(EditCategoryID);
}
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==EditTraderID )
{
switch( CompID )
{
case 0:
AddWeapon();
break;
case 1:
EditWeapon();
break;
case 2:
DeleteWeapon();
break;
case 3:
AddCategory();
break;
case 4:
EditCategory();
break;
case 5:
DeleteCategory();
break;
case 6:
EditingCategory = int(Value);
UpdateWeaponCat(EditingCategory);
break;
case 7:
EditingIndex = int(Value);
UpdateCategoryIndex();
break;
}
}
else if( ID==AddWeaponID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
AddNewWeapon();
break;
}
}
else if( ID==EditWeaponID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
EditCurrentWeapon();
break;
}
}
else if( ID==AddCategoryID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
AddNewCategory();
break;
}
}
else if( ID==EditCategoryID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
EditCurrentCategory();
break;
}
}
}
}
state EditChars
{
function BeginState()
{
bConfigDirty = false;
Rep.ClientOpenWindow(CharacterEditID,0.6,0.4,"Character Editor");
Rep.ClientAddComponent(CharacterEditID,0,0,true,false,0.1,0.4,0.14,0.11,"Add","Add a new character to the list.");
Rep.ClientAddComponent(CharacterEditID,1,0,true,false,0.3,0.4,0.14,0.11,"Edit","Edit the current character.");
Rep.ClientAddComponent(CharacterEditID,2,0,true,false,0.6,0.4,0.14,0.11,"Delete","Remove the current character from list.");
Rep.ClientAddComponent(CharacterEditID,3,2,true,false,0.35,0.82,0.3,0.15,"Close","Close menu");
SendCharacters();
}
function EndState()
{
if( bConfigDirty )
MutatorOwner.SaveConfig();
Rep.ClientCloseWindow(CharacterEditID);
}
function WindowClosed( name ID )
{
if( ID==CharacterEditID )
GoToState('MainMenu');
else Global.WindowClosed(ID);
}
function Tick( float Delta )
{
if( bSendCat )
{
if( Len(PendingCategories)<=200 )
{
bSendCat = false;
Rep.ClientAddComponent(CharacterEditID,4,6,true,false,0.025,0.2,0.95,0.15,PendingCategories,"Character:");
EditingCategory = 0;
}
else
{
Rep.ClientPendingData(Left(PendingCategories,200));
PendingCategories = Mid(PendingCategories,200);
}
}
Global.Tick(Delta);
}
final function SendCharacters()
{
local int i,j;
local string S;
bSendCat = true;
PendingCategories = "0;";
for( i=0; i<MutatorOwner.CustomCharacters.Length; ++i )
{
S = MutatorOwner.CustomCharacters[i];
j = InStr(S,":");
if( j>=0 )
S = Mid(S,j+1)$" ["$Left(S,j)$"]";
if( i==0 )
PendingCategories $= S;
else PendingCategories $= ":"$S;
}
if( i==0 )
PendingCategories $= "None";
}
final function DeleteCharacter()
{
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.CustomCharacters.Length )
{
MutatorOwner.CustomCharacters.Remove(EditingCategory,1);
bConfigDirty = true;
SendCharacters();
}
}
final function EditBoxWindow( name ID, string Title, string A, optional string C, optional string Cat )
{
Rep.ClientOpenWindow(ID,0.7,0.2,Title);
Rep.ClientAddComponent(ID,0,3,false,false,0.025,0.2,0.95,0.4,C,"Character:");
Rep.ClientAddComponent(ID,1,3,false,false,0.025,0.4,0.95,0.4,Cat,"Category:");
Rep.ClientAddComponent(ID,2,1,true,false,0.1,0.6,0.3,0.2,A,A$" this character.");
Rep.ClientAddComponent(ID,3,2,true,false,0.6,0.6,0.3,0.2,"Close","Close menu without doing anything");
PendingString = C;
PendingDataFeed = Cat;
}
final function AddCharacter()
{
EditBoxWindow(AddCategoryID,"Add Character","Add");
}
final function AddNewCharacter()
{
local int i;
local string S;
if( class<PlayerRecordClass>(DynamicLoadObject(PendingString$"Mod."$PendingString,class'Class',true))==None )
PlayerOwner.ClientMessage("Couldn't load any PlayerRecordClass for '"$PendingString$"Mod."$PendingString$"', are sure this is desired?");
if( PendingDataFeed!="" )
S = PendingDataFeed$":"$PendingString;
else S = PendingString;
i = MutatorOwner.CustomCharacters.Length;
MutatorOwner.CustomCharacters.Length = i+1;
MutatorOwner.CustomCharacters[i] = S;
bConfigDirty = true;
SendCharacters();
Rep.ClientCloseWindow(AddCategoryID);
}
final function EditCharacter()
{
local int i;
local string S;
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.CustomCharacters.Length )
{
S = MutatorOwner.CustomCharacters[EditingCategory];
i = InStr(S,":");
if( i==-1 )
EditBoxWindow(EditCategoryID,"Edit Character","Edit",S);
else EditBoxWindow(EditCategoryID,"Edit Character","Edit",Mid(S,i+1),Left(S,i));
}
}
final function EditCurrentCharacter()
{
local string S;
if( EditingCategory>=0 && !bSendCat && EditingCategory<MutatorOwner.CustomCharacters.Length )
{
if( class<PlayerRecordClass>(DynamicLoadObject(PendingString$"Mod."$PendingString,class'Class',true))==None )
PlayerOwner.ClientMessage("Couldn't load any PlayerRecordClass for '"$PendingString$"Mod."$PendingString$"', are sure this is desired?");
if( PendingDataFeed!="" )
S = PendingDataFeed$":"$PendingString;
else S = PendingString;
MutatorOwner.CustomCharacters[EditingCategory] = S;
bConfigDirty = true;
SendCharacters();
Rep.ClientCloseWindow(EditCategoryID);
}
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==CharacterEditID )
{
switch( CompID )
{
case 0:
AddCharacter();
break;
case 1:
EditCharacter();
break;
case 2:
DeleteCharacter();
break;
case 4:
EditingCategory = int(Value);
break;
}
}
else if( ID==AddCategoryID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
PendingDataFeed = Value;
break;
case 2:
AddNewCharacter();
break;
}
}
else if( ID==EditCategoryID )
{
switch( CompID )
{
case 0:
PendingString = Value;
break;
case 1:
PendingDataFeed = Value;
break;
case 2:
EditCurrentCharacter();
break;
}
}
}
}
state EditStatsM
{
function BeginState()
{
Rep.ClientOpenWindow(EditStatMenuID,0.4,0.3,"Stats Editor");
Rep.ClientAddComponent(EditStatMenuID,0,0,true,false,0.1,0.6,0.3,0.2,"Refresh","Refresh players list");
Rep.ClientAddComponent(EditStatMenuID,1,1,true,true,0.6,0.6,0.3,0.2,"Edit","Edit stats of selected player");
Rep.ClientAddComponent(EditStatMenuID,3,2,true,false,0.35,0.82,0.3,0.15,"Close","Close menu");
SetPlayerList();
}
function EndState()
{
Rep.ClientCloseWindow(EditStatMenuID);
}
final function SetPlayerList()
{
local Controller C;
local string S;
PlayerList.Length = 0;
EditingIndex = 0;
S = "0;None";
for( C=Level.ControllerList; C!=None; C=C.nextController )
{
if( C.bIsPlayer && xPlayer(C)!=None && ServerStStats(xPlayer(C).SteamStatsAndAchievements)!=none )
{
PlayerList[PlayerList.Length] = C.PlayerReplicationInfo.PlayerID;
S $= ":"$C.PlayerReplicationInfo.PlayerID$" - "$Repl(Left(C.PlayerReplicationInfo.PlayerName,16),":",".");
if( Len(S)>150 )
{
Rep.ClientPendingData(S);
S = "";
}
}
}
Rep.ClientAddComponent(EditStatMenuID,2,6,true,false,0.1,0.1,0.8,0.2,S,"Select player:");
Rep.ClientSetCompLock(EditStatMenuID,1,true);
}
final function EditPlayerEntry()
{
local Controller C;
local int i;
if( EditingIndex==0 || (EditingIndex-1)>=PlayerList.Length )
{
PlayerOwner.ClientMessage("You selected an invalid player to edit.");
return;
}
i = PlayerList[EditingIndex-1];
for( C=Level.ControllerList; C!=None; C=C.nextController )
{
if( C.bIsPlayer && xPlayer(C)!=None && C.PlayerReplicationInfo.PlayerID==i && ServerStStats(xPlayer(C).SteamStatsAndAchievements)!=none )
{
EditingStats = ServerStStats(xPlayer(C).SteamStatsAndAchievements);
if( !EditingStats.bStatsReadyNow || EditingStats.MyStatsObject==None )
PlayerOwner.ClientMessage("Your selected players stats aren't ready to be edited yet.");
else GoToState('EditStats');
return;
}
}
PlayerOwner.ClientMessage("Your selected player isn't available on server anymore, please refresh.");
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==EditStatMenuID )
{
switch( CompID )
{
case 0:
SetPlayerList();
break;
case 1:
EditPlayerEntry();
break;
case 2:
EditingIndex = int(Value);
Rep.ClientSetCompLock(EditStatMenuID,1,(EditingIndex==0));
break;
}
}
}
function WindowClosed( name ID )
{
if( ID==EditStatMenuID )
GoToState('MainMenu');
else Global.WindowClosed(ID);
}
}
state EditStats
{
function BeginState()
{
Rep.ClientOpenWindow(StatEditID,0.9,0.9,"Stats of "$EditingStats.PlayerOwner.PlayerReplicationInfo.PlayerName);
Rep.ClientAddComponent(StatEditID,0,1,true,false,0.1,0.9,0.15,0.05,"Save","Save changes of this player stats");
Rep.ClientAddComponent(StatEditID,1,0,true,false,0.47,0.9,0.15,0.05,"Update","Update stats to match client current stats");
Rep.ClientAddComponent(StatEditID,2,2,true,false,0.8,0.9,0.15,0.05,"Close","Close window without saving changes");
ReadStats();
}
function EndState()
{
Rep.ClientCloseWindow(StatEditID);
}
function Tick( float Delta )
{
if( EditingStats==None )
{
PlayerOwner.ClientMessage("Desired player disconnected.");
GoToState('EditStatsM');
}
Global.Tick(Delta);
}
function WindowClosed( name ID )
{
if( ID==StatEditID )
GoToState('EditStatsM');
else Global.WindowClosed(ID);
}
final function AddStat( out int ID, int CurValue, out float X, out float Y, string Info )
{
Rep.ClientAddComponent(StatEditID,ID++,5,true,false,X,Y,0.28,0.04,"0:1073741823:1:"$CurValue,Left(Info,13));
Y+=0.04;
if( Y>=0.85 )
{
X += 0.3;
Y = 0.025;
}
}
final function AddStatStr( out int ID, string CurValue, out float X, out float Y, string Info )
{
Rep.ClientAddComponent(StatEditID,ID++,3,true,false,X,Y,0.28,0.04,CurValue,Left(Info,13));
Y+=0.04;
if( Y>=0.85 )
{
X += 0.3;
Y = 0.025;
}
}
final function ReadStats()
{
local int i,j;
local float X,Y;
local StatsObject S;
local string Res;
i = 3;
S = EditingStats.MyStatsObject;
X = 0.015;
Y = 0.025;
NewStatValues.Length = 0;
for( j=0; j<StatNames.Length; ++j )
{
Res = "";
Res = S.GetPropertyText(StatNames[j]);
AddStat(i,int(Res),X,Y,StatNames[j]);
}
for( j=0; j<S.CC.Length; ++j )
AddStatStr(i,S.CC[j].V,X,Y,string(S.CC[j].N));
}
final function SaveStats()
{
local int i;
local StatsObject S;
Log(PlayerOwner.PlayerReplicationInfo.PlayerName$" edited stats of "$EditingStats.PlayerOwner.PlayerReplicationInfo.PlayerName,Class.Outer.Name);
S = EditingStats.MyStatsObject;
for( i=0; i<NewStatValues.Length; ++i )
if( NewStatValues[i]!="" )
{
if( i<StatNames.Length )
S.SetPropertyText(StatNames[i],NewStatValues[i]);
else S.CC[i-StatNames.Length].V = NewStatValues[i];
}
EditingStats.RepCopyStats();
EditingStats.NotifyStatChanged();
}
function SubmittedValue( name ID, int CompID, string Value )
{
if( ID==StatEditID )
{
switch( CompID )
{
case 0:
SaveStats();
break;
case 1:
ReadStats();
break;
case 2:
GoToState('EditStatsM');
break;
default:
if( CompID>=3 )
{
CompID-=3;
if( CompID>=NewStatValues.Length )
NewStatValues.Length = CompID+1;
NewStatValues[CompID] = Value;
}
}
}
}
}
defaultproperties
{
MainMenuID="i"
EditStatMenuID="Core"
StatEditID="j"
EditTraderID="Rand"
AddWeaponID="Warn"
EditWeaponID="Dot"
AddCategoryID="Lerp"
EditCategoryID="Eval"
ErrorID="Error"
CharacterEditID="Log"
EditSettingsID="S"
StatNames(0)="DamageHealedStat"
StatNames(1)="WeldingPointsStat"
StatNames(2)="ShotgunDamageStat"
StatNames(3)="HeadshotKillsStat"
StatNames(4)="StalkerKillsStat"
StatNames(5)="BullpupDamageStat"
StatNames(6)="MeleeDamageStat"
StatNames(7)="FlameThrowerDamageStat"
StatNames(8)="SelfHealsStat"
StatNames(9)="SoleSurvivorWavesStat"
StatNames(10)="CashDonatedStat"
StatNames(11)="FeedingKillsStat"
StatNames(12)="BurningCrossbowKillsStat"
StatNames(13)="GibbedFleshpoundsStat"
StatNames(14)="StalkersKilledWithExplosivesStat"
StatNames(15)="GibbedEnemiesStat"
StatNames(16)="BloatKillsStat"
StatNames(17)="SirenKillsStat"
StatNames(18)="KillsStat"
StatNames(19)="ExplosivesDamageStat"
StatNames(20)="TotalPlayTime"
StatNames(21)="WinsCount"
StatNames(22)="LostsCount"
StatNames(23)="TotalZedTimeStat"
SettingLines(0)=(Type=5,ExtraValue="-1:250:1:",Name="Min Perk Level",PropName="MinPerksLevel")
SettingLines(1)=(Type=5,ExtraValue="0:250:1:",Name="Max Perk Level",PropName="MaxPerksLevel")
SettingLines(2)=(Type=4,ExtraValue="0.0001:100:0.05:",Name="Requirement Scale",PropName="RequirementScaling")
SettingLines(3)=(Type=5,ExtraValue="0:10:1:",Name="Mid-Game save waves",PropName="MidGameSaveWaves")
SettingLines(4)=(Type=7,Name="Force perks for all",PropName="bForceGivePerk")
SettingLines(5)=(Type=7,Name="No perk saving",PropName="bNoSavingProgress")
SettingLines(6)=(Type=7,Name="Use PlayerName as ID",PropName="bUsePlayerNameAsID")
SettingLines(7)=(Type=7,Name="Message everyones levelup",PropName="bMessageAnyPlayerLevelUp")
SettingLines(8)=(Type=7,Name="Use lowest requirements",PropName="bUseLowestRequirements")
SettingLines(9)=(Type=7,Name="Black and white ZED-time",PropName="bBWZEDTime")
SettingLines(10)=(Type=7,Name="Use enhanced scoreboard",PropName="bUseEnhancedScoreboard")
SettingLines(11)=(Type=7,Name="Remove unused custom stats",PropName="bOverrideUnusedCustomStats")
SettingLines(12)=(Type=7,Name="Always allow perk changes",PropName="bAllowAlwaysPerkChanges")
SettingLines(13)=(Type=7,Name="Force everyone use custom char",PropName="bForceCustomChars")
SettingLines(14)=(Type=7,Name="Enable chat emoticons",PropName="bEnableChatIcons")
SettingLines(15)=(Type=7,Name="Over shoulder view",PropName="bEnhancedShoulderView")
SettingLines(16)=(Type=7,Name="Fix grenade exploit",PropName="bFixGrenadeExploit")
SettingLines(17)=(Type=7,Name="Use remote database",PropName="bUseRemoteDatabase")
SettingLines(18)=(Type=7,Name="Use FTP as database",PropName="bUseFTPLink")
SettingLines(19)=(Type=3,Name="Newspage URL",PropName="ServerNewsURL")
SettingLines(20)=(Type=3,Name="Remove DB URL",PropName="RemoteDatabaseURL")
SettingLines(21)=(Type=3,Name="Remove DB Password",PropName="RemotePassword")
SettingLines(22)=(Type=3,Name="Remove DB User",PropName="RemoteFTPUser")
SettingLines(23)=(Type=3,Name="Remote DB Dir",PropName="RemoteFTPDir")
SettingLines(24)=(Type=7,Name="No perk changes",PropName="bNoPerkChanges")
}

View file

@ -0,0 +1,216 @@
Class DatabaseUdpLink extends UdpLink;
var string PendingLine;
var IpAddr A;
var ServerPerksMut Mut;
var array<StatsObject> ToSave;
var array<ServerStStats> PendingData;
var transient float NextReqTimer;
var bool bConnectionReady,bHasPendingLine;
var byte LoopCounter;
enum ENetID
{
ID_None,
ID_Open,
ID_RequestPassword,
ID_HeresPassword,
ID_ConnectionClosed,
ID_PasswordCorrect,
ID_NewPlayer,
ID_BadNewPlayer,
ID_SendPlayerData,
ID_UpdatePlayer,
ID_KeepAlive,
};
function PostBeginPlay()
{
Disable('Tick');
}
function BeginEvent()
{
Mut.SaveAllStats = SaveAllStats;
Mut.RequestStats = RequestStats;
ReceiveMode = RMODE_Event;
if( BindPort(Rand(8000)+8000,true)>0 )
Resolve(Mut.RemoteDatabaseURL);
}
event Resolved( IpAddr Addr )
{
A = Addr;
A.Port = Mut.RemotePort;
SendText(A,Chr(ENetID.ID_Open));
Enable('Tick');
NextReqTimer = Level.TimeSeconds+5.f;
}
function Tick( float Delta ) // Every 5 seconds, try to open connection again.
{
if( !bConnectionReady && NextReqTimer<Level.TimeSeconds )
{
NextReqTimer = Level.TimeSeconds+5.f;
SendText(A,Chr(ENetID.ID_Open));
}
}
event ReceivedText( IpAddr Addr, string Text )
{
local byte First;
if( bHasPendingLine )
{
if( Asc(Right(Text,1))!=10 )
PendingLine = PendingLine$Text;
else
{
ReceivedPlayerData(PendingLine$Left(Text,Len(Text)-1));
PendingLine = "";
bHasPendingLine = false;
}
return;
}
First = Asc(Left(Text,1));
Text = Mid(Text,1);
if( Mut.bDebugDatabase )
Level.Game.Broadcast(Self,"Receive "$GetEnum(enum'ENetID',First)$": "$Text);
switch( First )
{
case ENetID.ID_PasswordCorrect:
bConnectionReady = true;
break;
case ENetID.ID_RequestPassword:
bConnectionReady = false;
SendText(A,Chr(ENetID.ID_HeresPassword)$Mut.RemotePassword);
break;
case ENetID.ID_ConnectionClosed:
bConnectionReady = false;
SendText(A,Chr(ENetID.ID_Open));
break;
case ENetID.ID_KeepAlive:
SendText(A,Chr(ENetID.ID_KeepAlive));
break;
case ENetID.ID_NewPlayer:
ReceivedPlayerID(Text);
break;
case ENetID.ID_SendPlayerData:
if( Asc(Right(Text,1))!=10 )
{
PendingLine = Text;
bHasPendingLine = true;
break;
}
ReceivedPlayerData(Left(Text,Len(Text)-1));
break;
default:
}
}
function SaveAllStats()
{
local int i;
ToSave = Mut.ActiveStats;
for( i=0; i<ToSave.Length; ++i )
{
if( !ToSave[i].bStatsChanged )
ToSave.Remove(i--,1);
}
if( ToSave.Length>0 )
GoToState('SaveAll');
}
function RequestStats( ServerStStats Other )
{
local int i;
if( !bConnectionReady )
return;
SendText(A,Chr(ENetID.ID_NewPlayer)$Other.MyStatsObject.Name$"*"$Mut.GetSafeName(Other.PlayerOwner.PlayerReplicationInfo.PlayerName));
for( i=0; i<PendingData.Length; ++i )
if( PendingData[i]==Other )
return;
PendingData[PendingData.Length] = Other;
}
final function ReceivedPlayerID( string S )
{
local int i,RID;
i = InStr(S,"|");
RID = int(Left(S,i));
S = Mid(S,i+1);
for( i=0; i<PendingData.Length; ++i )
{
if( PendingData[i]==None )
PendingData.Remove(i--,1);
else if( S~=string(PendingData[i].MyStatsObject.Name) )
{
PendingData[i].SetID(RID);
break;
}
}
}
final function ReceivedPlayerData( string S )
{
local int i,RID;
i = InStr(S,"|");
RID = int(Left(S,i));
S = Mid(S,i+1);
for( i=0; i<PendingData.Length; ++i )
{
if( PendingData[i]==None )
PendingData.Remove(i--,1);
else if( RID==PendingData[i].GetID() )
{
PendingData[i].GetData(S);
PendingData.Remove(i,1);
break;
}
}
}
State SaveAll
{
function BeginState()
{
SetTimer(0.05,true);
}
function EndState()
{
SetTimer(0,false);
}
function Timer()
{
local StatsObject S;
local string Line;
if( !bConnectionReady )
return;
S = ToSave[0];
if( Mut.bDebugDatabase )
Level.Game.Broadcast(Self,"Send Player stats: "$S.Name$" ("$(ToSave.Length-1)$" remain)");
S.bStatsChanged = false;
Line = Chr(ENetID.ID_UpdatePlayer)$S.ID$"|"$S.GetSaveData()$Chr(10);
while( Len(Line)>512 )
{
SendText(A,Left(Line,512));
Line = Mid(Line,512);
}
SendText(A,Line);
ToSave.Remove(0,1);
if( ToSave.Length==0 )
GoToState('');
}
Begin:
while( ++LoopCounter<150 )
{
if( Level.bLevelChange ) // Delay mapchange until all stats are uploaded.
Level.NextSwitchCountdown = FMax(Level.NextSwitchCountdown,1.f);
Sleep(0.5);
}
}
defaultproperties
{
}

View file

@ -0,0 +1,58 @@
Class FTPDataConnection extends TcpLink;
var bool bUpload,bWasOpened;
var string Data;
delegate OnCompleted();
delegate OnProgress();
function PostBeginPlay()
{
LinkMode = MODE_Text;
}
event Opened()
{
BeginUpload();
}
event Closed()
{
OnCompleted();
Destroy();
}
event ReceivedText( string Text )
{
if( bUpload )
Log(Text,'FTPD');
else
{
Data $= Text;
OnProgress();
}
}
function BeginUpload()
{
if( bWasOpened )
GoToState('Uploading');
else bWasOpened = true;
}
state Uploading
{
function BeginState()
{
Tick(0.f);
}
function Tick( float Delta )
{
if( Data!="" )
{
SendText(Left(Data,250));
Data = Mid(Data,250);
}
else Close();
}
}
defaultproperties
{
}

View file

@ -0,0 +1,684 @@
Class FTPTcpLink extends TcpLink;
var string TempFileName;
var array<ServerStStats> PendingLoaders;
var array<StatsObject> ToSave;
var ServerPerksMut Mut;
var IpAddr SiteAddress;
var FTPDataConnection DataConnection;
var transient float WelcomeTimer;
var array<string> TotalList;
var PlayerController WebAdminController;
var int IgnoreDateNum;
var byte RetryCounter;
var bool bConnectionBroken,bFullVerbose,bUploadAllStats,bTotalUpload,bFileInProgress,bLogAllCommands,bCheckedWeb,bPostUploadCheck,bIsAsciiMode,bObtainDir;
function BeginEvent()
{
Mut.SaveAllStats = SaveAllStats;
Mut.RequestStats = RequestStats;
if( Mut.bDebugDatabase )
{
bLogAllCommands = true;
bFullVerbose = true;
}
LinkMode = MODE_Line;
ReceiveMode = RMODE_Event;
Resolve(Mut.RemoteDatabaseURL);
}
final function ReportError( int Code, string InEr )
{
if( !bConnectionBroken )
{
Level.Game.Broadcast(Self,Code$" FTP Error: "$InEr);
Log(Code$" FTP Error: "$InEr,Class.Name);
}
bConnectionBroken = true;
GoToState('ErrorState');
}
event Resolved( IpAddr Addr )
{
SiteAddress = Addr;
SiteAddress.Port = Mut.RemotePort;
GoToState('Idle');
}
event ResolveFailed()
{
ReportError(0,"Couldn't resolve address, aborting...");
}
event Closed()
{
ReportError(1,"Connection was closed by FTP server!");
}
final function DebugLog( string Str )
{
if( !bCheckedWeb )
{
bCheckedWeb = true;
foreach DynamicActors(class'PlayerController',WebAdminController)
if( WebAdminController.IsA('MessagingSpectator') )
break;
//if( Level.NetMode==NM_StandAlone )
// WebAdminController = Level.GetLocalPlayerController();
}
if( WebAdminController!=None )
WebAdminController.ClientMessage(Str,'FTP');
Log(Str,'FTP');
}
event ReceivedLine( string Text )
{
if( bLogAllCommands )
DebugLog("ReceiveFTP "$GetStateName()$":"@Text);
ProcessResponse(int(Left(Text,3)),Mid(Text,4));
}
final function SendFTPLine( string Text )
{
if( bLogAllCommands )
DebugLog("SendFTP "$GetStateName()$":"@Text);
SendText(Text);
}
function SaveAllStats()
{
local int i;
if( bTotalUpload )
return;
ToSave = Mut.ActiveStats;
for( i=0; i<ToSave.Length; ++i )
{
if( !ToSave[i].bStatsChanged )
ToSave.Remove(i--,1);
}
if( ToSave.Length>0 )
bUploadAllStats = true;
}
function RequestStats( ServerStStats Other )
{
local int i;
if( bTotalUpload )
return;
for( i=0; i<PendingLoaders.Length; ++i )
{
if( PendingLoaders[i]==None )
PendingLoaders.Remove(i--,1);
else if( PendingLoaders[i]==Other )
return;
}
PendingLoaders[PendingLoaders.Length] = Other;
}
final function FullUpload()
{
if( Class'ServerPerksMut'.Default.FTPUploadAllIgnoreDate!="" )
{
bObtainDir = true;
IgnoreDateNum = GetDateInt(int(Mid(Class'ServerPerksMut'.Default.FTPUploadAllIgnoreDate,6,2)),int(Mid(Class'ServerPerksMut'.Default.FTPUploadAllIgnoreDate,4,2)),int(Mid(Class'ServerPerksMut'.Default.FTPUploadAllIgnoreDate,0,4)));
}
TotalList = GetPerObjectNames("ServerPerksStat","StatsObject",9999999);
bTotalUpload = true;
bUploadAllStats = true;
bFullVerbose = true;
HasMoreStats();
SaveAllStats();
}
final function bool HasMoreStats()
{
local byte i;
local int j;
if( TotalList.Length==0 )
return false;
j = ToSave.Length;
for( i=0; i<Min(20,TotalList.Length); ++i )
{
ToSave.Length = j+1;
ToSave[j] = new(None,TotalList[i]) Class'StatsObject';
++j;
}
TotalList.Remove(0,20);
return true;
}
final function CheckNextCommand()
{
if( bObtainDir )
{
GoToState('GetDir');
return;
}
while( PendingLoaders.Length>0 && PendingLoaders[0]==None )
PendingLoaders.Remove(0,1);
if( bUploadAllStats || (bTotalUpload && HasMoreStats()) )
GoToState('UploadStats','Begin');
else if( PendingLoaders.Length>0 )
GoToState('DownloadStats','Begin');
else
{
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: All done!");
if( Mut.FTPKeepAliveSec>0 && !Level.Game.bGameEnded )
GoToState('KeepAlive');
else GoToState('EndConnection');
}
}
function ProcessResponse( int Code, string Line )
{
switch( Code )
{
case 220: // Welcome
if( WelcomeTimer<Level.TimeSeconds )
{
SendFTPLine("USER "$Mut.RemoteFTPUser);
WelcomeTimer = Level.TimeSeconds+0.2;
}
break;
case 331: // Password required
SendFTPLine("PASS "$Mut.RemotePassword);
break;
case 230: // User logged in.
if( Mut.RemoteFTPDir!="" )
SendFTPLine("CWD "$Mut.RemoteFTPDir);
else
{
SendFTPLine("TYPE A");
bIsAsciiMode = true;
}
break;
case 250: // CWD command successful.
SendFTPLine("TYPE A");
bIsAsciiMode = true;
break;
case 200: // Type set to A
CheckNextCommand();
break;
case 226: // File successfully transferred
case 150: // Opening ASCII mode data connection
case 221: // Good-bye
break;
case 421: // No transfer timeout: closing control connection
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: Connection timed out, reconnecting!");
GoToState('EndConnection');
break;
case 221: // Good-bye
Close();
break;
default:
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: Unknown FTP code '"$Code$"': "$Line);
Log("Unknown FTP code '"$Code$"': "$Line,Class.Name);
}
}
function DataReceived();
function DataProgress()
{
SetTimer(60,false);
}
final function bool OpenDataConnection( string S, bool bUpload )
{
local int i,j;
local IpAddr A;
A = SiteAddress;
// Get destination port
S = Mid(S,InStr(S,"(")+1);
for( i=0; i<4; ++i ) // Skip IP address
S = Mid(S,InStr(S,",")+1);
i = InStr(S,",");
A.Port = int(Left(S,i))*256 + int(Mid(S,i+1));
// Now attempt to bind port and open connection.
for( j=0; j<20; ++j )
{
if( DataConnection!=None )
DataConnection.Destroy();
DataConnection = Spawn(Class'FTPDataConnection',Self);
DataConnection.bUpload = bUpload;
DataConnection.BindPort();
if( DataConnection.Open(A) )
{
DataConnection.OnCompleted = DataReceived;
DataConnection.OnProgress = DataProgress;
return true;
}
}
DataConnection.Destroy();
DataConnection = None;
ReportError(2,"Couldn't bind port for upload data connection!");
return false;
}
function Timer()
{
ReportError(3,"FTP connection timed out!");
}
state Idle
{
Ignores Timer;
final function StartConnection()
{
local int i;
for( i=0; i<40; ++i )
{
BindPort();
if( Open(SiteAddress) )
{
GoToState('InitConnection');
return;
}
}
ReportError(4,"Port couldn't be bound or connection failed to open!");
}
function SaveAllStats()
{
Global.SaveAllStats();
if( bUploadAllStats )
StartConnection();
}
function RequestStats( ServerStStats Other )
{
Global.RequestStats(Other);
StartConnection();
}
Begin:
Sleep(0.1f);
if( bUploadAllStats || PendingLoaders.Length>0 )
StartConnection();
}
state InitConnection
{
function BeginState()
{
SetTimer(10,false);
}
event Closed()
{
ReportError(5,"Connection was closed by FTP server!");
}
Begin:
Sleep(5.f);
SendFTPLine("USER "$Mut.RemoteFTPUser);
}
state ConnectionBase
{
event Closed()
{
GoToState('Idle');
}
Begin:
while( true )
{
if( bUploadAllStats && Level.bLevelChange ) // Delay mapchange until all stats are uploaded.
Level.NextSwitchCountdown = FMax(Level.NextSwitchCountdown,1.f);
Sleep(0.5);
}
}
state EndConnection extends ConnectionBase
{
function BeginState()
{
SendFTPLine("QUIT");
SetTimer(4,false);
}
}
state KeepAlive extends ConnectionBase
{
Ignores Timer;
function SaveAllStats()
{
Global.SaveAllStats();
if( bUploadAllStats )
StartConnection();
}
function RequestStats( ServerStStats Other )
{
Global.RequestStats(Other);
StartConnection();
}
final function StartConnection()
{
CheckNextCommand();
}
Begin:
while( true )
{
if( bUploadAllStats || PendingLoaders.Length>0 )
StartConnection();
Sleep(Mut.FTPKeepAliveSec);
SendFTPLine("NOOP");
}
}
state UploadStats extends ConnectionBase
{
function BeginState()
{
bUploadAllStats = false;
SetTimer(10,false);
}
function SaveAllStats();
final function InitDataConnection( string S )
{
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: Upload stats for "$ToSave[0].PlayerName$" ("$(ToSave.Length-1+TotalList.Length)$" remains)");
if( OpenDataConnection(S,true) )
{
DataConnection.Data = ToSave[0].GetSaveData();
TempFileName = ToSave[0].Name$".txt.tmp";
SendFTPLine("STOR "$TempFileName);
bFileInProgress = true;
}
}
final function NextPackage()
{
RetryCounter = 0;
ToSave[0].bStatsChanged = false;
ToSave.Remove(0,1);
if( ToSave.Length==0 )
CheckNextCommand();
else if( !bIsAsciiMode )
{
bIsAsciiMode = true;
SendFTPLine("TYPE A");
}
else SendFTPLine("PASV");
}
function ProcessResponse( int Code, string Line )
{
switch( Code )
{
case 200: // Type set to A/I
if( bPostUploadCheck )
{
SetTimer(5,false);
SendFTPLine("SIZE "$TempFileName);
}
else SendFTPLine("PASV");
break;
case 227: // Entering passive mode
if( !bFileInProgress )
InitDataConnection(Line);
break;
case 150: // Opening ASCII mode data connection for file
SetTimer(60,false);
if( DataConnection!=None )
DataConnection.BeginUpload();
break;
case 226: // File transfer completed.
if( bFileInProgress )
{
SetTimer(5,false);
bFileInProgress = false;
bPostUploadCheck = true;
SendFTPLine("TYPE I");
bIsAsciiMode = false;
}
break;
case 213: // File size response.
if( bPostUploadCheck )
{
SetTimer(5,false);
if( int(Line)<=5 )
{
bPostUploadCheck = false;
if( ++RetryCounter>=5 )
NextPackage();
else
{
if( bFullVerbose )
Level.Game.Broadcast(Self,"213 FTP Error: Stats upload failed for "$ToSave[0].PlayerName$" retrying...");
SendFTPLine("PASV");
}
}
else SendFTPLine("RNFR "$TempFileName);
}
break;
case 350: // Rename accepted.
if( bPostUploadCheck )
{
SetTimer(5,false);
SendFTPLine("RNTO "$Left(TempFileName,Len(TempFileName)-4));
}
break;
case 250: // File successfully renamed or moved
NextPackage();
break;
case 550: // Sorry, but that file doesn't exist
if( bPostUploadCheck )
{
SetTimer(5,false);
bPostUploadCheck = false;
if( ++RetryCounter>=5 )
NextPackage();
else
{
if( bFullVerbose )
Level.Game.Broadcast(Self,"550 FTP Error: Stats upload failed for "$ToSave[0].PlayerName$" retrying...");
SendFTPLine("PASV");
}
}
break;
default:
Global.ProcessResponse(Code,Line);
}
}
Begin:
if( !bIsAsciiMode )
{
SendFTPLine("TYPE A");
bIsAsciiMode = true;
}
else SendFTPLine("PASV");
while( true )
{
if( Level.bLevelChange ) // Delay mapchange until all stats are uploaded.
{
bFullVerbose = true;
Level.NextSwitchCountdown = FMax(Level.NextSwitchCountdown,1.f);
}
Sleep(0.5);
}
}
state DownloadStats extends ConnectionBase
{
function BeginState()
{
SetTimer(10,false);
}
final function InitDataConnection( string S )
{
while( PendingLoaders.Length>0 && PendingLoaders[0]==None )
PendingLoaders.Remove(0,1);
if( PendingLoaders.Length==0 )
{
CheckNextCommand();
return;
}
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: Download stats for "$PendingLoaders[0].MyStatsObject.PlayerName$" ("$(PendingLoaders.Length-1)$" remains)");
if( OpenDataConnection(S,false) )
{
SendFTPLine("RETR "$PendingLoaders[0].MyStatsObject.Name$".txt");
bFileInProgress = true;
}
}
function DataReceived()
{
bFileInProgress = false;
if( PendingLoaders[0]!=None )
{
if( DataConnection!=None )
PendingLoaders[0].GetData(DataConnection.Data);
else PendingLoaders[0].GetData("");
}
PendingLoaders.Remove(0,1);
while( PendingLoaders.Length>0 && PendingLoaders[0]==None )
PendingLoaders.Remove(0,1);
if( bUploadAllStats ) // Saving has higher priority.
GoToState('UploadStats');
else if( PendingLoaders.Length>0 )
SendFTPLine("PASV");
else CheckNextCommand();
}
function ProcessResponse( int Code, string Line )
{
switch( Code )
{
case 200: // Type set to A
SendFTPLine("PASV");
break;
case 227: // Entering passive mode
if( !bFileInProgress )
InitDataConnection(Line);
break;
case 150: // Opening ASCII mode data connection for file
SetTimer(60,false);
break;
case 550: // No such file or directory
SetTimer(10,false);
if( bFileInProgress )
{
if( DataConnection!=None )
DataConnection.Destroy();
DataReceived();
}
break;
default:
Global.ProcessResponse(Code,Line);
}
}
Begin:
if( !bIsAsciiMode )
{
bIsAsciiMode = true;
SendFTPLine("TYPE A");
}
else SendFTPLine("PASV");
while( true )
{
if( bUploadAllStats && Level.bLevelChange ) // Delay mapchange until all stats are uploaded.
{
bFullVerbose = true;
Level.NextSwitchCountdown = FMax(Level.NextSwitchCountdown,1.f);
}
Sleep(0.5);
}
}
state ErrorState
{
Ignores SaveAllStats,RequestStats;
Begin:
Sleep(1.f);
Mut.RespawnNetworkLink();
}
static final function int GetDateInt( int Day, int Month, int Year )
{
return Day + (Month*31) + (Year*366);
}
state GetDir
{
function BeginState()
{
SendFTPLine("TYPE I");
bObtainDir = false;
}
final function BeginPassiveCon( string S )
{
if( bFullVerbose )
Level.Game.Broadcast(Self,"FTP: Get directory");
if( OpenDataConnection(S,false) )
SendFTPLine("MLSD");
}
function DataReceived()
{
local array<string> SA;
local string S;
local int i,j,Da,Mo,Ye,ol;
if( DataConnection!=None )
{
Split(DataConnection.Data,Chr(13)$Chr(10),SA);
ol = TotalList.Length;
for( i=0; i<SA.Length; ++i )
{
S = SA[i];
j = InStr(S,"modify=");
if( j==-1 )
continue;
S = Mid(S,j+7);
j = InStr(S,";");
if( j==-1 )
continue;
Ye = int(Mid(S,0,4));
Mo = int(Mid(S,4,2));
Da = int(Mid(S,6,2));
j = InStr(S," ");
if( j==-1 )
continue;
S = Mid(S,j+1);
if( Right(S,4)!=".txt" )
continue;
if( IgnoreDateNum<=GetDateInt(Da,Mo,Ye) )
Log(S@"is newer: "$Da$"."$Mo$"."$Ye);
else
{
S = Left(S,Len(S)-4);
for( j=0; j<TotalList.Length; ++j )
if( TotalList[j]==S )
{
TotalList.Remove(j,1);
break;
}
}
}
Level.Game.Broadcast(Self,"Reduced list size: "$ol$" -> "$TotalList.Length);
}
CheckNextCommand();
}
function ProcessResponse( int Code, string Line )
{
switch( Code )
{
case 200: // Type set to I
bIsAsciiMode = false;
SendFTPLine("PASV");
break;
case 227: // Entering passive mode
BeginPassiveCon(Line);
break;
case 150: // Opening ASCII mode data connection for file
SetTimer(120,false);
break;
case 550: // No such file or directory
SetTimer(10,false);
if( bFileInProgress )
{
if( DataConnection!=None )
DataConnection.Destroy();
DataReceived();
}
break;
default:
Global.ProcessResponse(Code,Line);
}
}
}
defaultproperties
{
}

View file

@ -0,0 +1,18 @@
class FragFireFix extends FragFire;
var float PrevAmmo;
function DoFireEffect()
{
local float MaxAmmo,CurAmmo;
Weapon.GetAmmoCount(MaxAmmo,CurAmmo);
if (CurAmmo==0 && PrevAmmo==0)
return;
PrevAmmo=CurAmmo;
Super.DoFireEffect();
}
defaultproperties
{
}

View file

@ -0,0 +1,57 @@
//=============================================================================
// SRGameRules
//=============================================================================
class SRGameRules extends GameRules;
function PostBeginPlay()
{
if( Level.Game.GameRulesModifiers==None )
Level.Game.GameRulesModifiers = Self;
else Level.Game.GameRulesModifiers.AddGameRules(Self);
}
function AddGameRules(GameRules GR)
{
if ( GR!=Self )
Super.AddGameRules(GR);
}
function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local ClientPerkRepLink R;
local SRCustomProgress S;
if ( (NextGameRules != None) && NextGameRules.PreventDeath(Killed,Killer, damageType,HitLocation) )
return true;
if( xPlayer(Killer)!=None && Killed.Controller!=None && Killed.Controller!=Killer )
{
R = FindStatsFor(Killer);
if( R!=None )
for( S=R.CustomLink; S!=None; S=S.NextLink )
S.NotifyPlayerKill(Killed,damageType);
}
if( xPlayer(Killed.Controller)!=None && Killer!=None && Killer.Pawn!=None )
{
R = FindStatsFor(Killed.Controller);
if( R!=None )
for( S=R.CustomLink; S!=None; S=S.NextLink )
S.NotifyPlayerKilled(Killer.Pawn,damageType);
}
return false;
}
static final function ClientPerkRepLink FindStatsFor( Controller C )
{
local LinkedReplicationInfo L;
if( C.PlayerReplicationInfo==None )
return None;
for( L=C.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
if( ClientPerkRepLink(L)!=None )
return ClientPerkRepLink(L);
return None;
}
defaultproperties
{
}

View file

@ -0,0 +1,279 @@
/*
ServerPerks webadmin interface
*/
class ServerPerkWebAdmin extends xWebQueryHandler;
var ServerPerksMut Mut;
var string HomeURL,PerksURL,TraderCatURL,TraderInvURL;
function bool Init()
{
foreach Level.AllActors( class'ServerPerksMut', Mut )
break;
return true;
}
function Cleanup()
{
Mut = None;
}
function bool Query(WebRequest Request, WebResponse Response)
{
switch (Mid(Request.URI, 1))
{
case DefaultPage:
RequestFrame(Request, Response);
return true;
case HomeURL:
RequestMain(Request, Response);
return true;
case PerksURL:
RequestPerks(Request, Response);
return true;
case TraderCatURL:
TradetCat(Request, Response);
return true;
case TraderInvURL:
RequestInventory(Request, Response);
return true;
}
return false;
}
function RequestFrame(WebRequest Request, WebResponse Response)
{
Response.SendText("<html><head><title>SP</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"></head>");
Response.SendText("<frameset cols=\"*\" rows=\"*\" border=\"0\" scrolling=\"No\" frameborder=\"no\" noresize framespacing=\"0\" marginheight=\"0\">");
Response.SendText("<frame src=\""$HomeURL$"\" name=\"main\" border=\"0\" frameborder=\"no\" noresize framespacing=\"0\" marginheight=\"0\"></frameset>");
Response.SendText("<noframes><body bgcolor=\"#FFFFFF\"></body></noframes></html>");
}
final function SendHeader( WebResponse Response, optional bool bNoSettings )
{
Response.SendText("<html><head><title>SP</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"><link rel=\"stylesheet\" type=\"text/css\" href=\""$SiteCSSFile$"\"></head><body bgcolor=\""$SiteBG$"\" topmargin=\"0\" leftmargin=\"0\" marginheight=\"0\" marginwidth=\"0\"><body><div align=\"center\"><table cellpadding=\"1\" cellspacing=\"2\" style=\"width:600px\">");
if( !bNoSettings )
Response.SendText("<form method=\"post\">");
}
final function SendSubmitButton( WebResponse Response )
{
Response.SendText("<tr><td colspan=\"2\"><input type=\"submit\" name=\"submit\" value=\"Submit\" class=\"button\"></td></tr></form>");
}
final function SendFooter( WebResponse Response )
{
Response.SendText("</table></div></body></html>");
}
final function SendHeaderLine( WebResponse Response, string S )
{
Response.SendText("<tr><th colspan=\"2\">"$S$"</th></tr>");
}
final function SendURLLine( WebResponse Response, string URL, string DisplayStr )
{
Response.SendText("<tr><td colspan=\"2\"><a href=\""$URL$"\">"$DisplayStr$"</a></td></tr>");
}
final function SendTableLine( WebResponse Response, string S )
{
Response.SendText("<tr>"$S$"</tr>");
}
function RequestMain(WebRequest Request, WebResponse Response)
{
SendHeader(Response,true);
SendHeaderLine(Response,"ServerPerks settings");
SendURLLine(Response,PerksURL,"Edit perk classes");
SendURLLine(Response,TraderCatURL,"Edit trader categories");
SendURLLine(Response,TraderInvURL,"Edit trader inventory");
SendFooter(Response);
}
function RequestPerks(WebRequest Request, WebResponse Response)
{
local string S,Er;
local array<string> PA;
local int i,j;
local class<SRVeterancyTypes> VC;
// Process response.
if( Request.GetVariable("submit") == "Submit" )
{
S = Request.GetVariable("Perks", "");
if( S!="" )
{
Split(S,Chr(13)$Chr(10),PA);
if( PA.Length<=1 )
{
PA.Length = 0;
Split(S,Chr(10),PA);
}
Class'ServerPerksMut'.Default.Perks.Length = 0;
for( i=0; i<PA.Length; ++i )
{
VC = class<SRVeterancyTypes>(DynamicLoadObject(PA[i],Class'Class'));
if( VC!=None )
{
Class'ServerPerksMut'.Default.Perks.Length = j+1;
Class'ServerPerksMut'.Default.Perks[j++] = string(VC);
}
else if( Er=="" )
Er = PA[i];
else Er $= ", "$PA[i];
}
Class'ServerPerksMut'.Static.StaticSaveConfig();
}
}
SendHeader(Response);
SendHeaderLine(Response,"Perk classes:");
if( Er!="" )
SendHeaderLine(Response,"<font color=\"red\">[Warning] Perk classes failed to load:<BR>"$Er$"</font>");
// Send perk list
for( i=0; i<Class'ServerPerksMut'.Default.Perks.Length; ++i )
{
if( i==0 )
S = Class'ServerPerksMut'.Default.Perks[0];
else S = S$Chr(10)$Class'ServerPerksMut'.Default.Perks[i];
}
SendTableLine(Response,"<td colspan=\"3\"><textarea name=\"Perks\" rows=\"25\" cols=\"80\">"$S$"</textarea></td>");
SendSubmitButton(Response);
SendURLLine(Response,HomeURL,"Return to home");
SendFooter(Response);
}
function TradetCat(WebRequest Request, WebResponse Response)
{
local string S,D,A,B;
local int i,j;
// Process response.
if( Request.GetVariable("submit") == "Submit" )
{
for( i=Class'ServerPerksMut'.Default.WeaponCategories.Length; i>=0; --i )
{
j = int(Request.GetVariable("WI"$i, "-2"));
if( j==-2 )
continue;
S = Request.GetVariable("WC"$i, "");
if( S=="" || S=="NEW" )
{
if( i<Class'ServerPerksMut'.Default.WeaponCategories.Length )
Class'ServerPerksMut'.Default.WeaponCategories.Remove(i,1);
}
else
{
if( j>=0 )
S = string(j)$":"$S;
if( i>=Class'ServerPerksMut'.Default.WeaponCategories.Length )
Class'ServerPerksMut'.Default.WeaponCategories.Length = i+1;
Class'ServerPerksMut'.Default.WeaponCategories[i] = S;
}
}
Class'ServerPerksMut'.Static.StaticSaveConfig();
}
SendHeader(Response);
SendHeaderLine(Response,"Trader categories:");
// gather perks
D = " ><option value=\"-1\">None</option>";
for( i=0; i<Class'ServerPerksMut'.Default.Perks.Length; ++i )
D = D$"<option value=\""$i$"\">"$Class'ServerPerksMut'.Default.Perks[i]$"</option>";
D $= "<option value=\""$i$"\">Unperked</option></select>";
// categories
Response.SendText("<tr>"$S$"</tr>");
for( i=0; i<=Class'ServerPerksMut'.Default.WeaponCategories.Length; ++i )
{
S = "<tr><td><select class=\"mini\" name=\"WI"$i$"\""$D;
if( i==Class'ServerPerksMut'.Default.WeaponCategories.Length )
B = "NEW";
else
{
if( Divide(Class'ServerPerksMut'.Default.WeaponCategories[i],":",A,B) )
{
j = int(A);
if( j<Class'ServerPerksMut'.Default.Perks.Length )
A = ">"$Class'ServerPerksMut'.Default.Perks[j]$"<";
else A = ">Unperked<";
S = Repl(S,A," selected=\"selected\""$A); // Shitty hack.
}
else B = Class'ServerPerksMut'.Default.WeaponCategories[i];
}
S $= "</td><td><input class=\"textbox\" type=\"text\" name=\"WC"$i$"\" value=\""$B$"\" size=\"16\" maxlength=\"32\">";
Response.SendText(S$"</td></tr>");
}
SendSubmitButton(Response);
SendURLLine(Response,HomeURL,"Return to home");
SendFooter(Response);
}
function RequestInventory(WebRequest Request, WebResponse Response)
{
local string S,Er,A,B;
local array<string> PA;
local int i,j;
local class<Pickup> PC;
// Process response.
if( Request.GetVariable("submit") == "Submit" )
{
S = Request.GetVariable("Inv", "");
if( S!="" )
{
Split(S,Chr(13)$Chr(10),PA);
if( PA.Length<=1 )
{
PA.Length = 0;
Split(S,Chr(10),PA);
}
Class'ServerPerksMut'.Default.TraderInventory.Length = 0;
for( i=0; i<PA.Length; ++i )
{
if( !Divide(PA[i],":",A,B) )
{
A = "";
B = PA[i];
}
else A $= ":";
PC = class<Pickup>(DynamicLoadObject(B,Class'Class'));
if( PC!=None )
{
Class'ServerPerksMut'.Default.TraderInventory.Length = j+1;
Class'ServerPerksMut'.Default.TraderInventory[j++] = A$string(PC);
}
else if( Er=="" )
Er = PA[i];
else Er $= ", "$PA[i];
}
Class'ServerPerksMut'.Static.StaticSaveConfig();
}
}
SendHeader(Response);
SendHeaderLine(Response,"Inventory classes:");
if( Er!="" )
SendHeaderLine(Response,"<font color=\"red\">[Warning] Pickup classes failed to load:<BR>"$Er$"</font>");
// Send perk list
for( i=0; i<Class'ServerPerksMut'.Default.TraderInventory.Length; ++i )
{
if( i==0 )
S = Class'ServerPerksMut'.Default.TraderInventory[0];
else S = S$Chr(10)$Class'ServerPerksMut'.Default.TraderInventory[i];
}
SendTableLine(Response,"<td colspan=\"3\"><textarea name=\"Inv\" rows=\"25\" cols=\"80\">"$S$"</textarea></td>");
SendSubmitButton(Response);
SendURLLine(Response,HomeURL,"Return to home");
SendFooter(Response);
}
defaultproperties
{
HomeURL="ServerPerkMain"
PerksURL="ServerPerksPerks"
TraderCatURL="ServerPerksTraderCat"
TraderInvURL="ServerPerksTraderInv"
DefaultPage="ServerPerkWebAdmin"
Title="Server Perks"
}

View file

@ -0,0 +1,829 @@
// Written by .:..: (2009)
Class ServerPerksMut extends Mutator
Config(ServerPerks);
struct ChatIconType
{
var() config string IconTexture,IconTag;
var() config bool bCaseInsensitive;
};
var() globalconfig array<string> Perks,TraderInventory,WeaponCategories,CustomCharacters;
var() globalconfig int MinPerksLevel,MaxPerksLevel,RemotePort,MidGameSaveWaves,FTPKeepAliveSec;
var() globalconfig float RequirementScaling;
var() globalconfig string RemoteDatabaseURL,RemotePassword,RemoteFTPUser,RemoteFTPDir,ServerNewsURL,FTPUploadAllIgnoreDate;
var() globalconfig array<ChatIconType> SmileyTags;
var array<ClientPerkRepLink.FShopCategoryIndex> LoadedCategories;
var array<byte> LoadInvCategory;
var array< class<SRVeterancyTypes> > LoadPerks;
var array< class<Pickup> > LoadInventory;
var const int VersionNumber;
var array<PlayerController> PendingPlayers;
var array<StatsObject> ActiveStats;
var localized string ServerPerksGroup;
var transient InternetLink Link;
var KFGameType KFGT;
var int LastSavedWave,WaveCounter;
var SRGameRules RulesMod;
var transient array<name> AddedServerPackages;
var transient float NextCheckTimer;
var array<SRHUDKillingFloor.SmileyMessageType> SmileyMsgs;
var class<Object> SRScoreboardType,SRHudType,SRMenuType;
var class<PlayerController> SRControllerType;
var() globalconfig bool bUploadAllStats,bForceGivePerk,bNoSavingProgress,bUseRemoteDatabase,bUsePlayerNameAsID,bMessageAnyPlayerLevelUp,bNoPerkChanges
,bUseLowestRequirements,bBWZEDTime,bUseEnhancedScoreboard,bOverrideUnusedCustomStats,bAllowAlwaysPerkChanges,bEnableWebAdmin
,bForceCustomChars,bEnableChatIcons,bEnhancedShoulderView,bFixGrenadeExploit,bAdminEditStats,bUseFTPLink,bDebugDatabase;
var bool bEnabledEmoIcons;
function PostBeginPlay()
{
local int i,j;
local class<SRVeterancyTypes> V;
local class<Pickup> P;
local string S;
local byte Cat;
local class<PlayerRecordClass> PR;
local Texture T;
local KFLevelRules R;
if( RulesMod==None )
RulesMod = Spawn(Class'SRGameRules');
KFGT = KFGameType(Level.Game);
if( Level.Game.HUDType~=Class'KFGameType'.Default.HUDType || Level.Game.HUDType~=Class'KFStoryGameInfo'.Default.HUDType )
{
bEnabledEmoIcons = bEnableChatIcons;
Level.Game.HUDType = string(SRHudType);
}
if( bUseEnhancedScoreboard && (Level.Game.ScoreBoardType~=Class'KFGameType'.Default.ScoreBoardType || Level.Game.ScoreBoardType~=Class'KFStoryGameInfo'.Default.ScoreBoardType) )
Level.Game.ScoreBoardType = string(SRScoreboardType);
// Use own playercontroller class for security reasons.
if( Level.Game.PlayerControllerClass==Class'KFPlayerController' || Level.Game.PlayerControllerClass==Class'KFPlayerController_Story' )
{
Level.Game.PlayerControllerClass = SRControllerType;
Level.Game.PlayerControllerClassName = string(SRControllerType);
}
DeathMatch(Level.Game).LoginMenuClass = string(SRMenuType);
// Load perks.
for( i=0; i<Perks.Length; i++ )
{
V = class<SRVeterancyTypes>(DynamicLoadObject(Perks[i],Class'Class'));
if( V!=None )
{
LoadPerks[LoadPerks.Length] = V;
ImplementPackage(V);
}
}
// Setup categories
LoadedCategories.Length = WeaponCategories.Length;
for( i=0; i<WeaponCategories.Length; ++i )
{
S = WeaponCategories[i];
j = InStr(S,":");
if( j==-1 )
{
LoadedCategories[i].Name = S;
LoadedCategories[i].PerkIndex = 255;
}
else
{
LoadedCategories[i].Name = Mid(S,j+1);
LoadedCategories[i].PerkIndex = int(Left(S,j));
}
}
if( LoadedCategories.Length==0 )
{
LoadedCategories.Length = 1;
LoadedCategories[0].Name = "All";
LoadedCategories[0].PerkIndex = 255;
}
// Init rules
foreach AllActors(class'KFLevelRules',R)
break;
if( R==None && KFGT!=None )
{
R = KFGT.KFLRules;
if( R==None )
{
R = Spawn(class'KFLevelRules');
KFGT.KFLRules = R;
}
}
if( R!=None )
{
// Empty all stock weapons first.
R.MediItemForSale.Length = 0;
R.SuppItemForSale.Length = 0;
R.ShrpItemForSale.Length = 0;
R.CommItemForSale.Length = 0;
R.BersItemForSale.Length = 0;
R.FireItemForSale.Length = 0;
R.DemoItemForSale.Length = 0;
R.NeutItemForSale.Length = 0;
}
// Load up trader inventory.
for( i=0; i<TraderInventory.Length; i++ )
{
S = TraderInventory[i];
j = InStr(S,":");
if( j>0 )
{
Cat = Min(int(Left(S,j)),LoadedCategories.Length-1);
S = Mid(S,j+1);
}
else Cat = 0;
P = class<Pickup>(DynamicLoadObject(S,Class'Class'));
if( P!=None )
{
// Inform bots.
if( R!=None )
R.MediItemForSale[R.MediItemForSale.Length] = P;
LoadInventory[LoadInventory.Length] = P;
LoadInvCategory[LoadInvCategory.Length] = Cat;
if( P.Outer.Name!='KFMod' )
ImplementPackage(P);
}
}
// Load custom chars.
for( i=0; i<CustomCharacters.Length; ++i )
{
// Separate group from actual skin.
S = CustomCharacters[i];
j = InStr(S,":");
if( j>=0 )
S = Mid(S,j+1);
PR = class<PlayerRecordClass>(DynamicLoadObject(S$"Mod."$S,class'Class',true));
if( PR!=None )
{
if( PR.Default.MeshName!="" ) // Add mesh package.
ImplementPackage(DynamicLoadObject(PR.Default.MeshName,class'Mesh',true));
if( PR.Default.BodySkinName!="" ) // Add skin package.
ImplementPackage(DynamicLoadObject(PR.Default.BodySkinName,class'Material',true));
ImplementPackage(PR);
}
}
// Load chat icons
if( bEnabledEmoIcons )
{
j = 0;
for( i=0; i<SmileyTags.Length; ++i )
{
if( SmileyTags[i].IconTexture=="" || SmileyTags[i].IconTag=="" )
continue;
T = Texture(DynamicLoadObject(SmileyTags[i].IconTexture,class'Texture',true));
if( T==None )
continue;
ImplementPackage(T);
SmileyMsgs.Length = j+1;
SmileyMsgs[j].SmileyTex = T;
if( SmileyTags[i].bCaseInsensitive )
SmileyMsgs[j].SmileyTag = Caps(SmileyTags[i].IconTag);
else SmileyMsgs[j].SmileyTag = SmileyTags[i].IconTag;
SmileyMsgs[j].bInCAPS = SmileyTags[i].bCaseInsensitive;
++j;
}
bEnabledEmoIcons = (j!=0);
}
Log("Adding"@AddedServerPackages.Length@"additional serverpackages",Class.Outer.Name);
for( i=0; i<AddedServerPackages.Length; i++ )
AddToPackageMap(string(AddedServerPackages[i]));
AddedServerPackages.Length = 0;
if( bUseEnhancedScoreboard || (Level.Game.HUDType~=string(SRHudType)) )
AddToPackageMap("CountryFlagsTex");
if( bFixGrenadeExploit )
Class'Frag'.Default.FireModeClass[0] = Class'FragFireFix';
if( bUseRemoteDatabase )
{
Log("Using remote database:"@RemoteDatabaseURL$":"$RemotePort,Class.Outer.Name);
RespawnNetworkLink();
}
}
function Tick( float Delta )
{
Disable('Tick');
if( bEnableWebAdmin && Level.NetMode!=NM_StandAlone )
InitWebServer();
}
function InitWebServer()
{
local WebServer W;
local int i;
local UTServerAdmin U;
// Must increase the limit size.
class'WebConnection'.default.MaxValueLength = Max(class'WebConnection'.default.MaxValueLength,9999);
class'WebConnection'.default.MaxLineLength = Max(class'WebConnection'.default.MaxLineLength,10100);
foreach DynamicActors(class'WebServer',W)
{
for( i=0; i<10; ++i )
{
U = UTServerAdmin(W.ApplicationObjects[i]);
if( U!=None )
{
for( i=0; i<U.QueryHandlers.Length; ++i )
if( U.QueryHandlers[i].Class==Class'ServerPerkWebAdmin' )
return; // Already implemented.
U.QueryHandlers.Length = i+1;
U.QueryHandlers[i] = New(U)Class'ServerPerkWebAdmin';
U.QueryHandlers[i].Init();
return;
}
}
Log("ServerPerks WebAdmin interface failed to init: Missing UTServerAdmin application.",Class.Outer.Name);
return;
}
Log("ServerPerks WebAdmin interface failed to init: Missing WebServer object.",Class.Outer.Name);
}
function RespawnNetworkLink()
{
if( Link!=None )
Link.Destroy();
if( !bUseFTPLink )
{
Link = Spawn(Class'DatabaseUdpLink');
DatabaseUdpLink(Link).Mut = Self;
}
else
{
Link = Spawn(Class'FTPTcpLink');
FTPTcpLink(Link).Mut = Self;
}
Link.BeginEvent();
}
function Mutate(string MutateString, PlayerController Sender)
{
if( MutateString~="SPHelp" )
Sender.ClientMessage("ServerPerksMut mutate CMDs: EditStats, SetPerk, Debug, GetID");
if( Sender.PlayerReplicationInfo.bAdmin || Sender.PlayerReplicationInfo.bSilentAdmin || Viewport(Sender.Player)!=None )
{
if( bAdminEditStats && MutateString~="EditStats" )
Spawn(Class'AdminMenuHandle',Sender).MutatorOwner = Self;
else if( MutateString~="GetID" )
ListID(Sender);
else if( Left(MutateString,7)~="SetPerk" )
AdminChangePerk(Sender,Mid(MutateString,8));
}
if( NextCheckTimer<Level.TimeSeconds && MutateString~="Debug" ) // Allow developer to lookup bugs.
{
NextCheckTimer = Level.TimeSeconds+0.25;
Sender.ClientMessage("Debug info: "$Sender.SteamStatsAndAchievements);
if( ServerStStats(Sender.SteamStatsAndAchievements)!=None )
{
Sender.ClientMessage("Ready:"@ServerStStats(Sender.SteamStatsAndAchievements).bStatsReadyNow@ServerStStats(Sender.SteamStatsAndAchievements).bStatsChecking);
Sender.ClientMessage("MyStatsObject:"@ServerStStats(Sender.SteamStatsAndAchievements).MyStatsObject);
Sender.ClientMessage("Timer:"@Sender.SteamStatsAndAchievements.TimerCounter@Sender.SteamStatsAndAchievements.TimerRate);
}
Sender.ClientMessage("Perk info:");
DebugMessageProgress(Sender);
}
if ( NextMutator != None )
NextMutator.Mutate(MutateString, Sender);
}
final function AdminChangePerk( PlayerController Sender, string Cmd )
{
local int i,ID;
local Controller C;
local class<SRVeterancyTypes> V;
local bool bAll;
i = InStr(Cmd," ");
if( i==-1 )
{
Sender.ClientMessage("Usage: SetPerk <ID/All> <PerkName>");
return;
}
if( Left(Cmd,i)~="All" )
bAll = true;
else ID = int(Left(Cmd,i));
Cmd = Mid(Cmd,i+1);
for( i=0; i<LoadPerks.Length; ++i )
if( string(LoadPerks[i].Name)~=Cmd || LoadPerks[i].Default.VeterancyName~=Cmd )
{
V = LoadPerks[i];
break;
}
if( V==None )
{
Sender.ClientMessage("Unknown perk name: "$Cmd);
return;
}
for( C=Level.ControllerList; C!=None; C=C.nextController )
{
if( C.bIsPlayer && C.PlayerReplicationInfo!=None && (bAll || C.PlayerReplicationInfo.PlayerID==ID) && xPlayer(C)!=None && ServerStStats(xPlayer(C).SteamStatsAndAchievements)!=None )
{
ServerStStats(xPlayer(C).SteamStatsAndAchievements).ForcePerkChange(V);
xPlayer(C).ClientMessage("An admin has forced to change your perk to "$V.Default.VeterancyName);
if( !bAll )
{
Sender.ClientMessage("Changed "$C.PlayerReplicationInfo.PlayerName$" perk to: "$V);
break;
}
}
}
if( bAll )
Sender.ClientMessage("Changed everyones perk to: "$V);
}
final function ListID( PlayerController PC )
{
local Controller C;
PC.ClientMessage("Player IDs:");
for( C=Level.ControllerList; C!=None; C=C.nextController )
{
if( C.bIsPlayer && C.PlayerReplicationInfo!=None && xPlayer(C)!=None && ServerStStats(xPlayer(C).SteamStatsAndAchievements)!=None )
PC.ClientMessage("ID="$C.PlayerReplicationInfo.PlayerID$", Name="$C.PlayerReplicationInfo.PlayerName);
}
}
final function DebugMessageProgress( PlayerController PC )
{
local KFPlayerReplicationInfo PRI;
local class<SRVeterancyTypes> VC;
local int i,Req,Cur,NumR;
local byte Lvl;
local ClientPerkRepLink Rep;
PRI = KFPlayerReplicationInfo(PC.PlayerReplicationInfo);
if( PRI==None || Class<SRVeterancyTypes>(PRI.ClientVeteranSkill)==None )
return;
VC = Class<SRVeterancyTypes>(PRI.ClientVeteranSkill);
Lvl = PRI.ClientVeteranSkillLevel+1;
Rep = Class'SRGameRules'.Static.FindStatsFor(PC);
if( Rep==None )
return;
PC.ClientMessage("Perk: "$VC.Default.VeterancyName$" NextLevel: "$Lvl);
NumR = VC.Static.GetRequirementCount(Rep,Lvl);
for( i=0; i<NumR; ++i )
{
Cur = VC.Static.GetPerkProgressInt(Rep,Req,Lvl,i);
PC.ClientMessage("Requirement '"$i$"' GetPerkProgressInt: Current:"$Cur$" Final:"$Req);
VC.Static.GetPerkProgress(Rep,Lvl,i,Cur,Req);
PC.ClientMessage("GetPerkProgress: Current:"$Cur$" Final:"$Req);
}
}
final function ImplementPackage( Object O )
{
local int i;
if( O==None )
return;
while( O.Outer!=None )
O = O.Outer;
if( O.Name=='KFMod' )
return;
for( i=(AddedServerPackages.Length-1); i>=0; --i )
if( AddedServerPackages[i]==O.Name )
return;
AddedServerPackages[AddedServerPackages.Length] = O.Name;
}
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
if( PlayerController(Other)!=None )
{
PendingPlayers[PendingPlayers.Length] = PlayerController(Other);
SetTimer(0.1,false);
}
else if( Bot(Other)!=None && Bot(Other).PawnClass==Class'KFHumanPawn' )
{
Bot(Other).PawnClass = Class'SRHumanPawn';
Bot(Other).PreviousPawnClass = Class'SRHumanPawn';
}
else if( ServerStStats(Other)!=None )
SetServerPerks(ServerStStats(Other));
else if( ClientPerkRepLink(Other)!=None )
SetupRepLink(ClientPerkRepLink(Other));
return true;
}
function SetServerPerks( ServerStStats Stat )
{
local int i;
Stat.MutatorOwner = Self;
Stat.Rep.ServerWebSite = ServerNewsURL;
Stat.Rep.MinimumLevel = MinPerksLevel+1;
Stat.Rep.MaximumLevel = MaxPerksLevel+1;
Stat.Rep.RequirementScaling = RequirementScaling;
Stat.Rep.CachePerks.Length = LoadPerks.Length;
for( i=0; i<LoadPerks.Length; i++ )
Stat.Rep.CachePerks[i].PerkClass = LoadPerks[i];
}
function SetupRepLink( ClientPerkRepLink R )
{
local int i;
R.bMinimalRequirements = bUseLowestRequirements;
R.bBWZEDTime = bBWZEDTime;
R.bNoStandardChars = bForceCustomChars;
R.ShopInventory.Length = LoadInventory.Length;
for( i=0; i<LoadInventory.Length; ++i )
{
R.ShopInventory[i].PC = LoadInventory[i];
R.ShopInventory[i].CatNum = LoadInvCategory[i];
}
R.ShopCategories = LoadedCategories;
R.CustomChars = CustomCharacters;
if( bEnabledEmoIcons )
R.SmileyTags = SmileyMsgs;
}
function GetServerDetails( out GameInfo.ServerResponseLine ServerState )
{
local int i,l;
Super.GetServerDetails( ServerState );
l = ServerState.ServerInfo.Length;
ServerState.ServerInfo.Length = l+1;
ServerState.ServerInfo[l].Key = "SP: Version";
ServerState.ServerInfo[l].Value = string(VersionNumber);
l++;
ServerState.ServerInfo.Length = l+1;
ServerState.ServerInfo[l].Key = "SP: Min perk level";
ServerState.ServerInfo[l].Value = string(MinPerksLevel);
l++;
ServerState.ServerInfo.Length = l+1;
ServerState.ServerInfo[l].Key = "SP: Max perk level";
ServerState.ServerInfo[l].Value = string(MaxPerksLevel);
l++;
ServerState.ServerInfo.Length = l+1;
ServerState.ServerInfo[l].Key = "SP: Num trader weapons";
ServerState.ServerInfo[l].Value = string(LoadInventory.Length);
l++;
For( i=0; i<LoadPerks.Length; i++ )
{
ServerState.ServerInfo.Length = l+1;
ServerState.ServerInfo[l].Key = "SP: Perk "$(i+1);
ServerState.ServerInfo[l].Value = LoadPerks[i].Default.VeterancyName;
l++;
}
}
function Timer()
{
local int i;
for( i=(PendingPlayers.Length-1); i>=0; --i )
{
if( KFPCServ(PendingPlayers[i])!=None )
KFPCServ(PendingPlayers[i]).bUseAdvBehindview = bEnhancedShoulderView;
if( PendingPlayers[i]!=None && PendingPlayers[i].Player!=None && ServerStStats(PendingPlayers[i].SteamStatsAndAchievements)==None )
{
if( PendingPlayers[i].SteamStatsAndAchievements!=None )
PendingPlayers[i].SteamStatsAndAchievements.Destroy();
PendingPlayers[i].SteamStatsAndAchievements = Spawn(Class'ServerStStats',PendingPlayers[i]);
PendingPlayers[i].PlayerReplicationInfo.SteamStatsAndAchievements = PendingPlayers[i].SteamStatsAndAchievements;
}
}
PendingPlayers.Length = 0;
}
final function string GetPlayerID( PlayerController PC )
{
if( bUsePlayerNameAsID )
return PC.PlayerReplicationInfo.PlayerName;
return PC.GetPlayerIDHash();
}
function StatsObject GetStatsForPlayer( PlayerController PC )
{
local StatsObject S;
local string SId;
local int i;
if( Level.Game.bGameEnded )
return None;
SId = GetPlayerID(PC);
for( i=0; i<ActiveStats.Length; ++i )
if( string(ActiveStats[i].Name)~=SId )
{
S = ActiveStats[i];
break;
}
if( S==None )
{
S = new(None,SId) Class'StatsObject';
S.bStatsChanged = false;
ActiveStats[ActiveStats.Length] = S;
}
S.PlayerName = PC.PlayerReplicationInfo.PlayerName;
S.PlayerIP = PC.GetPlayerNetworkAddress();
return S;
}
function SaveStats()
{
local int i;
local ClientPerkRepLink CP;
if( bNoSavingProgress )
return;
Log("*** Saving "$ActiveStats.Length$" stat objects ***",Class.Outer.Name);
foreach DynamicActors(Class'ClientPerkRepLink',CP)
if( CP.StatObject!=None && ServerStStats(CP.StatObject).MyStatsObject!=None )
ServerStStats(CP.StatObject).MyStatsObject.SetCustomValues(CP.CustomLink);
if( bUseRemoteDatabase )
{
SaveAllStats();
return;
}
for( i=0; i<ActiveStats.Length; ++i )
if( ActiveStats[i].bStatsChanged )
{
ActiveStats[i].bStatsChanged = false;
ActiveStats[i].SaveConfig();
}
}
function CheckWinOrLose()
{
local bool bWin;
local Controller P;
local PlayerController Player;
bWin = (KFGameReplicationInfo(Level.GRI)!=None && KFGameReplicationInfo(Level.GRI).EndGameType==2);
for ( P = Level.ControllerList; P != none; P = P.nextController )
{
Player = PlayerController(P);
if ( Player != none )
{
if ( ServerStStats(Player.SteamStatsAndAchievements)!=None )
ServerStStats(Player.SteamStatsAndAchievements).WonLostGame(bWin);
}
}
}
function InitNextWave()
{
if( ++WaveCounter>=MidGameSaveWaves )
{
WaveCounter = 0;
SaveStats();
}
}
Auto state EndGameTracker
{
Begin:
if( bUploadAllStats && Level.NetMode==NM_StandAlone )
{
Sleep(1.f);
FTPTcpLink(Link).FullUpload();
while( true )
{
if( KFGT!=None )
KFGT.WaveCountDown = 60;
Sleep(1.f);
}
}
while( !Level.Game.bGameEnded )
{
Sleep(1.f);
if( MidGameSaveWaves>0 && KFGT!=None && KFGT.WaveNum!=LastSavedWave )
{
LastSavedWave = KFGT.WaveNum;
InitNextWave();
}
if( Level.bLevelChange )
{
SaveStats();
Stop;
}
}
CheckWinOrLose();
SaveStats();
}
static final function string GetSafeName( string S )
{
S = Repl(S,"=","-");
S = Repl(S,Chr(10),""); // LF
S = Repl(S,Chr(13),""); // CR
S = Repl(S,"\"","'"); // " -> '
return S;
}
delegate SaveAllStats();
delegate RequestStats( ServerStStats Other );
static function FillPlayInfo(PlayInfo PlayInfo)
{
Super.FillPlayInfo(PlayInfo);
PlayInfo.AddSetting(default.ServerPerksGroup,"MinPerksLevel","Min Perk Level",1,0, "Text", "4;-1:254");
PlayInfo.AddSetting(default.ServerPerksGroup,"MaxPerksLevel","Max Perk Level",1,0, "Text", "4;0:254");
PlayInfo.AddSetting(default.ServerPerksGroup,"RequirementScaling","Req Scaling",1,0, "Text", "6;0.01:4.00");
PlayInfo.AddSetting(default.ServerPerksGroup,"bForceGivePerk","Force perks",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bNoSavingProgress","No saving",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bAllowAlwaysPerkChanges","Unlimited perk changes",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bNoPerkChanges","No perk changes",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bUseRemoteDatabase","Use remote database",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bUseFTPLink","Use FTP remote database",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"RemoteDatabaseURL","Remote database URL",1,1,"Text","64");
PlayInfo.AddSetting(default.ServerPerksGroup,"RemotePort","Remote database port",1,0, "Text", "5;0:65535");
PlayInfo.AddSetting(default.ServerPerksGroup,"RemotePassword","Remote database password",1,0, "Text", "64");
PlayInfo.AddSetting(default.ServerPerksGroup,"RemoteFTPUser","Remote database user",1,0, "Text", "64");
PlayInfo.AddSetting(default.ServerPerksGroup,"RemoteFTPDir","Remote database dir",1,0, "Text", "64");
PlayInfo.AddSetting(default.ServerPerksGroup,"FTPKeepAliveSec","FTP Keep alive sec",1,0, "Text", "6;0:600");
PlayInfo.AddSetting(default.ServerPerksGroup,"MidGameSaveWaves","MidGame Save Waves",1,0, "Text", "5;0:10");
PlayInfo.AddSetting(default.ServerPerksGroup,"ServerNewsURL","Newspage URL",1,0, "Text", "64");
PlayInfo.AddSetting(default.ServerPerksGroup,"bUsePlayerNameAsID","Use PlayerName as ID",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bMessageAnyPlayerLevelUp","Notify any levelup",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bUseLowestRequirements","Use lowest req",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bBWZEDTime","BW ZED-time",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bUseEnhancedScoreboard","Enhanced scoreboard",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bForceCustomChars","Force Custom Chars",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bEnableChatIcons","Enable chat icons",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bEnhancedShoulderView","Shoulder view",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bFixGrenadeExploit","No Grenade Exploit",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bAdminEditStats","Admin edit stats",1,0, "Check");
PlayInfo.AddSetting(default.ServerPerksGroup,"bEnableWebAdmin","SP WebAdmin",1,0, "Check");
}
static event string GetDescriptionText(string PropName)
{
switch (PropName)
{
case "MinPerksLevel": return "Minimum perk level players can have.";
case "MaxPerksLevel": return "Maximum perk level players can have.";
case "RequirementScaling": return "Perk requirements scaling.";
case "bForceGivePerk": return "Force all players to get at least a random perk if they have none selected.";
case "bNoSavingProgress": return "Server shouldn't save perk progression.";
case "bUseRemoteDatabase": return "Instead of storing perk data locally on server, use remote data storeage server.";
case "bUseFTPLink": return "Use FTP transmission for remote database.";
case "RemoteDatabaseURL": return "URL of the remote database.";
case "RemotePort": return "Port of the remote database.";
case "RemotePassword": return "Password for server to access the remote database.";
case "RemoteFTPUser": return "User name for server to access the remote database (only for FTP mode).";
case "MidGameSaveWaves": return "Between how many waves should it mid-game save stats.";
case "bUsePlayerNameAsID": return "Use PlayerName's as ID instead of ID Hash.";
case "bMessageAnyPlayerLevelUp": return "Broadcast a global message anytime someone gains a perk upgrade.";
case "bUseLowestRequirements": return "Use lowest form of requirements for perks.";
case "bBWZEDTime": return "Make screen go black and white during ZED-time.";
case "bUseEnhancedScoreboard": return "Should use serverperk's own scoreboard.";
case "bAllowAlwaysPerkChanges": return "Allow unlimited perk changes.";
case "bForceCustomChars": return "Force players to use specified custom characters.";
case "bEnableChatIcons": return "Should enable chat icons to replace specific tags in chat.";
case "bEnhancedShoulderView": return "Should enable a more enhanced on shoulder behindview.";
case "bFixGrenadeExploit": return "Should fix unlimited grenades glitch/exploit.";
case "bAdminEditStats": return "Allow admins edit stats through an admin menu.";
case "ServerNewsURL": return "Server newspage menu URL for page contents.";
case "RemoteFTPDir": return "Remote FTP database data storage directory.";
case "FTPKeepAliveSec": return "Keep FTP database connection alive with this many sec interval.";
case "bEnableWebAdmin": return "Enable additional webserver interface.";
case "bNoPerkChanges": return "Never allow players change perk during match.";
}
return Super.GetDescriptionText(PropName);
}
defaultproperties
{
Perks(0)="ServerPerksP.SRVetSupportSpec"
Perks(1)="ServerPerksP.SRVetBerserker"
Perks(2)="ServerPerksP.SRVetCommando"
Perks(3)="ServerPerksP.SRVetFieldMedic"
Perks(4)="ServerPerksP.SRVetFirebug"
Perks(5)="ServerPerksP.SRVetSharpshooter"
Perks(6)="ServerPerksP.SRVetDemolitions"
TraderInventory(0)="5:KFMod.MP7MPickup"
TraderInventory(1)="5:KFMod.BlowerThrowerPickup"
TraderInventory(2)="5:KFMod.MP5MPickup"
TraderInventory(3)="5:KFMod.M7A3MPickup"
TraderInventory(4)="5:KFMod.KrissMPickup"
TraderInventory(5)="2:KFMod.ShotgunPickup"
TraderInventory(6)="2:KFMod.KSGPickup"
TraderInventory(7)="2:KFMod.BoomStickPickup"
TraderInventory(8)="2:KFMod.BenelliPickup"
TraderInventory(9)="2:KFMod.AA12Pickup"
TraderInventory(10)="2:KFMod.NailGunPickup"
TraderInventory(11)="2:KFMod.SPShotGunPickup"
TraderInventory(12)="1:KFMod.DualiesPickup"
TraderInventory(13)="1:KFMod.MK23Pickup"
TraderInventory(14)="1:KFMod.DualMK23Pickup"
TraderInventory(15)="1:KFMod.Magnum44Pickup"
TraderInventory(16)="1:KFMod.Dual44MagnumPickup"
TraderInventory(17)="1:KFMod.DeaglePickup"
TraderInventory(18)="1:KFMod.DualDeaglePickup"
TraderInventory(19)="3:KFMod.WinchesterPickup"
TraderInventory(20)="3:KFMod.CrossbowPickup"
TraderInventory(21)="3:KFMod.M14EBRPickup"
TraderInventory(22)="3:KFMod.M99Pickup"
TraderInventory(23)="3:KFMod.SPSniperPickup"
TraderInventory(24)="4:KFMod.BullpupPickup"
TraderInventory(25)="4:KFMod.AK47Pickup"
TraderInventory(26)="4:KFMod.MKb42Pickup"
TraderInventory(27)="4:KFMod.M4Pickup"
TraderInventory(28)="4:KFMod.SCARMK17Pickup"
TraderInventory(29)="4:KFMod.FNFAL_ACOG_Pickup"
TraderInventory(30)="4:KFMod.ThompsonPickup"
TraderInventory(31)="4:KFMod.SPThompsonPickup"
TraderInventory(32)="4:KFMod.ThompsonDrumPickup"
TraderInventory(33)="0:KFMod.MachetePickup"
TraderInventory(34)="0:KFMod.AxePickup"
TraderInventory(35)="0:KFMod.ChainsawPickup"
TraderInventory(36)="0:KFMod.KatanaPickup"
TraderInventory(37)="0:KFMod.ClaymoreSwordPickup"
TraderInventory(38)="0:KFMod.CrossbuzzsawPickup"
TraderInventory(39)="0:KFMod.ScythePickup"
TraderInventory(40)="0:KFMod.DwarfAxePickup"
TraderInventory(41)="7:KFMod.FlameThrowerPickup"
TraderInventory(42)="2:KFMod.TrenchgunPickup"
TraderInventory(43)="1:KFMod.FlareRevolverPickup"
TraderInventory(44)="1:KFMod.DualFlareRevolverPickup"
TraderInventory(45)="4:KFMod.MAC10Pickup"
TraderInventory(46)="7:KFMod.HuskGunPickup"
TraderInventory(47)="6:KFMod.PipeBombPickup"
TraderInventory(48)="6:KFMod.M79Pickup"
TraderInventory(49)="6:KFMod.M32Pickup"
TraderInventory(50)="4:KFMod.M4203Pickup"
TraderInventory(51)="6:KFMod.SPGrenadePickup"
TraderInventory(52)="6:KFMod.LAWPickup"
TraderInventory(53)="6:KFMod.SealSquealPickup"
TraderInventory(54)="6:KFMod.SeekerSixPickup"
TraderInventory(55)="8:KFMod.ZEDGunPickup"
TraderInventory(56)="8:KFMod.ZEDMKIIPickup"
TraderInventory(57)="2:KFMod.GoldenAA12Pickup"
TraderInventory(58)="4:KFMod.GoldenAK47Pickup"
TraderInventory(59)="2:KFMod.GoldenBenelliPickup"
TraderInventory(60)="0:KFMod.GoldenChainsawPickup"
TraderInventory(61)="1:KFMod.GoldenDeaglePickup"
TraderInventory(62)="1:KFMod.GoldenDualDeaglePickup"
TraderInventory(63)="7:KFMod.GoldenFTPickup"
TraderInventory(64)="0:KFMod.GoldenKatanaPickup"
TraderInventory(65)="6:KFMod.GoldenM79Pickup"
TraderInventory(66)="4:KFMod.CamoMP5MPickup"
TraderInventory(67)="2:KFMod.CamoShotgunPickup"
TraderInventory(68)="4:KFMod.CamoM4Pickup"
TraderInventory(69)="6:KFMod.CamoM32Pickup"
TraderInventory(70)="4:KFMod.NeonAK47Pickup"
TraderInventory(71)="5:KFMod.NeonKrissMPickup"
TraderInventory(72)="2:KFMod.NeonKSGPickup"
TraderInventory(73)="4:KFMod.NeonSCARMK17Pickup"
WeaponCategories(0)="4:Melee"
WeaponCategories(1)="2:Pistol"
WeaponCategories(2)="1:Shotgun"
WeaponCategories(3)="2:Sniper"
WeaponCategories(4)="3:Machine Gun"
WeaponCategories(5)="0:Medic Gun"
WeaponCategories(6)="6:Explosive"
WeaponCategories(7)="5:Flame Thrower"
WeaponCategories(8)="7:Misc"
MaxPerksLevel=6
RemotePort=5000
RequirementScaling=1.000000
RemoteDatabaseURL="192.168.1.33"
RemotePassword="Pass"
RemoteFTPUser="User"
SmileyTags(0)=(iconTexture="ServerPerks.I_Mad",IconTag=">:(")
SmileyTags(1)=(iconTexture="ServerPerks.I_Frown",IconTag=":(")
SmileyTags(2)=(iconTexture="ServerPerks.I_GreenLickB",IconTag=":)")
SmileyTags(3)=(iconTexture="ServerPerks.I_Tongue",IconTag=":P",bCaseInsensitive=True)
SmileyTags(4)=(iconTexture="ServerPerks.I_GreenLick",IconTag=":d")
SmileyTags(5)=(iconTexture="ServerPerks.I_BigGrin",IconTag=":D")
SmileyTags(6)=(iconTexture="ServerPerks.I_Indiffe",IconTag=":|")
SmileyTags(7)=(iconTexture="ServerPerks.I_Ohwell",IconTag=":/")
SmileyTags(8)=(iconTexture="ServerPerks.I_RedFace",IconTag=":*")
SmileyTags(9)=(iconTexture="ServerPerks.I_RedFace",IconTag=":-*")
SmileyTags(10)=(iconTexture="ServerPerks.I_Ban",IconTag="Ban?",bCaseInsensitive=True)
SmileyTags(11)=(iconTexture="ServerPerks.I_Cool",IconTag="B)")
SmileyTags(12)=(iconTexture="ServerPerks.I_Hmm",IconTag="Hmm")
SmileyTags(13)=(iconTexture="ServerPerks.I_Scream",IconTag="XD")
SmileyTags(14)=(iconTexture="ServerPerks.I_Spam",IconTag="SPAM")
VersionNumber=750
ServerPerksGroup="Server Perks"
SRScoreboardType=Class'ServerPerks.SRScoreBoard'
SRHudType=Class'ServerPerks.SRHUDKillingFloor'
SRMenuType=Class'ServerPerks.SRInvasionLoginMenu'
SRControllerType=Class'ServerPerks.KFPCServ'
bUseEnhancedScoreboard=True
bEnableWebAdmin=True
bEnableChatIcons=True
bEnhancedShoulderView=True
bFixGrenadeExploit=True
bAdminEditStats=True
GroupName="KF-Stats"
FriendlyName="Server Veterancy Handler V7"
Description="Use perks as privately on this server config instead of getting from global steam stats."
}

View file

@ -0,0 +1,681 @@
// Written by .:..: (2009)
Class ServerStStats extends SRStatsBase;
var StatsObject MyStatsObject;
var ServerPerksMut MutatorOwner;
var byte SelectingIndex;
var bool bHasChanged,bStatsChecking,bStHasInit,bHadSwitchedVet,bSwitchIsOKNow;
var class<SRVeterancyTypes> SelectingPerk;
function int GetID()
{
return MyStatsObject.ID;
}
function SetID( int ID )
{
MyStatsObject.ID = ID;
}
function PreBeginPlay()
{
local LinkedReplicationInfo L;
PlayerOwner = KFPlayerController(Owner);
if( Rep==None )
{
Rep = Spawn(Class'ClientPerkRepLink',Owner);
Rep.StatObject = Self;
if( PlayerOwner.PlayerReplicationInfo.CustomReplicationInfo==None )
PlayerOwner.PlayerReplicationInfo.CustomReplicationInfo = Rep;
else
{
for( L=PlayerOwner.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
if( L.NextReplicationInfo==None )
{
L.NextReplicationInfo = Rep;
break;
}
}
}
Super.PreBeginPlay();
}
function PostBeginPlay()
{
if( Rep!=None )
Rep.SpawnCustomLinks();
bStatsReadyNow = MutatorOwner.bNoSavingProgress || !MutatorOwner.bUseRemoteDatabase;
MyStatsObject = MutatorOwner.GetStatsForPlayer(PlayerOwner);
if( MyStatsObject!=None && MyStatsObject.bStatsChanged )
bStatsReadyNow = true; // Server has already the most fresh stats for this client.
KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill = None;
if( !bStatsReadyNow )
{
Timer();
SetTimer(1+FRand(),true);
return;
}
bSwitchIsOKNow = true;
if( MyStatsObject!=None )
{
// Apply selected character.
if( MyStatsObject.SelectedChar!="" )
ApplyCharacter(MyStatsObject.SelectedChar);
else if( Rep.bNoStandardChars && Rep.CustomChars.Length>0 )
{
MyStatsObject.SelectedChar = Rep.PickRandomCustomChar();
ApplyCharacter(MyStatsObject.SelectedChar);
}
RepCopyStats();
bHasChanged = true;
CheckPerks(true);
SelectingIndex = MyStatsObject.PerkIndex;
if( MutatorOwner.bNoPerkChanges && MyStatsObject.ChosenPerk!=None )
ServerSelectPerk(MyStatsObject.ChosenPerk);
else ServerSelectPerkName(MyStatsObject.GetSelectedPerk());
SelectingIndex = 255;
}
else CheckPerks(true);
if( MutatorOwner.bForceGivePerk && KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill==None )
ServerSelectPerk(Rep.PickRandomPerk());
bSwitchIsOKNow = false;
bHadSwitchedVet = false;
SetTimer(0.1,false);
}
function ApplyCharacter( string CN )
{
PlayerOwner.PawnSetupRecord = class'xUtil'.static.FindPlayerRecord(CN);
if( PlayerOwner.PawnSetupRecord.VoiceClassName!="" )
PlayerOwner.PlayerReplicationInfo.SetCharacterVoice(PlayerOwner.PawnSetupRecord.VoiceClassName);
PlayerOwner.PlayerReplicationInfo.SetCharacterName(CN);
}
function ChangeCharacter( string CN )
{
local bool bCustom;
bCustom = Rep.IsCustomCharacter(CN);
if( !bCustom )
{
if( Rep.bNoStandardChars && Rep.CustomChars.Length>0 ) // Prevent from switching to a standard character.
{
CN = Rep.PickRandomCustomChar();
bCustom = true;
}
else CN = Class'KFGameType'.Static.GetValidCharacter(CN); // Make sure client doesn't chose some invalid character.
}
ApplyCharacter(CN);
if( !bCustom ) // Was not a custom character, don't save client char on server.
CN = "";
if( MyStatsObject!=None )
MyStatsObject.SetSelectedChar(CN);
}
function GetData( string D )
{
bStatsReadyNow = true;
MyStatsObject.SetSaveData(D);
RepCopyStats();
bHasChanged = true;
bSwitchIsOKNow = true;
// Apply selected character.
if( MyStatsObject.SelectedChar!="" )
ApplyCharacter(MyStatsObject.SelectedChar);
else if( Rep.bNoStandardChars && Rep.CustomChars.Length>0 )
{
MyStatsObject.SelectedChar = Rep.PickRandomCustomChar();
ApplyCharacter(MyStatsObject.SelectedChar);
}
CheckPerks(true);
SelectingIndex = MyStatsObject.PerkIndex;
ServerSelectPerkName(MyStatsObject.GetSelectedPerk());
SelectingIndex = 255;
Rep.SendClientPerks();
if( MutatorOwner.bForceGivePerk && KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill==None )
ServerSelectPerk(Rep.PickRandomPerk());
bHadSwitchedVet = false;
bSwitchIsOKNow = false;
SetTimer(0.1,false);
}
function Timer()
{
if( !bStatsReadyNow )
{
MutatorOwner.RequestStats(Self);
return;
}
if( !bStHasInit )
{
if( PlayerOwner.SteamStatsAndAchievements!=None && PlayerOwner.SteamStatsAndAchievements!=Self )
PlayerOwner.SteamStatsAndAchievements.Destroy();
PlayerOwner.SteamStatsAndAchievements = Self;
PlayerOwner.PlayerReplicationInfo.SteamStatsAndAchievements = Self;
bStHasInit = true;
Rep.SendClientPerks();
}
if( bStatsChecking )
{
bStatsChecking = false;
CheckPerks(false);
}
}
function RepCopyStats()
{
// Copy all stats from data object for client replication
Rep.RDamageHealedStat = MyStatsObject.DamageHealedStat;
Rep.RWeldingPointsStat = MyStatsObject.WeldingPointsStat;
Rep.RShotgunDamageStat = MyStatsObject.ShotgunDamageStat;
Rep.RHeadshotKillsStat = MyStatsObject.HeadshotKillsStat;
Rep.RStalkerKillsStat = MyStatsObject.StalkerKillsStat;
Rep.RBullpupDamageStat = MyStatsObject.BullpupDamageStat;
Rep.RMeleeDamageStat = MyStatsObject.MeleeDamageStat;
Rep.RFlameThrowerDamageStat = MyStatsObject.FlameThrowerDamageStat;
Rep.RSelfHealsStat = MyStatsObject.SelfHealsStat;
Rep.RSoleSurvivorWavesStat = MyStatsObject.SoleSurvivorWavesStat;
Rep.RCashDonatedStat = MyStatsObject.CashDonatedStat;
Rep.RFeedingKillsStat = MyStatsObject.FeedingKillsStat;
Rep.RBurningCrossbowKillsStat = MyStatsObject.BurningCrossbowKillsStat;
Rep.RGibbedFleshpoundsStat = MyStatsObject.GibbedFleshpoundsStat;
Rep.RStalkersKilledWithExplosivesStat = MyStatsObject.StalkersKilledWithExplosivesStat;
Rep.RGibbedEnemiesStat = MyStatsObject.GibbedEnemiesStat;
Rep.RBloatKillsStat = MyStatsObject.BloatKillsStat;
Rep.RTotalZedTimeStat = MyStatsObject.TotalZedTimeStat;
Rep.RSirenKillsStat = MyStatsObject.SirenKillsStat;
Rep.RKillsStat = MyStatsObject.KillsStat;
Rep.RExplosivesDamageStat = MyStatsObject.ExplosivesDamageStat;
Rep.TotalPlayTime = MyStatsObject.TotalPlayTime;
Rep.WinsCount = MyStatsObject.WinsCount;
Rep.LostsCount = MyStatsObject.LostsCount;
MyStatsObject.GetCustomValues(Rep.CustomLink);
}
final function ForcePerkChange( Class<SRVeterancyTypes> VetType )
{
bSwitchIsOKNow = true;
ServerSelectPerk(VetType);
bSwitchIsOKNow = false;
}
function ServerSelectPerkName( name N )
{
local byte i,Index;
if( N=='' )
return;
Index = 255;
for( i=0; i<Rep.CachePerks.Length; i++ )
{
if( Rep.CachePerks[i].PerkClass.Name==N )
{
ServerSelectPerk(Rep.CachePerks[i].PerkClass);
return;
}
else if( Rep.CachePerks[i].PerkClass.Default.PerkIndex==SelectingIndex )
Index = i;
}
if( Index!=255 )
ServerSelectPerk(Rep.CachePerks[Index].PerkClass); // Fallback to using saved index.
}
function ServerSelectPerk( Class<SRVeterancyTypes> VetType )
{
local byte i;
if( !bStatsReadyNow )
return;
if( VetType==None || !SelectionOK(VetType) )
{
if( !bSwitchIsOKNow )
PlayerOwner.ClientMessage("Your desired perk is unavailable.");
return;
}
else if( KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill==VetType )
{
if( SelectingPerk!=None )
{
SelectingPerk = None;
PlayerOwner.ClientMessage("You will remain the same perk now.");
}
return;
}
if( MyStatsObject!=None )
{
MyStatsObject.ChosenPerk = VetType;
MyStatsObject.SetSelectedPerk(VetType.Name);
MyStatsObject.PerkIndex = VetType.Default.PerkIndex;
}
if( !Level.Game.bWaitingToStartMatch && !bSwitchIsOKNow && !MutatorOwner.bAllowAlwaysPerkChanges )
{
if( MutatorOwner.bNoPerkChanges && KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill!=None )
{
PlayerOwner.ClientMessage("You are not allowed to change perk during game.");
return;
}
if( PlayerOwner.Pawn!=None )
{
if( KFGameReplicationInfo(Level.GRI).bWaveInProgress || bHadSwitchedVet )
i = 1;
else
{
bHadSwitchedVet = true;
i = 0;
}
if( i==1 )
{
PlayerOwner.ClientMessage(Repl(PlayerOwner.YouWillBecomePerkString, "%Perk%", VetType.Default.VeterancyName));
SelectingPerk = VetType;
PlayerOwner.SelectedVeterancy = VetType; // Force KFGameType update this.
return;
}
}
}
SelectingPerk = None;
for( i=0; i<Rep.CachePerks.Length; i++ )
{
if( Rep.CachePerks[i].PerkClass==VetType )
{
if( Rep.CachePerks[i].CurrentLevel==0 )
return;
PlayerOwner.SelectedVeterancy = VetType;
KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill = VetType;
KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkillLevel = Rep.CachePerks[i].CurrentLevel-1;
if( KFHumanPawn(PlayerOwner.Pawn)!=None )
{
KFHumanPawn(PlayerOwner.Pawn).VeterancyChanged();
DropToCarryLimit(KFHumanPawn(PlayerOwner.Pawn));
}
}
}
}
function DropToCarryLimit( KFHumanPawn P )
{
local byte c;
local Inventory I;
while( P.CurrentWeight>P.MaxCarryWeight && ++c<6 )
{
for ( I = P.Inventory; I != none; I = I.Inventory )
{
if ( KFWeapon(I) != none && !KFWeapon(I).bKFNeverThrow )
{
I.Velocity = P.Velocity;
I.DropFrom(P.Location + VRand() * 10);
break; // Drop weapons until player is capable of carrying them all.
}
}
}
}
function bool SelectionOK( Class<SRVeterancyTypes> VetType )
{
local byte i;
for( i=0; i<Rep.CachePerks.Length; i++ )
{
if( Rep.CachePerks[i].PerkClass==VetType )
return (Rep.CachePerks[i].CurrentLevel>0);
}
return false;
}
function CheckPerks( bool bInit )
{
local byte i,NewLevel;
if( !bStatsReadyNow )
return;
if( bInit )
{
for( i=0; i<Rep.CachePerks.Length; i++ )
Rep.CachePerks[i].CurrentLevel = Rep.CachePerks[i].PerkClass.Static.PerkIsAvailable(Rep);
return;
}
for( i=0; i<Rep.CachePerks.Length; i++ )
{
NewLevel = Rep.CachePerks[i].CurrentLevel;
while( NewLevel<Rep.MaximumLevel && Rep.CachePerks[i].PerkClass.Static.LevelIsFinished(Rep,NewLevel) )
++NewLevel;
if( NewLevel>Rep.CachePerks[i].CurrentLevel )
{
if( KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill==Rep.CachePerks[i].PerkClass )
KFPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo).ClientVeteranSkillLevel = NewLevel-1;
Rep.CachePerks[i].CurrentLevel = NewLevel;
Rep.ClientPerkLevel(i,NewLevel);
if( MutatorOwner.bMessageAnyPlayerLevelUp )
BroadcastLocalizedMessage(Class'KFVetEarnedMessagePL',(NewLevel-1),PlayerOwner.PlayerReplicationInfo,,Rep.CachePerks[i].PerkClass);
}
}
}
final function DelayedStatCheck()
{
if( MyStatsObject!=None )
MyStatsObject.bStatsChanged = true;
if( bStatsChecking || !bStatsReadyNow )
return;
bStatsChecking = true;
SetTimer(1,false);
}
function NotifyStatChanged()
{
bHasChanged = true;
DelayedStatCheck();
}
function MatchEnded()
{
}
function AddDamageHealed(int Amount, optional bool bMP7MHeal, optional bool bMP5MHeal)
{
bHasChanged = true;
Rep.RDamageHealedStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.DamageHealedStat+=Amount;
DelayedStatCheck();
}
function AddWeldingPoints(int Amount)
{
bHasChanged = true;
Rep.RWeldingPointsStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.WeldingPointsStat+=Amount;
DelayedStatCheck();
}
function AddShotgunDamage(int Amount)
{
bHasChanged = true;
Rep.RShotgunDamageStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.ShotgunDamageStat+=Amount;
DelayedStatCheck();
}
function AddHeadshotKill(bool bLaserSightedEBRHeadshot)
{
bHasChanged = true;
Rep.RHeadshotKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.HeadshotKillsStat++;
DelayedStatCheck();
}
function AddStalkerKill()
{
bHasChanged = true;
Rep.RStalkerKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.StalkerKillsStat++;
DelayedStatCheck();
}
function AddBullpupDamage(int Amount)
{
bHasChanged = true;
Rep.RBullpupDamageStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.BullpupDamageStat+=Amount;
DelayedStatCheck();
}
function AddMeleeDamage(int Amount)
{
bHasChanged = true;
Rep.RMeleeDamageStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.MeleeDamageStat+=Amount;
DelayedStatCheck();
}
function AddFlameThrowerDamage(int Amount)
{
bHasChanged = true;
Rep.RFlameThrowerDamageStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.FlameThrowerDamageStat+=Amount;
DelayedStatCheck();
}
function WaveEnded()
{
if( SelectingPerk!=None )
{
bSwitchIsOKNow = true;
ServerSelectPerk(SelectingPerk);
bSwitchIsOKNow = false;
bHadSwitchedVet = true;
}
else bHadSwitchedVet = false;
}
function MatchStarting()
{
bHadSwitchedVet = false;
}
function AddKill(bool bLaserSightedEBRM14Headshotted, bool bMeleeKill, bool bZEDTimeActive, bool bM4Kill, bool bBenelliKill, bool bRevolverKill, bool bMK23Kill, bool bFNFalKill, bool bBullpupKill, string MapName)
{
bHasChanged = true;
Rep.RKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.KillsStat++;
DelayedStatCheck();
}
function AddBloatKill(bool bWithBullpup)
{
bHasChanged = true;
Rep.RBloatKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.BloatKillsStat++;
DelayedStatCheck();
}
function AddSirenKill(bool bLawRocketImpact)
{
bHasChanged = true;
Rep.RSirenKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.SirenKillsStat++;
DelayedStatCheck();
}
function AddStalkerKillWithExplosives()
{
bHasChanged = true;
Rep.RStalkersKilledWithExplosivesStat++;
if( MyStatsObject!=None )
MyStatsObject.StalkersKilledWithExplosivesStat++;
DelayedStatCheck();
}
function AddFireAxeKill();
function AddChainsawScrakeKill();
function AddBurningCrossbowKill()
{
bHasChanged = true;
Rep.RBurningCrossbowKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.BurningCrossbowKillsStat++;
DelayedStatCheck();
}
function AddFeedingKill()
{
bHasChanged = true;
Rep.RFeedingKillsStat++;
if( MyStatsObject!=None )
MyStatsObject.FeedingKillsStat++;
DelayedStatCheck();
}
function OnGrenadeExploded();
function AddGrenadeKill();
function OnShotHuntingShotgun();
function AddHuntingShotgunKill();
function KilledEnemyWithBloatAcid();
function KilledFleshpound(bool bWithMeleeAttack, bool bWithAA12, bool bWithKnife, bool bWithClaymore);
function AddMedicKnifeKill();
function AddGibKill(bool bWithM79)
{
bHasChanged = true;
Rep.RGibbedEnemiesStat++;
if( MyStatsObject!=None )
MyStatsObject.GibbedEnemiesStat++;
DelayedStatCheck();
}
function AddFleshpoundGibKill()
{
bHasChanged = true;
Rep.RGibbedFleshpoundsStat++;
if( MyStatsObject!=None )
MyStatsObject.GibbedFleshpoundsStat++;
DelayedStatCheck();
}
function AddSelfHeal()
{
bHasChanged = true;
Rep.RSelfHealsStat++;
if( MyStatsObject!=None )
MyStatsObject.SelfHealsStat++;
DelayedStatCheck();
}
function AddOnlySurvivorOfWave()
{
bHasChanged = true;
Rep.RSoleSurvivorWavesStat++;
if( MyStatsObject!=None )
MyStatsObject.SoleSurvivorWavesStat++;
DelayedStatCheck();
}
function AddDonatedCash(int Amount)
{
bHasChanged = true;
Rep.RCashDonatedStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.CashDonatedStat+=Amount;
DelayedStatCheck();
}
function AddZedTime(float Amount)
{
bHasChanged = true;
Rep.RTotalZedTimeStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.TotalZedTimeStat+=Amount;
}
function AddExplosivesDamage(int Amount)
{
bHasChanged = true;
Rep.RExplosivesDamageStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.ExplosivesDamageStat+=Amount;
}
function AddHeadshotKills(int Amount)
{
bHasChanged = true;
Rep.RHeadshotKillsStat+= Amount;
if( MyStatsObject!=None )
MyStatsObject.HeadshotKillsStat+= Amount;
DelayedStatCheck();
}
function AddStalkerKills(int Amount)
{
bHasChanged = true;
Rep.RStalkerKillsStat+=Amount;
if( MyStatsObject!=None )
MyStatsObject.StalkerKillsStat+= Amount;
DelayedStatCheck();
}
function WonLostGame( bool bDidWin )
{
if( bDidWin )
{
++Rep.WinsCount;
if( MyStatsObject!=None )
++MyStatsObject.WinsCount;
}
else
{
++Rep.LostsCount;
if( MyStatsObject!=None )
++MyStatsObject.LostsCount;
}
bHasChanged = true;
DelayedStatCheck();
GoToState('');
}
// Allow no default functionality with the stats.
function OnStatsAndAchievementsReady();
function PostNetBeginPlay();
function InitializeSteamStatInt(int Index, int Value);
function SetSteamAchievementCompleted(int Index);
event SetLocalAchievementCompleted(int Index);
function ServerSteamStatsAndAchievementsInitialized();
function UpdateAchievementProgress();
function int GetAchievementCompletedCount();
event OnPerkAvailable();
function WonLongGame(string MapName, float Difficulty);
function AddDemolitionsPipebombKill();
function AddSCARKill();
function AddCrawlerKilledInMidair();
function Killed8ZedsWithGrenade();
function Killed10ZedsWithPipebomb();
function KilledHusk(bool bDamagedFriendly);
function AddMac10BurnDamage(int Amount);
function AddGorefastBackstab();
function ScrakeKilledByFire();
function KilledCrawlerWithCrossbow();
function OnLARReloaded();
function AddStalkerKillWithLAR();
function KilledHuskWithPistol();
function AddDroppedTier3Weapon();
function Survived10SecondsAfterVomit();
function CheckChristmasAchievementsCompleted();
function PlayerDied();
function KilledPatriarch(bool bPatriarchHealed, bool bKilledWithLAW, bool bSuicidalDifficulty, bool bOnlyUsedCrossbows, bool bClaymore, string MapName);
function WonGame(string MapName, float Difficulty, bool bLong);
function Destroyed()
{
if( Rep!=None )
{
if( MyStatsObject!=None )
MyStatsObject.SetCustomValues(Rep.CustomLink);
Rep.Destroy();
Rep = None;
}
if( PlayerOwner!=None && !PlayerOwner.bDeleteMe )
{
// Was destroyed mid-game for random reason, respawn.
MutatorOwner.PendingPlayers[MutatorOwner.PendingPlayers.Length] = PlayerOwner;
MutatorOwner.SetTimer(0.1,false);
}
Super.Destroyed();
}
Auto state PlaytimeTimer
{
Begin:
while( true )
{
Sleep(1.f);
if( bStatsReadyNow && !PlayerOwner.PlayerReplicationInfo.bOnlySpectator )
{
++Rep.TotalPlayTime;
if( MyStatsObject!=None )
++MyStatsObject.TotalPlayTime;
}
}
}
defaultproperties
{
SelectingIndex=255
}

View file

@ -0,0 +1,20 @@
// Because this is most likely going to fail on init, spawn the real stats actor here...
Class ServerTempStats extends KFSteamStatsAndAchievements;
function PostBeginPlay()
{
local KFPlayerController PlayerOwner;
PlayerOwner = KFPlayerController(Owner);
if( PlayerOwner.Player==None )
return; // very bad...
Spawn(Class'ServerStStats',PlayerOwner);
}
defaultproperties
{
bUsedCheats=True
RemoteRole=ROLE_None
LifeSpan=0.100000
bNetNotify=False
}

View file

@ -0,0 +1,132 @@
class StatConvert extends Commandlet;
const MagicNumber="76561197960265728";
const MagicLength=12;
function int Main( string Parms )
{
local array<string> List;
local int i,j,z;
local StatsObject A,B;
local string NewID;
List = GetPerObjectNames("ServerPerksStat","StatsObject",9999999);
for( i=0; i<List.Length; ++i )
{
if( !IsLinuxID(List[i]) )
{
Log("Skip "$List[i],'Log');
continue;
}
NewID = ToWindowsID(List[i]);
if( NewID=="" )
{
Log("Skip Unknown ID "$List[i],'Log');
continue;
}
A = new(None,List[i]) Class'StatsObject';
B = new(None,NewID) Class'StatsObject';
Log("Port "$List[i]$" ("$A.PlayerName$") -> "$NewID$" ("$B.PlayerName$")",'Log');
if( B.PlayerName=="" )
{
B.PlayerName = A.PlayerName;
B.PlayerIP = A.PlayerIP;
B.SelectedVeterancy = A.SelectedVeterancy;
B.SelectedChar = A.SelectedChar;
}
B.DamageHealedStat+=A.DamageHealedStat;
B.WeldingPointsStat+=A.WeldingPointsStat;
B.ShotgunDamageStat+=A.ShotgunDamageStat;
B.HeadshotKillsStat+=A.HeadshotKillsStat;
B.StalkerKillsStat+=A.StalkerKillsStat;
B.BullpupDamageStat+=A.BullpupDamageStat;
B.MeleeDamageStat+=A.MeleeDamageStat;
B.FlameThrowerDamageStat+=A.FlameThrowerDamageStat;
B.SelfHealsStat+=A.SelfHealsStat;
B.SoleSurvivorWavesStat+=A.SoleSurvivorWavesStat;
B.CashDonatedStat+=A.CashDonatedStat;
B.FeedingKillsStat+=A.FeedingKillsStat;
B.BurningCrossbowKillsStat+=A.BurningCrossbowKillsStat;
B.GibbedFleshpoundsStat+=A.GibbedFleshpoundsStat;
B.StalkersKilledWithExplosivesStat+=A.StalkersKilledWithExplosivesStat;
B.GibbedEnemiesStat+=A.GibbedEnemiesStat;
B.BloatKillsStat+=A.BloatKillsStat;
B.SirenKillsStat+=A.SirenKillsStat;
B.KillsStat+=A.KillsStat;
B.ExplosivesDamageStat+=A.ExplosivesDamageStat;
B.TotalPlayTime+=A.TotalPlayTime;
B.WinsCount+=A.WinsCount;
B.LostsCount+=A.LostsCount;
B.TotalZedTimeStat+=A.TotalZedTimeStat;
// Merge custom stats.
for( j=(A.CC.Length-1); j>=0; --j )
{
for( z=(B.CC.Length-1); z>=0; --z )
if( B.CC[z].N==A.CC[j].N )
break;
if( z==-1 )
{
z = B.CC.Length;
B.CC.Length = z+1;
B.CC[z].N = A.CC[j].N;
B.CC[z].V = A.CC[j].V;
}
else if( IsDigitStr(B.CC[z].V) && IsDigitStr(A.CC[j].V) )
B.CC[z].V = string(int(B.CC[z].V) + int(A.CC[j].V));
else B.CC[z].V = A.CC[j].V;
}
A.ClearConfig();
B.SaveConfig();
}
return 0;
}
static final function bool IsLinuxID( string S )
{
return (Len(S)<MagicLength);
}
static final function string ToWindowsID( string S )
{
local int i,la,lb,rest,p;
local string Result;
la = Len(S);
lb = Len(MagicNumber);
for( i=1; i<=lb; ++i )
{
if( i<=la )
{
if( !IsDigit(Asc(Mid(S,la-i,1))) ) // Not a SteamID, something else here.
return "";
p = int(Mid(S,la-i,1));
}
else p = 0;
p += rest + int(Mid(MagicNumber,lb-i,1));
if( p>=10 )
{
rest = p/10;
p -= rest*10;
}
else rest = 0;
Result = string(p)$Result;
}
if( rest>0 )
Result = string(rest)$Result;
return Result;
}
static final function bool IsDigit( int S )
{
return (S>=48 && S<=57);
}
static final function bool IsDigitStr( string S )
{
return (string(int(S))==S);
}
defaultproperties
{
HelpCmd="Convert ServerPerks stats LinuxID to WindowsID."
HelpUsage="Run UCC.exe ServerPerksMut.StatConvert /norunaway"
HelpWebLink="http://forums.tripwireinteractive.com/showthread.php?t=36065"
IsServer=False
IsClient=False
}

View file

@ -0,0 +1,203 @@
// Written by .:..: (2009)
Class StatsObject extends Object
PerObjectConfig
Config(ServerPerksStat);
struct FCustomConfig
{
var config name N;
var config string V;
};
var config array<FCustomConfig> CC;
var config string PlayerName,PlayerIP,SelectedVeterancy,SelectedChar;
var config int DamageHealedStat, WeldingPointsStat, ShotgunDamageStat, HeadshotKillsStat,
StalkerKillsStat, BullpupDamageStat, MeleeDamageStat, FlameThrowerDamageStat,
SelfHealsStat, SoleSurvivorWavesStat, CashDonatedStat, FeedingKillsStat,
BurningCrossbowKillsStat, GibbedFleshpoundsStat, StalkersKilledWithExplosivesStat,
GibbedEnemiesStat, BloatKillsStat, SirenKillsStat, KillsStat, ExplosivesDamageStat, TotalPlayTime, WinsCount, LostsCount;
var config byte PerkIndex;
var config float TotalZedTimeStat;
var transient class<SRVeterancyTypes> ChosenPerk;
var name StrToNameProp;
var int ID;
var bool bStatsChanged;
final function name StringToName( string S )
{
if( S=="" )
return '';
SetPropertyText("StrToNameProp",S);
return StrToNameProp;
}
final function SetCustomValues( SRCustomProgress L )
{
local int i,c;
if( Class'ServerPerksMut'.Default.bOverrideUnusedCustomStats )
CC.Length = 0;
c = CC.Length;
for( L=L; L!=None; L=L.NextLink )
{
// Look for excisting entry.
for( i=0; i<c; ++i )
if( CC[i].N==L.Class.Name )
{
CC[i].V = L.GetProgress();
break;
}
// Add new entry.
if( i==c )
{
CC.Length = c+1;
CC[c].N = L.Class.Name;
CC[c].V = L.GetProgress();
++c;
}
}
}
final function GetCustomValues( SRCustomProgress L )
{
local int i,c;
c = CC.Length;
for( L=L; L!=None; L=L.NextLink )
{
for( i=0; i<c; ++i )
if( CC[i].N==L.Class.Name )
{
L.SetProgress(CC[i].V);
break;
}
}
}
final function SetSelectedChar( string S )
{
SelectedChar = S;
bStatsChanged = true;
}
final function SetSelectedPerk( name S )
{
SelectedVeterancy = string(S);
}
final function name GetSelectedPerk()
{
local int i;
i = InStr(SelectedVeterancy,".");
if( i>=0 )
SelectedVeterancy = Mid(SelectedVeterancy,i+1);
return StringToName(SelectedVeterancy);
}
final function string GetSaveData()
{
local string Result;
Result = SelectedVeterancy$":"$PerkIndex$","$DamageHealedStat$","$WeldingPointsStat$","$ShotgunDamageStat$","$HeadshotKillsStat$","$StalkerKillsStat;
Result = Result$","$BullpupDamageStat$","$MeleeDamageStat$","$FlameThrowerDamageStat$","$SelfHealsStat$","$SoleSurvivorWavesStat;
Result = Result$","$CashDonatedStat$","$FeedingKillsStat$","$BurningCrossbowKillsStat$","$GibbedFleshpoundsStat$","$StalkersKilledWithExplosivesStat;
Result = Result$","$GibbedEnemiesStat$","$BloatKillsStat$","$SirenKillsStat$","$KillsStat$","$ExplosivesDamageStat;
Result = Result$","$int(TotalZedTimeStat)$","$TotalPlayTime$","$WinsCount$","$LostsCount$",'"$SelectedChar$"'";
Result = Result$","$GetPropertyText("CC");
return Result;
}
static final function int GetNextValue( out string S )
{
local int i,Result;
i = InStr(S,",");
if( i==-1 )
{
Result = int(S);
S = "";
}
else
{
Result = int(Left(S,i));
S = Mid(S,i+1);
}
return Result;
}
static final function string GetNextStrValue( out string S )
{
local int i;
local string Result;
i = InStr(S,",");
if( i==-1 )
{
if( Left(S,1)=="'" )
Result = Mid(S,1,Len(S)-2);
//else bad string.
S = "";
}
else if( Left(S,1)=="'" && Mid(S,i-1,1)=="'" )
{
Result = Mid(S,1,i-2);
S = Mid(S,i+1);
}
else // bad string again.
{
S = Mid(S,i+1);
}
return Result;
}
final function SetSaveData( string S )
{
local int i;
i = InStr(S,",");
if( i==-1 )
return;
SelectedVeterancy = Left(S,i);
S = Mid(S,i+1);
i = InStr(SelectedVeterancy,":");
if( i==-1 )
PerkIndex = 255;
else
{
PerkIndex = int(Mid(SelectedVeterancy,i+1));
SelectedVeterancy = Left(SelectedVeterancy,i);
}
DamageHealedStat = GetNextValue(S);
WeldingPointsStat = GetNextValue(S);
ShotgunDamageStat = GetNextValue(S);
HeadshotKillsStat = GetNextValue(S);
StalkerKillsStat = GetNextValue(S);
BullpupDamageStat = GetNextValue(S);
MeleeDamageStat = GetNextValue(S);
FlameThrowerDamageStat = GetNextValue(S);
SelfHealsStat = GetNextValue(S);
SoleSurvivorWavesStat = GetNextValue(S);
CashDonatedStat = GetNextValue(S);
FeedingKillsStat = GetNextValue(S);
BurningCrossbowKillsStat = GetNextValue(S);
GibbedFleshpoundsStat = GetNextValue(S);
StalkersKilledWithExplosivesStat = GetNextValue(S);
GibbedEnemiesStat = GetNextValue(S);
BloatKillsStat = GetNextValue(S);
SirenKillsStat = GetNextValue(S);
KillsStat = GetNextValue(S);
ExplosivesDamageStat = GetNextValue(S);
TotalZedTimeStat = GetNextValue(S);
TotalPlayTime = GetNextValue(S);
WinsCount = GetNextValue(S);
LostsCount = GetNextValue(S);
SelectedChar = GetNextStrValue(S);
CC.Length = 0;
if( Len(S)>0 )
SetPropertyText("CC",S);
}
defaultproperties
{
PerkIndex=255
id=-1
}

View file

@ -0,0 +1,18 @@
<html>
<head><title>Index of /kf_sources/ServerPerksMut/Classes/</title></head>
<body>
<h1>Index of /kf_sources/ServerPerksMut/Classes/</h1><hr><pre><a href="../">../</a>
<a href="AdminMenuHandle.uc">AdminMenuHandle.uc</a> 06-Oct-2019 10:31 27582
<a href="DatabaseUdpLink.uc">DatabaseUdpLink.uc</a> 06-Oct-2019 10:31 4557
<a href="FTPDataConnection.uc">FTPDataConnection.uc</a> 06-Oct-2019 10:31 759
<a href="FTPTcpLink.uc">FTPTcpLink.uc</a> 06-Oct-2019 10:31 15614
<a href="FragFireFix.uc">FragFireFix.uc</a> 06-Oct-2019 10:31 281
<a href="SRGameRules.uc">SRGameRules.uc</a> 06-Oct-2019 10:31 1638
<a href="ServerPerkWebAdmin.uc">ServerPerkWebAdmin.uc</a> 06-Oct-2019 10:31 8657
<a href="ServerPerksMut.uc">ServerPerksMut.uc</a> 06-Oct-2019 10:31 30591
<a href="ServerStStats.uc">ServerStStats.uc</a> 06-Oct-2019 10:31 18774
<a href="ServerTempStats.uc">ServerTempStats.uc</a> 06-Oct-2019 10:31 495
<a href="StatConvert.uc">StatConvert.uc</a> 06-Oct-2019 10:31 3572
<a href="StatsObject.uc">StatsObject.uc</a> 06-Oct-2019 10:31 5094
</pre><hr></body>
</html>