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

314 lines
8.0 KiB
Ucode

//for storing long numerical data that can't fit inside int
Class ScrnCustomProgressBytes extends SRCustomProgress
Abstract;
const CHR_MAX_VAL = 0x3FFFFFC0; //don't use 6 lowest and 2 highest bits in unicode character (16-bit)
const CHR_STEP = 0x40; // 0x40 = 1 >>> 6
const CHR_1ST_BIT = 6;
const CHR_CONTROL_VALUE = 1; // highest bits of a character must be "01"
const MAX_SIZE = 512;
// most left char contains Data[0], right - Data[Data.length-1]
var private string OldValueStr; // previous value that client received
var private string CurrentValueStr; // value to store in perks DB
var protected array<byte> Data;
var protected bool bFixedDataSize; //if true, Data.Length won't be modified (sublasses need to overrire InitData() to it)
var protected bool bReplicateDataToClients; // if true, CurrentValueStr will be always replicated to clients
//reason why CurrentValueStr has been updated
enum EValueSource
{
SRC_None,
SRC_External, // updated from outer source (e.g. perk DB)
SRC_Internal // updated from Data
};
var private transient EValueSource ValueSource;
enum EStringStoreMethod
{
SM_Hex, //store data as string representation of hex values (2 chars per byte, but data is human-readable)
SM_Unicode //store data inside unicode character (1 char per byte, but human will see crazy unicode chars instead of data)
};
var protected EStringStoreMethod StoreMethod;
replication
{
reliable if( bReplicateDataToClients && bNetOwner && ( bNetDirty || bNetInitial ) && Role==ROLE_Authority )
ValueSource, CurrentValueStr;
}
simulated function PostNetReceive()
{
super.PostNetReceive();
if ( Role < Role_Authority && OldValueStr != CurrentValueStr ) {
//log(GetItemName(String(name))$".PostNetReceive() OldValueStr="$OldValueStr $ " CurrentValueStr="$CurrentValueStr, 'ScrnBalance');
UpdateData();
OldValueStr = CurrentValueStr;
}
}
simulated function EValueSource GetValueSource()
{
return ValueSource;
}
simulated function PostBeginPlay()
{
super.PostBeginPlay();
InitData();
}
simulated function InitData();
simulated function string GetProgress()
{
return CurrentValueStr;
}
simulated function int GetProgressInt()
{
return 0;
}
simulated function string GetDisplayString()
{
return class'SRCustomProgress'.default.ProgressName; //Lazy modder! Kill him!! :)
}
function SetProgress( string S )
{
if ( Len(S) > MAX_SIZE) {
log("ScrnCustomProgressBytes: Passed string is longer than " $ String(MAX_SIZE), 'ScrnBalance');
S = left(S, MAX_SIZE); //trim long string
}
//log("SetProgress("$S$")", 'ScrnBalance');
CurrentValueStr = S;
ValueSource = SRC_External;
if ( bReplicateDataToClients ) {
NetUpdateTime = Level.TimeSeconds - 1; //send CurrentValueStr to clients
}
UpdateData();
}
function IncrementProgress( int Count );
static function String ByteToBin(byte AValue)
{
local int i;
local string s;
while ( i++ < 8 ) {
S = String(AValue & 1) $ S;
AValue = AValue >>> 1;
}
return s;
}
static function String ByteToHex(byte AValue, optional bool bAddPrefix)
{
local int i;
local string s,c;
local byte a;
while ( i++ < 2 ) {
a = AValue & 0xF;
if ( a < 10 )
c = chr(0x30 + a);
else
c = chr(0x41 + a-10);
S = c $ S;
AValue = AValue >>> 4;
}
if ( bAddPrefix )
S = "0x" $ S;
return S;
}
static function String IntToHex(int AValue, optional bool bAddPrefix)
{
local int i;
local string s,c;
local byte a;
while ( i++ < 8 ) {
a = AValue & 0xF;
if ( a < 10 )
c = chr(0x30 + a);
else
c = chr(0x41 + a-10);
S = c $ S;
AValue = AValue >>> 4;
}
if ( bAddPrefix )
S = "0x" $ S;
return S;
}
static function byte HexToByte(String S)
{
local int i, end, a;
local byte result;
if ( left(S, 2) ~= "0x" )
i = 2; //skip prefix
end = min(i + 2, len(S));
while ( i < end ) {
result = result << 4;
a = asc(mid(s, i, 1));
if ( a >= 0x30 && a <= 0x39 )
a -= 0x30; //digit
else if ( a >= 0x41 && a <= 0x46 )
a = a + 10 - 0x41; //A-F
else if ( a >= 0x61 && a <= 0x46 )
a = a + 10 - 0x61; //a-f
else
return 0; // illegal character
result = result | a;
i++;
}
return result;
}
static function LogBytes(array<byte> AValues)
{
local int i, a, count, c_len;
local string i_str;
count = AValues.Length;
c_len=len(count);
for ( i=0; i < count; ++i) {
i_str = string(i+1);
while ( len(i_str) < c_len )
i_str = "0" $ i_str;
a = AValues[i];
log("#"$i_str$"/"$count$":" @ ByteToBin(a) @ ByteToHex(a,true) @ a, 'ScrnBalance');
}
}
simulated function LogValues()
{
log("ScrnCustomProgressBytes: Value of '"$CurrentValueStr$"':", 'ScrnBalance');
LogBytes(Data);
}
static function StringToValues(string AString, out array<byte> AValues)
{
if ( default.StoreMethod == SM_Unicode)
UnicodeStringToValues(AString, AValues);
else
HexStringToValues(AString, AValues);
}
static function UnicodeStringToValues(string AString, out array<byte> AValues)
{
local int i, count, val;
local string c;
if ( !default.bFixedDataSize )
AValues.Length = len(AString);
count = min(len(AString), AValues.Length);
for ( i = 0; i < count; ++i ) {
c = mid(AString,i,1);
val = asc(c) >>> CHR_1ST_BIT;
if ( val>>>8 != CHR_CONTROL_VALUE ) {
// in case server admin manually edits stat table and screws the data
log("ScrnCustomProgressBytes: Illegal Character #"$i$" '"$c$"' ("$asc(c)$") in string '"$AString$"'", 'ScrnBalance');
}
else {
val = val & 0xFF; //clear a highest byte;
AValues[i] = val;
}
}
}
static function HexStringToValues(string AString, out array<byte> AValues)
{
local int i, count;
if ( !default.bFixedDataSize )
AValues.Length = len(AString)/2; //2 chars per one byte
count = min(len(AString)/2, AValues.Length);
for ( i = 0; i < count; ++i ) {
AValues[i] = HexToByte(mid(AString,i*2,2));
}
}
static function string ValuesToString(array<byte> AValues)
{
if ( default.StoreMethod == SM_Unicode)
return ValuesToUnicodeString(AValues);
else
return ValuesToHexString(AValues);
}
static function string ValuesToUnicodeString(array<byte> AValues)
{
local int i;
local string s;
for ( i = 0; i < AValues.length; ++i ) {
// add contro value, shift left 8 bits, fill data, shift dummy bits, which fill with random numbers
s $= chr((CHR_CONTROL_VALUE<<8 | AValues[i])<<CHR_1ST_BIT | rand(0x3F));
}
return s;
}
static function string ValuesToHexString(array<byte> AValues)
{
local int i;
local string s;
for ( i = 0; i < AValues.length; ++i ) {
s $= ByteToHex(AValues[i], false);
}
return s;
}
simulated function UpdateData()
{
StringToValues(CurrentValueStr, Data);
//LogValues();
}
//clients can't update String, they just receive it from server
function UpdateValueString()
{
CurrentValueStr = ValuesToString(Data);
ValueSource = SRC_Internal;
if ( bReplicateDataToClients )
NetUpdateTime = Level.TimeSeconds - 1; //send CurrentValueStr to clients
}
/*
protected function FillWithTestData(int ValueCount)
{
local int i;
Data.Length = ValueCount;
for ( i = 0; i < ValueCount; ++i ) {
Data[i] = i;
}
UpdateString();
}
*/
defaultproperties
{
bNetNotify=True
}