- Joined
- Jun 18, 2010
- Messages
- 86
- Reaction score
- 147
Hai.
I wish to introduce a little tweak for engine.exe that hasn't been done in the whole KalOnline scene yet. (Haven't seen any.)
Little parenthesis : in case this is the wrong section, I demand your pardon and beg of you to correct my mistake.
Now, back to the topic.
Before hurrying to the code and copy/pasting the poop in 'kthxbye' mode, you should know that I have been playing with the good ol' p-server engine.exe for quite a while now, for nearly two years.
Roaming in it with no real aim, discovering stuff, remembering those, reusing them later, creating tweaks, cheats, workarounds, et cetera.
Most of it is nothing that could be released on a server development forum, since some of them are real cheats, others are just cookie-like mods.
(cf. accelerating the in-game daytime, having 3 days pass by in 3 seconds (sweet effect, for real.))
So yeah, I've kinda been looking forward to releasing something useful to the server dev community (so far I've been doing only very bad things /evil_laugh).
Guess the time has come now ┐( ̄ー ̄)┌
A bit of history before that :
A few years ago, as I was still discovering server maps and kal files on my local server,
I noticed that the FPS counter was quite irregular (even back on my Pentium IV, 2.0GHz).
I went to the edge of the map, where no ground is drawn anymore, and looked up the sky.
2500 frames per second.
Now I don't know whether these korean people have forwarded themselves so far in the future they were already expecting Sony's new hyper-mega-epic 2500Hz display monitor, or just engineered their code like mentally disturbed camels. (Looking forward to this)
Anywho, now that I got a sweet new computer, quad core 3.0Ghz, 285gtx, quite a beast (well, at least it was a year ago. /emo)
It happens that the CPU usage engine.exe sucks is just exaggerated.
No matter what environment it runs in, engine.exe will use ALL the power it has at hand, causing overheating at laptops, overall slowdowns and, omg.. wait, I think my CPU just got baked! ( ゜Д゜ )
Now what really shocked me (YA IT HURT MY FEELINGS~!), is that after so many years, none has even thought of making this, nor has inixsoft (or have they ? don't have client, so can't know either way.)
But uhm, well, whatever.
Let's get straight to the point (finally).
In the first place, this is an add-on for C++ projects, and NOT a complete project on its own.
If you want to use it, you will have to create a proxy dll project yourself.
Now about the rights, if you use my code, I DEMAND credits.
Besides, since this is the very first and only fps limiter, I will know whenever someone uses it or not.
Beware.
About the code :
The code is commented. Not everything, but enough to get the idea, I hope.
I redirect the 2 calls to that WorldRefreshing function to mine, and hold the whole loop inside it.
This allows me to easily add/modify/remove anything, at any time.
To install the FPS Limiter, you have to call CTweaks::HookWorldRefresh().
And this HAS to be done before engine.exe code is executed. (In theory it's possible to to do before the 3d environment is loaded, but it's quite risky.)
Here it goes.
Seriously, this game doesn't need to run at any higher than 30fps.
Using this FPS limiter, I managed to drop the CPU usage by nearly 60%, whilst having the game still completely playable.
PS: If I ever find another tweak that could be useful here, I will update this thread only and ask a moderator to change its title.
Edit: Added namespace use and includes. Oh and I forgot to mention I compile every of my projects with VisualC++ 2010 Express SP1.
Edit2: Moved namespace and includes to the correct file and fixed a critical error in the code.
I wish to introduce a little tweak for engine.exe that hasn't been done in the whole KalOnline scene yet. (Haven't seen any.)
Little parenthesis : in case this is the wrong section, I demand your pardon and beg of you to correct my mistake.
Now, back to the topic.
Before hurrying to the code and copy/pasting the poop in 'kthxbye' mode, you should know that I have been playing with the good ol' p-server engine.exe for quite a while now, for nearly two years.
Roaming in it with no real aim, discovering stuff, remembering those, reusing them later, creating tweaks, cheats, workarounds, et cetera.
Most of it is nothing that could be released on a server development forum, since some of them are real cheats, others are just cookie-like mods.
(cf. accelerating the in-game daytime, having 3 days pass by in 3 seconds (sweet effect, for real.))
So yeah, I've kinda been looking forward to releasing something useful to the server dev community (so far I've been doing only very bad things /evil_laugh).
Guess the time has come now ┐( ̄ー ̄)┌
A bit of history before that :
A few years ago, as I was still discovering server maps and kal files on my local server,
I noticed that the FPS counter was quite irregular (even back on my Pentium IV, 2.0GHz).
I went to the edge of the map, where no ground is drawn anymore, and looked up the sky.
2500 frames per second.
Now I don't know whether these korean people have forwarded themselves so far in the future they were already expecting Sony's new hyper-mega-epic 2500Hz display monitor, or just engineered their code like mentally disturbed camels. (Looking forward to this)
Anywho, now that I got a sweet new computer, quad core 3.0Ghz, 285gtx, quite a beast (well, at least it was a year ago. /emo)
It happens that the CPU usage engine.exe sucks is just exaggerated.
No matter what environment it runs in, engine.exe will use ALL the power it has at hand, causing overheating at laptops, overall slowdowns and, omg.. wait, I think my CPU just got baked! ( ゜Д゜ )
Now what really shocked me (YA IT HURT MY FEELINGS~!), is that after so many years, none has even thought of making this, nor has inixsoft (or have they ? don't have client, so can't know either way.)
But uhm, well, whatever.
Let's get straight to the point (finally).
In the first place, this is an add-on for C++ projects, and NOT a complete project on its own.
If you want to use it, you will have to create a proxy dll project yourself.
Now about the rights, if you use my code, I DEMAND credits.
Besides, since this is the very first and only fps limiter, I will know whenever someone uses it or not.
Beware.
About the code :
The code is commented. Not everything, but enough to get the idea, I hope.
I redirect the 2 calls to that WorldRefreshing function to mine, and hold the whole loop inside it.
This allows me to easily add/modify/remove anything, at any time.
To install the FPS Limiter, you have to call CTweaks::HookWorldRefresh().
And this HAS to be done before engine.exe code is executed. (In theory it's possible to to do before the 3d environment is loaded, but it's quite risky.)
Here it goes.
PHP:
/* Tweaks.h */
#include <iostream>
#include <Windows.h>
using namespace std;
class CTweaks
{
public:
CTweaks(); // Constructor
void HookWorldRefresh(); // World refreshing function hooker
private:
// This function has to be static, otherwise it wouldn't be possible to get a pointer to it.
static WPARAM __thiscall WorldRefresh(void*); // Engine World refreshing thread
typedef int (__cdecl *WREngineFunc)(void); // Unknown WorldRefresh function's prototype
typedef int (__thiscall *RefreshEntities)(void*); // Entity worldrefreshing function's prototype
DWORD WREngineFuncPtr; // Pointer to unknown worldrefreshing function
DWORD RefreshEntitiesPtr; // Pointer to entity worldrefreshing function
BYTE *WREngineActive; // Pointer to possible 'this' instance
int MaxFPS; // Maximum frames per second
int MaxTime; // Maximum milliseconds accepted to engage frame blocker
DWORD SleepTime; // Time to block the frame each round
};
extern CTweaks *Tweaks;
PHP:
/* Tweaks.cpp */
#include "Tweaks.h"
// Only one single instance of this class is needed
// So there is no need to worry about where nor how we initialize it.
CTweaks *Tweaks = new CTweaks;
CTweaks::CTweaks()
{
// Engine addresses pointers
this->WREngineFuncPtr = 0x004a73b0;
this->RefreshEntitiesPtr = 0x00425960;
this->WREngineActive = (BYTE*)0x006ddf58;
}
void CTweaks::HookWorldRefresh()
{
this->MaxFPS = 30;
// Right here it is possible to load the max fps from a configuration file, or what so ever.
// Example : this->MaxFPS = Config->Engine.FPSLimit;
// Two calls to our WorldRefresh function, only one of them is used, but the second one is just in case.
this->MaxTime = (1000/this->MaxFPS);
Core->Intercept(INST_CALL,0x00505869,(DWORD)this->WorldRefresh,5);
Core->Intercept(INST_CALL,0x00505bfc,(DWORD)this->WorldRefresh,5);
#ifndef SILENT_MODE
cout << "FPS Limiter installed." << endl;
#endif
}
WPARAM __thiscall CTweaks::WorldRefresh(void *thisPointer)
{
// Since this function is running as a thread, InixSoft had to declare it as static (just as done here)
// Therefore, since the 'this' instance can only be used with non-static member functions,
// The calling convention is not __thiscall anymore but __fastcall, and the 'this' pointer is passed as argument.
// EDIT: After some tests, I figured __thiscall is sometimes required, so try it like this.
// If it doesn't work out, change it back to __fastcall
// Default function vars
void *that;
char state;
struct tagMSG Msg;
HACCEL hAccel;
// Our variables
// We will be using high resolution timers
// <3 precision
int PerformanceTime;
unsigned __int64 lpFrequency;
unsigned __int64 lpPerformanceCount;
unsigned __int64 lpPerformanceStep;
// This > That. Ok ?
that = thisPointer;
// Checking for the CPU's frequency, unit is in Herz.
QueryPerformanceFrequency((LARGE_INTEGER*)&lpFrequency);
hAccel = LoadAcceleratorsA(NULL, (LPCSTR)0x71);
Msg.message = 0;
PeekMessageA(&Msg, NULL, WM_NULL, WM_NULL, PM_NOREMOVE);
while (Msg.message != 0x12)
{
// EXTREMELY important!!
// Since we call functions that weren't compiled with the same compiler (type, version, et cetera) as we use
// There are high chances that those functions modify, either the stack or the registers in a way OUR compiler would never expect.
// Such corruption is especially noticeable in loops, causing crashes in most cases.
// This is why it is critical to save the stack before and after those functions are executed.
__asm pushad
if (*((BYTE*)that+0x9d)) state = (!!PeekMessageA(&Msg, NULL, WM_NULL, WM_NULL, PM_REMOVE));
else state = (!!GetMessageA(&Msg, NULL, 0, 0));
if (state)
{
if ((!hAccel) || (!(*(DWORD*)that+0x38)) || (!TranslateAcceleratorA(*((HWND*)that+0x38), hAccel, &Msg)))
{
TranslateMessage(&Msg);
DispatchMessageA(&Msg);
}
// Not quite sure about what this function really does
// I know it's executed when the mouse is moved.
((WREngineFunc)Tweaks->WREngineFuncPtr)();
}
else
{
if ((*((BYTE*)that+0x9d)) && (*(BYTE*)Tweaks->WREngineActive))
{
// Querying the performance count before and after world refreshing function
QueryPerformanceCounter((LARGE_INTEGER*)&lpPerformanceCount);
// This function refreshes the whole kal world, including object movements in time, daytime, et cetera, well, pretty much everything.
// Could also be called rendering function.
int tmpActive = ((RefreshEntities)Tweaks->RefreshEntitiesPtr)(that);
QueryPerformanceCounter((LARGE_INTEGER*)&lpPerformanceStep);
if (tmpActive < 0) SendMessageA(*((HWND*)that+0x38), WM_COMMAND, 0x9C46, 0);
// From here on, we will calculate the difference between the two counters
// And base the time we'll need to block the frames from being displayed for on that amount.
PerformanceTime = (int)(lpFrequency / (lpPerformanceStep-lpPerformanceCount));
if (PerformanceTime) PerformanceTime = 1000 / PerformanceTime;
if (Tweaks->MaxTime > PerformanceTime)
{
// This var could be used as local variable, but let's rather keep it global.
Tweaks->SleepTime = (Tweaks->MaxTime - PerformanceTime);
Sleep(Tweaks->SleepTime);
}
}
}
// Restoring the stack.
__asm popad
}
if (hAccel) DestroyAcceleratorTable(hAccel);
return Msg.wParam;
}
Seriously, this game doesn't need to run at any higher than 30fps.
Using this FPS limiter, I managed to drop the CPU usage by nearly 60%, whilst having the game still completely playable.
PS: If I ever find another tweak that could be useful here, I will update this thread only and ask a moderator to change its title.
Edit: Added namespace use and includes. Oh and I forgot to mention I compile every of my projects with VisualC++ 2010 Express SP1.
Edit2: Moved namespace and includes to the correct file and fixed a critical error in the code.
Last edited: