Fixed modified GET, SET commands from being able to read/write right after the end of a static array.
Exposed property MDeltaTimer[10] (editconst, transient, float) so it gets serialized on garbage collection.
The above change fixes Timing Fix causing the game to slow down during the first second after map load.
Corrected the entire boolean properties' bitmasks, this fixes ToggleTimingFix command.
Added 'ActorSlots' command that logs which slots are empty or taken by deleted actors.
Added 'PackageMap' command that prints existing UPackageMap(s) info to the log.
The whole boolean properties issue was a big one.
A lot of undocumented stuff were causing my config Booleans to not have a [1-0] behaviour, but 2048-0, 1024-0, etc.
This caused inconsistencies between UnrealScript and Native code disagreeing when a property was false or true, causing most of the save/load problems with the ToggleTimingFix command.
Let's analyse the D3D7 sources:
Variable definitions of the renderer object:
Code: Select all
BITFIELD UseMipmapping;
BITFIELD UseTrilinear;
BITFIELD UseMultitexture;
BITFIELD UsePalettes;
BITFIELD UseGammaCorrection;
BITFIELD Use3dfx;
BITFIELD UseD3DSoftwareRenderer;
BITFIELD UseTripleBuffering;
BITFIELD UseVSync;
BITFIELD UseVertexSpecular;
BITFIELD UseAlphaPalettes;
BITFIELD UsePrecache;
BITFIELD UseVideoMemoryVB;
BITFIELD UseAGPTextures;
BITFIELD UseVertexFog;
BITFIELD UseNonlocalTextures;
BITFIELD Use32BitZBuffer;
StaticConstructor, where the UProperty objects are defined to expose properties to UnrealScript interface.
Code: Select all
void StaticConstructor()
{
guard(UD3DRenderDevice::StaticConstructor);
new(GetClass(),TEXT("UseMipmapping"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseMipmapping ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseTrilinear"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseTrilinear ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseMultitexture"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseMultitexture ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UsePalettes"), RF_Public)UBoolProperty ( CPP_PROPERTY(UsePalettes ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseGammaCorrection"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseGammaCorrection ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("Use3dfx"), RF_Public)UBoolProperty ( CPP_PROPERTY(Use3dfx ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseTripleBuffering"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseTripleBuffering ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseVSync"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseVSync ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UsePrecache"), RF_Public)UBoolProperty ( CPP_PROPERTY(UsePrecache ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseVideoMemoryVB"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseVideoMemoryVB ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseAGPTextures"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseAGPTextures ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("UseVertexFog"), RF_Public)UBoolProperty ( CPP_PROPERTY(UseVertexFog ), TEXT("Options"), CPF_Config );
new(GetClass(),TEXT("Use32BitZBuffer"), RF_Public)UBoolProperty ( CPP_PROPERTY(Use32BitZBuffer ), TEXT("Options"), CPF_Config );
...
This system is WRONG.
As you can see, the BITFIELD properties aren't packed into a single 4 byte block, when a class's properties are relinked post-StaticConstructor() said properties may suffer changes as it happens with UBoolProperty:
When relinking c++ created UBoolProperty objects, it starts with the oldest created property and goes backwards,
it then assigns an incremental bitmask to any consequent UBoolProperty properties.
In this case:
Changing Use32BitZBuffer sets the value between 0,1
Changing UseVertexFog does so between 0,2
Changing UseAGPTextures does so between 0,4
... and so on, because all of the properties were defined together when pointing to non-packed BITFIELD c++ properties.
Forcibly changing UseAGPTextures to 1 via memory would make the Properties dialog display it as FALSE, but the renderer would still operate as if it was TRUE.
It's kind of annoying if you're actually enabling/disabling these settings in native code as it will certainly differ with the UScript behaviour, causing SET,GET,SAVE inconsistancies.
Also, since it's being relinked backwards, you NEED to invert either the UBoolProperty creation statements, or the c++ property order!!!
This is how I've done it:
Most of automatically generated classes headers include this, in this case, UnXC_Game.h
Code: Select all
#if _MSC_VER
#pragma pack (push,4)
#endif
So when we define our c++ class we pack the BITFIELD properties
I used two 4 byte blocks to separate booleans here, one for status vars and another for config vars.
As you can see
Code: Select all
class XC_ENGINE_API UXC_GameEngine : public UGameEngine
{
public:
INT DummyData_0; //Safety for 451 games
UXC_TravelManager* TravelManager; //Travel manager subsystem
ULevel* GLevel2;
INT TmpSize; //Init hacking
INT LastMapIdx;
FString MapCachedPrefix;
TArray<FString> MapCache;
FXC_TimeManager XC_TimeManager;
//Config
TArray<FString> NoBrushTrackerFix;
BITFIELD bDisableTimingFix:1 GCC_PACK(4);
BITFIELD bDisableBrushTracker:1;
BITFIELD bSortMaplistByFolder:1;
BITFIELD bSortMaplistGlobal:1;
BITFIELD bAutoTravelManager:1;
BITFIELD bCacheConvertAtJoin:1;
INT DummyData_1 GCC_PACK(4);
//Hook data
TArray<AActor*> PreLoginHooks; //Actor pointers
Native ExecDoNothing; //Taken from native 3550
Native OldGetMapName; //Keeping this here...
TArray<BYTE> OldGetWeapon;
TArray<BYTE> OldPreLogin;
TArray<BYTE> OldSetEnemy;
TArray<BYTE> OldRocketTick;
//Hook status
BITFIELD bHackingInit:1 GCC_PACK(4);
BITFIELD bHackingTracker:1;
BITFIELD b451Hack:1;
BITFIELD bHackedEngine:1; //Main switch for general engine hacks (GetWeapon, PreLogin)
BITFIELD bSetEnemy:1;
BITFIELD bRocketTick:1;
INT DummyData_2 GCC_PACK(4); //Safety for 451 games
Now we go to a part of UXC_GameEngine::StaticConstructor()...
Check out how it's done:
Instead of using one of the Bitfield's locations, I used DummyData vars minus 4 (size of INT) for easier visibility.
Also, noticed the flipped order and the extra UObjectProperty created in the middle to break the link between both UBoolProperty blocks, resetting the bitmask back to 1.
Code: Select all
new( TheClass,TEXT("bCacheConvertAtJoin"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
new( TheClass,TEXT("bAutoTravelManager"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
new( TheClass,TEXT("bSortMaplistGlobal"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
new( TheClass,TEXT("bSortMaplistByFolder"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
new( TheClass,TEXT("bDisableBrushTracker"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
new( TheClass,TEXT("bDisableTimingFix"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_1)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_Config );
//UBool properties on different bitfields must not be defined together, or else everything's gon' get fucked up (bitmaks become consequent when they shouldn't)
new( TheClass, TEXT("TravelManager"), RF_Public) UObjectProperty( CPP_PROPERTY(TravelManager), TEXT("XC_GameEngine"), CPF_Edit|CPF_EditConst , UXC_TravelManager::StaticClass() );
new( TheClass,TEXT("bRocketTick"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
new( TheClass,TEXT("bSetEnemy"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
new( TheClass,TEXT("bHackedEngine"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
new( TheClass,TEXT("b451Hack"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
new( TheClass,TEXT("bHackingTracker"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
new( TheClass,TEXT("bHackingInit"), RF_Public) UBoolProperty( CPP_PROPERTY(DummyData_2)-4, TEXT("XC_GameEngine"), CPF_Native|CPF_Edit|CPF_EditConst );
PD:
If you want to expose arrays, you have to do it like this (static and dynamic arrays):
Code: Select all
UArrayProperty *MapCacheArray = new( TheClass,TEXT("MapCache"), RF_Public) UArrayProperty ( CPP_PROPERTY(MapCache), TEXT("XC_GameEngine"), CPF_Native|CPF_Transient|CPF_Edit|CPF_EditConst );
MapCacheArray->Inner = new (MapCacheArray, TEXT("StringProperty0"), RF_Public) UStrProperty;
MapCacheArray->Inner->PropertyFlags = CPF_Native|CPF_Edit|CPF_EditConst;
MapCacheArray->Inner->ElementSize = 12; //SIZE OF TARRAY!!!
UIntProperty *MSecArray = new( TheClass, TEXT("MSecTimer"), RF_Public) UIntProperty( CPP_PROPERTY(XC_TimeManager), TEXT("XC_GameEngine"), CPF_Native|CPF_Transient|CPF_Edit|CPF_EditConst );
MSecArray->ArrayDim = 10;
MSecArray->ElementSize = 4; //SIZE OF INT!!!