From 97294a4117d32f7c256ed0e4721c0ca8fb40e2aa Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Tue, 19 Sep 2023 22:31:08 +0300 Subject: [PATCH] speed up item synchronization and add trader items limit --- CTI/Classes/CTI.uc | 38 ++-- CTI/Classes/CTI_LocalMessage.uc | 10 +- CTI/Classes/CTI_RepInfo.uc | 297 +++++++++++++++++--------------- CTI/Classes/Trader.uc | 51 +++++- Localization/INT/CTI.int | Bin 426 -> 476 bytes Localization/RUS/CTI.rus | Bin 438 -> 484 bytes 6 files changed, 227 insertions(+), 169 deletions(-) diff --git a/CTI/Classes/CTI.uc b/CTI/Classes/CTI.uc index 12de0cf..f5f0a92 100644 --- a/CTI/Classes/CTI.uc +++ b/CTI/Classes/CTI.uc @@ -1,7 +1,7 @@ class CTI extends Info config(CTI); -const LatestVersion = 3; +const LatestVersion = 4; const CfgRemoveItems = class'RemoveItems'; const CfgAddItems = class'AddItems'; @@ -22,10 +22,12 @@ var private config E_LogLevel LogLevel; var private config String UnlockDLC; var private config bool bPreloadContent; var private config bool bOfficialWeaponsList; +var private config bool bDisableItemLimitCheck; -var private KFGameInfo KFGI; +var private KFGameInfo KFGI; var private KFGameReplicationInfo KFGRI; +var private Array > WeapDefs; var private Array > RemoveItems; var private Array > AddItems; @@ -33,8 +35,6 @@ var private Array RepInfos; var private bool ReadyToSync; -var private Array PreloadContent; - public simulated function bool SafeDestroy() { `Log_Trace(); @@ -95,6 +95,8 @@ private function PreInit() bOfficialWeaponsList = false; case 2: + case 3: + bDisableItemLimitCheck = false; case MaxInt: `Log_Info("Config updated to version" @ LatestVersion); @@ -129,7 +131,7 @@ private function PreInit() if (!Unlocker.static.IsValidTypeUnlockDLC(UnlockDLC, LogLevel)) { - `Log_Warn("Wrong 'UnlockDLC' (" $ UnlockDLC $ "), return to default value (False)"); + `Log_Warn("Wrong 'UnlockDLC' value (" $ UnlockDLC $ "), return to default value (False)"); UnlockDLC = "False"; SaveConfig(); } @@ -189,34 +191,35 @@ private function PostInit() CfgRemoveItems.default.bAll, CfgRemoveItems.default.bHRG, CfgRemoveItems.default.bDLC, + bDisableItemLimitCheck, LogLevel); + WeapDefs = Trader.static.GetTraderWeapDefs(KFGRI, LogLevel); + ReadyToSync = true; foreach RepInfos(RepInfo) { if (RepInfo.PendingSync) { - RepInfo.ServerSync(); + RepInfo.Replicate(WeapDefs); } } } -private function Preload(Array > Content) +private function Preload(const out Array > Content) { - local Array > OfficialWeapons; + local Array PreloadContent; local S_PreloadContent SPC; `Log_Trace(); - OfficialWeapons = Trader.static.GetTraderWeapons(); - foreach Content(SPC.KFWD) { SPC.KFWC = class (DynamicLoadObject(SPC.KFWD.default.WeaponClassPath, class'Class')); if (SPC.KFWC != None) { - if (OfficialWeapons.Find(SPC.KFWC) != INDEX_NONE) + if (SPC.KFWC.GetPackageName() == 'CTI' || SPC.KFWC.GetPackageName() == 'KFGameContent') { `Log_Debug("Skip preload:" @ SPC.KFWD.GetPackageName() $ "." $ SPC.KFWD); continue; @@ -271,26 +274,19 @@ public function bool CreateRepInfo(Controller C) `Log_Trace(); - if (C == None) return false; + if (C == None || KFPlayerController(C) == None) return false; RepInfo = Spawn(class'CTI_RepInfo', C); if (RepInfo == None) return false; - RepInfo.PrepareSync( - Self, - LogLevel, - RemoveItems, - AddItems, - CfgRemoveItems.default.bAll, - CfgRemoveItems.default.bHRG, - CfgRemoveItems.default.bDLC); + RepInfo.PrepareSync(Self, KFPlayerController(C), LogLevel); RepInfos.AddItem(RepInfo); if (ReadyToSync) { - RepInfo.ServerSync(); + RepInfo.Replicate(WeapDefs); } else { diff --git a/CTI/Classes/CTI_LocalMessage.uc b/CTI/Classes/CTI_LocalMessage.uc index d01eb08..432bb56 100644 --- a/CTI/Classes/CTI_LocalMessage.uc +++ b/CTI/Classes/CTI_LocalMessage.uc @@ -16,13 +16,17 @@ var private localized String IncompatibleGRIWarning; var const String SecondsShortDefault; var private localized String SecondsShort; +var const String PleaseWaitDefault; +var private localized String PleaseWait; + enum E_CTI_LocalMessageType { CTI_SyncItems, CTI_WaitingGRI, CTI_IncompatibleGRI, CTI_IncompatibleGRIWarning, - CTI_SecondsShort + CTI_SecondsShort, + CTI_PleaseWait }; public static function String GetLocalizedString( @@ -50,6 +54,9 @@ public static function String GetLocalizedString( case CTI_SecondsShort: return (default.SecondsShort != "" ? default.SecondsShort : default.SecondsShortDefault); + + case CTI_PleaseWait: + return (default.PleaseWait != "" ? default.PleaseWait : default.PleaseWaitDefault); } return ""; @@ -62,4 +69,5 @@ defaultproperties IncompatibleGRIDefault = "Incompatible GRI:" IncompatibleGRIWarningDefault = "You can enter the game, but the trader may not work correctly."; SecondsShortDefault = "s" + PleaseWaitDefault = "Please wait" } \ No newline at end of file diff --git a/CTI/Classes/CTI_RepInfo.uc b/CTI/Classes/CTI_RepInfo.uc index 9ac3958..1fc18d0 100644 --- a/CTI/Classes/CTI_RepInfo.uc +++ b/CTI/Classes/CTI_RepInfo.uc @@ -1,21 +1,23 @@ class CTI_RepInfo extends ReplicationInfo; +const CAPACITY = 64; // max: 128 + const Trader = class'Trader'; const LocalMessage = class'CTI_LocalMessage'; +struct ReplicationStruct +{ + var int Size; + var int Transfered; + + var class Items[CAPACITY]; + var int Length; +}; + var public bool PendingSync; var private CTI CTI; var private E_LogLevel LogLevel; -var private Array > RemoveItems; -var private Array > AddItems; -var private bool ReplaceMode; -var private bool RemoveHRG; -var private bool RemoveDLC; -var private bool PreloadContent; - -var private int Recieved; -var private int SyncSize; var private KFPlayerController KFPC; var private KFGFxWidget_PartyInGame PartyInGameWidget; @@ -29,10 +31,13 @@ var private int NotificationPercent; var private int WaitingGRI; var private int WaitingGRILimit; +var private ReplicationStruct RepData; +var private Array > RepArray; + replication { if (bNetInitial && Role == ROLE_Authority) - LogLevel, ReplaceMode, RemoveHRG, RemoveDLC, SyncSize; + LogLevel; } public simulated function bool SafeDestroy() @@ -42,25 +47,116 @@ public simulated function bool SafeDestroy() return (bPendingDelete || bDeleteMe || Destroy()); } -public function PrepareSync( - CTI _CTI, - E_LogLevel _LogLevel, - Array > _RemoveItems, - Array > _AddItems, - bool _ReplaceMode, - bool _RemoveHRG, - bool _RemoveDLC) +public function Replicate(const out Array > WeapDefs) { `Log_Trace(); - CTI = _CTI; - LogLevel = _LogLevel; - RemoveItems = _RemoveItems; - AddItems = _AddItems; - ReplaceMode = _ReplaceMode; - RemoveHRG = _RemoveHRG; - RemoveDLC = _RemoveDLC; - SyncSize = RemoveItems.Length + AddItems.Length; + RepArray = WeapDefs; + RepData.Size = RepArray.Length; + + if (WorldInfo.NetMode == NM_StandAlone) + { + Progress(RepArray.Length, RepArray.Length); + return; + } + + Sync(); +} + +private reliable server function Sync() +{ + local int LocalIndex; + local int GlobalIndex; + + `Log_Trace(); + + LocalIndex = 0; + GlobalIndex = RepData.Transfered; + + while (LocalIndex < CAPACITY && GlobalIndex < RepData.Size) + { + RepData.Items[LocalIndex++] = RepArray[GlobalIndex++]; + } + + if (RepData.Transfered == GlobalIndex) return; // Finished + + RepData.Transfered = GlobalIndex; + RepData.Length = LocalIndex; + + Send(RepData); + + Progress(RepData.Transfered, RepData.Size); +} + +private reliable client function Send(ReplicationStruct RD) +{ + local int LocalIndex; + + `Log_Trace(); + + for (LocalIndex = 0; LocalIndex < RD.Length; LocalIndex++) + { + RepArray.AddItem(RD.Items[LocalIndex]); + } + + Progress(RD.Transfered, RD.Size); + + Sync(); +} + +public function PrepareSync(CTI _CTI, KFPlayerController _KFPC, E_LogLevel _LogLevel) +{ + `Log_Trace(); + + CTI = _CTI; + KFPC = _KFPC; + LogLevel = _LogLevel; +} + +private simulated function Progress(int Value, int Size) +{ + `Log_Trace(); + + `Log_Debug("Replicated:" @ Value @ "/" @ Size); + + if (ROLE < ROLE_Authority) + { + NotifyProgress(Value, Size); + if (Value >= Size) Finished(); + } +} + +private simulated function Finished() +{ + local KFGameReplicationInfo KFGRI; + + `Log_Trace(); + + if (WorldInfo.GRI == None && WaitingGRI++ < WaitingGRILimit) + { + `Log_Debug("Finished: Waiting GRI" @ WaitingGRI); + NotifyWaitingGRI(); + SetTimer(1.0f, false, nameof(Finished)); + return; + } + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + if (KFGRI != None) + { + `Log_Debug("Finished: Trader.static.OverwriteTraderItems"); + Trader.static.OverwriteTraderItems(KFGRI, RepArray, LogLevel); + `Log_Info("Trader items successfully synchronized!"); + } + else + { + `Log_Error("Incompatible Replication info:" @ String(WorldInfo.GRI)); + NotifyIncompatibleGRI(); + } + + ShowReadyButton(); + + Cleanup(); + SafeDestroy(); } private simulated function KFPlayerController GetKFPC() @@ -151,6 +247,8 @@ private simulated function ShowReadyButton() { `Log_Trace(); + ClearTimer(nameof(KeepNotification)); + if (CheckPartyInGameWidget()) { Notification.SetVisible(false); @@ -176,48 +274,6 @@ private simulated function UpdateNotification(String Title, String Left, String } } -private reliable client function ClientSync(class WeapDef, optional bool Remove = false) -{ - `Log_Trace(); - - if (WeapDef == None) - { - `Log_Fatal("WeapDef is:" @ WeapDef); - Cleanup(); - ConsoleCommand("Disconnect"); - SafeDestroy(); - return; - } - - if (!IsTimerActive(nameof(KeepNotification))) - { - SetTimer(0.1f, true, nameof(KeepNotification)); - } - - if (Remove) - { - RemoveItems.AddItem(WeapDef); - } - else - { - AddItems.AddItem(WeapDef); - } - - Recieved = RemoveItems.Length + AddItems.Length; - - NotificationHeaderText = (Remove ? "-" : "+") @ WeapDef.static.GetItemName(); - NotificationLeftText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_SyncItems); - NotificationRightText = Recieved @ "/" @ SyncSize; - if (SyncSize != 0) - { - NotificationPercent = (float(Recieved) / float(SyncSize)) * 100; - } - - `Log_Debug("ClientSync:" @ (Remove ? "-" : "+") @ String(WeapDef) @ NotificationRightText); - - ServerSync(); -} - private simulated function KeepNotification() { HideReadyButton(); @@ -228,91 +284,55 @@ private simulated function KeepNotification() NotificationPercent); } -private simulated reliable client function ClientSyncFinished() -{ - local KFGameReplicationInfo KFGRI; - - `Log_Trace(); - - if (WorldInfo.GRI == None && WaitingGRI++ < WaitingGRILimit) - { - `Log_Debug("ClientSyncFinished: Waiting GRI" @ WaitingGRI); - NotificationHeaderText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_WaitingGRI); - NotificationLeftText = String(WaitingGRI) $ LocalMessage.static.GetLocalizedString(LogLevel, CTI_SecondsShort); - NotificationRightText = ""; - NotificationPercent = 0; - SetTimer(1.0f, false, nameof(ClientSyncFinished)); - return; - } - - NotificationHeaderText = ""; - NotificationLeftText = ""; - NotificationRightText = ""; - NotificationPercent = 0; - - KFGRI = KFGameReplicationInfo(WorldInfo.GRI); - if (KFGRI != None) - { - `Log_Debug("ClientSyncFinished: Trader.static.ModifyTrader"); - Trader.static.ModifyTrader(KFGRI, RemoveItems, AddItems, ReplaceMode, RemoveHRG, RemoveDLC, LogLevel); - } - else - { - `Log_Error("Incompatible Replication info:" @ String(WorldInfo.GRI)); - WriteToChatLocalized( - CTI_IncompatibleGRI, - class'KFLocalMessage'.default.InteractionColor, - WorldInfo.GRI == None ? "None" : String(WorldInfo.GRI.class)); - WriteToChatLocalized( - CTI_IncompatibleGRIWarning, - class'KFLocalMessage'.default.InteractionColor); - } - - ClearTimer(nameof(KeepNotification)); - ShowReadyButton(); - - Cleanup(); - SafeDestroy(); -} - private reliable server function Cleanup() { `Log_Trace(); `Log_Debug("Cleanup"); - if (!CTI.DestroyRepInfo(Controller(Owner))) + if (!CTI.DestroyRepInfo(GetKFPC())) { `Log_Debug("Cleanup (forced)"); SafeDestroy(); } } -public reliable server function ServerSync() +private simulated function NotifyWaitingGRI() { - `Log_Trace(); - - PendingSync = false; - - if (bPendingDelete || bDeleteMe) return; - - if (SyncSize <= Recieved || WorldInfo.NetMode == NM_StandAlone) + if (!IsTimerActive(nameof(KeepNotification))) { - `Log_Debug("ServerSync: Finished"); - ClientSyncFinished(); + SetTimer(0.1f, true, nameof(KeepNotification)); } - else + + NotificationHeaderText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_WaitingGRI); + NotificationLeftText = String(WaitingGRI) $ LocalMessage.static.GetLocalizedString(LogLevel, CTI_SecondsShort); + NotificationRightText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_PleaseWait); + NotificationPercent = 0; + KeepNotification(); +} + +private simulated function NotifyProgress(int Value, int Size) +{ + if (!IsTimerActive(nameof(KeepNotification))) { - if (Recieved < RemoveItems.Length) - { - `Log_Debug("ServerSync[-]:" @ (Recieved + 1) @ "/" @ SyncSize @ RemoveItems[Recieved]); - ClientSync(RemoveItems[Recieved++], true); - } - else - { - `Log_Debug("ServerSync[+]:" @ (Recieved + 1) @ "/" @ SyncSize @ AddItems[Recieved - RemoveItems.Length]); - ClientSync(AddItems[Recieved++ - RemoveItems.Length], false); - } + SetTimer(0.1f, true, nameof(KeepNotification)); } + + NotificationHeaderText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_SyncItems); + NotificationLeftText = Value @ "/" @ Size; + NotificationRightText = LocalMessage.static.GetLocalizedString(LogLevel, CTI_PleaseWait); + NotificationPercent = (float(Value) / float(Size)) * 100; + KeepNotification(); +} + +private simulated function NotifyIncompatibleGRI() +{ + WriteToChatLocalized( + CTI_IncompatibleGRI, + class'KFLocalMessage'.default.InteractionColor, + String(WorldInfo.GRI.class)); + WriteToChatLocalized( + CTI_IncompatibleGRIWarning, + class'KFLocalMessage'.default.InteractionColor); } defaultproperties @@ -322,9 +342,8 @@ defaultproperties bSkipActorPropertyReplication = false PendingSync = false - Recieved = 0 NotificationPercent = 0 WaitingGRI = 0 - WaitingGRILimit = 15 + WaitingGRILimit = 30 } diff --git a/CTI/Classes/Trader.uc b/CTI/Classes/Trader.uc index 2d938a3..3703b52 100644 --- a/CTI/Classes/Trader.uc +++ b/CTI/Classes/Trader.uc @@ -1,6 +1,11 @@ class Trader extends Object abstract; +// Bug: +// The wrong weapon is purchased if the index is greater than 256 😡 +// Some greedy guy saved 3 bytes for no reason again +const ITEMS_LIMIT = 256; + private delegate int ByPrice(class A, class B) { return A.default.BuyPrice > B.default.BuyPrice ? -1 : 0; @@ -22,7 +27,7 @@ public static function KFGFxObject_TraderItems GetTraderItems(optional KFGameRep return KFGFxObject_TraderItems(DynamicLoadObject(TraderItemsPath, class'KFGFxObject_TraderItems')); } -public static function Array > GetTraderWeapDefs(optional KFGameReplicationInfo KFGRI = None,optional E_LogLevel LogLevel = LL_Trace) +public static function Array > GetTraderWeapDefs(optional KFGameReplicationInfo KFGRI = None, optional E_LogLevel LogLevel = LL_Trace) { local Array > KFWeapDefs; local KFGFxObject_TraderItems TraderItems; @@ -41,7 +46,7 @@ public static function Array > GetTraderWeapDefs(optio return KFWeapDefs; } -public static function Array > GetTraderWeapons(optional KFGameReplicationInfo KFGRI = None,optional E_LogLevel LogLevel = LL_Trace) +public static function Array > GetTraderWeapons(optional KFGameReplicationInfo KFGRI = None, optional E_LogLevel LogLevel = LL_Trace) { local Array > KFWeapons; local class KFWeapon; @@ -88,19 +93,18 @@ public static function Array > GetTraderWeapDefsDLC(KF public static simulated function ModifyTrader( KFGameReplicationInfo KFGRI, - Array > RemoveItems, - Array > AddItems, + const out Array > RemoveItems, + const out Array > AddItems, bool ReplaceMode, bool RemoveHRG, bool RemoveDLC, + bool bDisableItemLimitCheck, E_LogLevel LogLevel) { local KFGFxObject_TraderItems TraderItems; local STraderItem Item; - local class WeapDef; local Array > WeapDefs; local int Index; - local int MaxItemID; `Log_TraceStatic(); @@ -128,13 +132,44 @@ public static simulated function ModifyTrader( WeapDefs.Sort(ByPrice); + if (!bDisableItemLimitCheck && WeapDefs.Length > ITEMS_LIMIT) + { + `Log_Warn("The total number of items has reached the limit (" $ ITEMS_LIMIT $ ")," @ (WeapDefs.Length - ITEMS_LIMIT) @ "items will not be added."); + `Log_Warn("Excluded items:"); + for (Index = ITEMS_LIMIT; Index < WeapDefs.Length; ++Index) + { + `Log_Warn("[" $ Index + 1 $ "]" @ String(WeapDefs[Index])); + } + WeapDefs.Length = ITEMS_LIMIT; + } + + OverwriteTraderItems(KFGRI, WeapDefs, LogLevel); +} + +public static simulated function OverwriteTraderItems( + KFGameReplicationInfo KFGRI, + const out Array > WeapDefs, + E_LogLevel LogLevel) +{ + local KFGFxObject_TraderItems TraderItems; + local STraderItem Item; + local class WeapDef; + local int MaxItemID; + + `Log_TraceStatic(); + + TraderItems = GetTraderItems(KFGRI, LogLevel); + TraderItems.SaleItems.Length = 0; MaxItemID = 0; + + `Log_Debug("Trader Items:"); foreach WeapDefs(WeapDef) { Item.WeaponDef = WeapDef; - Item.ItemID = ++MaxItemID; + Item.ItemID = MaxItemID++; TraderItems.SaleItems.AddItem(Item); + `Log_Debug("[" $ MaxItemID $ "]" @ String(WeapDef)); } TraderItems.SetItemsInfo(TraderItems.SaleItems); @@ -142,7 +177,7 @@ public static simulated function ModifyTrader( KFGRI.TraderItems = TraderItems; } -private static function bool WeaponClassIsUnique(String WeaponClassPath, Array > WeapDefs, E_LogLevel LogLevel) +private static function bool WeaponClassIsUnique(String WeaponClassPath, const out Array > WeapDefs, E_LogLevel LogLevel) { local class WeapDef; diff --git a/Localization/INT/CTI.int b/Localization/INT/CTI.int index 1d0b6ecf8f57ed1cd6c69b7fc385ceb079af63d4..cf1ab503318147c8d119cd7a2a4fbb86f89c5530 100644 GIT binary patch delta 58 vcmZ3*e200%Dn_FKh8%`ehD3&9AQ=v%GZ{)4Y#Edoki``k%Aqn!3|tHVn2`&^ delta 7 Ocmcb^yo!0lDno;e E09s=SyZ`_I delta 7 OcmaFDyp4IoHbwvqYy$`Y